diff --git a/docs/.vuepress/utils/v3Comparison.json b/docs/.vuepress/utils/v3Comparison.json index 4d5edc3ce0..2ddde468ce 100644 --- a/docs/.vuepress/utils/v3Comparison.json +++ b/docs/.vuepress/utils/v3Comparison.json @@ -79,7 +79,6 @@ "chatExtras", "enhancedPOI", "generalExtensions", - "lssmaql", "messageTemplates", "schoolingOverview" ] diff --git a/src/components/LoadingIndicator.vue b/src/components/LoadingIndicator.vue index 1fe3371bac..db97269aaa 100644 --- a/src/components/LoadingIndicator.vue +++ b/src/components/LoadingIndicator.vue @@ -85,10 +85,10 @@ onMounted(() => { // if there are no file-sizes cached, fetch them if (Object.values(fileSizes.value).length === 0) { useAPIStore() - .request({ - url: rootStore.lssmUrl('/static/fileSizes.json', true), - feature: 'loading-indicator', - }) + .request( + rootStore.lssmUrl('/static/fileSizes.json', true), + 'loading-indicator' + ) .then(res => res.json()) .then(sizes => { fileSizes.value = sizes; diff --git a/src/components/settings.vue b/src/components/settings.vue index b32118c760..db956a6c71 100644 --- a/src/components/settings.vue +++ b/src/components/settings.vue @@ -848,10 +848,7 @@ export default Vue.extend< (window[PREFIX] as Vue).$settings = this; useAPIStore() - .request({ - url: this.rootStore.lssmUrl('/branches.json'), - feature: 'settings', - }) + .request(this.rootStore.lssmUrl('/branches.json'), 'settings') .then(res => res.json() as Promise) .then(branches => (this.branches = branches)); }, diff --git a/src/core.ts b/src/core.ts index ded98d8a01..7a7c5adf88 100644 --- a/src/core.ts +++ b/src/core.ts @@ -120,8 +120,6 @@ utils(Vue); const locale = LSSM.$stores.root.locale; - LSSM.$stores.api._initAPIsFromBroadcast().then(); - import('./natives/checkboxMultiSelect').then(({ default: multiSelect }) => multiSelect(LSSM) ); diff --git a/src/importableScripts/LSSMStorage.ts b/src/importableScripts/LSSMStorage.ts new file mode 100644 index 0000000000..a48444530c --- /dev/null +++ b/src/importableScripts/LSSMStorage.ts @@ -0,0 +1,108 @@ +import type { Mission } from 'typings/Mission'; +import type { MissionsById } from '@workers/stores/api/missionTypes.worker'; + +export default class LSSMStorage { + readonly #DB_NAME = `${PREFIX}-storage-v2`; + + #db: IDBDatabase | null = null; + + async #upgradeDB({ oldVersion }: IDBVersionChangeEvent) { + if (!this.#db) return; + + const transactions: Promise[] = []; + + const addTransaction = (transaction: IDBTransaction) => + transactions.push( + new Promise(resolve => + transaction.addEventListener('complete', () => resolve()) + ) + ); + + // In version 1, we introduced storing missionTypes + if (oldVersion < 1) { + addTransaction( + this.#db.createObjectStore('missionTypes', { keyPath: 'id' }) + .transaction + ); + } + + await Promise.all(transactions); + } + + #setDB(db: IDBDatabase) { + if (this.#db) return; + this.#db = db; + } + + #openDB() { + if (this.#db) return Promise.resolve(this.#db); + return new Promise((resolve, reject) => { + const request = indexedDB.open(this.#DB_NAME, 1); + + let upgradeNeeded = false; + + request.addEventListener('success', () => { + if (upgradeNeeded) return; + this.#setDB(request.result); + return resolve(request.result); + }); + request.addEventListener('error', () => reject(request.error)); + + request.addEventListener('upgradeneeded', async event => { + upgradeNeeded = true; + this.#setDB(request.result); + await this.#upgradeDB(event); + return resolve(request.result); + }); + }); + } + + #closeDB() { + if (this.#db) this.#db.close(); + this.#db = null; + } + + // region missionTypes + public storeMissionTypes(missionTypes: MissionsById) { + return this.#openDB() + .then(db => { + const tx = db.transaction('missionTypes', 'readwrite'); + const store = tx.objectStore('missionTypes'); + Object.values(missionTypes).forEach(missionType => { + store.put(missionType); + }); + return new Promise((resolve, reject) => { + tx.addEventListener('complete', () => resolve()); + tx.addEventListener('error', () => reject(tx.error)); + }); + }) + .finally(() => this.#closeDB()); + } + + public getMissionTypes() { + return this.#openDB() + .then(db => { + const tx = db.transaction('missionTypes', 'readonly'); + const store = tx.objectStore('missionTypes'); + const request = store.getAll(); + return new Promise((resolve, reject) => { + request.addEventListener('success', () => + resolve(request.result) + ); + request.addEventListener('error', () => + reject(request.error) + ); + }); + }) + .then(missionTypes => { + // indexedDB returns an array, so we need to convert it to an object + const missionTypesObject: MissionsById = {}; + missionTypes.forEach(missionType => { + missionTypesObject[missionType.id] = missionType; + }); + return missionTypesObject; + }) + .finally(() => this.#closeDB()); + } + // endregion +} diff --git a/src/importableScripts/checkRequestInit.ts b/src/importableScripts/checkRequestInit.ts new file mode 100644 index 0000000000..8e09de5c5c --- /dev/null +++ b/src/importableScripts/checkRequestInit.ts @@ -0,0 +1,16 @@ +/** + * Checks if the RequestInit has the LSSM-Header set and throws an error if not. + * @param init - The RequestInit to check against. + * @throws Error If the LSSM-Header is not set. + */ +export default (init: RequestInit) => { + const headers = new Headers(init.headers); + + // CAVEAT: headers are stored lowercase + // if the LSSM-Header is not set, abort the request! + if (!headers.has('x-lss-manager')) { + throw new Error( + 'No X-LSS-Manager Header has been set. Aborting the request!' + ); + } +}; diff --git a/src/mainpageCore.ts b/src/mainpageCore.ts index d4ab070fd6..a61626ce1d 100644 --- a/src/mainpageCore.ts +++ b/src/mainpageCore.ts @@ -1,5 +1,3 @@ -import he from 'he'; - import loadingIndicatorStorageKey from '../build/plugins/LoadingProgressPluginStorageKey'; import LSSMMenu from './LSSM-Menu.vue'; import telemetry from './modules/telemetry/main'; @@ -115,7 +113,7 @@ export default async (LSSM: Vue): Promise => { control.style.setProperty('cursor', 'pointer'); LSSM.$stores.api .getSettings('mainPage-core_map-expand') - .then(({ value: { design_mode } }) => + .then(({ design_mode }) => control.addEventListener('click', () => { window.mapExpand(design_mode >= 3); }) @@ -142,51 +140,28 @@ export default async (LSSM: Vue): Promise => { event: 'radioMessage', post: false, callback(radioMessage: RadioMessage) { - if ( - radioMessage.type === 'vehicle_fms' && - radioMessage.user_id === window.user_id - ) - LSSM.$stores.api.radioMessage(radioMessage); + LSSM.$stores.api.updateVehicleFromRadioMessage(radioMessage); }, }); - await LSSM.$stores.api.getBuildings('mainPage-core_initial-update'); - await LSSM.$stores.api.getMissions('mainPage-core_initial-update', true); + LSSM.$stores.api.getBuildings('mainPage-core_initial-update').then(); LSSM.$stores.root.hook({ event: 'buildingMarkerAdd', - callback(buildingMarker: BuildingMarkerAdd) { - if (buildingMarker.user_id !== window.user_id) return; - const buildings = LSSM.$stores.api.buildings; - const building = buildings.find( - ({ id }) => id === buildingMarker.id - ); - if ( - !building || - building.caption !== he.decode(buildingMarker.name) - ) { - LSSM.$stores.api - .getBuilding( - buildingMarker.id, - 'mainPage-core_buildingMarkerAdd' - ) - .then(building => - LSSM.$stores.api - .getVehiclesAtBuilding( - building.id, - 'mainPage-core_buildingMarkerAdd' - ) - .then(() => - LSSM.$stores.event.createAndDispatchEvent({ - name: 'buildingMarkerAdd', - detail: { - marker: buildingMarker, - building, - }, - }) - ) - ); - } + async callback(buildingMarker: BuildingMarkerAdd) { + const building = + await LSSM.$stores.api.updateBuildingFromBuildingMarkerAdd( + buildingMarker + ); + + if (!building) return; + LSSM.$stores.event.createAndDispatchEvent({ + name: 'buildingMarkerAdd', + detail: { + marker: buildingMarker, + building, + }, + }); }, }); }; diff --git a/src/modules/asyncButtons/assets/arr.ts b/src/modules/asyncButtons/assets/arr.ts index a403fdd803..18ffe59bec 100644 --- a/src/modules/asyncButtons/assets/arr.ts +++ b/src/modules/asyncButtons/assets/arr.ts @@ -45,22 +45,18 @@ export default (LSSM: Vue, $m: $m, MODULE_ID: string): void => { ?.getAttribute('content') || '' ); await LSSM.$stores.api - .request({ - url: btn.href, - init: { - method: 'POST', - body: url.searchParams.toString(), - credentials: 'include', - mode: 'cors', - headers: { - 'Accept': - 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', - 'Content-Type': - 'application/x-www-form-urlencoded', - 'Upgrade-Insecure-Requests': '1', - }, + .request(btn.href, `${MODULE_ID}-arr`, { + method: 'POST', + body: url.searchParams.toString(), + credentials: 'include', + mode: 'cors', + headers: { + 'Accept': + 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Content-Type': + 'application/x-www-form-urlencoded', + 'Upgrade-Insecure-Requests': '1', }, - feature: `${MODULE_ID}-arr`, }) .then(({ status }) => { if (status !== 200) return; diff --git a/src/modules/asyncButtons/assets/buildingPersonal.ts b/src/modules/asyncButtons/assets/buildingPersonal.ts index cc943f4fa7..69406995c9 100644 --- a/src/modules/asyncButtons/assets/buildingPersonal.ts +++ b/src/modules/asyncButtons/assets/buildingPersonal.ts @@ -36,9 +36,10 @@ export default (LSSM: Vue, $m: $m, MODULE_ID: string): void => { ?.getAttribute('content') || '' ); await LSSM.$stores.api - .request({ - url: btn.href, - init: { + .request( + btn.href, + `${MODULE_ID}-buildingPersonal`, + { method: 'POST', body: url.searchParams.toString(), credentials: 'include', @@ -50,9 +51,8 @@ export default (LSSM: Vue, $m: $m, MODULE_ID: string): void => { 'application/x-www-form-urlencoded', 'Upgrade-Insecure-Requests': '1', }, - }, - feature: `${MODULE_ID}-buildingPersonal`, - }) + } + ) .then(({ status }) => { if (status !== 200) return; btn.parentElement?.parentElement?.parentElement?.classList.add( diff --git a/src/modules/asyncButtons/assets/buildings.ts b/src/modules/asyncButtons/assets/buildings.ts index 9c9d006185..4805066d60 100644 --- a/src/modules/asyncButtons/assets/buildings.ts +++ b/src/modules/asyncButtons/assets/buildings.ts @@ -22,10 +22,10 @@ export default ( e.preventDefault(); taxBtns.forEach(taxBtn => taxBtn.classList.add('disabled')); await LSSM.$stores.api - .request({ - url: btn.getAttribute('href') ?? '', - feature: `${MODULE_ID}-buildings`, - }) + .request( + btn.getAttribute('href') ?? '', + `${MODULE_ID}-buildings` + ) .then(({ status }) => { if (status === 200) { document @@ -72,9 +72,10 @@ export default ( ?.getAttribute('content') || '' ); await LSSM.$stores.api - .request({ - url: btn.getAttribute('href') ?? '', - init: { + .request( + btn.getAttribute('href') ?? '', + 'ab-buildings-extensionState', + { method: 'POST', body: url.searchParams.toString(), headers: { @@ -84,9 +85,8 @@ export default ( 'application/x-www-form-urlencoded', 'Upgrade-Insecure-Requests': '1', }, - }, - feature: 'ab-buildings-extensionState', - }) + } + ) .then(({ status }) => { if (status === 200) { extensionStateBtns.forEach(tbtn => diff --git a/src/modules/asyncButtons/assets/forumpost.ts b/src/modules/asyncButtons/assets/forumpost.ts index 230809a74d..2c5d7392a3 100644 --- a/src/modules/asyncButtons/assets/forumpost.ts +++ b/src/modules/asyncButtons/assets/forumpost.ts @@ -44,20 +44,16 @@ export default (LSSM: Vue, $m: $m, MODULE_ID: string): void => { ?.getAttribute('content') || '' ); await LSSM.$stores.api - .request({ - url: btn.href, - init: { - method: 'POST', - body: url.searchParams.toString(), - credentials: 'include', - mode: 'cors', - headers: { - 'content-type': - 'application/x-www-form-urlencoded', - 'upgrade-insecure-requests': '1', - }, + .request(btn.href, `${MODULE_ID}-forumpost`, { + method: 'POST', + body: url.searchParams.toString(), + credentials: 'include', + mode: 'cors', + headers: { + 'content-type': + 'application/x-www-form-urlencoded', + 'upgrade-insecure-requests': '1', }, - feature: `${MODULE_ID}-forumpost`, }) .then(({ status }) => { if (status !== 200) return; diff --git a/src/modules/asyncButtons/assets/memberlist.ts b/src/modules/asyncButtons/assets/memberlist.ts index 714c2e3fc1..50fb0b9948 100644 --- a/src/modules/asyncButtons/assets/memberlist.ts +++ b/src/modules/asyncButtons/assets/memberlist.ts @@ -14,10 +14,7 @@ export default (LSSM: Vue, $m: $m, MODULE_ID: string): void => { t: HTMLAnchorElement ) => { await LSSM.$stores.api - .request({ - url: t.getAttribute('href') ?? '', - feature: `${MODULE_ID}-memberlist`, - }) + .request(t.getAttribute('href') ?? '', `${MODULE_ID}-memberlist`) .then(({ status }) => { if (status !== 200) return; const href = t.getAttribute('href')?.split('/'); diff --git a/src/modules/asyncButtons/assets/missions.ts b/src/modules/asyncButtons/assets/missions.ts index 030a97b97b..c4290452a7 100644 --- a/src/modules/asyncButtons/assets/missions.ts +++ b/src/modules/asyncButtons/assets/missions.ts @@ -27,10 +27,10 @@ export default ( return; e.preventDefault(); LSSM.$stores.api - .request({ - url: target.getAttribute('href') ?? '', - feature: `${MODULE_ID}-missions-prisoners`, - }) + .request( + target.getAttribute('href') ?? '', + `${MODULE_ID}-missions-prisoners` + ) .then(() => { const vehicleId = target.parentElement?.getAttribute( @@ -145,18 +145,14 @@ export default ( missionId.toString() ); LSSM.$stores.api - .request({ - url: '/mission_replies', - feature: `${MODULE_ID}_missionReply`, - init: { - credentials: 'include', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: url.searchParams.toString(), - method: 'POST', - mode: 'cors', + .request('/mission_replies', `${MODULE_ID}_missionReply`, { + credentials: 'include', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', }, + body: url.searchParams.toString(), + method: 'POST', + mode: 'cors', }) .then(() => { let missionReplies = diff --git a/src/modules/buildingHover/main.ts b/src/modules/buildingHover/main.ts index 1fdc30b785..6746b6b98b 100644 --- a/src/modules/buildingHover/main.ts +++ b/src/modules/buildingHover/main.ts @@ -1,12 +1,12 @@ import type { Building } from 'typings/Building'; import type { ModuleMainFunction } from 'typings/Module'; import type { PointTuple } from 'leaflet'; -import type { Vehicle } from 'typings/Vehicle'; +import type { VehiclesByBuilding } from '@workers/stores/api/vehicles.worker'; import type { BuildingMarker, RadioMessage } from 'typings/Ingame'; export default (async ({ LSSM, MODULE_ID }) => { - await LSSM.$stores.api.autoUpdateBuildings(MODULE_ID); - await LSSM.$stores.api.autoUpdateVehicles(MODULE_ID); + await LSSM.$stores.api.getBuildings(MODULE_ID); + await LSSM.$stores.api.getVehicles(MODULE_ID); const vehicleTypes = LSSM.$stores.translations.vehicles; @@ -19,13 +19,13 @@ export default (async ({ LSSM, MODULE_ID }) => { }, }); - let vehiclesByBuilding: Record; + let vehiclesByBuilding: VehiclesByBuilding; let buildings: Building[]; const updateBuildings = () => { vehiclesByBuilding = LSSM.$stores.api.vehiclesByBuilding; - buildings = LSSM.$stores.api.buildings; + buildings = LSSM.$stores.api.buildingsArray; }; updateBuildings(); @@ -40,7 +40,9 @@ export default (async ({ LSSM, MODULE_ID }) => { if (hasTt) marker.unbindTooltip(); - const vehicles = vehiclesByBuilding[marker.building_id] || []; + const vehicles = Object.values( + vehiclesByBuilding[marker.building_id] ?? [] + ); vehicles.sort((a, b) => a.caption > b.caption ? 1 : b.caption > a.caption ? -1 : 0 ); @@ -165,12 +167,10 @@ export default (async ({ LSSM, MODULE_ID }) => { ) return; const { id, fms, fms_real } = radioMessage; - const vehicle = LSSM.$stores.api.vehicles.find(v => v.id === id); + const vehicle = LSSM.$stores.api.vehicles[id]; if (!vehicle) return; updateBuildings(); - const v = vehiclesByBuilding[vehicle.building_id].find( - v => v.id === vehicle.id - ); + const v = vehiclesByBuilding[vehicle.building_id][vehicle.id]; if (v) { v.fms_show = fms; v.fms_real = fms_real; diff --git a/src/modules/buildingListFilter/main.ts b/src/modules/buildingListFilter/main.ts index c599965de7..871c4b506b 100644 --- a/src/modules/buildingListFilter/main.ts +++ b/src/modules/buildingListFilter/main.ts @@ -205,7 +205,7 @@ export default (async ({ ) ?.getAttribute('data-building_id') ?? '-1' ); - if (buildingsByType[small]?.find(b => b.id === id)) { + if (buildingsByType[small]?.[id]) { building.setAttribute( 'building_type_id', small.toString() diff --git a/src/modules/chatExtras/assets/userSelection.ts b/src/modules/chatExtras/assets/userSelection.ts index a0b3848ee1..486afdd8a7 100644 --- a/src/modules/chatExtras/assets/userSelection.ts +++ b/src/modules/chatExtras/assets/userSelection.ts @@ -12,7 +12,7 @@ import type { User } from 'typings/api/AllianceInfo'; export default (LSSM: Vue) => { // enable auto-update of allianceinfo API to keep users list up-to-date - LSSM.$stores.api.autoUpdateAllianceInfo('ce-user_selection').then(); + LSSM.$stores.api.getAllianceInfo('ce-user_selection').then(); // chatInput is the input field in the chat panel const chatInput = document.querySelector( @@ -256,7 +256,7 @@ export default (LSSM: Vue) => { // adjust choicePopup if necessary on every input event chatInput.addEventListener('input', () => { - const users = LSSM.$stores.api.allianceinfo?.users; + const users = LSSM.$stores.api.allianceinfo.users; if (!users) return; const selectionStart = chatInput.selectionStart ?? 0; diff --git a/src/modules/creditsextension/creditsextension.vue b/src/modules/creditsextension/creditsextension.vue index 452e6e256b..04be2e3bd0 100644 --- a/src/modules/creditsextension/creditsextension.vue +++ b/src/modules/creditsextension/creditsextension.vue @@ -193,9 +193,10 @@ import { faListUl } from '@fortawesome/free-solid-svg-icons/faListUl'; import { faPiggyBank } from '@fortawesome/free-solid-svg-icons/faPiggyBank'; import { faTable } from '@fortawesome/free-solid-svg-icons/faTable'; import { mapState } from 'pinia'; -import { useAPIStore } from '@stores/api'; +import { defineAPIStore, useAPIStore } from '@stores/api'; import { defineRootStore, useRootStore } from '@stores/index'; +import type { CreditsInfo } from 'typings/api/Credits'; import type { IconDefinition } from '@fortawesome/fontawesome-svg-core'; import type VueI18n from 'vue-i18n'; @@ -217,13 +218,6 @@ export default Vue.extend< coinsInNav: boolean; showToplistPosition: boolean; saleActive: boolean; - creditsAPI: { - credits_user_total: number; - user_toplist_position: number; - credits_alliance_active: boolean; - credits_alliance_current: number; - credits_alliance_total: number; - }; hideAllianceFunds: boolean; showAlertProgressBar: boolean; alertProgressPercentage: number; @@ -236,6 +230,7 @@ export default Vue.extend< highlight(): void; }, { + creditsAPI: CreditsInfo; credits: number; creditsLocalized: string; coins: number; @@ -278,13 +273,6 @@ export default Vue.extend< coinsInNav: false, showToplistPosition: false, saleActive: false, - creditsAPI: { - credits_user_total: 0, - user_toplist_position: 0, - credits_alliance_active: false, - credits_alliance_current: 0, - credits_alliance_total: 0, - }, hideAllianceFunds: false, showAlertProgressBar: false, alertProgressPercentage: 0, @@ -310,6 +298,9 @@ export default Vue.extend< }, computed: { ...mapState(defineRootStore, ['credits', 'coins']), + ...mapState(defineAPIStore, { + creditsAPI: 'credits', + }), creditsLocalized() { return this.credits.toLocaleString(); }, @@ -371,26 +362,6 @@ export default Vue.extend< )?.src; if (coinsIcon) this.coinsIcon = coinsIcon; - useAPIStore().autoUpdateCredits( - this.MODULE_ID, - ({ - value: { - credits_user_total, - user_toplist_position, - credits_alliance_active, - credits_alliance_current, - credits_alliance_total, - }, - }) => - this.$set(this, 'creditsAPI', { - credits_user_total, - user_toplist_position, - credits_alliance_active, - credits_alliance_current, - credits_alliance_total, - }) - ); - this.getSetting('creditsInNavbar').then(value => this.$set(this, 'creditsInNav', value) ); @@ -410,7 +381,9 @@ export default Vue.extend< this.getSetting<{ enabled: boolean; value: { credits: number }[] }>( 'alerts' ).then(({ value: alerts }) => { - if (!alerts.length) return; + if (!alerts.length) + return useAPIStore().getCredits('creditsextension-initial'); + const alertValues = alerts.map(({ credits }) => credits).sort(); const findNextAlertValue = (current: number) => @@ -432,7 +405,7 @@ export default Vue.extend< useAPIStore() .getCredits('creditsextension-alerts-initial') .then( - ({ value: { credits_user_current } }) => + ({ credits_user_current }) => (this.alertProgressPercentage = (credits_user_current / findNextAlertValue(credits_user_current)) * diff --git a/src/modules/dailyCreditsSummary/assets/getEntries.ts b/src/modules/dailyCreditsSummary/assets/getEntries.ts index 2f90153213..b6dade9bda 100644 --- a/src/modules/dailyCreditsSummary/assets/getEntries.ts +++ b/src/modules/dailyCreditsSummary/assets/getEntries.ts @@ -9,7 +9,7 @@ export default async ( const $m: $m = (key: string, args?: Record) => LSSM.$t(`modules.dailyCreditsSummary.${key}`, args); - const missions = await LSSM.$stores.api.getMissionsArray( + const missions = await LSSM.$stores.api.getMissionTypesArray( 'dailyCreditSummary-getMissions' ); diff --git a/src/modules/dashboard/components/building-list.vue b/src/modules/dashboard/components/building-list.vue index aca035c904..c33463d95f 100644 --- a/src/modules/dashboard/components/building-list.vue +++ b/src/modules/dashboard/components/building-list.vue @@ -227,7 +227,9 @@ export default Vue.extend< const dispatchCenterBuildings = useTranslationStore().dispatchCenterBuildings; dispatchCenterBuildings.forEach(type => - dispatchBuildings.push(...(buildingsByType[type] ?? [])) + dispatchBuildings.push( + ...Object.values(buildingsByType[type] ?? []) + ) ); dispatchBuildings.sort((a, b) => !a.id @@ -241,7 +243,7 @@ export default Vue.extend< const bedBuildings: Building[] = []; const bedBuildingsType = useTranslationStore().bedBuildings; bedBuildingsType.forEach(type => - bedBuildings.push(...(buildingsByType[type] ?? [])) + bedBuildings.push(...Object.values(buildingsByType[type] ?? [])) ); return { buildingTypeNames: Object.fromEntries( @@ -260,7 +262,7 @@ export default Vue.extend< dispatchCenterBuildings, bedBuildings, bedBuildingsType, - apiStore, + apiStore: useAPIStore(), translationStore, } as BuildingList; }, @@ -314,10 +316,10 @@ export default Vue.extend< }, setDispatchCenter(building, dispatchBuilding) { this.apiStore - .request({ - url: `/buildings/${building.id}/leitstelle-set/${dispatchBuilding.id}`, - feature: `dashboard-buildingList-fastDispatchChooser`, - }) + .request( + `/buildings/${building.id}/leitstelle-set/${dispatchBuilding.id}`, + `dashboard-buildingList-fastDispatchChooser` + ) .then(() => { const dispatchBtn = document.querySelector( diff --git a/src/modules/dashboard/components/building-types.vue b/src/modules/dashboard/components/building-types.vue index 40c0e61a00..204c16a617 100644 --- a/src/modules/dashboard/components/building-types.vue +++ b/src/modules/dashboard/components/building-types.vue @@ -137,8 +137,9 @@ export default Vue.extend< const removeNull = ( value: S | null ): value is S => !!value; - const buildingsOfType = - buildingsByType[buildingType]; + const buildingsOfType = Object.values( + buildingsByType[buildingType] ?? {} + ); const extensionsOfType = {} as Record< string, Extension[] @@ -237,7 +238,7 @@ export default Vue.extend< buildingTypes[ buildingType ].maxBuildingsFunction?.( - apiStore.buildings.length + apiStore.buildingsArray.length ) ?? '–', buildings: buildingsOfType, }, @@ -309,7 +310,6 @@ export default Vue.extend< categoryColors, groups, faBuilding, - apiStore, }; }, computed: { diff --git a/src/modules/dashboard/components/chart-summary.vue b/src/modules/dashboard/components/chart-summary.vue index 96c9e6f788..90f339fed6 100644 --- a/src/modules/dashboard/components/chart-summary.vue +++ b/src/modules/dashboard/components/chart-summary.vue @@ -6,7 +6,7 @@ {{ $sm('buildings.title') }}: {{ Object.values(buildings) - .reduce((a, b) => (a += b.length), 0) + .reduce((a, b) => (a += Object.keys(b).length), 0) .toLocaleString() }} @@ -43,7 +43,7 @@ {{ $sm('vehicles.title') }}: {{ Object.values(vehicles) - .reduce((a, b) => a + b.length, 0) + .reduce((a, b) => a + Object.keys(b).length, 0) .toLocaleString() }} @@ -222,9 +222,7 @@ export default Vue.extend< computed: { ...mapState(defineAPIStore, { personalCount: store => - store.buildings - .map(b => b.personal_count) - .reduce((a, b) => a + b, 0), + store.buildingsArray.reduce((a, b) => a + b.personal_count, 0), }), maxMissions() { return window.mission_count_max; @@ -234,9 +232,10 @@ export default Vue.extend< }, water() { return Object.entries(this.vehicles).reduce( - (a, [type, { length }]) => + (a, [type, vehicles]) => a + - length * (this.waterByType[parseInt(type.toString())] ?? 0), + Object.keys(vehicles).length * + (this.waterByType[parseInt(type.toString())] ?? 0), 0 ); }, @@ -245,9 +244,9 @@ export default Vue.extend< (this.water * (100 + Object.entries(this.vehicles).reduce( - (a, [type, { length }]) => + (a, [type, vehicles]) => a + - length * + Object.keys(vehicles).length * (this.waterBonusByType[ parseInt(type.toString()) ] ?? 0), @@ -261,9 +260,10 @@ export default Vue.extend< }, foam() { return Object.entries(this.vehicles).reduce( - (a, [type, { length }]) => + (a, [type, vehicles]) => a + - length * (this.foamByType[parseInt(type.toString())] ?? 0), + Object.keys(vehicles).length * + (this.foamByType[parseInt(type.toString())] ?? 0), 0 ); }, @@ -272,9 +272,9 @@ export default Vue.extend< (this.foam * (100 + Object.entries(this.vehicles).reduce( - (a, [type, { length }]) => + (a, [type, vehicles]) => a + - length * + Object.keys(vehicles).length * (this.foamBonusByType[ parseInt(type.toString()) ] ?? 0), @@ -321,7 +321,9 @@ export default Vue.extend< Object.values( this.vehicleCategories[category].vehicles[group] ).forEach(type => { - const value = (this.vehicles[type] || []).length; + const value = Object.keys( + this.vehicles[type] ?? [] + ).length; sum += value; const color = this.vehicleTypeColors[type]; groupColor += parseInt(color.replace(/^#/u, ''), 16); @@ -350,7 +352,9 @@ export default Vue.extend< Object.values( this.vehicleCategories[category].vehicles[groups[0]] ).forEach(type => { - const value = (this.vehicles[type] || []).length; + const value = Object.values( + this.vehicles[type] ?? [] + ).length; sum += value; data.push({ id: `${category}_${type}`, @@ -466,7 +470,9 @@ export default Vue.extend< ...types.map(type => { return { name: this.buildingTypeNames[type], - y: (this.buildings[category] || []).filter( + y: Object.values( + this.buildings[category] || [] + ).filter( building => building.building_type === type ).length, @@ -478,7 +484,8 @@ export default Vue.extend< isSum: !this.buildingsAsColumn, color: this.buildingCategories[category].color, drilldown: category, - y: this.buildings[category]?.length ?? 0, + y: Object.keys(this.buildings[category] ?? {}) + .length, }, ], }; @@ -508,7 +515,7 @@ export default Vue.extend< id: category, type: 'column', data: types.map(building_type => { - const buildings = ( + const buildings = Object.values( this.buildings[category] || [] ).filter( building => @@ -526,9 +533,11 @@ export default Vue.extend< ) ) return; - this.vehiclesByBuilding[ - building.id - ].forEach(vehicle => { + Object.values( + this.vehiclesByBuilding[ + building.id + ] + ).forEach(vehicle => { if ( !vehicle_types.hasOwnProperty( vehicle.vehicle_type diff --git a/src/modules/dashboard/components/dispatchcenter-view.vue b/src/modules/dashboard/components/dispatchcenter-view.vue index 3983d311cc..3c51eb5d60 100644 --- a/src/modules/dashboard/components/dispatchcenter-view.vue +++ b/src/modules/dashboard/components/dispatchcenter-view.vue @@ -341,12 +341,12 @@ diff --git a/src/modules/lssmaql/register.json b/src/modules/lssmaql/register.json index a644ec28c9..698fca3ff9 100644 --- a/src/modules/lssmaql/register.json +++ b/src/modules/lssmaql/register.json @@ -1 +1 @@ -{ "dev": true, "github": 51, "location": "^/?$" } \ No newline at end of file +{ "dev": true, "github": 51, "location": "^/?$", "noapp": true } \ No newline at end of file diff --git a/src/modules/missionHelper/missionHelper.vue b/src/modules/missionHelper/missionHelper.vue index 0e4cbb5d8c..1f387a5b07 100644 --- a/src/modules/missionHelper/missionHelper.vue +++ b/src/modules/missionHelper/missionHelper.vue @@ -33,7 +33,7 @@ :icon="faSyncAlt" :spin="isReloading" fixed-width - @click="reloadSpecs(true)" + @click="reloadSpecs" >
{{ $m('tip.reload') }} @@ -679,11 +679,9 @@ export default Vue.extend< }, }, methods: { - async reloadSpecs(force = false) { + async reloadSpecs() { this.isReloading = true; - await this.apiStore.getMissions('missionHelper-getMission', force); - const missionHelpBtn = document.querySelector('#mission_help'); this.isDiyMission = !missionHelpBtn; @@ -698,7 +696,9 @@ export default Vue.extend< this.isReloading = false; }, async getMission(id) { - const missionsById = this.apiStore.missions; + const missionsById = await this.apiStore.getMissionTypes( + 'missionHelper-getMission' + ); const mission: Mission | undefined = missionsById[id]; if (mission) { if (this.settings.expansions && mission.additional) { diff --git a/src/modules/redesign/components/aaos.vue b/src/modules/redesign/components/aaos.vue index 5711a8ab87..9f25d1a009 100644 --- a/src/modules/redesign/components/aaos.vue +++ b/src/modules/redesign/components/aaos.vue @@ -232,13 +232,14 @@ export default Vue.extend< LSSM.aaos.authenticity_token ); LSSM.lightbox.apiStore - .request({ - url: `/${ + .request( + `/${ type === 'arr' ? 'aaos' : 'vehicle_groups' }/${id}`, - init: { + 'redesign-aaos-delete', + { credentials: 'include', headers: { 'Content-Type': @@ -254,9 +255,8 @@ export default Vue.extend< body: url.searchParams.toString(), method: 'POST', mode: 'cors', - }, - feature: 'redesign-aaos-delete', - }) + } + ) .then(() => { LSSM.$set( LSSM.lightbox.data.categories[category], @@ -290,10 +290,10 @@ export default Vue.extend< title: this.lightbox.$sm('delete.confirm'), async handler() { LSSM.lightbox.apiStore - .request({ - url: '/aao/alle_loeschen', - feature: 'redesign-aaos-delete-all', - }) + .request( + '/aao/alle_loeschen', + 'redesign-aaos-delete-all' + ) .then(() => { Object.entries( LSSM.aaos.categories diff --git a/src/modules/redesign/components/alliance_avatar.vue b/src/modules/redesign/components/alliance_avatar.vue index f134fd8367..15de9af0b8 100644 --- a/src/modules/redesign/components/alliance_avatar.vue +++ b/src/modules/redesign/components/alliance_avatar.vue @@ -84,9 +84,10 @@ export default Vue.extend< ); formData.append('commit', 'save'); this.lightbox.apiStore - .request({ - url: `/verband/avatar/upload`, - init: { + .request( + `/verband/avatar/upload`, + `redesign-alliance-avatar-edit`, + { credentials: 'include', headers: { 'Accept': @@ -100,9 +101,8 @@ export default Vue.extend< body: formData, method: 'POST', mode: 'cors', - }, - feature: `redesign-alliance-avatar-edit`, - }) + } + ) .then(({ url }) => { const img = this.image; this.image = ''; @@ -127,9 +127,10 @@ export default Vue.extend< deleteAvatar() { this.$set(this.lightbox, 'loading', true); this.lightbox.apiStore - .request({ - url: `/verband/avatar/delete`, - init: { + .request( + `/verband/avatar/delete`, + `redesign-alliance-avatar-delete`, + { credentials: 'include', referrer: new URL( `verband/avatar`, @@ -137,9 +138,8 @@ export default Vue.extend< ).toString(), method: 'GET', mode: 'cors', - }, - feature: `redesign-alliance-avatar-delete`, - }) + } + ) .then(() => { this.$set(this.lightbox.data, 'image', ''); this.lightbox.finishLoading('avatar-deleted'); diff --git a/src/modules/redesign/components/alliances.vue b/src/modules/redesign/components/alliances.vue index 321d2cd38f..9d232321af 100644 --- a/src/modules/redesign/components/alliances.vue +++ b/src/modules/redesign/components/alliances.vue @@ -159,10 +159,7 @@ export default Vue.extend< (this.$refs.urlSearch as HTMLInputElement)?.value?.trim() ?? ''; if (search) url.searchParams.set('caption', search); this.lightbox.apiStore - .request({ - url: toString(), - feature: `redesign-alliances-load-prev-${this.startPage}`, - }) + .request(url, `redesign-alliances-load-prev-${this.startPage}`) .then((res: Response) => res.text()) .then(async html => { import('../parsers/alliances').then(async parser => { @@ -201,10 +198,7 @@ export default Vue.extend< (this.$refs.urlSearch as HTMLInputElement)?.value?.trim() ?? ''; if (search) url.searchParams.set('caption', search); this.lightbox.apiStore - .request({ - url, - feature: `redesign-alliances-load-next-${this.endPage}`, - }) + .request(url, `redesign-alliances-load-next-${this.endPage}`) .then((res: Response) => res.text()) .then(async html => { import('../parsers/alliances').then(async parser => { diff --git a/src/modules/redesign/components/assets/einsatz.vue b/src/modules/redesign/components/assets/einsatz.vue index 6a8d0e730a..bcd94ce932 100644 --- a/src/modules/redesign/components/assets/einsatz.vue +++ b/src/modules/redesign/components/assets/einsatz.vue @@ -13,8 +13,6 @@