232 lines
8.7 KiB
TypeScript
232 lines
8.7 KiB
TypeScript
import { Injectable } from '@angular/core';
|
||
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
|
||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||
|
||
import { Answer } from '../enums/answer.enum';
|
||
import { Choice } from '../models/choice.model';
|
||
import { Poll } from '../models/poll.model';
|
||
import { Owner } from '../models/owner.model';
|
||
import { ApiService } from './api.service';
|
||
import { ToastService } from './toast.service';
|
||
import { UserService } from './user.service';
|
||
import { UuidService } from './uuid.service';
|
||
import { HttpClient } from '@angular/common/http';
|
||
import { environment } from '../../../environments/environment';
|
||
import { StorageService } from './storage.service';
|
||
import { Title } from '@angular/platform-browser';
|
||
|
||
@Injectable({
|
||
providedIn: 'root',
|
||
})
|
||
export class PollService implements Resolve<Poll> {
|
||
private _poll: BehaviorSubject<Poll | undefined> = new BehaviorSubject<Poll | undefined>(undefined);
|
||
public readonly poll: Observable<Poll | undefined> = this._poll.asObservable();
|
||
|
||
constructor(
|
||
private http: HttpClient,
|
||
private router: Router,
|
||
private apiService: ApiService,
|
||
private storageService: StorageService,
|
||
private userService: UserService,
|
||
private uuidService: UuidService,
|
||
private titleService: Title,
|
||
private toastService: ToastService
|
||
) {}
|
||
|
||
/**
|
||
* auto fetch a poll when route is looking for one in the administration pattern
|
||
* @param route
|
||
* @param state
|
||
*/
|
||
public async resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Poll> {
|
||
const segments: string[] = state.url.split('/');
|
||
const wantedSlug: string = segments.includes('poll') ? segments[segments.indexOf('poll') + 1] : '';
|
||
if (!wantedSlug && state.url.includes('administration')) {
|
||
// creation of new poll
|
||
const poll = new Poll(this.userService.getCurrentUser(), this.uuidService.getUUID(), '');
|
||
this._poll.next(poll);
|
||
this.router.navigate(['poll/' + poll.slug + '/administration']);
|
||
}
|
||
if (!this._poll.getValue() || !this._poll.getValue().slug || this._poll.getValue().slug !== wantedSlug) {
|
||
await this.loadPollBySlug(wantedSlug);
|
||
}
|
||
if (this._poll.getValue()) {
|
||
return this._poll.getValue();
|
||
} else {
|
||
this.router.navigate(['page-not-found']);
|
||
return;
|
||
}
|
||
}
|
||
|
||
getAllAvailablePolls() {
|
||
const baseHref = environment.api.version.apiV1.baseHref;
|
||
console.log('getAllAvailablePolls baseHref', baseHref);
|
||
const headers = ApiService.makeHeaders();
|
||
console.log('getAllAvailablePolls headers', headers);
|
||
try {
|
||
this.http.get(`${baseHref}/poll`, headers).subscribe((res: Observable<any>) => {
|
||
console.log('getAllAvailablePolls res', res);
|
||
});
|
||
} catch (e) {
|
||
console.error('getAllAvailablePolls e', e);
|
||
}
|
||
}
|
||
|
||
public async loadPollBySlug(slug: string): Promise<void> {
|
||
console.log('slug', slug);
|
||
if (slug) {
|
||
const poll: Poll | undefined = await this.apiService.getPollBySlug(slug);
|
||
|
||
console.log({ loadPollBySlugResponse: poll });
|
||
if (poll) {
|
||
this.updateCurrentPoll(poll);
|
||
this.titleService.setTitle(`☑️ ${poll.title} - ${environment.appTitle}`);
|
||
} else {
|
||
this.toastService.display(`sondage ${slug} non trouvé`);
|
||
}
|
||
}
|
||
}
|
||
public async loadPollBySlugWithPasswordHash(slug: string, hash: string): Promise<void> {
|
||
console.log('slug', slug);
|
||
if (slug) {
|
||
const poll: Poll | undefined = await this.apiService.getPollBySlugWithHash(slug, hash);
|
||
console.log({ loadPollBySlugResponse: poll });
|
||
this.updateCurrentPoll(poll);
|
||
}
|
||
}
|
||
|
||
public updateCurrentPoll(poll: Poll): void {
|
||
this.storageService.setChoicesForVoteStack(poll.choices);
|
||
|
||
console.log('poll', poll);
|
||
this._poll.next(poll);
|
||
}
|
||
|
||
/**
|
||
* make a uniq slug for the current poll creation
|
||
* @param config
|
||
*/
|
||
makeSlug(config: Poll): string {
|
||
console.log('config', config);
|
||
let str = '';
|
||
str =
|
||
config.configuration.dateCreated.getFullYear() +
|
||
'_' +
|
||
(config.configuration.dateCreated.getMonth() + 1) +
|
||
'_' +
|
||
config.configuration.dateCreated.getDate() +
|
||
'_' +
|
||
config.owner.pseudo +
|
||
'_' +
|
||
config.title;
|
||
str = str.replace(/^\s+|\s+$/g, ''); // trim
|
||
str = str.toLowerCase();
|
||
|
||
// remove accents, swap ñ for n, etc
|
||
const from = 'àáäâèéëêìíïîòóöôùúüûñç·/_,:;';
|
||
const to = 'aaaaeeeeiiiioooouuuunc------';
|
||
for (let i = 0, l = from.length; i < l; i++) {
|
||
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
|
||
}
|
||
|
||
str = str
|
||
.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
|
||
.replace(/\s+/g, '-') // collapse whitespace and replace by -
|
||
.replace(/-+/g, '-'); // collapse dashes
|
||
|
||
return str + '-' + this.uuidService.getUUID();
|
||
}
|
||
|
||
public async saveCurrentPoll(): Promise<void> {
|
||
const pollUrl: Subscription = await this.apiService.createPoll(this._poll.getValue());
|
||
// TODO: Maybe handle the url to update currentPoll according to backend response
|
||
if (pollUrl) {
|
||
console.log('pollUrl', pollUrl);
|
||
this.toastService.display('Le sondage a été enregistré.');
|
||
} else {
|
||
this.toastService.display('Le sondage n’a été correctement enregistré, veuillez ré-essayer.');
|
||
}
|
||
}
|
||
|
||
public saveParticipation(choice: Choice, user: Owner, response: Answer): void {
|
||
const currentPoll = this._poll.getValue();
|
||
currentPoll.choices.find((c) => c.name === choice.name)?.updateParticipation(user, response);
|
||
this.updateCurrentPoll(currentPoll);
|
||
this.apiService.createParticipation(currentPoll.slug, choice.name, user.pseudo, response);
|
||
this.toastService.display('Votre participation au sondage a été enregistrée.');
|
||
}
|
||
|
||
public async deleteAllAnswers(): Promise<void> {
|
||
await this.apiService.deletePollAnswers(this._poll.getValue().slug);
|
||
this.toastService.display('Les participations des votants à ce sondage ont été supprimées.');
|
||
}
|
||
|
||
public async addComment(comment: string): Promise<void> {
|
||
await this.apiService.createComment(this._poll.getValue().slug, comment);
|
||
this.toastService.display('Votre commentaire a été enregistré.');
|
||
}
|
||
|
||
public async deleteComments(): Promise<void> {
|
||
await this.apiService.deletePollComments(this._poll.getValue().slug);
|
||
this.toastService.display('Les commentaires de ce sondage ont été supprimés.');
|
||
}
|
||
|
||
newPollFromForm(form: any): any {
|
||
const newpoll = new Poll(
|
||
this.userService.getCurrentUser(),
|
||
this.uuidService.getUUID(),
|
||
form.controls.title.value
|
||
);
|
||
/**
|
||
* convert to API version 1 config poll
|
||
*/
|
||
const apiV1Poll = {
|
||
menuVisible: true,
|
||
expiracyDateDefaultInDays: newpoll.configuration.expiresDaysDelay,
|
||
deletionDateAfterLastModification: newpoll.configuration.expiracyAfterLastModificationInDays,
|
||
pollType: newpoll.configuration.isAboutDate ? 'dates' : 'classic', // classic or dates
|
||
title: newpoll.title,
|
||
description: newpoll.description,
|
||
myName: newpoll.owner.pseudo,
|
||
myComment: '',
|
||
isAdmin: true, // when we create a poll, we are admin on it
|
||
myVoteStack: {},
|
||
myTempVoteStack: 0,
|
||
myEmail: newpoll.owner.email,
|
||
myPolls: [], // list of retrieved polls from the backend api
|
||
/*
|
||
date specific poll, we have the choice to setup different hours (timeList) for all possible dates (dateList), or use the same hours for all dates
|
||
*/
|
||
allowSeveralHours: 'true',
|
||
// access
|
||
visibility: newpoll.configuration.areResultsPublic, // visible to one with the link:
|
||
voteChoices: newpoll.configuration.isMaybeAnswerAvailable ? 'yes, maybe, no' : 'yes', // possible answers to a vote choice: only "yes", "yes, maybe, no"
|
||
creationDate: new Date(),
|
||
expirationDate: '', // expiracy date
|
||
voteStackId: null, // id of the vote stack to update
|
||
pollId: null, // id of the current poll when created. data given by the backend api
|
||
pollSlug: null, // id of the current poll when created. data given by the backend api
|
||
currentPoll: null, // current poll selected with createPoll or getPoll of ConfigService
|
||
passwordAccess: newpoll.configuration.isProtectedByPassword,
|
||
password: newpoll.configuration.password,
|
||
customUrl: newpoll.slug, // custom slug in the url, must be unique
|
||
customUrlIsUnique: null, // given by the backend
|
||
urlSlugPublic: null,
|
||
urlPublic: null,
|
||
urlAdmin: null,
|
||
adminKey: '', // key to change config of the poll
|
||
owner_modifier_token: '', // key to change a vote stack
|
||
canModifyAnswers: newpoll.configuration.isAllowingtoChangeOwnAnswers, // bool for the frontend selector
|
||
whoCanChangeAnswers: newpoll.configuration.whoCanChangeAnswers, // everybody, self, nobody (: just admin)
|
||
dateList: newpoll.dateChoices, // sets of days as strings, config to set identical time for days in a special days poll
|
||
timeList: newpoll.timeChoices, // ranges of time expressed as strings
|
||
|
||
answers: newpoll.choices,
|
||
// modals
|
||
displayConfirmVoteModalAdmin: false,
|
||
};
|
||
console.log('apiV1Poll', apiV1Poll);
|
||
return apiV1Poll;
|
||
}
|
||
}
|