Untitled
document.addEventListener('alpine:init', () => { Alpine.data('transdevApp', () => ({ scheduleTickNr: 0, adsTickNr: 0, lastSuccessfulAPIFetch: '', heapSize: '', loadsInProgress: 0, hasErrors: false, firstDataGet: false, adsLayerVisible: false, currentAd: -1, scheduleData: [] as ScheduleEntity[], appSettings: { ...appDefaults, ...window.appSettings }, ads: window.initialAds, newAds: [] as Advertisement[] | false, get availableAds(): Advertisement[] { return this.ads.filter((ad) => this.isCurrentTimeBetween(ad.visible_time_from, ad.visible_time_to)); }, /** * Returns true if current time is between startTime and endTime * If startTime > endTime, it is assumed that the time range is over midnight * @param startTime * @param endTime */ isCurrentTimeBetween: (startTime: string | null | undefined, endTime: string | null | undefined): boolean => { if (!startTime || !endTime || startTime === '' || endTime === '') return true; const currentTime = new Date(); const currentMs = currentTime.getHours() * 60 * 60 * 1000 + currentTime.getMinutes() * 60 * 1000 + currentTime.getSeconds() * 1000 + currentTime.getMilliseconds(); const [startHours, startMinutes] = startTime.split(':').map(Number); const [endHours, endMinutes] = endTime.split(':').map(Number); const startMs = startHours * 60 * 60 * 1000 + startMinutes * 60 * 1000; const endMs = endHours * 60 * 60 * 1000 + endMinutes * 60 * 1000; if (startMs < endMs) { return currentMs >= startMs && currentMs <= endMs; } else { return currentMs >= startMs || currentMs <= endMs; } }, loadAdsList: ()=>{}, init() { const hardcodedDepartures: Departure[] = [ { id: 1, station_id: 1, type: 'platform', state: 'OK', platform: '1', transport_full_name: 'bus', transport_name: '707', transport_number: '1', transport_description: null, transport_class: null, transport_operator: null, origin_name: null, destination_name: 'Zuhause', hints: null, departure_planned: '2024-08-02T03:55:25.000000Z', departure_estimated: null, departure_delay: 5, created_at: '2024-08-02T03:55:25.000000Z', updated_at: '2024-08-02T03:55:25.000000Z' }, // Add more hardcoded departure objects as needed ]; const parseResponse = (response: Departure[]): ScheduleEntity[] => { return response.map((entity) => { const departureTime = new Date(entity.departure_planned); const estimatedTime = entity.departure_estimated === null ? new Date(0) : new Date(entity.departure_estimated); const connection: ScheduleEntity = { platform: entity.platform, departureTime, estimatedTime, departureString: getTimeGerman(departureTime), estimatedString: getTimeGerman(estimatedTime), destination: entity.destination_name, transportTitle: entity.transport_name ? `${entity.transport_name} ${entity.transport_number}` : (entity.transport_full_name || null), transportClass: entity.transport_class, transportIcon: getTranportIcon(entity.transport_class), hint: entity.hints || '', delay: (estimatedTime > departureTime) ? (estimatedTime.getTime() - departureTime.getTime()) / (60 * 1000) : 0 }; return connection; }).sort((a, b) => a.departureTime.getTime() - b.departureTime.getTime()); }; const getTranportIcon = (transportClass: TransportClass): string | null => { switch (transportClass) { case 'BUS': case 'CITY_BUS': case 'EXPRESS_BUS': case 'REGIONAL_BUS': case 'CALL_BUS': case 'CITIZEN_BUS': return 'busLogo.svg'; case 'TRAM': case 'UNDERGROUND': return 'tramLogo.svg'; case 'REGIONAL_TRAIN': case 'NATIONAL_TRAIN': case 'INTERNATIONAL_TRAIN': return 'trainLogo.svg'; case 'SUBURBAN_TRAIN': return 'sLogo.svg'; default: return null; } } const getTimeGerman = (date: Date) => { return date.toLocaleDateString('de-DE', { hour: 'numeric', minute: 'numeric' }).split(',')[1]; }; const doAppTick = async () => { this.scheduleTickNr++; const departuresData = hardcodedDepartures; if (departuresData !== null) { try { this.lastSuccessfulAPIFetch = (new Date()).toLocaleDateString('de-DE', { year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric' }); this.firstDataGet = true; this.scheduleData = parseResponse(departuresData); } catch (error) { this.hasErrors = true; throw new Error('Error in departures API response:' + error); } return; } }; const findNextAdIndex = (ads: Partial<Advertisement>[]) => { const nextAd = ads.findIndex(ad => ad.id === this.currentAd); if (nextAd === -1) { return 0; } return nextAd === (ads.length - 1) ? 0 : nextAd + 1; } /** * Tick load ads, but they updates only if they are in not visible time range */ const doAdsTick = () => { this.adsTickNr++; let defaultSpaceBetweenAds = this.appSettings.departuresInterval * 1000; // const availableAds = ads.filter((ad) => isCurrentTimeBetween(ad.visible_time_from, ad.visible_time_to)); if (this.availableAds.length === 0) { // do nothing setTimeout(() => { checkNewAds(); doAdsTick(); }, defaultSpaceBetweenAds); return; } const nextAdIndex = findNextAdIndex(this.availableAds); const currentAdVisibilityTime = () => { return this.availableAds[nextAdIndex].ad_interval ? this.availableAds[nextAdIndex].ad_interval! * 1000 : this.appSettings.adInterval * 1000; } this.currentAd = this.availableAds[nextAdIndex].id!; this.adsLayerVisible = true; setTimeout(() => { this.adsLayerVisible = false; checkNewAds(); }, currentAdVisibilityTime()); setTimeout(() => { doAdsTick(); }, currentAdVisibilityTime() + defaultSpaceBetweenAds); } const checkNewAds = () => { if (this.newAds !== false && this.newAds.length > 0) { this.ads = this.newAds; this.newAds = []; } } this.loadAdsList = async () => { const adsData = window.initialAds; //
Leave a Comment