-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(nickname.component): nickname component introduced and feature t…
…oggle for voting event flow It is possible to enable the voting event flow setting the property "enableVotingEventFlow" to true (boolean) in the default configuration defined in the mongo db
- Loading branch information
1 parent
d54f942
commit f939ede
Showing
12 changed files
with
312 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface Credentials { | ||
userId?: string; | ||
nickname?: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<div class="nickname-section"> | ||
<div> | ||
<span class="event-label">Voting for event: <strong>{{appSession.getSelectedVotingEvent().name}}</strong></span> | ||
</div> | ||
</div> | ||
|
||
<div class="nickname-section"> | ||
<div class="nickname"> | ||
<label>Nickname</label> | ||
<input #nickname type="text" class="nickname-input" id="nickname"> | ||
</div> | ||
|
||
<div class="form-actions"> | ||
<button #startButton mat-flat-button color="accent" [class.button-disabled]="!(isValidInputData$ | async)" | ||
[class.button-enabled]="(isValidInputData$ | async)" [disabled]="!(isValidInputData$ | async)"> | ||
<span class="button-text"> Start Session </span> | ||
</button> | ||
</div> | ||
|
||
<div *ngIf="message$ | async as message" class="message" [innerHTML]="message"></div> | ||
|
||
</div> | ||
<div class="banner-section"> | ||
<a [href]="(configuration$ | async)?.bannerTargetUrl" target="_blank"><img | ||
[src]="(configuration$ | async)?.bannerImageUrl"></a> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
@import "../../../styles/abstracts/mixins"; | ||
|
||
$login-sections-max-width: 480px; | ||
|
||
.nickname-section { | ||
margin: 1.3rem auto; | ||
padding: 0 1.3rem; | ||
max-width: $login-sections-max-width; | ||
|
||
.event-selection { | ||
display: block; | ||
} | ||
|
||
mat-form-field { | ||
width: 100%; | ||
} | ||
|
||
.nickname { | ||
margin-bottom: 2rem; | ||
|
||
.nickname-input { | ||
width: 100%; | ||
@include byor-input; | ||
} | ||
|
||
label { | ||
display: block; | ||
margin-bottom: .3rem; | ||
} | ||
} | ||
|
||
.button-disabled { | ||
border: none; | ||
} | ||
|
||
.button-text { | ||
color: #ffffff; | ||
} | ||
} | ||
|
||
.banner-section { | ||
margin: 0 auto; | ||
padding: 6rem 1.3rem; | ||
max-width: $login-sections-max-width; | ||
text-align: center; | ||
|
||
a { | ||
border-bottom: none; | ||
img { | ||
max-width: 100%; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { NicknameComponent } from './nickname.component'; | ||
import { RouterTestingModule } from '@angular/router/testing'; | ||
import { AppMaterialModule } from 'src/app/app-material.module'; | ||
import { HttpClientModule } from '@angular/common/http'; | ||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | ||
import { VotingEvent } from 'src/app/models/voting-event'; | ||
import { AppSessionService } from 'src/app/app-session.service'; | ||
|
||
class MockAppSessionService { | ||
private votingEvent: VotingEvent; | ||
|
||
constructor() { | ||
this.votingEvent = { _id: '123', name: 'an event', status: 'open', creationTS: 'abc' }; | ||
} | ||
|
||
getSelectedVotingEvent() { | ||
return this.votingEvent; | ||
} | ||
} | ||
|
||
describe('NicknameComponent', () => { | ||
let component: NicknameComponent; | ||
let fixture: ComponentFixture<NicknameComponent>; | ||
|
||
beforeEach(async(() => { | ||
TestBed.configureTestingModule({ | ||
declarations: [NicknameComponent], | ||
imports: [RouterTestingModule, AppMaterialModule, HttpClientModule, BrowserAnimationsModule], | ||
providers: [{ provide: AppSessionService, useClass: MockAppSessionService }] | ||
}).compileComponents(); | ||
})); | ||
|
||
beforeEach(() => { | ||
fixture = TestBed.createComponent(NicknameComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { Component, OnInit, AfterViewInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; | ||
import { Router } from '@angular/router'; | ||
|
||
import { Observable, Subject, fromEvent, Subscription, NEVER } from 'rxjs'; | ||
import { shareReplay, map, share, switchMap, tap, filter } from 'rxjs/operators'; | ||
|
||
import { AppSessionService } from 'src/app/app-session.service'; | ||
import { ConfigurationService } from 'src/app/services/configuration.service'; | ||
import { BackendService } from 'src/app/services/backend.service'; | ||
import { VoteCredentials } from 'src/app/models/vote-credentials'; | ||
import { ErrorService } from 'src/app/services/error.service'; | ||
import { logError } from 'src/app/utils/utils'; | ||
|
||
@Component({ | ||
selector: 'byor-nickname', | ||
templateUrl: './nickname.component.html', | ||
styleUrls: ['./nickname.component.scss'] | ||
}) | ||
export class NicknameComponent implements AfterViewInit, OnDestroy, OnInit { | ||
constructor( | ||
private router: Router, | ||
private errorService: ErrorService, | ||
public appSession: AppSessionService, | ||
private configurationService: ConfigurationService, | ||
private backend: BackendService | ||
) {} | ||
|
||
isValidInputData$: Observable<boolean>; | ||
message$ = new Subject<string>(); | ||
configuration$: Observable<any>; | ||
|
||
goToVoteSubscription: Subscription; | ||
|
||
@ViewChild('nickname') voterFirstName: ElementRef; | ||
@ViewChild('startButton', { read: ElementRef }) startButtonRef: ElementRef; | ||
|
||
ngOnInit() { | ||
this.configuration$ = this.configurationService.defaultConfiguration().pipe(shareReplay(1)); | ||
} | ||
|
||
ngAfterViewInit() { | ||
// notify when nickname is changed | ||
const nickname$ = fromEvent(this.voterFirstName.nativeElement, 'keyup').pipe(map(() => this.voterFirstName.nativeElement.value)); | ||
|
||
const startButtonClick$ = fromEvent(this.startButtonRef.nativeElement, 'click'); | ||
|
||
// the main subscription | ||
this.goToVoteSubscription = this.goToVote$(nickname$, startButtonClick$).subscribe( | ||
(nickname) => { | ||
this.appSession.setCredentials({ nickname }); | ||
this.router.navigate(['vote/start']); | ||
}, | ||
(error) => { | ||
logError(error); | ||
let _errMsg = error; | ||
if (error.message) { | ||
_errMsg = error.message; | ||
} | ||
this.errorService.setError(_errMsg); | ||
this.errorService.setErrorMessage(error); | ||
this.router.navigate(['error']); | ||
} | ||
); | ||
} | ||
|
||
ngOnDestroy() { | ||
if (this.goToVoteSubscription) { | ||
this.goToVoteSubscription.unsubscribe(); | ||
} | ||
} | ||
|
||
// this method takes in input all Observable created out of DOM events which are relevant for this Component | ||
// in this way we can easily test this logic with marble tests | ||
goToVote$(nickname$: Observable<string>, startButtonClick$: Observable<any>) { | ||
// notifies when the input data provided changes - the value notified is true of false | ||
// depending on the fact that the input data is valid or not | ||
this.isValidInputData$ = nickname$.pipe( | ||
map((nickname) => this.isNicknameValid(nickname)), | ||
share() // share() is used since this Observable is used also on the Html template | ||
); | ||
|
||
const clickOnVote$ = nickname$.pipe( | ||
switchMap((nickname) => (this.isNicknameValid(nickname) ? startButtonClick$.pipe(map(() => nickname)) : NEVER)) | ||
); | ||
|
||
// notifies when the user has clicked to go to voting session and he has not voted yet | ||
return clickOnVote$.pipe( | ||
switchMap((nickname) => { | ||
const votingEvent = this.appSession.getSelectedVotingEvent(); | ||
// to remove when we refactor hasAlreadyVoted | ||
const oldCredentials: VoteCredentials = { voterId: { firstName: nickname, lastName: '' }, votingEvent }; | ||
return this.backend.hasAlreadyVoted(oldCredentials).pipe( | ||
tap((hasAlreadyVoted) => { | ||
if (hasAlreadyVoted) { | ||
this.message$.next(`You have already voted for <strong>${votingEvent.name}</strong>`); | ||
} | ||
}), | ||
filter((hasAlreadyVoted) => !hasAlreadyVoted), | ||
map(() => nickname) | ||
); | ||
}) | ||
); | ||
} | ||
|
||
isNicknameValid(nickname: string) { | ||
return nickname && nickname.trim().length > 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.