mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-20 00:23:03 +00:00
refactor: use SwiperJS in schedule module
This commit is contained in:
@@ -12,23 +12,14 @@
|
||||
* 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,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
SimpleChanges,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import {Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {IonDatetime, Platform} from '@ionic/angular';
|
||||
import {SCUuid} from '@openstapps/core';
|
||||
import {Platform} from '@ionic/angular';
|
||||
import {SCISO8601Date, SCUuid} from '@openstapps/core';
|
||||
import {last} from 'lodash-es';
|
||||
import moment, {Moment} from 'moment';
|
||||
import {DateFormatPipe} from 'ngx-moment';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {SharedAxisChoreographer} from '../../../animation/animation-choreographer';
|
||||
import {
|
||||
materialFade,
|
||||
materialManualFade,
|
||||
@@ -36,6 +27,7 @@ import {
|
||||
} from '../../../animation/material-motion';
|
||||
import {ScheduleProvider} from '../schedule.provider';
|
||||
import {ScheduleEvent, ScheduleResponsiveBreakpoint} from './schema/schema';
|
||||
import {SwiperComponent} from 'swiper/angular';
|
||||
|
||||
/**
|
||||
* Component that displays the schedule
|
||||
@@ -46,42 +38,21 @@ import {ScheduleEvent, ScheduleResponsiveBreakpoint} from './schema/schema';
|
||||
styleUrls: ['calendar-view.scss'],
|
||||
animations: [materialFade, materialSharedAxisX, materialManualFade],
|
||||
})
|
||||
export class CalendarViewComponent implements OnDestroy, OnInit, OnChanges {
|
||||
export class CalendarViewComponent implements OnDestroy, OnInit {
|
||||
/**
|
||||
* UUID subscription
|
||||
*/
|
||||
private _uuidSubscription: Subscription;
|
||||
|
||||
/**
|
||||
* The day that is routed to
|
||||
* The day that the schedule started out on
|
||||
*/
|
||||
protected routeDate: Moment;
|
||||
baselineDate: Moment;
|
||||
|
||||
/**
|
||||
* @see {blockDateTimeChange}
|
||||
* Oldest event to newest event
|
||||
*/
|
||||
anticipateDatetimeChangeBlocked = false;
|
||||
|
||||
/**
|
||||
* @see {blockDateTimeChange}
|
||||
*/
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
readonly anticipateDatetimeChangeTimeoutMs: 100;
|
||||
|
||||
/**
|
||||
* Animation state for cards
|
||||
*/
|
||||
cardsAnimationState: 'in' | 'out' = 'out';
|
||||
|
||||
/**
|
||||
* The cursor
|
||||
*/
|
||||
@ViewChild('cursor', {read: HTMLElement}) cursor?: HTMLElement;
|
||||
|
||||
/**
|
||||
* Choreographer
|
||||
*/
|
||||
dateLabelsChoreographer: SharedAxisChoreographer<Moment[]>;
|
||||
dateRange: [Date, Date];
|
||||
|
||||
/**
|
||||
* The date range to initially display
|
||||
@@ -106,11 +77,17 @@ export class CalendarViewComponent implements OnDestroy, OnInit, OnChanges {
|
||||
to: 22,
|
||||
};
|
||||
|
||||
todaySlideIndex: number;
|
||||
|
||||
initialSlideIndex?: Promise<number>;
|
||||
|
||||
/**
|
||||
* Layout of the schedule
|
||||
*/
|
||||
@Input() layout: ScheduleResponsiveBreakpoint;
|
||||
|
||||
@ViewChild('mainSwiper', {static: false}) swiperRef: SwiperComponent;
|
||||
|
||||
/**
|
||||
* Get the date format for the date field
|
||||
*/
|
||||
@@ -128,15 +105,20 @@ export class CalendarViewComponent implements OnDestroy, OnInit, OnChanges {
|
||||
scale = 60;
|
||||
|
||||
/**
|
||||
* date -> (uid -> event)
|
||||
* unix -> (uid -> event)
|
||||
*/
|
||||
testSchedule: Record<string, Record<SCUuid, ScheduleEvent>> = {};
|
||||
testSchedule: Record<SCISO8601Date, Record<SCUuid, ScheduleEvent>> = {};
|
||||
|
||||
/**
|
||||
* UUIDs
|
||||
*/
|
||||
uuids: SCUuid[];
|
||||
|
||||
/**
|
||||
* For use in templates
|
||||
*/
|
||||
moment = moment;
|
||||
|
||||
constructor(
|
||||
protected readonly scheduleProvider: ScheduleProvider,
|
||||
protected readonly activatedRoute: ActivatedRoute,
|
||||
@@ -152,88 +134,22 @@ export class CalendarViewComponent implements OnDestroy, OnInit, OnChanges {
|
||||
this.hours = [...Array.from({length: this.hoursAmount}).keys()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Because of some stupid Ionic implementation, there is no
|
||||
* way to wait for the datetime picker to be dismissed without
|
||||
* listening for (ionChange). Unfortunately that also includes
|
||||
* changes caused by a page change, so whenever we do that,
|
||||
* we have to block the event for a few milliseconds.
|
||||
*/
|
||||
blockDateTimeChange() {
|
||||
this.anticipateDatetimeChangeBlocked = true;
|
||||
setTimeout(() => {
|
||||
this.anticipateDatetimeChangeBlocked = false;
|
||||
}, this.anticipateDatetimeChangeTimeoutMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine displayed dates according to display size
|
||||
*/
|
||||
determineDisplayDates() {
|
||||
// let's boldly assume that we at least display one day
|
||||
|
||||
const out = [moment(this.routeDate).startOf(this.layout.startOf)];
|
||||
for (let i = 1; i < this.layout.days; i++) {
|
||||
out.push(out[0].clone().add(i, 'day'));
|
||||
}
|
||||
|
||||
this.displayDates = [
|
||||
out.map(it => it.clone().subtract(this.layout.days, 'days')),
|
||||
out,
|
||||
out.map(it => it.clone().add(this.layout.days, 'days')),
|
||||
];
|
||||
|
||||
this.dateLabelsChoreographer?.changeViewForState(this.getDateLabels(), 0);
|
||||
// void this.mainSlides.slideTo(this.mode === 'schedule' ? 0 : 1, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date labels
|
||||
*/
|
||||
getDateLabels(): Moment[] {
|
||||
return (this.displayDates[1] ?? this.displayDates[0]).map(it => it.clone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Jump to a date
|
||||
*/
|
||||
jumpToDate(alt: IonDatetime, offset = 0, date?: Moment) {
|
||||
if (this.anticipateDatetimeChangeBlocked) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newDate = (date ?? moment(alt.value)).subtract(offset, 'days');
|
||||
const direction = this.routeDate.isBefore(newDate)
|
||||
? 1
|
||||
: this.routeDate.isAfter(newDate)
|
||||
? -1
|
||||
: 0;
|
||||
|
||||
this.blockDateTimeChange();
|
||||
this.routeDate = newDate;
|
||||
this.determineDisplayDates();
|
||||
|
||||
this.dateLabelsChoreographer.changeViewForState(
|
||||
this.getDateLabels(),
|
||||
direction,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load events
|
||||
*/
|
||||
async loadEvents(): Promise<void> {
|
||||
this.cardsAnimationState = 'out';
|
||||
async loadEvents(): Promise<number> {
|
||||
const dateSeries = await this.scheduleProvider.getDateSeries(this.uuids);
|
||||
|
||||
this.testSchedule = {};
|
||||
|
||||
for (const series of dateSeries) {
|
||||
for (const series of dateSeries.dates) {
|
||||
for (const date of series.dates) {
|
||||
const parsedDate = moment(date).startOf('day').unix();
|
||||
const index = moment(date)
|
||||
.startOf('day')
|
||||
.diff(this.baselineDate, 'days');
|
||||
|
||||
// fall back to default
|
||||
(this.testSchedule[parsedDate] ?? (this.testSchedule[parsedDate] = {}))[
|
||||
(this.testSchedule[index] ?? (this.testSchedule[index] = {}))[
|
||||
series.uid
|
||||
] = {
|
||||
dateSeries: series,
|
||||
@@ -245,20 +161,7 @@ export class CalendarViewComponent implements OnDestroy, OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
this.cursor?.scrollIntoView();
|
||||
this.cardsAnimationState = 'in';
|
||||
}
|
||||
|
||||
/**
|
||||
* On Changes
|
||||
*/
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
const layout = changes.layout?.currentValue as
|
||||
| ScheduleResponsiveBreakpoint
|
||||
| undefined;
|
||||
if (layout) {
|
||||
this.determineDisplayDates();
|
||||
}
|
||||
return this.todaySlideIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -272,13 +175,6 @@ export class CalendarViewComponent implements OnDestroy, OnInit, OnChanges {
|
||||
* Initialize
|
||||
*/
|
||||
ngOnInit() {
|
||||
this._uuidSubscription = this.scheduleProvider.uuids$.subscribe(
|
||||
async result => {
|
||||
this.uuids = result;
|
||||
await this.loadEvents();
|
||||
},
|
||||
);
|
||||
|
||||
let dayString: string | number | null =
|
||||
this.activatedRoute.snapshot.paramMap.get('date');
|
||||
if (dayString == undefined || dayString === 'now') {
|
||||
@@ -288,37 +184,30 @@ export class CalendarViewComponent implements OnDestroy, OnInit, OnChanges {
|
||||
? urlFragment
|
||||
: moment.now();
|
||||
}
|
||||
this.routeDate = moment(dayString).startOf('day');
|
||||
this.dateLabelsChoreographer = new SharedAxisChoreographer(
|
||||
this.getDateLabels(),
|
||||
);
|
||||
|
||||
this.determineDisplayDates();
|
||||
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());
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change page
|
||||
*/
|
||||
async onPageChange(direction: number) {
|
||||
this.blockDateTimeChange();
|
||||
const amount = direction * this.displayDates[0].length;
|
||||
|
||||
this.routeDate.add(amount, 'days');
|
||||
onPageChange(index: number) {
|
||||
window.history.replaceState(
|
||||
{},
|
||||
'',
|
||||
`#/${this.routeFragment}/${this.routeDate.format('YYYY-MM-DD')}`,
|
||||
);
|
||||
|
||||
for (const slide of this.displayDates) {
|
||||
for (const date of slide) {
|
||||
date.add(amount, 'days');
|
||||
}
|
||||
}
|
||||
|
||||
this.dateLabelsChoreographer.changeViewForState(
|
||||
this.getDateLabels(),
|
||||
direction > 0 ? 1 : direction < 0 ? -1 : 0,
|
||||
`${this.routeFragment}/${this.baselineDate
|
||||
.clone()
|
||||
.add(index, 'days')
|
||||
.format('YYYY-MM-DD')}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +1,89 @@
|
||||
<div>
|
||||
<ion-button fill="clear" class="left-button" (click)="mainSlides.prevPage()">
|
||||
<!--
|
||||
~ Copyright (C) 2021 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/>.
|
||||
-->
|
||||
<div class="header">
|
||||
<ion-button
|
||||
fill="clear"
|
||||
class="left-button"
|
||||
(click)="mainSwiper.pageBackwards()"
|
||||
>
|
||||
<ion-icon slot="icon-only" name="chevron-back-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" class="right-button" (click)="mainSlides.nextPage()">
|
||||
<ion-button
|
||||
fill="clear"
|
||||
class="right-button"
|
||||
(click)="mainSwiper.pageForward()"
|
||||
>
|
||||
<ion-icon slot="icon-only" name="chevron-forward-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-item>
|
||||
<ion-grid
|
||||
class="day-labels"
|
||||
[@materialSharedAxisX]="dateLabelsChoreographer.animationState"
|
||||
(@materialSharedAxisX.done)="dateLabelsChoreographer.animationDone()"
|
||||
<infinite-swiper
|
||||
class="header-swiper"
|
||||
#headerSwiper
|
||||
[slidesPerView]="layout.days"
|
||||
[controller]="mainSwiper"
|
||||
>
|
||||
<ion-row>
|
||||
<ion-col
|
||||
*ngFor="
|
||||
let item of dateLabelsChoreographer.currentValue;
|
||||
let idx = index
|
||||
"
|
||||
<ng-template let-index>
|
||||
<div
|
||||
*ngIf="index | dateFromIndex: baselineDate as date"
|
||||
class="day-labels"
|
||||
>
|
||||
<ion-button expand="block" fill="clear" (click)="datetime.open()">
|
||||
<ion-label>
|
||||
{{
|
||||
item
|
||||
| amDateFormat: ((item | dateIsThis: 'week') ? 'dddd' : 'll')
|
||||
date
|
||||
| amDateFormat: ((date | dateIsThis: 'week') ? 'dddd' : 'll')
|
||||
}}
|
||||
</ion-label>
|
||||
</ion-button>
|
||||
|
||||
<!-- This poor datetime element is a phantom element to provide us with a date picker -->
|
||||
<ion-datetime
|
||||
class="phantom"
|
||||
#datetime
|
||||
[displayFormat]="localDateFormat"
|
||||
[value]="item.toISOString()"
|
||||
(ionChange)="jumpToDate(datetime, idx)"
|
||||
[value]="date.toISOString()"
|
||||
(ionChange)="
|
||||
mainSwiper.goToIndex(
|
||||
moment($event.detail.value).diff(baselineDate, 'days')
|
||||
)
|
||||
"
|
||||
>
|
||||
</ion-datetime>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</div>
|
||||
</ng-template>
|
||||
</infinite-swiper>
|
||||
</ion-item>
|
||||
</div>
|
||||
<ion-content>
|
||||
<stapps-infinite-slides
|
||||
#mainSlides
|
||||
(pageChangeCallback)="onPageChange($event.direction)"
|
||||
<infinite-swiper
|
||||
#mainSwiper
|
||||
[controller]="headerSwiper"
|
||||
[slidesPerView]="layout.days"
|
||||
(indexChange)="onPageChange($event)"
|
||||
>
|
||||
<ion-slide class="slide" *ngFor="let slide of displayDates">
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col *ngFor="let item of slide">
|
||||
<div
|
||||
class="vertical-line"
|
||||
[style.height.px]="hoursAmount * scale"
|
||||
></div>
|
||||
<stapps-schedule-cursor
|
||||
*ngIf="item | dateIsThis: 'date'"
|
||||
[hoursRange]="hoursRange"
|
||||
[scale]="scale"
|
||||
#cursor
|
||||
>
|
||||
</stapps-schedule-cursor>
|
||||
<div [@materialManualFade]="cardsAnimationState">
|
||||
<stapps-schedule-card
|
||||
*ngFor="let entry of testSchedule[item.unix()] | keyvalue"
|
||||
[scheduleEvent]="entry.value"
|
||||
[fromHour]="hoursRange.from"
|
||||
[scale]="scale"
|
||||
>
|
||||
</stapps-schedule-card>
|
||||
</div>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-slide>
|
||||
</stapps-infinite-slides>
|
||||
|
||||
<ng-template let-index>
|
||||
<schedule-day
|
||||
[day]="index | dateFromIndex: baselineDate"
|
||||
[hoursRange]="hoursRange"
|
||||
[scale]="scale"
|
||||
[uuids]="uuids"
|
||||
[dateSeries]="testSchedule[index]"
|
||||
>
|
||||
</schedule-day>
|
||||
</ng-template>
|
||||
</infinite-swiper>
|
||||
<div
|
||||
class="hour-lines"
|
||||
*ngFor="let i of hours"
|
||||
|
||||
@@ -1,4 +1,24 @@
|
||||
div {
|
||||
/*!
|
||||
* Copyright (C) 2021 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/>.
|
||||
*/
|
||||
|
||||
.header-swiper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: relative;
|
||||
|
||||
.left-button, .right-button {
|
||||
@@ -18,64 +38,19 @@ div {
|
||||
}
|
||||
}
|
||||
|
||||
.day-labels {
|
||||
// phantom element
|
||||
.phantom {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 8px 20px 8px 30px;
|
||||
width: 100%;
|
||||
|
||||
ion-row {
|
||||
padding-right: 20px;
|
||||
|
||||
ion-col {
|
||||
ion-button {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
font-size: large;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// phantom element
|
||||
ion-datetime {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
height: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
visibility: hidden;
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
.slide {
|
||||
ion-grid {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: fit-content;
|
||||
width: 100%;
|
||||
padding-top: 8px;
|
||||
|
||||
ion-row {
|
||||
ion-col {
|
||||
width: 100%;
|
||||
|
||||
.vertical-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-left: 1px solid #dbdbdb;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
stapps-schedule-card {
|
||||
z-index: 4;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.day-labels {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hour-lines {
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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, EventEmitter, Input, Output, ViewChild} from '@angular/core';
|
||||
import {IonSlides} from '@ionic/angular';
|
||||
|
||||
/**
|
||||
* Component that can display infinite slides
|
||||
*/
|
||||
@Component({
|
||||
selector: 'stapps-infinite-slides',
|
||||
templateUrl: 'infinite-slides.html',
|
||||
styleUrls: ['infinite-slides.scss'],
|
||||
})
|
||||
export class InfiniteSlidesComponent {
|
||||
/**
|
||||
* If the view was initialized
|
||||
*/
|
||||
initialized = false;
|
||||
|
||||
/**
|
||||
* Callback for when the page has changed
|
||||
*
|
||||
* The caller needs to replace the component here
|
||||
*/
|
||||
@Output() pageChangeCallback: EventEmitter<{
|
||||
/**
|
||||
* The current page
|
||||
*/
|
||||
currentPage: number;
|
||||
|
||||
/**
|
||||
* The direction that was scrolled
|
||||
*/
|
||||
direction: number;
|
||||
}> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* The virtual page we are currently on
|
||||
*/
|
||||
page = 0;
|
||||
|
||||
/**
|
||||
* Options for IonSlides
|
||||
*/
|
||||
@Input() slideOpts = {
|
||||
initialSlide: 1,
|
||||
speed: 200,
|
||||
loop: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Slider element
|
||||
*/
|
||||
@ViewChild('slides') slides: IonSlides;
|
||||
|
||||
/**
|
||||
* Slide to next page
|
||||
*/
|
||||
async nextPage() {
|
||||
await this.slides.slideNext(this.slideOpts.speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change page
|
||||
*/
|
||||
async onPageChange(direction: number) {
|
||||
if (!this.initialized) {
|
||||
// setting the initial page to 1 causes a page change to
|
||||
// be emitted initially, which intern would cause the
|
||||
// page to actually change one to far, so we listen for
|
||||
// that first page change and skip it
|
||||
this.initialized = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.page += direction;
|
||||
|
||||
this.pageChangeCallback.emit({
|
||||
currentPage: this.page,
|
||||
direction: direction,
|
||||
});
|
||||
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
this.slides.slideTo(1, 0, false).then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Slide to previous page
|
||||
*/
|
||||
async prevPage() {
|
||||
await this.slides.slidePrev(this.slideOpts.speed);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<ion-slides
|
||||
#slides
|
||||
pager="false"
|
||||
[options]="slideOpts"
|
||||
(ionSlideNextEnd)="onPageChange(1)"
|
||||
(ionSlidePrevEnd)="onPageChange(-1)"
|
||||
>
|
||||
<ng-content></ng-content>
|
||||
</ion-slides>
|
||||
@@ -1,4 +0,0 @@
|
||||
ion-slides {
|
||||
width: 100%;
|
||||
height: 1100px; // BIG TODO: This is completely bypasses the scale parameter
|
||||
}
|
||||
256
src/app/modules/schedule/page/grid/infinite-swiper.component.ts
Normal file
256
src/app/modules/schedule/page/grid/infinite-swiper.component.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import {
|
||||
AfterViewInit,
|
||||
Component,
|
||||
ContentChild,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
QueryList,
|
||||
SimpleChanges,
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
ViewChildren,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core';
|
||||
import Swiper from 'swiper';
|
||||
import {drop, dropRight, forEach, range, take, takeRight, zip} from 'lodash-es';
|
||||
import {materialManualFade} from '../../../../animation/material-motion';
|
||||
|
||||
export interface SlideContext {
|
||||
$implicit: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for specified amount of time
|
||||
*/
|
||||
async function wait(ms?: number) {
|
||||
await new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an infinite version of the swiper
|
||||
*
|
||||
* The basic principle it works on is
|
||||
* 1. The user can never swiper further than the amount of visible slides
|
||||
* 2. Only out of view slides are re-initialized
|
||||
*/
|
||||
@Component({
|
||||
selector: 'infinite-swiper',
|
||||
templateUrl: 'infinite-swiper.html',
|
||||
styleUrls: ['infinite-swiper.scss'],
|
||||
animations: [materialManualFade],
|
||||
})
|
||||
export class InfiniteSwiperComponent
|
||||
implements OnInit, AfterViewInit, OnDestroy, OnChanges
|
||||
{
|
||||
@Input() controller?: InfiniteSwiperComponent;
|
||||
|
||||
@Input() slidesPerView = 5;
|
||||
|
||||
virtualIndex = 0;
|
||||
|
||||
@ContentChild(TemplateRef) userSlideTemplateRef: TemplateRef<SlideContext>;
|
||||
|
||||
@Output() indexChange = new EventEmitter<number>();
|
||||
|
||||
slidesArray: number[];
|
||||
|
||||
@ViewChild('swiper', {static: true})
|
||||
swiperElement: ElementRef<HTMLDivElement>;
|
||||
|
||||
@ViewChildren('slideContainers', {read: ViewContainerRef})
|
||||
slideContainers: QueryList<ViewContainerRef>;
|
||||
|
||||
swiper: Swiper;
|
||||
|
||||
visibilityState: 'in' | 'out' = 'in';
|
||||
|
||||
private preventControllerCallback = false;
|
||||
|
||||
ngOnInit() {
|
||||
this.createSwiper();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.initSwiper();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.swiper.destroy();
|
||||
this.clearSlides();
|
||||
}
|
||||
|
||||
async ngOnChanges(changes: SimpleChanges) {
|
||||
if ('slidesPerView' in changes) {
|
||||
const change = changes.slidesPerView;
|
||||
if (change.isFirstChange()) return;
|
||||
|
||||
// little bit of a cheesy trick just to reinitialize
|
||||
// everything... But you know, it works just fine.
|
||||
// And how often are you realistically going to
|
||||
// resize your window.
|
||||
this.visibilityState = 'out';
|
||||
await wait(250);
|
||||
|
||||
this.ngOnDestroy();
|
||||
this.createSwiper();
|
||||
await wait();
|
||||
this.initSwiper();
|
||||
|
||||
this.visibilityState = 'in';
|
||||
}
|
||||
}
|
||||
|
||||
createSwiper() {
|
||||
this.resetSlides();
|
||||
|
||||
// I have absolutely no clue why two results are returned here.
|
||||
// Probably a bug, so be on the lookout if you get odd errors
|
||||
const [swiper] = new Swiper('.swiper', {
|
||||
// TODO: evaluate if the controller has decent performance, some time in the future
|
||||
// modules: [Controller],
|
||||
slidesPerView: this.slidesPerView,
|
||||
initialSlide: this.slidesPerView,
|
||||
init: false,
|
||||
}) as unknown as [Swiper, Swiper];
|
||||
this.swiper = swiper;
|
||||
}
|
||||
|
||||
initSwiper() {
|
||||
this.swiper.init(this.swiperElement.nativeElement);
|
||||
// SwiperJS controller still has some performance issues unfortunately...
|
||||
// So unfortunately we are kind of forced to use a workaround :/
|
||||
// TODO: evaluate if the controller has decent performance, some time in the future
|
||||
/*setTimeout(() => {
|
||||
this.swiper.controller.control = this.controller?.swiper;
|
||||
});*/
|
||||
|
||||
this.shiftSlides();
|
||||
|
||||
this.swiper.on('activeIndexChange', () => {
|
||||
if (!this.preventControllerCallback) {
|
||||
this.controller?.controllerSlideTo(this.swiper.activeIndex);
|
||||
}
|
||||
});
|
||||
|
||||
this.swiper.on('slideChangeTransitionEnd', () => {
|
||||
this.shiftSlides(this.swiper.activeIndex);
|
||||
this.indexChange.emit(this.virtualIndex);
|
||||
this.preventControllerCallback = false;
|
||||
});
|
||||
}
|
||||
|
||||
clearSlides() {
|
||||
for (const container of this.slideContainers) {
|
||||
while (container.length > 0) {
|
||||
container.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pageForward() {
|
||||
this.swiper.slideTo(this.slidesPerView * 2);
|
||||
}
|
||||
|
||||
pageBackwards() {
|
||||
this.swiper.slideTo(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is require to not cause a callback loop
|
||||
* when the controller slides
|
||||
*/
|
||||
private async controllerSlideTo(index: number) {
|
||||
// TODO: prevent virtual index falling out of sync
|
||||
this.preventControllerCallback = true;
|
||||
this.swiper.slideTo(index);
|
||||
await wait(400);
|
||||
if (this.controller && this.virtualIndex !== this.controller.virtualIndex) {
|
||||
console.warn(
|
||||
`Virtual indices fell out of sync ${this.virtualIndex} : ${this.controller.virtualIndex}, correcting...`,
|
||||
);
|
||||
await this.controller.goToIndex(this.virtualIndex, false);
|
||||
}
|
||||
}
|
||||
|
||||
async goToIndex(index: number, runCallbacks = true) {
|
||||
if (runCallbacks) {
|
||||
this.controller?.goToIndex(index, false);
|
||||
}
|
||||
|
||||
this.visibilityState = 'out';
|
||||
|
||||
await wait(250);
|
||||
|
||||
this.virtualIndex = index;
|
||||
this.clearSlides();
|
||||
this.shiftSlides();
|
||||
|
||||
this.visibilityState = 'in';
|
||||
}
|
||||
|
||||
shiftSlides(activeIndex = this.slidesPerView) {
|
||||
const delta = this.slidesPerView - activeIndex;
|
||||
const deltaAmount = Math.abs(delta);
|
||||
const direction = delta > 0;
|
||||
this.virtualIndex -= delta;
|
||||
const containers = this.slideContainers.toArray();
|
||||
|
||||
const slides = containers.map(it =>
|
||||
it.length > 0 ? it.detach(0) : undefined,
|
||||
);
|
||||
|
||||
// delete slides that are going to be dropped
|
||||
for (const slide of (direction ? takeRight : take)(slides, deltaAmount)) {
|
||||
slide?.destroy();
|
||||
}
|
||||
|
||||
// reuse existing slides
|
||||
const newElements: undefined[] = Array.from({length: deltaAmount});
|
||||
const shiftedSlides = direction
|
||||
? [...newElements, ...dropRight(slides, deltaAmount)]
|
||||
: [...drop(slides, deltaAmount), ...newElements];
|
||||
|
||||
forEach(zip(containers, shiftedSlides), ([container, element], i) => {
|
||||
// TODO: we should be able to skip this... In theory.
|
||||
while (container!.length > 0) {
|
||||
console.warn('Slide container is not empty after detach!');
|
||||
container!.remove();
|
||||
}
|
||||
|
||||
if (element) {
|
||||
container!.insert(element);
|
||||
} else {
|
||||
container!.createEmbeddedView(this.userSlideTemplateRef, {
|
||||
$implicit: this.virtualIndex + (i - this.slidesPerView),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.swiper.slideTo(this.slidesPerView, 0, false);
|
||||
}
|
||||
|
||||
resetSlides() {
|
||||
this.slidesArray = range(0, this.slidesPerView * 3);
|
||||
}
|
||||
}
|
||||
26
src/app/modules/schedule/page/grid/infinite-swiper.html
Normal file
26
src/app/modules/schedule/page/grid/infinite-swiper.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!--
|
||||
~ Copyright (C) 2021 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/>.
|
||||
-->
|
||||
|
||||
<div #swiper class="swiper">
|
||||
<div class="swiper-wrapper">
|
||||
<div
|
||||
[@materialManualFade]="visibilityState"
|
||||
class="swiper-slide"
|
||||
*ngFor="let index of slidesArray"
|
||||
>
|
||||
<ng-container #slideContainers></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
19
src/app/modules/schedule/page/grid/infinite-swiper.scss
Normal file
19
src/app/modules/schedule/page/grid/infinite-swiper.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* Copyright (C) 2021 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/>.
|
||||
*/
|
||||
|
||||
.swiper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018, 2019 StApps
|
||||
* Copyright (C) 2021 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.
|
||||
@@ -13,8 +13,8 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import moment from 'moment';
|
||||
import {HoursRange} from '../schema/schema';
|
||||
import moment from 'moment';
|
||||
|
||||
/**
|
||||
* Component that displays the schedule
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*!
|
||||
* Copyright (C) 2021 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/>.
|
||||
*/
|
||||
|
||||
div {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -5,7 +20,7 @@ div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
top: 4px;
|
||||
z-index: 0;
|
||||
|
||||
div {
|
||||
@@ -13,9 +28,8 @@ div {
|
||||
height: fit-content;
|
||||
|
||||
hr {
|
||||
width: calc(100% - 8px);
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
margin-left: 4px;
|
||||
margin-right: 16px;
|
||||
margin-top: 8px;
|
||||
height: 2px;
|
||||
@@ -29,7 +43,6 @@ div {
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: -4px;
|
||||
border-radius: 50% 0 50% 50%;
|
||||
transform: rotateZ(45deg);
|
||||
background-color: var(--ion-color-primary);
|
||||
|
||||
68
src/app/modules/schedule/page/grid/schedule-day.component.ts
Normal file
68
src/app/modules/schedule/page/grid/schedule-day.component.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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} from '@angular/core';
|
||||
import moment from 'moment';
|
||||
import {Range, ScheduleEvent} from '../schema/schema';
|
||||
import {ScheduleProvider} from '../../schedule.provider';
|
||||
import {SCISO8601Duration, SCUuid} from '@openstapps/core';
|
||||
import {materialFade} from '../../../../animation/material-motion';
|
||||
|
||||
@Component({
|
||||
selector: 'schedule-day',
|
||||
templateUrl: 'schedule-day.html',
|
||||
styleUrls: ['schedule-day.scss'],
|
||||
animations: [materialFade],
|
||||
})
|
||||
export class ScheduleDayComponent {
|
||||
@Input() day: moment.Moment;
|
||||
|
||||
@Input() hoursRange: Range<number>;
|
||||
|
||||
@Input() uuids: SCUuid[];
|
||||
|
||||
@Input() scale: number;
|
||||
|
||||
@Input() frequencies?: SCISO8601Duration[];
|
||||
|
||||
@Input() dateSeries?: Record<string, ScheduleEvent>;
|
||||
|
||||
constructor(protected readonly scheduleProvider: ScheduleProvider) {}
|
||||
|
||||
// ngOnInit() {
|
||||
// this.dateSeries = this.fetchDateSeries();
|
||||
// }
|
||||
|
||||
// TODO: backend bug results in the wrong date series being returned
|
||||
/* async fetchDateSeries(): Promise<ScheduleEvent[]> {
|
||||
const dateSeries = await this.scheduleProvider.getDateSeries(
|
||||
this.uuids,
|
||||
this.frequencies,
|
||||
this.momentDay.clone().startOf('day').toISOString(),
|
||||
this.momentDay.clone().endOf('day').toISOString(),
|
||||
);
|
||||
|
||||
for (const series of dateSeries.dates) {
|
||||
console.log(JSON.stringify(series.dates));
|
||||
}
|
||||
|
||||
return dateSeries.dates.map(it => ({
|
||||
dateSeries: it,
|
||||
time: {
|
||||
start: moment(it.dates.find(date => date === this.day)).hours(),
|
||||
duration: it.duration,
|
||||
},
|
||||
}));
|
||||
} */
|
||||
}
|
||||
35
src/app/modules/schedule/page/grid/schedule-day.html
Normal file
35
src/app/modules/schedule/page/grid/schedule-day.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!--
|
||||
~ Copyright (C) 2021 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/>.
|
||||
-->
|
||||
<div>
|
||||
<div class="vertical-line"></div>
|
||||
<stapps-schedule-cursor
|
||||
class="cursor"
|
||||
*ngIf="day | dateIsThis: 'date'"
|
||||
[hoursRange]="hoursRange"
|
||||
[scale]="scale"
|
||||
>
|
||||
</stapps-schedule-cursor>
|
||||
<div *ngIf="dateSeries as dateSeries">
|
||||
<!-- TODO: entry/exit animation -->
|
||||
<stapps-schedule-card
|
||||
class="schedule-card"
|
||||
*ngFor="let entry of dateSeries | entries"
|
||||
[scheduleEvent]="entry"
|
||||
[fromHour]="hoursRange.from"
|
||||
[scale]="scale"
|
||||
>
|
||||
</stapps-schedule-card>
|
||||
</div>
|
||||
</div>
|
||||
37
src/app/modules/schedule/page/grid/schedule-day.scss
Normal file
37
src/app/modules/schedule/page/grid/schedule-day.scss
Normal file
@@ -0,0 +1,37 @@
|
||||
/*!
|
||||
* Copyright (C) 2021 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/>.
|
||||
*/
|
||||
|
||||
.schedule-card {
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
left: 0;
|
||||
z-index: 4;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.vertical-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: #dbdbdb;
|
||||
}
|
||||
@@ -115,7 +115,7 @@ export class ScheduleSingleEventsComponent implements OnInit, OnDestroy {
|
||||
|
||||
// TODO: replace with filter
|
||||
return ScheduleSingleEventsComponent.groupDateSeriesToDays(
|
||||
dateSeries.filter(it => isNil(it.repeatFrequency)),
|
||||
dateSeries.dates.filter(it => isNil(it.repeatFrequency)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,8 +71,8 @@ export class ScheduleViewComponent extends CalendarViewComponent {
|
||||
/**
|
||||
* Load events
|
||||
*/
|
||||
// @Override
|
||||
async loadEvents(): Promise<void> {
|
||||
// TODO: @Override
|
||||
/*async loadEvents(): Promise<void> {
|
||||
this.cardsAnimationState = 'out';
|
||||
const dateSeries = await this.scheduleProvider.getDateSeries(
|
||||
this.uuids,
|
||||
@@ -82,7 +82,7 @@ export class ScheduleViewComponent extends CalendarViewComponent {
|
||||
|
||||
this.testSchedule = {};
|
||||
|
||||
for (const series of dateSeries) {
|
||||
for (const series of dateSeries.dates) {
|
||||
if (series.dates.length > 0) {
|
||||
const date = moment(moment.now())
|
||||
.startOf('week')
|
||||
@@ -104,5 +104,5 @@ export class ScheduleViewComponent extends CalendarViewComponent {
|
||||
|
||||
this.cursor?.scrollIntoView();
|
||||
this.cardsAnimationState = 'in';
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020 StApps
|
||||
* Copyright (C) 2021 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.
|
||||
@@ -21,6 +21,11 @@ interface DateRange {
|
||||
start: number;
|
||||
}
|
||||
|
||||
export interface Range<T> {
|
||||
from: T;
|
||||
to: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal interface to provide information about a custom event
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018, 2019 StApps
|
||||
* Copyright (C) 2021 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.
|
||||
@@ -25,13 +25,16 @@ import {UtilModule} from '../../util/util.module';
|
||||
import {DataModule} from '../data/data.module';
|
||||
import {DataProvider} from '../data/data.provider';
|
||||
import {CalendarViewComponent} from './page/calendar-view.component';
|
||||
import {InfiniteSlidesComponent} from './page/grid/infinite-slides.component';
|
||||
import {ScheduleCursorComponent} from './page/grid/schedule-cursor.component';
|
||||
import {ModalEventCreatorComponent} from './page/modal/modal-event-creator.component';
|
||||
import {SchedulePageComponent} from './page/schedule-page.component';
|
||||
import {ScheduleSingleEventsComponent} from './page/schedule-single-events.component';
|
||||
import {ScheduleViewComponent} from './page/schedule-view.component';
|
||||
import {ScheduleProvider} from './schedule.provider';
|
||||
import {SwiperModule} from 'swiper/angular';
|
||||
import {ScheduleDayComponent} from './page/grid/schedule-day.component';
|
||||
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||
import {InfiniteSwiperComponent} from './page/grid/infinite-swiper.component';
|
||||
|
||||
const settingsRoutes: Routes = [
|
||||
{path: 'schedule', redirectTo: 'schedule/calendar/now'},
|
||||
@@ -48,23 +51,26 @@ const settingsRoutes: Routes = [
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CalendarViewComponent,
|
||||
InfiniteSlidesComponent,
|
||||
ModalEventCreatorComponent,
|
||||
ScheduleCardComponent,
|
||||
ScheduleCursorComponent,
|
||||
SchedulePageComponent,
|
||||
ScheduleSingleEventsComponent,
|
||||
ScheduleDayComponent,
|
||||
ScheduleViewComponent,
|
||||
InfiniteSwiperComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
UtilModule,
|
||||
IonicModule.forRoot(),
|
||||
TranslateModule.forChild(),
|
||||
RouterModule.forChild(settingsRoutes),
|
||||
DataModule,
|
||||
FormsModule,
|
||||
IonicModule.forRoot(),
|
||||
MomentModule,
|
||||
RouterModule.forChild(settingsRoutes),
|
||||
SwiperModule,
|
||||
TranslateModule.forChild(),
|
||||
UtilModule,
|
||||
ThingTranslateModule,
|
||||
],
|
||||
providers: [ScheduleProvider, DataProvider, DateFormatPipe],
|
||||
})
|
||||
|
||||
@@ -112,9 +112,17 @@ export class ScheduleProvider implements OnDestroy {
|
||||
frequencies?: Array<SCISO8601Duration>,
|
||||
from?: SCISO8601Date | 'now',
|
||||
to?: SCISO8601Date | 'now',
|
||||
): Promise<SCDateSeries[]> {
|
||||
): Promise<{
|
||||
dates: SCDateSeries[];
|
||||
min: SCISO8601Date;
|
||||
max: SCISO8601Date;
|
||||
}> {
|
||||
if (uuids.length === 0) {
|
||||
return [];
|
||||
return {
|
||||
dates: [],
|
||||
min: '',
|
||||
max: '',
|
||||
};
|
||||
}
|
||||
|
||||
const filters: SCSearchFilter[] = [
|
||||
@@ -159,12 +167,14 @@ export class ScheduleProvider implements OnDestroy {
|
||||
if (from || to) {
|
||||
const bounds: Bounds<string> = {};
|
||||
if (from) {
|
||||
console.log(from);
|
||||
bounds.lowerBound = {
|
||||
limit: from,
|
||||
mode: 'inclusive',
|
||||
};
|
||||
}
|
||||
if (to) {
|
||||
console.log(to);
|
||||
bounds.upperBound = {
|
||||
limit: to,
|
||||
mode: 'inclusive',
|
||||
@@ -179,17 +189,22 @@ export class ScheduleProvider implements OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
await this.dataProvider.search({
|
||||
filter: {
|
||||
arguments: {
|
||||
filters: filters,
|
||||
operation: 'and',
|
||||
},
|
||||
type: 'boolean',
|
||||
const result = await this.dataProvider.search({
|
||||
filter: {
|
||||
arguments: {
|
||||
filters: filters,
|
||||
operation: 'and',
|
||||
},
|
||||
})
|
||||
).data as SCDateSeries[];
|
||||
type: 'boolean',
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
dates: result.data as SCDateSeries[],
|
||||
// TODO: https://gitlab.com/openstapps/backend/-/issues/100
|
||||
min: new Date(2021, 11, 1).toISOString(),
|
||||
max: new Date(2022, 1, 24).toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user