feat: a calendar to subscribe to, and the backlog's corners
- Each person can subscribe their dates in Google Calendar, Outlook or
Apple Calendar: GET /api/calendar/{token}.ics (no session) brings
their unfinished tickets with a due date (assigned or followed), the
planned and running sprints and the planned releases of their
projects, as all-day events linking to the web. The token is made,
renewed or dropped under /me/calendar (and in GraphQL); in the web,
from «Calendario» in «Mi trabajo».
- The backlog panel keeps the selected rows' background inside its
rounded corners.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
export * from './lib/notifications';
|
||||
export * from './lib/facades/projects.facade';
|
||||
export * from './lib/facades/calendar.facade';
|
||||
export * from './lib/facades/project.facade';
|
||||
export * from './lib/facades/board.facade';
|
||||
export * from './lib/facades/issue.facade';
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { inject, Injectable, signal } from '@angular/core';
|
||||
import { CalendarFeed, CalendarGateway } from '@task-stream/web/tracker/domain';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/**
|
||||
* My calendar subscription for presenters: the secret URL to paste into a
|
||||
* calendar app, made, renewed or dropped here. Per person and seldom
|
||||
* touched, so it lives in this facade alone, without a store.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CalendarFacade {
|
||||
private readonly gateway = inject(CalendarGateway);
|
||||
|
||||
readonly feed = signal<CalendarFeed | null>(null);
|
||||
readonly loaded = signal(false);
|
||||
readonly busy = signal(false);
|
||||
readonly error = signal<string | null>(null);
|
||||
|
||||
load(): void {
|
||||
this.run(this.gateway.mine());
|
||||
}
|
||||
|
||||
/** A new URL: the previous one, if any, stops working. */
|
||||
create(): void {
|
||||
this.run(this.gateway.create());
|
||||
}
|
||||
|
||||
revoke(): void {
|
||||
this.busy.set(true);
|
||||
this.gateway.revoke().subscribe({
|
||||
next: () => this.done(null),
|
||||
error: () => this.failed(),
|
||||
});
|
||||
}
|
||||
|
||||
private run(call: Observable<CalendarFeed | null>): void {
|
||||
this.busy.set(true);
|
||||
this.error.set(null);
|
||||
call.subscribe({
|
||||
next: (feed) => this.done(feed),
|
||||
error: () => this.failed(),
|
||||
});
|
||||
}
|
||||
|
||||
private done(feed: CalendarFeed | null): void {
|
||||
this.feed.set(feed);
|
||||
this.loaded.set(true);
|
||||
this.busy.set(false);
|
||||
}
|
||||
|
||||
private failed(): void {
|
||||
this.loaded.set(true);
|
||||
this.busy.set(false);
|
||||
this.error.set('No se ha podido. Inténtalo de nuevo en un momento.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user