diff --git a/src/app/admin/slide-list/slide-list.component.html b/src/app/admin/slide-list/slide-list.component.html index 8953079..2694ef6 100644 --- a/src/app/admin/slide-list/slide-list.component.html +++ b/src/app/admin/slide-list/slide-list.component.html @@ -16,10 +16,16 @@ + + + class="large link red fitted trash alternate outline icon"> {{slide.title}} {{slideTeam(slide.team)}} diff --git a/src/app/admin/slide-list/slide-list.component.ts b/src/app/admin/slide-list/slide-list.component.ts index 489a00d..418ad8b 100644 --- a/src/app/admin/slide-list/slide-list.component.ts +++ b/src/app/admin/slide-list/slide-list.component.ts @@ -35,6 +35,34 @@ export class SlideListComponent implements OnInit { return team === null ? 'All teams' : team.name; } + public moveUp(slide: Slide) { + if (!this.isFirstSlide(slide)) { + this.slideService.moveUp(slide).subscribe(slides => this.slides = slides); + } + } + + public moveDown(slide: Slide) { + if (!this.isLastSlide(slide)) { + this.slideService.moveDown(slide).subscribe(slides => this.slides = slides); + } + } + + public upClass(slide: Slide) { + const first = this.isFirstSlide(slide); + return { + 'blue link': !first, + 'inverted grey': first, + }; + } + + public downClass(slide: Slide) { + const last = this.isLastSlide(slide); + return { + 'blue link': !last, + 'inverted grey': last, + }; + } + public delete(slide: Slide) { if (confirm(`Are you sure you want to delete the slide '${slide.title}'`)) { this.slideService.delete(slide).subscribe(result => { @@ -45,6 +73,14 @@ export class SlideListComponent implements OnInit { } } + public isFirstSlide(slide: Slide) { + return slide.position === 0; + } + + public isLastSlide(slide: Slide) { + return slide.position === this.slides.length - 1; + } + public visibleClass(slide: Slide) { return { 'green check': slide.isVisible, diff --git a/src/app/display/display.module.ts b/src/app/display/display.module.ts index 8d68310..bbd8895 100644 --- a/src/app/display/display.module.ts +++ b/src/app/display/display.module.ts @@ -8,6 +8,7 @@ import { SettingsComponent } from './settings/settings.component'; import { SuiModule } from 'ng2-semantic-ui'; import { SlideComponent } from './slide/slide.component'; import { SlideShowComponent } from './slide-show/slide-show.component'; +import { SlideShowService } from './slide-show.service'; @NgModule({ imports: [ @@ -17,6 +18,7 @@ import { SlideShowComponent } from './slide-show/slide-show.component'; DisplayRoutingModule ], exports: [SlideComponent], - declarations: [CommitTrackerComponent, SettingsComponent, SlideComponent, SlideShowComponent] + declarations: [CommitTrackerComponent, SettingsComponent, SlideComponent, SlideShowComponent], + providers: [SlideShowService] }) export class DisplayModule { } diff --git a/src/app/display/slide-show.service.spec.ts b/src/app/display/slide-show.service.spec.ts new file mode 100644 index 0000000..7820080 --- /dev/null +++ b/src/app/display/slide-show.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { SlideShowService } from './slide-show.service'; + +describe('SlideShowService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [SlideShowService] + }); + }); + + it('should be created', inject([SlideShowService], (service: SlideShowService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/display/slide-show.service.ts b/src/app/display/slide-show.service.ts new file mode 100644 index 0000000..24dfe15 --- /dev/null +++ b/src/app/display/slide-show.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@angular/core'; +import { Slide } from '../shared/slide'; +import { SlideService } from '../shared/service/slide.service'; +import { Router } from '@angular/router'; +import { SettingsService } from '../shared/service/settings.service'; + +@Injectable() +export class SlideShowService { + + private currentSlideIndex = 0; + private slides: Array = []; + + constructor(private slideService: SlideService, + private settingsService: SettingsService, + private router: Router) { + } + + public init() { + this.router.navigate(['/commit-tracker']); + } + + public nextSlide() { + if (this.currentSlideIndex === this.slides.length - 1) { + this.router.navigate(['/commit-tracker']); + } else { + this.currentSlideIndex++; + this.router.navigate(['/slideshow', this.slides[this.currentSlideIndex]]); + } + } + + private reloadSlides() { + const team = this.settingsService.team; + this.slideService.list().subscribe( + slides => this.slides = slides.filter( + slide => slide.team.id === team.id + ) + ); + } + +} diff --git a/src/app/display/slide/slide.component.css b/src/app/display/slide/slide.component.css index 3703d02..333951d 100644 --- a/src/app/display/slide/slide.component.css +++ b/src/app/display/slide/slide.component.css @@ -9,100 +9,96 @@ font-family: "Source Sans Pro", Helvetica, sans-serif; font-size: 42px; font-weight: normal; - color: #fff; } - -section { - line-height: 1.3; - font-weight: inherit; } + color: #fff; + padding: 4rem; + text-align: center; +} /********************************************* * HEADERS *********************************************/ -h1, -h2, -h3, -h4, -h5, -h6 { +:host ::ng-deep h1, +:host ::ng-deep h2, +:host ::ng-deep h3, +:host ::ng-deep h4, +:host ::ng-deep h5, +:host ::ng-deep h6 { margin: 0 0 20px 0; color: #fff; font-family: "Source Sans Pro", Helvetica, sans-serif; font-weight: 600; - line-height: 1.2; + line-height: 1.2 !important; letter-spacing: normal; text-transform: uppercase; text-shadow: none; - word-wrap: break-word; } + word-wrap: break-word; +} -h1 { - font-size: 2.5em; } - -h2 { - font-size: 1.6em; } - -h3 { - font-size: 1.3em; } - -h4 { - font-size: 1em; } - -h1 { - text-shadow: none; } +:host ::ng-deep h1 { + font-size: 2.5em !important; + text-shadow: none !important; +} +:host ::ng-deep h2 {font-size: 1.6em !important;} +:host ::ng-deep h3 {font-size: 1.3em !important;} +:host ::ng-deep h4 {font-size: 1em !important;} /********************************************* * OTHER *********************************************/ -p { +:host ::ng-deep p { margin: 20px 0; - line-height: 1.3; } + line-height: 1.3 !important; } /* Ensure certain elements are never larger than the slide itself */ -img, -video, -iframe { +:host ::ng-deep img, +:host ::ng-deep video, +:host ::ng-deep iframe { max-width: 95%; max-height: 95%; } -strong, -b { +:host ::ng-deep strong, +:host ::ng-deep b { font-weight: bold; } -em { +:host ::ng-deep em { font-style: italic; } -ol, -dl, -ul { +:host ::ng-deep ol, +:host ::ng-deep dl, +:host ::ng-deep ul { display: inline-block; text-align: left; margin: 0 0 0 1em; } -ol { +:host ::ng-deep ol { list-style-type: decimal; } -ul { +:host ::ng-deep ul { list-style-type: disc; } -ul ul { +:host ::ng-deep li { + line-height: 1.1; } + +:host ::ng-deep ul ul { list-style-type: square; } -ul ul ul { +:host ::ng-deep ul ul ul { list-style-type: circle; } -ul ul, -ul ol, -ol ol, -ol ul { +:host ::ng-deep ul ul, +:host ::ng-deep ul ol, +:host ::ng-deep ol ol, +:host ::ng-deep ol ul { display: block; - margin-left: 40px; } + margin-left: 10px; } -dt { +:host ::ng-deep dt { font-weight: bold; } -dd { +:host ::ng-deep dd { margin-left: 40px; } -blockquote { +:host ::ng-deep blockquote { display: block; position: relative; width: 70%; @@ -110,16 +106,16 @@ blockquote { padding: 5px; font-style: italic; background: rgba(255, 255, 255, 0.05); - box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + box-shadow: 0 0 2px rgba(0, 0, 0, 0.2); } -blockquote p:first-child, -blockquote p:last-child { +:host ::ng-deep blockquote p:first-child, +:host ::ng-deep blockquote p:last-child { display: inline-block; } -q { +:host ::ng-deep q { font-style: italic; } -pre { +:host ::ng-deep pre { display: block; position: relative; width: 90%; @@ -129,116 +125,110 @@ pre { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); } -code { +:host ::ng-deep code { font-family: monospace; text-transform: none; } -pre code { +:host ::ng-deep pre code { display: block; padding: 5px; overflow: auto; max-height: 400px; word-wrap: normal; } -table { +:host ::ng-deep table { margin: auto; border-collapse: collapse; border-spacing: 0; } -table th { +:host ::ng-deep table th { font-weight: bold; } -table th, -table td { +:host ::ng-deep table th, +:host ::ng-deep table td { text-align: left; padding: 0.2em 0.5em 0.2em 0.5em; border-bottom: 1px solid; } -table th[align="center"], -table td[align="center"] { +:host ::ng-deep table th[align="center"], +:host ::ng-deep table td[align="center"] { text-align: center; } -table th[align="right"], -table td[align="right"] { +:host ::ng-deep table th[align="right"], +:host ::ng-deep table td[align="right"] { text-align: right; } -table tbody tr:last-child th, -table tbody tr:last-child td { +:host ::ng-deep table tbody tr:last-child th, +:host ::ng-deep table tbody tr:last-child td { border-bottom: none; } -sup { +:host ::ng-deep sup { vertical-align: super; } -sub { +:host ::ng-deep sub { vertical-align: sub; } -small { +:host ::ng-deep small { display: inline-block; font-size: 0.6em; line-height: 1.2em; vertical-align: top; } -small * { +:host ::ng-deep small * { vertical-align: top; } /********************************************* * LINKS *********************************************/ -a { +:host ::ng-deep a { color: #42affa; text-decoration: none; -webkit-transition: color .15s ease; -moz-transition: color .15s ease; transition: color .15s ease; } -a:hover { +:host ::ng-deep a:hover { color: #8dcffc; text-shadow: none; border: none; } -.roll span:after { +:host ::ng-deep .roll span:after { color: #fff; background: #068de9; } /********************************************* * IMAGES *********************************************/ -section img { - margin: 15px 0px; +:host ::ng-deep section img { + margin: 15px 0; background: rgba(255, 255, 255, 0.12); border: 4px solid #fff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } -section img.plain { +:host ::ng-deep section img.plain { border: 0; box-shadow: none; } -a img { +:host ::ng-deep a img { -webkit-transition: all .15s linear; -moz-transition: all .15s linear; transition: all .15s linear; } -a:hover img { +:host ::ng-deep a:hover img { background: rgba(255, 255, 255, 0.2); border-color: #42affa; box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } -/********************************************* - * NAVIGATION CONTROLS - *********************************************/ -.controls { - color: #42affa; } - /********************************************* * PROGRESS BAR *********************************************/ -.progress { +:host ::ng-deep .progress { background: rgba(0, 0, 0, 0.2); color: #42affa; } -.progress span { +:host ::ng-deep .progress span { -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } \ No newline at end of file diff --git a/src/app/shared/service/slide.service.ts b/src/app/shared/service/slide.service.ts index c89551e..e5b8ffa 100644 --- a/src/app/shared/service/slide.service.ts +++ b/src/app/shared/service/slide.service.ts @@ -10,15 +10,18 @@ import { Slide } from '../slide'; export class SlideService implements Resolve>{ private apiEndPoint = environment.apiUrl + '/api/slide'; + private apiEndPointPosition = environment.apiUrl + '/api/slide-position'; private cachedSlides: Array = []; constructor(private httpClient: HttpClient) {} private static prepareSlideData(slide: Slide) { const slideToSave = Object.assign({}, slide); - slideToSave.team = slideToSave.team.id === null - ? null - : slideToSave.team.id; + try { + slideToSave.team = slideToSave.team.id === null + ? null + : slideToSave.team.id; + } catch (e) {} return slideToSave; } @@ -36,6 +39,14 @@ export class SlideService implements Resolve>{ : this.update(slide); } + public moveUp(slide: Slide): Observable> { + return this.changePosition(slide, slide.position - 1); + } + + public moveDown(slide: Slide): Observable> { + return this.changePosition(slide, slide.position + 1); + } + public create(slide: Slide): Observable { return this.httpClient.post(this.apiEndPoint, SlideService.prepareSlideData(slide)); } @@ -44,6 +55,12 @@ export class SlideService implements Resolve>{ return this.httpClient.put(`${this.apiEndPoint}/${slide.id.toString()}`, SlideService.prepareSlideData(slide)); } + public changePosition(slide: Slide, position: number): Observable> { + return this.httpClient.put>(`${this.apiEndPointPosition}/${slide.id.toString()}`, { + position: position + }); + } + public delete(slide: Slide): Observable { return this.httpClient.delete(`${this.apiEndPoint}/${slide.id.toString()}`); } diff --git a/src/app/shared/slide.ts b/src/app/shared/slide.ts index 0b74dd1..095bcad 100644 --- a/src/app/shared/slide.ts +++ b/src/app/shared/slide.ts @@ -8,4 +8,5 @@ export class Slide { isVisible = true; createdAt: String = null; updatedAt: String = null; + position = 0; }