Skip to content

Commit

Permalink
Display how much time is left in ScheduledEvents (hobbyfarm#193)
Browse files Browse the repository at this point in the history
* Display Event times in accesscode modal

* Display time left string under event name

* Run prettier

* Also work when the time is up

* use const

* Display to the user that he might finish any active sessions

* remove duplicate ctx
  • Loading branch information
jggoebel authored Jan 31, 2024
1 parent 30c1157 commit 5c76657
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 22 deletions.
14 changes: 11 additions & 3 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<clr-dropdown *ngIf="ctx.valid">
<button class="nav-text" clrDropdownToggle>
<cds-icon shape="layers" solid></cds-icon>
{{ ctx.scheduledEventName }}
{{ ctx.scheduledEvent.name }}
<cds-icon
shape="angle"
direction="down"
Expand All @@ -28,7 +28,7 @@
clrDropdownItem
*ngFor="let a of scheduledEvents | keyvalue"
(click)="setAccessCode(a.key)"
>{{ a.value }}</a
>{{ a.value.name }}</a
>
</clr-dropdown-menu>
</clr-dropdown>
Expand Down Expand Up @@ -124,7 +124,11 @@ <h3 class="modal-title">{{ aboutTitle }}</h3>
</div>
</clr-modal>

<clr-modal #accesscodemodal [(clrModalOpen)]="accessCodeModalOpened">
<clr-modal
#accesscodemodal
[(clrModalOpen)]="accessCodeModalOpened"
[clrModalSize]="'lg'"
>
<h3 class="modal-title">Manage Access Codes</h3>
<div class="modal-body">
<clr-alert
Expand Down Expand Up @@ -222,6 +226,7 @@ <h3 class="modal-title">Manage Access Codes</h3>
>
<clr-dg-column>Access Code</clr-dg-column>
<clr-dg-column>Valid</clr-dg-column>
<clr-dg-column>Access until</clr-dg-column>
<clr-dg-row *clrDgItems="let a of accesscodes" [clrDgItem]="a">
<clr-dg-cell>{{ a }}</clr-dg-cell>
<clr-dg-cell>
Expand All @@ -240,6 +245,9 @@ <h3 class="modal-title">Manage Access Codes</h3>
</ng-container>
<span> {{ getScheduledEventNameForAccessCode(a) }}</span>
</clr-dg-cell>
<clr-dg-cell [class]="getTimestampColor(a)">
<span> {{ getScheduledEventEndTimestampForAccessCode(a) }}</span>
</clr-dg-cell>
</clr-dg-row>
</clr-datagrid>
</ng-container>
Expand Down
4 changes: 4 additions & 0 deletions src/app/app.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@
color: var(--clr-color-success-500, green);
}

clr-dg-cell.red {
color: var(--clr-color-danger-500, red);
}

.header-actions {
clr-dropdown-menu {
a {
Expand Down
25 changes: 22 additions & 3 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
TypedInput,
TypedSettingsService,
} from './services/typedSettings.service';
import { ScheduledEvent } from 'src/data/ScheduledEvent';

@Component({
selector: 'app-root',
Expand Down Expand Up @@ -56,7 +57,7 @@ export class AppComponent implements OnInit {

public accesscodes: string[] = [];
public selectedAccesscodesForDeletion: string[] = [];
public scheduledEvents: Map<string, string> = new Map();
public scheduledEvents: Map<string, ScheduledEvent> = new Map();
public ctx: Context = {} as Context;

public email = '';
Expand Down Expand Up @@ -177,7 +178,7 @@ export class AppComponent implements OnInit {
this.ctx = c;
this.userService
.getScheduledEvents()
.subscribe((se: Map<string, string>) => {
.subscribe((se: Map<string, ScheduledEvent>) => {
se = new Map(Object.entries(se));
this.scheduledEvents = se;
});
Expand Down Expand Up @@ -298,7 +299,25 @@ export class AppComponent implements OnInit {
}

public getScheduledEventNameForAccessCode(ac: string) {
return this.scheduledEvents?.get(ac);
return this.scheduledEvents?.get(ac)?.name;
}

public getScheduledEventEndTimestampForAccessCode(ac: string) {
return this.scheduledEvents?.get(ac)?.end_timestamp;
}

public getTimestampColor(ac: string) {
const target = this.scheduledEvents?.get(ac)?.end_timestamp;
if (target) {
const now = new Date();
const targetDate = new Date(target);
const timeDiff = targetDate.getTime() - now.getTime();
if (timeDiff <= 0) {
return 'red';
}
}

return 'green';
}

public saveAccessCode(activate = false) {
Expand Down
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import {
hostIcon,
eyeIcon,
eyeHideIcon,
clockIcon,
} from '@cds/core/icon';

ClarityIcons.addIcons(
Expand Down Expand Up @@ -95,6 +96,7 @@ ClarityIcons.addIcons(
hostIcon,
eyeIcon,
eyeHideIcon,
clockIcon,
);

export function tokenGetter() {
Expand Down
21 changes: 18 additions & 3 deletions src/app/home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,27 @@ <h1>Active session</h1>

<div class="clr-row" *ngIf="ctx.valid">
<div class="clr-col">
<h1>{{ ctx.scheduledEventName }}</h1>
<h3>Select a scenario</h3>
<h1>{{ ctx.scheduledEvent.name }}</h1>
<ng-container *ngIf="isTimeLeft()">
<div>
<cds-icon shape="clock"></cds-icon>
{{ getTimeLeftString(ctx.scheduledEvent.end_timestamp) }} left
</div>
<p>{{ ctx.scheduledEvent.description }}</p>
<h3>Select a scenario</h3>
</ng-container>
<ng-container *ngIf="!isTimeLeft()">
<cds-icon shape="clock"></cds-icon> Your time ran out at
{{ ctx.scheduledEvent.end_timestamp }}.
<p>
You will be able to finish any active sessions, but you are unable to
start any new scenario.
</p>
</ng-container>
</div>
</div>

<div class="clr-row">
<div class="clr-row" *ngIf="isTimeLeft()">
<div class="clr-col-12" *ngIf="!ctx.valid">
<h1>Add AccessCode</h1>
Add AccessCodes to your account by clicking on your username on the top
Expand Down
4 changes: 4 additions & 0 deletions src/app/home.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@
.alert-home {
width: 100%;
}

cds-icon[shape='clock'] {
color: black;
}
41 changes: 41 additions & 0 deletions src/app/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,45 @@ export class HomeComponent implements OnInit, OnDestroy {
this.contextSubscription.unsubscribe();
this.progressSubscription.unsubscribe();
}

isTimeLeft() {
const target = this.ctx?.scheduledEvent?.end_timestamp;

if (target) {
const now = new Date();
const targetDate = new Date(target);
const timeDiff = targetDate.getTime() - now.getTime();
return timeDiff > 0;
}

return false;
}

getTimeLeftString(target: string) {
const now = new Date();
const targetDate = new Date(target);
const timeDiff = targetDate.getTime() - now.getTime();

if (timeDiff <= 0) {
return 'Time already passed';
}

// Convert time difference from milliseconds
const minutes = Math.floor((timeDiff / 1000 / 60) % 60);
const hours = Math.floor((timeDiff / (1000 * 60 * 60)) % 24);
const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24));

let timeUntil = '';
if (days > 0) {
timeUntil += `${days}d `;
}
if (hours > 0) {
timeUntil += `${hours}h `;
}
if (minutes > 0) {
timeUntil += `${minutes}m `;
}

return timeUntil.trim();
}
}
10 changes: 6 additions & 4 deletions src/app/services/context.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { SettingsService } from './settings.service';
import { UserService } from './user.service';
import { ScheduledEvent } from 'src/data/ScheduledEvent';

export interface Context {
accessCode: string;
scheduledEventName: string;
scheduledEvent: ScheduledEvent;
valid: boolean;
}

Expand Down Expand Up @@ -50,7 +51,7 @@ export class ContextService {
refresh(force = false) {
this.userService
.getScheduledEvents(force)
.subscribe((se: Map<string, string>) => {
.subscribe((se: Map<string, ScheduledEvent>) => {
se = new Map(Object.entries(se));

if (se.size == 0) {
Expand All @@ -69,8 +70,9 @@ export class ContextService {
ctxAccessCode = se.keys().next().value;
}
this.currentContext.accessCode = ctxAccessCode;
this.currentContext.scheduledEventName =
se.get(this.currentContext.accessCode) ?? 'None';
this.currentContext.scheduledEvent =
se.get(this.currentContext.accessCode) ??
({ name: 'None' } as ScheduledEvent);
this.currentContext.valid = true;
this.updateContext(this.currentContext);
});
Expand Down
23 changes: 14 additions & 9 deletions src/app/services/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
extractResponseContent,
GargantuaClientFactory,
} from './gargantua.service';
import { ScheduledEvent } from 'src/data/ScheduledEvent';

@Injectable({
providedIn: 'root',
Expand All @@ -17,11 +18,11 @@ export class UserService {
private _acModified = new BehaviorSubject(false);

private fetchedSEs = false;
private scheduledEvents$: Observable<Map<string, string>> | null = null;
private cachedScheduledEventsList: Map<string, string> = new Map();
private bh: BehaviorSubject<Map<string, string>> = new BehaviorSubject(
this.cachedScheduledEventsList,
);
private scheduledEvents$: Observable<Map<string, ScheduledEvent>> | null =
null;
private cachedScheduledEventsList: Map<string, ScheduledEvent> = new Map();
private bh: BehaviorSubject<Map<string, ScheduledEvent>> =
new BehaviorSubject(this.cachedScheduledEventsList);

public getModifiedObservable() {
return this._acModified.asObservable();
Expand Down Expand Up @@ -65,16 +66,20 @@ export class UserService {
);
}

public getScheduledEvents(force = false): Observable<Map<string, string>> {
public getScheduledEvents(
force = false,
): Observable<Map<string, ScheduledEvent>> {
if (!force && this.fetchedSEs) {
return of(this.cachedScheduledEventsList);
} else if (this.scheduledEvents$) {
// If request is in-flight, return the ongoing Observable
return this.scheduledEvents$;
} else {
this.scheduledEvents$ = this.garg.get('/scheduledevents').pipe(
map<any, Map<string, string>>(extractResponseContent),
tap((p: Map<string, string>) => this.setScheduledEventsCache(p)),
map<any, Map<string, ScheduledEvent>>(extractResponseContent),
tap((p: Map<string, ScheduledEvent>) =>
this.setScheduledEventsCache(p),
),
// Use shareReplay to multicast and replay the last emitted value to new subscribers
shareReplay(1),
// On complete or error, set the inflight observable to null
Expand All @@ -84,7 +89,7 @@ export class UserService {
return this.scheduledEvents$;
}
}
public setScheduledEventsCache(list: Map<string, string>) {
public setScheduledEventsCache(list: Map<string, ScheduledEvent>) {
this.cachedScheduledEventsList = list;
this.fetchedSEs = true;
this.bh.next(list);
Expand Down
6 changes: 6 additions & 0 deletions src/data/ScheduledEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class ScheduledEvent {
id: string;
name: string;
description: string;
end_timestamp: string;
}

0 comments on commit 5c76657

Please sign in to comment.