* Home key added to navigation, navigates to dashboard

* route animation reworked into routeTransition to avoid memory leaks
This commit is contained in:
Dávid Danyi 2018-09-24 15:54:27 +02:00
parent 41ad9d9a28
commit 8aa0828701
14 changed files with 138 additions and 137 deletions

23
src/app/admin/admin-routing.module.ts Normal file → Executable file
View File

@ -11,63 +11,56 @@ import { SlideResolverService } from './slide-resolver.service';
import { SlideService } from '../shared/service/slide.service'; import { SlideService } from '../shared/service/slide.service';
import { DashboardComponent } from './dashboard/dashboard.component'; import { DashboardComponent } from './dashboard/dashboard.component';
const routes: Routes = [ const routes: Routes = [
{ {
path: 'admin', path: 'admin',
redirectTo: '/dashboard', redirectTo: '/dashboard',
pathMatch: 'full' pathMatch: 'full',
// canActivate: [AuthGuardService, RoleGuardService],
}, { }, {
path: 'dashboard', path: 'dashboard',
component: DashboardComponent, component: DashboardComponent,
// canActivate: [AuthGuardService, RoleGuardService],
}, { }, {
path: 'admin/teams', path: 'admin/teams',
component: TeamListComponent, component: TeamListComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
teams: TeamService, teams: TeamService,
} }
}, { }, {
path: 'admin/team/new', path: 'admin/team/new',
component: TeamEditorComponent, component: TeamEditorComponent,
// canActivate: [AuthGuardService, RoleGuardService],
}, { }, {
path: 'admin/team/edit/:id', path: 'admin/team/edit/:id',
component: TeamEditorComponent, component: TeamEditorComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
team: TeamResolverService, team: TeamResolverService,
} },
}, { }, {
path: 'admin/slides', path: 'admin/slides',
component: SlideListComponent, component: SlideListComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
slides: SlideService, slides: SlideService,
teams: TeamService, teams: TeamService,
} },
}, { }, {
path: 'admin/slide/new', path: 'admin/slide/new',
component: SlideEditorComponent, component: SlideEditorComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
teams: TeamService, teams: TeamService,
} },
}, { }, {
path: 'admin/slide/edit/:id', path: 'admin/slide/edit/:id',
component: SlideEditorComponent, component: SlideEditorComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
slide: SlideResolverService, slide: SlideResolverService,
teams: TeamService, teams: TeamService,
} },
} }
]; ];
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],
exports: [RouterModule] exports: [RouterModule]
}) })
export class AdminRoutingModule { export class AdminRoutingModule {}
}

View File

@ -1,2 +1,4 @@
<div class="pause-indicator" *ngIf="paused">Slideshow is paused</div> <div class="pause-indicator" *ngIf="paused">Slideshow is paused</div>
<router-outlet></router-outlet> <main [@routerTransition]="getAnimationData(o)">
<router-outlet #o="outlet"></router-outlet>
</main>

View File

@ -1,15 +1,21 @@
import { Component, HostListener, OnInit } from '@angular/core'; import { Component, HostListener, OnInit } from '@angular/core';
import { TimerService } from './shared/service/timer.service'; import { TimerService } from './shared/service/timer.service';
import { SlideShowService } from './display/slide-show.service'; import { SlideShowService } from './display/slide-show.service';
import { Router, RouterOutlet } from '@angular/router';
import { slideInOutAnimation } from './shared/slide-in-out-animation';
import { AnimationDirection, SettingsService } from './shared/service/settings.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.css'] styleUrls: ['./app.component.css'],
animations: [slideInOutAnimation],
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
constructor(private timerService: TimerService, constructor(private timerService: TimerService,
private slideShowService: SlideShowService) {} private slideShowService: SlideShowService,
private settings: SettingsService,
private router: Router) {}
public ngOnInit() {} public ngOnInit() {}
@ -17,6 +23,9 @@ export class AppComponent implements OnInit {
private keyPressed(key: string) { private keyPressed(key: string) {
if (this.timerService.autoSwitch) { if (this.timerService.autoSwitch) {
switch (key) { switch (key) {
case 'Home':
this.router.navigate(['/dashboard']);
break;
case ' ': case ' ':
this.timerService.togglePause(); this.timerService.togglePause();
break; break;
@ -35,4 +44,14 @@ export class AppComponent implements OnInit {
public get paused(): boolean { public get paused(): boolean {
return this.timerService.paused; return this.timerService.paused;
} }
public getAnimationData(outlet: RouterOutlet) {
return {
value: outlet.activatedRouteData.state ? outlet.activatedRouteData.state : false,
params: {
offsetEnter: this.settings.animationDirection === AnimationDirection.LEFT ? -100 : 100,
offsetLeave: this.settings.animationDirection === AnimationDirection.LEFT ? 100 : -100,
}
};
}
} }

View File

@ -1,5 +1,11 @@
:host { :host {
background-color: #444; background-color: #444;
position: fixed;
display: block;
top: 0;
left: 0;
right: 0;
bottom: 0;
} }
.ui.label.inprogress { .ui.label.inprogress {

View File

@ -1,11 +1,10 @@
import {Component, HostBinding, OnDestroy, OnInit} from '@angular/core'; import {Component, OnDestroy, OnInit} from '@angular/core';
import {Title} from '@angular/platform-browser'; import {Title} from '@angular/platform-browser';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {Subscription, timer} from 'rxjs'; import {Subscription, timer} from 'rxjs';
import {slideInOutAnimation} from '../../shared/slide-in-out-animation';
import {CommitTrackerService} from '../../shared/service/commit-tracker.service'; import {CommitTrackerService} from '../../shared/service/commit-tracker.service';
import {AnimationDirection, SettingsService} from '../../shared/service/settings.service'; import {SettingsService} from '../../shared/service/settings.service';
import {Commit} from '../../shared/commit'; import {Commit} from '../../shared/commit';
import {CommitStatus} from '../../shared/commit-status.enum'; import {CommitStatus} from '../../shared/commit-status.enum';
import {Result} from '../../shared/result.enum'; import {Result} from '../../shared/result.enum';
@ -19,20 +18,12 @@ const DEFAULT_AVATAR = '/assets/riddler.png';
selector: 'app-commit-tracker', selector: 'app-commit-tracker',
templateUrl: './commit-tracker.component.html', templateUrl: './commit-tracker.component.html',
styleUrls: ['./commit-tracker.component.css'], styleUrls: ['./commit-tracker.component.css'],
animations: [slideInOutAnimation]
}) })
export class CommitTrackerComponent implements OnInit, OnDestroy { export class CommitTrackerComponent implements OnInit, OnDestroy {
public CommitStatus = CommitStatus; public CommitStatus = CommitStatus;
private refreshCommitTrackerTimer: Subscription; private refreshCommitTrackerTimer: Subscription;
@HostBinding('@slideInOutAnimation')
public get slideInOutAnimation() {
return this.settings.animationDirection === AnimationDirection.RIGHT
? {value: 'right', params: {offsetEnter: 100, offsetLeave: -100}}
: {value: 'left', params: {offsetEnter: -100, offsetLeave: 100}};
}
constructor(private commitTrackerService: CommitTrackerService, constructor(private commitTrackerService: CommitTrackerService,
private settings: SettingsService, private settings: SettingsService,
private titleService: Title, private titleService: Title,

View File

@ -12,104 +12,112 @@ import { KanbanService } from './shared';
import { WatchersComponent } from './watchers/watchers.component'; import { WatchersComponent } from './watchers/watchers.component';
import { WatcherService } from './shared/watcher.service'; import { WatcherService } from './shared/watcher.service';
const routes: Routes = [ const routes: Routes = [
{ {
path: 'slideshow/:id', path: 'slideshow/:id',
component: SlideShowComponent, component: SlideShowComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
slide: SlideResolverService, slide: SlideResolverService,
}, },
data: { data: {
autoSwitchable: false autoSwitchable: false,
state: 'slideshow',
} }
}, { }, {
path: 'slideshow-odd/:id', path: 'slideshow-odd/:id',
component: SlideShowComponent, component: SlideShowComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
slide: SlideResolverService, slide: SlideResolverService,
}, },
data: { data: {
autoSwitchable: true autoSwitchable: true,
state: 'slideshow-odd',
} }
}, { }, {
path: 'slideshow-even/:id', path: 'slideshow-even/:id',
component: SlideShowComponent, component: SlideShowComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
slide: SlideResolverService, slide: SlideResolverService,
}, },
data: { data: {
autoSwitchable: true autoSwitchable: true,
state: 'slideshow-even',
} }
}, { }, {
path: 'commit-tracker', path: 'commit-tracker',
component: CommitTrackerComponent, component: CommitTrackerComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
commits: CommitTrackerService, commits: CommitTrackerService,
}, },
data: { data: {
autoSwitchable: true autoSwitchable: true,
state: 'commit-tracker',
} }
}, { }, {
path: 'commit-tracker-fixed', path: 'commit-tracker-fixed',
component: CommitTrackerComponent, component: CommitTrackerComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
commits: CommitTrackerService, commits: CommitTrackerService,
},
data: {
autoSwitchable: false,
state: 'commit-tracker-fixed',
} }
}, { }, {
path: 'kanban', path: 'kanban',
component: KanbanBoardComponent, component: KanbanBoardComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
kanbanBoard: KanbanService, kanbanBoard: KanbanService,
}, },
data: { data: {
autoSwitchable: true autoSwitchable: true,
state: 'kanban',
} }
}, { }, {
path: 'kanban-fixed', path: 'kanban-fixed',
component: KanbanBoardComponent, component: KanbanBoardComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
kanbanBoard: KanbanService, kanbanBoard: KanbanService,
}, },
data: { data: {
autoSwitchable: false autoSwitchable: false,
state: 'kanban-fixed',
} }
}, { }, {
path: 'watchers', path: 'watchers',
component: WatchersComponent, component: WatchersComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
watchers: WatcherService, watchers: WatcherService,
}, },
data: { data: {
autoSwitchable: true autoSwitchable: true,
state: 'watchers',
} }
}, { }, {
path: 'watchers-fixed', path: 'watchers-fixed',
component: WatchersComponent, component: WatchersComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
watchers: WatcherService, watchers: WatcherService,
}, },
data: { data: {
autoSwitchable: false autoSwitchable: false,
state: 'watchers-fixed',
} }
}, { }, {
path: 'settings', path: 'settings',
component: SettingsComponent, component: SettingsComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: { resolve: {
teams: TeamService, teams: TeamService,
}, },
data: {
autoSwitchable: true,
// state: 'settings',
}
} }
]; ];
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],
exports: [RouterModule] exports: [RouterModule]

View File

@ -1,5 +1,10 @@
:host { :host {
display: inline-block; position: fixed;
display: block;
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 100vh; height: 100vh;
overflow-x: hidden; overflow-x: hidden;
overflow-y: scroll; overflow-y: scroll;

View File

@ -1,37 +1,28 @@
import {Component, HostBinding, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {Title} from '@angular/platform-browser'; import {Title} from '@angular/platform-browser';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {KanbanBoard, KanbanEntry, KanbanService,} from '../shared'; import {KanbanBoard, KanbanEntry, KanbanService,} from '../shared';
import {slideInOutAnimation} from '../../shared/slide-in-out-animation'; import {SettingsService} from '../../shared/service/settings.service';
import {AnimationDirection, SettingsService} from '../../shared/service/settings.service';
@Component({ @Component({
selector: 'app-kanban-board', selector: 'app-kanban-board',
templateUrl: './kanban-board.component.html', templateUrl: './kanban-board.component.html',
styleUrls: ['./kanban-board.component.css'], styleUrls: ['./kanban-board.component.css'],
animations: [slideInOutAnimation]
}) })
export class KanbanBoardComponent implements OnInit { export class KanbanBoardComponent implements OnInit {
@HostBinding('@slideInOutAnimation')
public get slideInOutAnimation() {
return this.settingService.animationDirection === AnimationDirection.RIGHT
? {value: 'right', params: {offsetEnter: 100, offsetLeave: -100}}
: {value: 'left', params: {offsetEnter: -100, offsetLeave: 100}};
}
constructor(private titleService: Title, constructor(private titleService: Title,
private route: ActivatedRoute, private route: ActivatedRoute,
private kanbanService: KanbanService, private kanbanService: KanbanService,
private settingService: SettingsService) { private settings: SettingsService) {
} }
/** /**
* Set page title, and handle preloaded kanbanBoard data * Set page title, and handle preloaded kanbanBoard data
*/ */
ngOnInit() { ngOnInit() {
this.titleService.setTitle(`${this.settingService.team.name} : Kanban board`); this.titleService.setTitle(`${this.settings.team.name} : Kanban board`);
this.route.data.subscribe((data: { this.route.data.subscribe((data: {
kanbanBoard: KanbanBoard, kanbanBoard: KanbanBoard,
}) => { }) => {
@ -48,7 +39,7 @@ export class KanbanBoardComponent implements OnInit {
} }
get inprogressWipLimit(): number { get inprogressWipLimit(): number {
return this.settingService.team.inprogressColumn.wipLimit; return this.settings.team.inprogressColumn.wipLimit;
} }
get inprogressWipCount(): number { get inprogressWipCount(): number {
@ -67,12 +58,12 @@ export class KanbanBoardComponent implements OnInit {
*/ */
get inprogressWipClass() { get inprogressWipClass() {
return { return {
'over-wip': this.inprogressWipCount > this.settingService.team.inprogressColumn.wipLimit, 'over-wip': this.inprogressWipCount > this.settings.team.inprogressColumn.wipLimit,
}; };
} }
get verificationWipLimit(): number { get verificationWipLimit(): number {
return this.settingService.team.verificationColumn.wipLimit; return this.settings.team.verificationColumn.wipLimit;
} }
get verificationWipCount(): number { get verificationWipCount(): number {
@ -91,23 +82,23 @@ export class KanbanBoardComponent implements OnInit {
*/ */
get verificationWipClass() { get verificationWipClass() {
return { return {
'over-wip': this.verificationWipCount > this.settingService.team.verificationColumn.wipLimit, 'over-wip': this.verificationWipCount > this.settings.team.verificationColumn.wipLimit,
}; };
} }
get backlogLabel(): string { get backlogLabel(): string {
return this.settingService.team.backlogColumn.label; return this.settings.team.backlogColumn.label;
} }
get inProgressLabel(): string { get inProgressLabel(): string {
return this.settingService.team.inprogressColumn.label; return this.settings.team.inprogressColumn.label;
} }
get verificationLabel(): string { get verificationLabel(): string {
return this.settingService.team.verificationColumn.label; return this.settings.team.verificationColumn.label;
} }
get doneLabel(): string { get doneLabel(): string {
return this.settingService.team.doneColumn.label; return this.settings.team.doneColumn.label;
} }
} }

10
src/app/display/settings/settings.component.ts Normal file → Executable file
View File

@ -15,20 +15,20 @@ export class SettingsComponent implements OnInit {
public slideInterval: number; public slideInterval: number;
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
private settingsService: SettingsService, private settings: SettingsService,
private router: Router) {} private router: Router) {}
ngOnInit() { ngOnInit() {
this.route.data.subscribe((data: {teams: Array<Team>}) => { this.route.data.subscribe((data: {teams: Array<Team>}) => {
this.teams = data.teams; this.teams = data.teams;
this.selectedTeam = this.teams.find(team => team.id === this.settingsService.team.id); this.selectedTeam = this.teams.find(team => team.id === this.settings.team.id);
this.slideInterval = this.settingsService.slideInterval; this.slideInterval = this.settings.slideInterval;
}); });
} }
public saveSettings() { public saveSettings() {
this.settingsService.team = this.selectedTeam; this.settings.team = this.selectedTeam;
this.settingsService.slideInterval = this.slideInterval; this.settings.slideInterval = this.slideInterval;
this.router.navigate(['/admin']); this.router.navigate(['/admin']);
} }
} }

8
src/app/display/slide-show/slide-show.component.css Normal file → Executable file
View File

@ -0,0 +1,8 @@
:host {
position: fixed;
display: block;
top: 0;
left: 0;
right: 0;
bottom: 0;
}

View File

@ -1,32 +1,21 @@
import {Component, HostBinding, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {Title} from '@angular/platform-browser'; import {Title} from '@angular/platform-browser';
import * as marked from 'marked'; import * as marked from 'marked';
import {Slide, SlideType} from '../../shared/slide'; import {Slide, SlideType} from '../../shared/slide';
import {slideInOutAnimation} from '../../shared/slide-in-out-animation';
import {AnimationDirection, SettingsService} from '../../shared/service/settings.service';
@Component({ @Component({
selector: 'app-slide-show', selector: 'app-slide-show',
templateUrl: './slide-show.component.html', templateUrl: './slide-show.component.html',
styleUrls: ['./slide-show.component.css'], styleUrls: ['./slide-show.component.css'],
animations: [slideInOutAnimation]
}) })
export class SlideShowComponent implements OnInit { export class SlideShowComponent implements OnInit {
private md; private md;
public slide: Slide; public slide: Slide;
@HostBinding('@slideInOutAnimation')
public get slideInOutAnimation() {
return this.settings.animationDirection === AnimationDirection.RIGHT
? {value: 'right', params: {offsetEnter: 100, offsetLeave: -100}}
: {value: 'left', params: {offsetEnter: -100, offsetLeave: 100}};
}
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
private titleService: Title, private titleService: Title) {
private settings: SettingsService) {
this.md = marked.setOptions({}); this.md = marked.setOptions({});
} }

View File

@ -1,5 +1,11 @@
:host { :host {
background-color: #444; background-color: #444;
position: fixed;
display: block;
top: 0;
left: 0;
right: 0;
bottom: 0;
} }
/* avatar */ /* avatar */

View File

@ -1,8 +1,7 @@
import {Component, HostBinding, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {Title} from '@angular/platform-browser'; import {Title} from '@angular/platform-browser';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {AnimationDirection, SettingsService} from '../../shared/service/settings.service'; import {SettingsService} from '../../shared/service/settings.service';
import {slideInOutAnimation} from '../../shared/slide-in-out-animation';
import {WatcherService} from '../shared/watcher.service'; import {WatcherService} from '../shared/watcher.service';
import {WatchedIssue} from '../shared/watched-issue.model'; import {WatchedIssue} from '../shared/watched-issue.model';
import {environment} from '../../../environments/environment'; import {environment} from '../../../environments/environment';
@ -13,17 +12,9 @@ const DEFAULT_AVATAR = '/assets/riddler.png';
selector: 'app-watchers', selector: 'app-watchers',
templateUrl: './watchers.component.html', templateUrl: './watchers.component.html',
styleUrls: ['./watchers.component.css'], styleUrls: ['./watchers.component.css'],
animations: [slideInOutAnimation]
}) })
export class WatchersComponent implements OnInit { export class WatchersComponent implements OnInit {
@HostBinding('@slideInOutAnimation')
public get slideInOutAnimation() {
return this.settingService.animationDirection === AnimationDirection.RIGHT
? {value: 'right', params: {offsetEnter: 100, offsetLeave: -100}}
: {value: 'left', params: {offsetEnter: -100, offsetLeave: 100}};
}
constructor(private titleService: Title, constructor(private titleService: Title,
private route: ActivatedRoute, private route: ActivatedRoute,
private watcherService: WatcherService, private watcherService: WatcherService,

View File

@ -1,42 +1,34 @@
// import the required animation functions from the angular animations module // import the required animation functions from the angular animations module
import { animate, state, style, transition, trigger } from '@angular/animations'; import {animate, group, query, state, style, transition, trigger} from '@angular/animations';
export const slideInOutAnimation = export const slideInOutAnimation =
// trigger name for attaching this animation to an element using the [@triggerName] syntax trigger('routerTransition', [
trigger('slideInOutAnimation', [ transition('* <=> *', [
query(':enter, :leave', style({
// end state styles for route container (host)
state('*', style({
// the view covers the whole screen with a semi tranparent background
position: 'fixed', position: 'fixed',
top: 0, top: 0,
left: 0, left: 0,
right: 0, right: 0,
bottom: 0 bottom: 0
})), }), {
optional: true
// route 'enter' transition
transition(':enter', [
// styles at start of transition
style({
// start with the content positioned off the right of the screen,
// -400% is required instead of -100% because the negative position adds to the width of the element
transform: 'translateX({{offsetEnter}}%)'
}), }),
group([
// animation and styles at end of transition query(':enter', [
style({ transform: 'translateX({{offsetEnter}}%)'}),
animate('.75s cubic-bezier(0.175, 0.885, 0.32, 1.275)', style({ animate('.75s cubic-bezier(0.175, 0.885, 0.32, 1.275)', style({
// transition the right position to 0 which slides the content into view transform: 'translateX(0%)'
transform: 'translateX(0)'
})) }))
]), ], {
optional: true
// route 'leave' transition }),
transition(':leave', [ query(':leave', [
// animation and styles at end of transition style({ transform: 'translateX(0%)' }),
animate('.75s cubic-bezier(0.175, 0.885, 0.32, 1.275)', style({ animate('.75s cubic-bezier(0.175, 0.885, 0.32, 1.275)', style({
// transition the right position to -400% which slides the content out of view
transform: 'translateX({{offsetLeave}}%)' transform: 'translateX({{offsetLeave}}%)'
})) }))
], { optional: true })
])
]) ])
]); ]);