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'; import { DateUtilitiesService } from './date.utilities.service'; @Injectable({ providedIn: 'root', }) export class PollService implements Resolve { _poll: BehaviorSubject = new BehaviorSubject(undefined); public readonly poll: Observable = this._poll.asObservable(); constructor( private http: HttpClient, private router: Router, private apiService: ApiService, private storageService: StorageService, private userService: UserService, private uuidService: UuidService, private dateUtils: DateUtilitiesService, private titleService: Title, private toastService: ToastService ) { this._poll.next(new Poll(null, 'titre', 'custom-title')); } /** * 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 { console.log('resolve route,state', route, state); const segments: string[] = state.url.split('/'); const wantedcustom_url: string = segments.includes('poll') ? segments[segments.indexOf('poll') + 1] : ''; if ( !this._poll.getValue() || !this._poll.getValue().custom_url || this._poll.getValue().custom_url !== wantedcustom_url ) { await this.loadPollBycustom_url(wantedcustom_url); } 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) => { console.log('getAllAvailablePolls res', res); }); } catch (e) { console.error('getAllAvailablePolls e', e); } } public async loadPollBycustom_url(custom_url: string): Promise { console.log('custom_url', custom_url); if (custom_url) { const poll: Poll | undefined = await this.apiService.getPollByCustomUrl(custom_url); console.log('loadPollBycustom_urlResponse', poll); if (poll) { this.updateCurrentPoll(poll); this.titleService.setTitle(`☑️ ${poll.title} - ${environment.appTitle}`); } else { this.toastService.display(`sondage ${custom_url} non trouvé`); } } else { this.toastService.display(`sondage sans custom url : ${custom_url}`); } } public async loadPollBycustom_urlWithPasswordHash(custom_url: string, hash: string): Promise { console.log('custom_url', custom_url); if (custom_url) { const poll: Poll | undefined = await this.apiService.getPollByCustomUrlWithHash(custom_url, hash); console.log({ loadPollBycustom_urlResponse: poll }); this.updateCurrentPoll(poll); } } /** * update poll and parse its fields * @param poll */ public updateCurrentPoll(poll: Poll): void { console.log('update poll with: ', poll); if (!this.storageService.vote_stack.id || this.storageService.vote_stack.poll_custom_url !== poll.custom_url) { console.log('set base choices', poll.choices); // set the choices only the first time the poll loads this.storageService.setChoicesForVoteStack(poll.choices); } this.toastService.display('sondage bien mis à jour', 'success'); console.log('poll', poll); this._poll.next(poll); } /** * make a uniq custom_url for the current poll creation * @param poll */ makecustom_url(poll: Poll): string { console.log('config', poll); let str = ''; const creation_date = new Date(poll.creation_date); str = creation_date.getFullYear() + '_' + (creation_date.getMonth() + 1) + '_' + creation_date.getDate() + '_' + poll.owner.pseudo + '_' + poll.title; return this.convertTextToSlug(str) + '-' + this.uuidService.getUUID(); } public convertTextToSlug(str: string) { str = str.trim(); 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; } 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.custom_url, choice.name, user.pseudo, response); this.toastService.display('Votre participation au sondage a été enregistrée.'); } public async deleteAllAnswers(): Promise { await this.apiService.deletePollAnswers(this._poll.getValue().custom_url); this.toastService.display('Les participations des votants à ce sondage ont été supprimées.'); } public async addComment(comment: string): Promise { await this.apiService.createComment(this._poll.getValue().custom_url, comment); this.toastService.display('Votre commentaire a été enregistré.'); } public async deleteComments(): Promise { await this.apiService.deletePollComments(this._poll.getValue().custom_url); this.toastService.display('Les commentaires de ce sondage ont été supprimés.'); } /** * @description convert to API version 1 data transition object * @param form */ newPollFromForm(form: any): Poll { const newOwner = this.storageService.vote_stack.owner; const newpoll = new Poll(newOwner, form.value.custom_url, form.value.title); const pollKeys = Object.keys(newpoll); const formFields = Object.keys(form.value); console.log('pollKeys, formFields', pollKeys, formFields); newpoll.allowed_answers = ['yes']; for (const pk of pollKeys) { if (formFields.indexOf(pk) !== -1) { const field = form.value[pk]; newpoll[pk] = field; } else { console.log('manque pollKey', pk); } } if (form.value.isMaybeAnswerAvailable) { newpoll.allowed_answers.push('maybe'); } if (form.value.isNoAnswerAvailable) { newpoll.allowed_answers.push('no'); } newpoll.description = form.value.description; newpoll.has_several_hours = form.value.hasSeveralHours; newpoll.hasSeveralHours = form.value.hasSeveralHours; newpoll.max_count_of_answers = form.value.allowComments; newpoll.maxCountOfAnswers = form.value.maxCountOfAnswers; newpoll.password = form.value.password; newpoll.kind = form.value.kind; newpoll.allow_comments = form.value.allowComments; // merge choices from storage newpoll.choices = Object.assign([], this.storageService.choices); newpoll.dateChoices = Object.assign([], this.storageService.dateChoices); newpoll.timeSlices = Object.assign([], this.storageService.timeSlices); console.log('this.storageService.timeSlices', this.storageService.timeSlices, newpoll.timeSlices); return newpoll; } public getAdministrationUrl(): string { let url = ''; if (this._poll && this._poll.getValue) { const polltemp = this._poll.getValue(); if (polltemp) { url = `${environment.frontDomain}#/poll/admin/${polltemp.admin_key}`; } } return url; } public getParticipationUrl(): string { let url = ''; if (this._poll && this._poll.getValue) { const polltemp = this._poll.getValue(); if (polltemp) { url = `${environment.frontDomain}#/poll/${polltemp.custom_url}/consultation`; } } // TODO handle pass access return url; } }