mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-18 15:42:54 +00:00
275 lines
6.8 KiB
TypeScript
275 lines
6.8 KiB
TypeScript
/*
|
|
* Copyright (C) 2022 StApps
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation, version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
|
import {ActivatedRoute} from '@angular/router';
|
|
import {SCISO8601Date, SCUuid} from '@openstapps/core';
|
|
import moment, {Moment} from 'moment';
|
|
import {
|
|
materialFade,
|
|
materialManualFade,
|
|
materialSharedAxisX,
|
|
} from '../../../../animation/material-motion';
|
|
import {ScheduleProvider} from '../../../calendar/schedule.provider';
|
|
import {ScheduleEvent, ScheduleResponsiveBreakpoint} from '../schema/schema';
|
|
import {SwiperComponent} from 'swiper/angular';
|
|
import {InfiniteSwiperComponent} from '../grid/infinite-swiper.component';
|
|
import {IonContent, IonDatetime} from '@ionic/angular';
|
|
import {Subscription} from 'rxjs';
|
|
import {CalendarService} from '../../../calendar/calendar.service';
|
|
import {getScheduleCursorOffset} from '../grid/schedule-cursor-offset';
|
|
|
|
/**
|
|
* Component that displays the schedule
|
|
*/
|
|
@Component({
|
|
selector: 'stapps-calendar-component',
|
|
templateUrl: 'calendar-component.html',
|
|
styleUrls: ['calendar-component.scss'],
|
|
animations: [materialFade, materialSharedAxisX, materialManualFade],
|
|
})
|
|
export class CalendarComponent implements OnInit, OnDestroy {
|
|
/**
|
|
* The day that the schedule started out on
|
|
*/
|
|
@Input() baselineDate: Moment;
|
|
|
|
/**
|
|
* Range of date of the slides shown on screen.
|
|
*/
|
|
dateRange: {
|
|
startDate: string;
|
|
endDate: string;
|
|
} = {
|
|
startDate: '',
|
|
endDate: '',
|
|
};
|
|
|
|
calendarServiceSubscription: Subscription;
|
|
|
|
prevHeaderIndex = 0;
|
|
|
|
/**
|
|
* Hours for grid
|
|
*/
|
|
@Input() hours: number[];
|
|
|
|
/**
|
|
* Range of hours to display
|
|
*/
|
|
@Input() readonly hoursRange = {
|
|
from: 5,
|
|
to: 22,
|
|
};
|
|
|
|
todaySlideIndex: number;
|
|
|
|
initialSlideIndex?: Promise<number>;
|
|
|
|
/**
|
|
* Layout of the schedule
|
|
*/
|
|
@Input() layout: ScheduleResponsiveBreakpoint;
|
|
|
|
/**
|
|
* Route fragment
|
|
*/
|
|
routeFragment = 'schedule/calendar';
|
|
|
|
/**
|
|
* Vertical scale of the schedule (distance between hour lines)
|
|
*/
|
|
@Input() scale = 70;
|
|
|
|
/**
|
|
* unix -> (uid -> event)
|
|
*/
|
|
@Input() testSchedule: Record<SCISO8601Date, Record<SCUuid, ScheduleEvent>> =
|
|
{};
|
|
|
|
/**
|
|
* UUIDs
|
|
*/
|
|
@Input() uuids: SCUuid[];
|
|
|
|
/**
|
|
* UUID subscription
|
|
*/
|
|
uuidSubscription: Subscription;
|
|
|
|
@Input() useInfiniteSwiper = true;
|
|
|
|
@Input() weekDates: Array<Moment>;
|
|
|
|
constructor(
|
|
protected readonly activatedRoute: ActivatedRoute,
|
|
protected readonly calendarService: CalendarService,
|
|
protected readonly scheduleProvider: ScheduleProvider,
|
|
) {}
|
|
|
|
ngOnInit() {
|
|
this.onInit();
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
this.onDestroy();
|
|
}
|
|
|
|
onInit() {
|
|
let dayString: string | number | null =
|
|
this.activatedRoute.snapshot.paramMap.get('date');
|
|
if (dayString == undefined || dayString === 'now') {
|
|
const fragments = window.location.href.split('/');
|
|
const urlFragment: string = fragments[fragments.length - 1] ?? '';
|
|
|
|
dayString = /^\d{4}-\d{2}-\d{2}$/.test(urlFragment)
|
|
? urlFragment
|
|
: moment.now();
|
|
}
|
|
|
|
this.baselineDate = moment(dayString).startOf('day');
|
|
|
|
this.initialSlideIndex = new Promise(resolve => {
|
|
this.uuidSubscription = this.scheduleProvider.uuids$.subscribe(
|
|
async result => {
|
|
this.uuids = result;
|
|
resolve(await this.loadEvents());
|
|
},
|
|
);
|
|
});
|
|
|
|
this.dateRange.startDate = this.calculateDateFromIndex(0, 0, 'DD.MM.YY');
|
|
this.dateRange.endDate = this.calculateDateFromIndex(
|
|
0,
|
|
this.layout.days - 1,
|
|
'DD.MM.YY',
|
|
);
|
|
}
|
|
|
|
async scrollCursorIntoView(content: IonContent) {
|
|
const scrollElement = await content.getScrollElement();
|
|
scrollElement.scrollTo({
|
|
top:
|
|
getScheduleCursorOffset(this.hoursRange.from, this.scale) -
|
|
scrollElement.clientHeight / 3,
|
|
});
|
|
}
|
|
|
|
onDestroy() {
|
|
this.uuidSubscription.unsubscribe();
|
|
if (this.calendarServiceSubscription) {
|
|
this.calendarServiceSubscription.unsubscribe();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get date from baseline date and index of current slide.
|
|
*
|
|
* @param index number
|
|
* @param delta number - is added to index
|
|
* @param dateFormat string
|
|
*/
|
|
calculateDateFromIndex(index: number, delta = 0, dateFormat = 'YYYY-MM-DD') {
|
|
return moment(this.baselineDate)
|
|
.add(index + delta, 'days')
|
|
.format(dateFormat);
|
|
}
|
|
|
|
/**
|
|
* Change page
|
|
*/
|
|
onPageChange(index: number) {
|
|
this.setDateRange(index);
|
|
|
|
window.history.replaceState(
|
|
{},
|
|
'',
|
|
`${this.routeFragment}/${this.calculateDateFromIndex(index)}`,
|
|
);
|
|
}
|
|
|
|
setDateRange(index: number) {
|
|
this.dateRange.startDate = this.calculateDateFromIndex(
|
|
index,
|
|
0,
|
|
'DD.MM.YY',
|
|
);
|
|
this.dateRange.endDate = this.calculateDateFromIndex(
|
|
index,
|
|
this.layout.days - 1,
|
|
'DD.MM.YY',
|
|
);
|
|
}
|
|
|
|
onHeaderSwipe(index: number, infiniteController: InfiniteSwiperComponent) {
|
|
if (index < this.prevHeaderIndex) {
|
|
infiniteController?.pageBackwards();
|
|
}
|
|
if (index > this.prevHeaderIndex) {
|
|
infiniteController?.pageForward();
|
|
}
|
|
this.prevHeaderIndex = index;
|
|
}
|
|
|
|
syncSwiper(self: SwiperComponent, other: SwiperComponent) {
|
|
other.swiperRef.slideTo(self.swiperRef.activeIndex);
|
|
}
|
|
|
|
presentDatePopover(
|
|
mainSwiper: InfiniteSwiperComponent,
|
|
headerSwiper: InfiniteSwiperComponent,
|
|
index: number,
|
|
popoverDateTime: IonDatetime,
|
|
) {
|
|
const nextIndex =
|
|
moment(popoverDateTime.value).diff(this.baselineDate, 'days') -
|
|
headerSwiper.virtualIndex -
|
|
index;
|
|
|
|
mainSwiper.goToIndex(nextIndex).then(() => {
|
|
this.setDateRange(nextIndex);
|
|
});
|
|
popoverDateTime.confirm(true);
|
|
}
|
|
|
|
/**
|
|
* Load events
|
|
*/
|
|
async loadEvents(): Promise<number> {
|
|
const dateSeries = await this.scheduleProvider.getDateSeries(this.uuids);
|
|
|
|
this.testSchedule = {};
|
|
|
|
for (const series of dateSeries.dates) {
|
|
for (const date of series.dates) {
|
|
const index = moment(date)
|
|
.startOf('day')
|
|
.diff(this.baselineDate, 'days');
|
|
|
|
// fall back to default
|
|
(this.testSchedule[index] ?? (this.testSchedule[index] = {}))[
|
|
series.uid
|
|
] = {
|
|
dateSeries: series,
|
|
time: {
|
|
start: moment(date).hours(),
|
|
duration: series.duration,
|
|
},
|
|
};
|
|
}
|
|
}
|
|
return this.todaySlideIndex;
|
|
}
|
|
}
|