* default routes and redirects added

* timers refactored into a timer service from the app.component
* settings service now provides observable setting changes
* slideshow is now working
* commit-tracker style changed to dark look, colored lines were removed
* dashboard menu order changed
* slide/committracker is now animated
This commit is contained in:
Dávid Danyi 2018-04-19 17:06:08 +02:00
parent 90ddebf46b
commit 6eb1cfaea1
19 changed files with 203 additions and 134 deletions

View File

@ -14,6 +14,11 @@ import { DashboardComponent } from './dashboard/dashboard.component';
const routes: Routes = [
{
path: 'admin',
redirectTo: '/dashboard',
pathMatch: 'full'
// canActivate: [AuthGuardService, RoleGuardService],
}, {
path: 'dashboard',
component: DashboardComponent,
// canActivate: [AuthGuardService, RoleGuardService],
}, {

View File

@ -4,12 +4,12 @@
<div class="ui four cards">
<a class="ui raised yellow card" [routerLink]="['/commit-tracker']">
<div class="content">
<div class="header">Commit tracker</div>
<div class="header">Start slideshow</div>
<div class="meta">
<span class="category">Local</span>
</div>
<div class="description">
<p>View commits of the current configured team members.</p>
<p>Starts playing the slideshow on this device.</p>
</div>
</div>
</a>
@ -24,17 +24,6 @@
</div>
</div>
</a>
<a class="ui raised green card" [routerLink]="['/admin/teams']">
<div class="content">
<div class="header">Teams</div>
<div class="meta">
<span class="category">Global</span>
</div>
<div class="description">
<p>Create teams, manage team members.</p>
</div>
</div>
</a>
<a class="ui raised blue card" [routerLink]="['/admin/slides']">
<div class="content">
<div class="header">Slides</div>
@ -46,5 +35,16 @@
</div>
</div>
</a>
<a class="ui raised green card" [routerLink]="['/admin/teams']">
<div class="content">
<div class="header">Teams</div>
<div class="meta">
<span class="category">Global</span>
</div>
<div class="description">
<p>Create teams, manage team members.</p>
</div>
</div>
</a>
</div>
</div>

View File

@ -3,7 +3,7 @@
<a class="ui primary button"
[routerLink]="['/admin/slide/new']"><i class="plus sign icon"></i>New slide</a>
<a class="ui button"
[routerLink]="['/admin']"><i class="left angle icon"></i>Back to dashboard</a>
[routerLink]="['/dashboard']"><i class="left angle icon"></i>Back to dashboard</a>
<table *ngIf="slides?.length" class="ui large padded celled definition table">
<thead>
<tr>

View File

@ -3,7 +3,7 @@
<a class="ui primary button"
[routerLink]="['/admin/team/new']"><i class="plus sign icon"></i>New team</a>
<a class="ui button"
[routerLink]="['/admin']"><i class="left angle icon"></i>Back to dashboard</a>
[routerLink]="['/dashboard']"><i class="left angle icon"></i>Back to dashboard</a>
<table *ngIf="teams?.length" class="ui large padded celled definition table">
<thead>
<tr>

View File

@ -6,8 +6,10 @@ import { Routes, RouterModule } from '@angular/router';
// import { PageNotFoundComponent } from "./page-not-found/page-not-found.component";
const routes: Routes = [
// {path: '', component: HomeComponent, canActivate: [AuthGuardService]},
// {path: '**', component: PageNotFoundComponent},
{path: '', redirectTo: '/dashboard', pathMatch: 'full'},
{path: '**', redirectTo: '/dashboard', pathMatch: 'full'}
// {path: '', component: HomeComponent, canActivate: [AuthGuardService]},
// {path: '**', component: PageNotFoundComponent},
];
@NgModule({

View File

@ -1,70 +1,12 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { CommitTrackerService } from './shared/service/commit-tracker.service';
import { SettingsService } from './shared/service/settings.service';
import { SelfUpdaterService } from './shared/service/self-updater.service';
import { ActivationEnd, Router } from '@angular/router';
import { Subject } from 'rxjs/Subject';
import { TimerObservable } from 'rxjs/observable/TimerObservable';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/switchMap';
const TIMER_UPDATE_POLL_INTERVAL = 30000;
const TIMER_COMMITTRACKER_REFRESH = 5000;
import { Component, OnInit } from '@angular/core';
import { TimerService } from './shared/service/timer.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
private selfUpdateCheckerTimer: Subscription;
private refreshCommitTrackerTimer: Subscription;
private slideshowTimer: Subscription;
private autoSwitch = false;
constructor(private commitTrackerService: CommitTrackerService,
private settings: SettingsService,
private selfUpdaterService: SelfUpdaterService,
private router: Router) {}
public ngOnInit() {
const timerSUC = TimerObservable.create(TIMER_UPDATE_POLL_INTERVAL, TIMER_UPDATE_POLL_INTERVAL);
this.selfUpdateCheckerTimer = timerSUC.subscribe(() => {
this.selfUpdaterService.checkAndReloadIfNecessary();
});
const timerCT = TimerObservable.create(TIMER_COMMITTRACKER_REFRESH, TIMER_COMMITTRACKER_REFRESH);
this.refreshCommitTrackerTimer = timerCT.subscribe(() => {
this.commitTrackerService.getTeamCommits(this.settings.team.members.map(member => member.signum));
});
const timerSS = new Subject<number>();
this.slideshowTimer = timerSS.switchMap((period: number) => TimerObservable.create(period))
.subscribe(() => {
this.changeSlide(timerSS);
});
timerSS.next(this.settings.slideInterval);
this.autoSwitch = false;
this.router.events
.filter(event => event instanceof ActivationEnd)
.subscribe((event: ActivationEnd) => {
this.autoSwitch = !!event.snapshot.data.autoSwitchable;
});
}
public ngOnDestroy() {
this.refreshCommitTrackerTimer.unsubscribe();
this.selfUpdateCheckerTimer.unsubscribe();
this.slideshowTimer.unsubscribe();
}
private changeSlide(timer: Subject<number>) {
if (this.autoSwitch) {
console.log('Slide should have changed here');
}
timer.next(this.settings.slideInterval);
}
export class AppComponent implements OnInit {
constructor(private timerService: TimerService) {}
public ngOnInit() {}
}

View File

@ -14,6 +14,7 @@ import { DisplayModule } from './display/display.module';
import { CommitTrackerService } from './shared/service/commit-tracker.service';
import { SettingsService } from './shared/service/settings.service';
import { SelfUpdaterService } from './shared/service/self-updater.service';
import { TimerService } from './shared/service/timer.service';
@NgModule({
declarations: [
@ -29,7 +30,7 @@ import { SelfUpdaterService } from './shared/service/self-updater.service';
AdminModule,
AppRoutingModule, // must be last RouterModule import for ** route to work
],
providers: [TeamService, SlideService, CommitTrackerService, SettingsService, SelfUpdaterService],
providers: [TeamService, SlideService, CommitTrackerService, SettingsService, SelfUpdaterService, TimerService],
bootstrap: [AppComponent]
})
export class AppModule {

View File

@ -1,3 +1,7 @@
:host {
background-color: #444;
}
.ui.label.inprogress {
position: relative;
}

View File

@ -1,5 +1,5 @@
<div class="ui main wide-container">
<table *ngIf="commits?.length" class="ui large padded celled table">
<div class="ui main wide-container dark">
<table *ngIf="commits?.length" class="ui large padded inverted celled2 table">
<thead>
<tr>
<th class="collapsing"><i class="user icon"></i>Owner</th>
@ -12,7 +12,7 @@
</tr>
</thead>
<tbody>
<tr *ngFor="let commit of commits" [ngClass]="rowClasses(commit)">
<tr *ngFor="let commit of commits">
<td class="collapsing">{{commit.owner}}</td>
<td class="collapsing">{{commit.gerrit_time}}</td>
<td>{{commit.gerrit_change_subject}}</td>

View File

@ -1,23 +1,35 @@
import { Component, OnInit } from '@angular/core';
import { Component, HostBinding, OnDestroy, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import { TimerObservable } from 'rxjs/observable/TimerObservable';
import { slideInOutAnimation } from '../../shared/slide-in-out-animation';
import { CommitTrackerService } from '../../shared/service/commit-tracker.service';
import { SettingsService } from '../../shared/service/settings.service';
import { Commit } from '../../shared/commit';
import { CommitStatus } from '../../shared/commit-status.enum';
import { Result } from '../../shared/result.enum';
import { Build } from '../../shared/build';
const TIMER_COMMITTRACKER_REFRESH = 10000;
@Component({
selector: 'app-commit-tracker',
templateUrl: './commit-tracker.component.html',
styleUrls: ['./commit-tracker.component.css']
styleUrls: ['./commit-tracker.component.css'],
animations: [slideInOutAnimation]
})
export class CommitTrackerComponent implements OnInit {
export class CommitTrackerComponent implements OnInit, OnDestroy {
public CommitStatus = CommitStatus;
private refreshCommitTrackerTimer: Subscription;
@HostBinding('@slideInOutAnimation')
slideIn = false;
constructor(private commitTrackerService: CommitTrackerService,
private settings: SettingsService,
private titleService: Title,
private route: ActivatedRoute) {
}
@ -25,6 +37,16 @@ export class CommitTrackerComponent implements OnInit {
ngOnInit() {
this.titleService.setTitle('Commit-tracker : MTAStv');
this.route.data.subscribe((data: { commits: Array<Commit> }) => this.commits = data.commits);
const timerCT = TimerObservable.create(TIMER_COMMITTRACKER_REFRESH, TIMER_COMMITTRACKER_REFRESH);
this.refreshCommitTrackerTimer = timerCT.subscribe(() => {
this.commitTrackerService.getTeamCommits(this.settings.team.members.map(member => member.signum))
.subscribe(commits => this.commits = commits);
});
}
ngOnDestroy() {
this.refreshCommitTrackerTimer.unsubscribe();
}
get commits(): Array<Commit> {
@ -118,27 +140,4 @@ export class CommitTrackerComponent implements OnInit {
return false;
}
}
public rowClasses(commit: Commit) {
return {
positive: this.isAllOk(commit),
negative: this.hasAnyFailure(commit)
};
}
private isAllOk(commit: Commit) {
return [
commit.cfl_result,
commit.sfl_result,
commit.nfl_result,
].every(result => result === Result.Success);
}
private hasAnyFailure(commit: Commit) {
return [
commit.cfl_result,
commit.sfl_result,
commit.nfl_result,
].some(result => result === Result.Failure);
}
}

View File

@ -16,6 +16,26 @@ const routes: Routes = [
resolve: {
slide: SlideResolverService,
},
data: {
autoSwitchable: false
}
}, {
path: 'slideshow-odd/:id',
component: SlideShowComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: {
slide: SlideResolverService,
},
data: {
autoSwitchable: true
}
}, {
path: 'slideshow-even/:id',
component: SlideShowComponent,
// canActivate: [AuthGuardService, RoleGuardService],
resolve: {
slide: SlideResolverService,
},
data: {
autoSwitchable: true
}

View File

@ -25,6 +25,6 @@
</div>
<button type="submit" class="ui positive button"><i class="save outline icon"></i>Save</button>
<a class="ui button"
[routerLink]="['/admin']"><i class="left angle icon"></i>Back to dashboard</a>
[routerLink]="['/dashboard']"><i class="left angle icon"></i>Back to dashboard</a>
</form>
</div>

View File

@ -7,24 +7,28 @@ import { SettingsService } from '../shared/service/settings.service';
@Injectable()
export class SlideShowService {
private currentSlideIndex = 0;
private oddEven = false;
private currentSlideIndex = -1;
private slides: Array<Slide> = [];
constructor(private slideService: SlideService,
private settingsService: SettingsService,
private router: Router) {
}
public init() {
this.router.navigate(['/commit-tracker']);
this.reloadSlides();
}
public nextSlide() {
if (this.currentSlideIndex === this.slides.length - 1) {
this.currentSlideIndex = -1;
this.reloadSlides();
this.router.navigate(['/commit-tracker']);
} else {
this.oddEven = !this.oddEven;
this.currentSlideIndex++;
this.router.navigate(['/slideshow', this.slides[this.currentSlideIndex]]);
this.router.navigate([
this.oddEven ? '/slideshow-odd' : '/slideshow-even',
this.slides[this.currentSlideIndex].id
]);
}
}
@ -32,9 +36,8 @@ export class SlideShowService {
const team = this.settingsService.team;
this.slideService.list().subscribe(
slides => this.slides = slides.filter(
slide => slide.team.id === team.id
slide => slide.team === null || slide.team.id === team.id
)
);
}
}

View File

@ -17,9 +17,7 @@ export class SlideShowComponent implements OnInit {
public slide: Slide;
@HostBinding('@slideInOutAnimation')
get slideIn() {
return '';
}
slideIn = true;
constructor(private route: ActivatedRoute,
private titleService: Title) {

View File

@ -1,10 +1,11 @@
:host {
position: absolute;
display: block;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
width: 100%;
height: 100%;
background: #222;
font-family: "Source Sans Pro", Helvetica, sans-serif;
font-size: 42px;
@ -13,7 +14,9 @@
padding: 4rem;
text-align: center;
}
:host ::ng-deep div.present {
max-height: 100%;
}
/*********************************************
* HEADERS
*********************************************/
@ -53,8 +56,10 @@
:host ::ng-deep img,
:host ::ng-deep video,
:host ::ng-deep iframe {
object-fit: scale-down;
max-width: 95%;
max-height: 95%; }
max-height: 95%;
}
:host ::ng-deep strong,
:host ::ng-deep b {

View File

@ -1,4 +1,6 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { Team } from '../team';
const DEFAULT_SLIDE_INTERVAL = 30000;
@ -9,8 +11,10 @@ const SELECTED_TEAM_KEY = 'team';
@Injectable()
export class SettingsService {
constructor() {
}
private teamSubject: Subject<Team> = new Subject<Team>();
private intervalSubject: Subject<number> = new Subject<number>();
constructor() {}
get team(): Team {
try {
@ -23,6 +27,7 @@ export class SettingsService {
set team(team: Team) {
localStorage.setItem(SELECTED_TEAM_KEY, JSON.stringify(team));
this.teamSubject.next(team);
}
get slideInterval(): number {
@ -36,5 +41,14 @@ export class SettingsService {
set slideInterval(interval: number) {
localStorage.setItem(SLIDE_INTERVAL_KEY, JSON.stringify(interval));
this.intervalSubject.next(interval);
}
get slideIntervalChanged(): Observable<number> {
return this.intervalSubject.asObservable();
}
get teamChanged(): Observable<Team> {
return this.teamSubject.asObservable();
}
}

View File

@ -0,0 +1,15 @@
import { TestBed, inject } from '@angular/core/testing';
import { TimerService } from './timer.service';
describe('TimerService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [TimerService]
});
});
it('should be created', inject([TimerService], (service: TimerService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,62 @@
import { Injectable, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { SettingsService } from './settings.service';
import { SelfUpdaterService } from './self-updater.service';
import { TimerObservable } from 'rxjs/observable/TimerObservable';
import { Subscription } from 'rxjs/Subscription';
import { ActivationEnd, Router } from '@angular/router';
import { SlideShowService } from '../../display/slide-show.service';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/switchMap';
const TIMER_UPDATE_POLL_INTERVAL = 30000;
@Injectable()
export class TimerService implements OnDestroy {
private autoSwitch = false;
private slideShowTimer: Subscription;
private selfUpdateCheckerTimer: Subscription;
private slideTimerSubject: Subject<number> = new Subject<number>();
private slideIntervalSubscription: Subscription;
constructor(private settings: SettingsService,
private selfUpdaterService: SelfUpdaterService,
private router: Router,
private slideShowService: SlideShowService) {
const timerSUC = TimerObservable.create(TIMER_UPDATE_POLL_INTERVAL, TIMER_UPDATE_POLL_INTERVAL);
this.selfUpdateCheckerTimer = timerSUC.subscribe(() => {
this.selfUpdaterService.checkAndReloadIfNecessary();
});
this.slideShowTimer = this.slideTimerSubject.switchMap((period: number) => TimerObservable.create(period))
.subscribe(() => this.changeSlide());
this.setSlideTimer(this.settings.slideInterval);
this.autoSwitch = false;
this.router.events
.filter(event => event instanceof ActivationEnd)
.subscribe((event: ActivationEnd) => this.autoSwitch = !!event.snapshot.data.autoSwitchable);
this.slideIntervalSubscription = this.settings.slideIntervalChanged.subscribe(
interval => this.setSlideTimer(interval)
);
}
public ngOnDestroy() {
this.slideShowTimer.unsubscribe();
this.selfUpdateCheckerTimer.unsubscribe();
this.slideIntervalSubscription.unsubscribe();
}
private changeSlide() {
if (this.autoSwitch) {
this.slideShowService.nextSlide();
}
this.setSlideTimer(this.settings.slideInterval);
}
public setSlideTimer(delay: number) {
this.slideTimerSubject.next(delay);
}
}

View File

@ -17,27 +17,26 @@ export const slideInOutAnimation =
// 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
right: '-400%'
transform: 'translateX(100%)'
}),
// animation and styles at end of transition
animate('.5s ease-in-out', 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
right: 0
transform: 'translateX(0)'
}))
]),
// route 'leave' transition
transition(':leave', [
// animation and styles at end of transition
animate('.5s ease-in-out', 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
right: '-400%'
transform: 'translateX(-100%)'
}))
])
]);