import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; import { BehaviorSubject, Observable } from 'rxjs'; import { Answer } from '../enums/answer.enum'; import { Choice } from '../models/choice.model'; import { Poll } from '../models/poll.model'; import { User } from '../models/user.model'; import { ApiService } from './api.service'; import { ToastService } from './toast.service'; import { UserService } from './user.service'; import { UuidService } from './uuid.service'; import { Form } from '@angular/forms'; import { PollConfig } from '../../features/old-stuff/config/PollConfig'; import { environment } from '../../../environments/environment'; import { DateChoice, defaultAnswers, otherDefaultDates, PollAnswer, } from '../../features/old-stuff/config/defaultConfigs'; @Injectable({ providedIn: 'root', }) export class PollService implements Resolve { private _poll: BehaviorSubject = new BehaviorSubject(undefined); public readonly poll: Observable = this._poll.asObservable(); constructor( private router: Router, private apiService: ApiService, private userService: UserService, private uuidService: UuidService, private toastService: ToastService ) {} public async resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { 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; } } public async loadPollBySlug(slug: string): Promise { console.log('slug', slug); if (slug) { const poll: Poll | undefined = await this.apiService.getPollBySlug(slug); console.log({ loadPollBySlugResponse: poll }); this.updateCurrentPoll(poll); } } public updateCurrentPoll(poll: Poll): void { this._poll.next(poll); } /** * make a uniq slug for the current poll creation * @param config */ makeSlug(config: Poll): string { 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 { const pollUrl: string = 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: User, response: Answer): void { const currentPoll = this._poll.getValue(); currentPoll.choices.find((c) => c.label === choice.label)?.updateParticipation(user, response); this.updateCurrentPoll(currentPoll); this.apiService.createParticipation(currentPoll.slug, choice.label, 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().slug); 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().slug, comment); this.toastService.display('Votre commentaire a été enregistré.'); } public async deleteComments(): Promise { await this.apiService.deletePollComments(this._poll.getValue().slug); this.toastService.display('Les commentaires de ce sondage ont été supprimés.'); } public buildAnswersByChoiceLabelByPseudo(poll: Poll): Map> { const pseudos: Set = new Set(); poll.choices.forEach((choice: Choice) => { choice.participants.forEach((users: Set) => { users.forEach((user: User) => { pseudos.add(user.pseudo); }); }); }); const list = new Map>(); pseudos.forEach((pseudo: string) => { list.set( pseudo, new Map( poll.choices.map((choice: Choice) => { return [choice.label, undefined]; }) ) ); }); poll.choices.forEach((choice: Choice) => { choice.participants.forEach((users: Set, answer: Answer) => { users.forEach((user: User) => { list.get(user.pseudo).set(choice.label, answer); }); }); }); return list; } newPollFromForm(form: 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 whoModifiesAnswers: newpoll.configuration.whoCanChangeAnswers, // everybody, self, nobody (: just admin) 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, }; return apiV1Poll; } }