UNPKG

@studiohyperdrive/ngx-cookies

Version:
396 lines (388 loc) 15.8 kB
import * as i0 from '@angular/core'; import { Injectable, InjectionToken, Directive, Optional, Inject, Input } from '@angular/core'; import * as CookieConsent from 'vanilla-cookieconsent'; import { Subject, BehaviorSubject, combineLatest, startWith, map, distinctUntilChanged, tap, takeUntil } from 'rxjs'; import * as i1 from '@studiohyperdrive/ngx-core'; import { flatten } from 'lodash'; /** * A service that implements the CookieConsent from https://cookieconsent.orestbida.com and provides the necessary Observables to handle this setup. */ class NgxCookieService { constructor(windowsService) { this.windowsService = windowsService; /** * Subject to hold the onFirstConsent event */ this.firstCookiesConsentedSubject = new Subject(); /** * Subject to hold the onConsent event */ this.cookiesConsentedSubject = new Subject(); /** * Subject to hold the consentChanged event */ this.cookiesConsentChangedSubject = new Subject(); /** * Subject to hold the modalVisible event */ this.modalVisibleSubject = new BehaviorSubject(false); /** * Subject to hold the cookiesChanged event */ this.cookiesChangedSubject = new Subject(); /** * An event triggered only the very first time that the user expresses their choice of consent */ this.firstCookiesConsented$ = this.firstCookiesConsentedSubject.asObservable(); /** * An event triggering the choice of consent on every page load. */ this.cookiesConsented$ = this.cookiesConsentedSubject.asObservable(); /** * An event triggered when the user modifies their preferences and only if consent has already been provided. */ this.cookiesConsentChanged$ = this.cookiesConsentChangedSubject.asObservable(); /** * Whether the cookies modal is currently visible */ this.modalVisible$ = this.modalVisibleSubject.asObservable(); /** * Emits every time the set cookies have been changed */ this.cookiesChanged$ = this.cookiesChangedSubject.asObservable(); } /** * Sets up the CookieConsent. * * For more information of on this configuration, check the documentation * https://cookieconsent.orestbida.com/essential/getting-started.html * * @param categories - The categories of cookies * @param language - The language settings of the cookies * @param configuration - An optional set configuration settings */ setupCookiesHandler(categories, language, configuration) { // Iben: If we're not in the browser, we early exit, so server-side rendering can be enabled if (!this.windowsService.isBrowser()) { return; } // Iben: If no categories were provided, we early exit and throw an error, as the cookie preference cannot be shown without. if (!categories || Object.keys(categories).length === 0) { console.error('NgxCookies: No categories were provided for the cookie handler. At least one category is required.'); return; } // Iben: Setup the onFirstConsent listener const onFirstConsent = (event) => { this.firstCookiesConsentedSubject.next(event); }; onFirstConsent.bind(this); // Iben: Setup the onConsent listener const onConsent = (event) => { this.cookiesConsentedSubject.next(event); }; onConsent.bind(this); // Iben: Setup the onChange listener const onChange = (event) => { this.cookiesConsentChangedSubject.next(event); }; onChange.bind(this); // Iben: Setup the onModalShow listener const onModalShow = () => { this.modalVisibleSubject.next(true); }; onModalShow.bind(this); // Iben: Setup the onModalHide listener const onModalHide = () => { this.modalVisibleSubject.next(false); }; onModalHide.bind(this); // Iben: Run the CookieConsent handler CookieConsent.run({ ...(configuration || {}), onFirstConsent, onConsent, onChange, onModalHide, onModalShow, categories, language, }); } /** * Whether a certain category of cookies was accepted * * @param name - The name of the category */ hasAcceptedCategory(name) { // Iben: Return every time the cookie consent has changed return combineLatest([ this.cookiesConsented$.pipe(startWith(undefined)), this.cookiesConsentChanged$.pipe(startWith(undefined)), ]).pipe(map(() => { // Iben: Check if the category was accepted return CookieConsent.acceptedCategory(name); })); } /** * Accept a category of cookies * * @param name - The name of the categories */ acceptCategory(name) { CookieConsent.acceptCategory(name); } /** * Whether a certain service within a category was accepted * * @param category - The name of the category * @param name - The name of the service */ hasAcceptedService(category, name) { // Iben: Return every time the cookie consent has changed return combineLatest([ this.cookiesConsented$.pipe(startWith(undefined)), this.cookiesConsentChanged$.pipe(startWith(undefined)), ]).pipe(map(() => { // Iben: Check if the service within the provided category was accepted return CookieConsent.acceptedService(name, category); })); } /** * Accept a category of cookies * * @param category - The name of the category * @param name - The name of the service */ acceptService(category, name) { CookieConsent.acceptService(name, category); } /** * Show the cookie modal */ showModal() { CookieConsent.show(true); } /** * Get a cookie * * @param cookie - Name of the cookie */ getCookie(cookie) { return (CookieConsent.getCookie('data') || {})[cookie]; } /** * Get a cookie as an observable * * @param cookie - Name of the cookie */ getCookieObservable(cookie) { // Iben: Return every time the set cookies are changed return this.cookiesChanged$.pipe(startWith(CookieConsent.getCookie('data') || {}), // Iben: Get the cookie value map(() => { return this.getCookie(cookie); }), // Iben: As the cookiesChanged event emits every time all cookies are rest, we check if the cookie value was really changed distinctUntilChanged()); } /** * Set a cookie * * @param cookie - The cookie we wish to set */ setCookie(cookie) { // Iben: Set the cookie const isSet = CookieConsent.setCookieData({ value: { [cookie.name]: cookie.value }, mode: 'update', }); // Iben: If the cookie was not set, we return if (!isSet) { return; } // Iben: Update the subject so we can notify listeners this.cookiesChangedSubject.next(CookieConsent.getCookie('data')); } /** * Remove a cookie * * @param cookie - The cookie we wish to remove */ removeCookie(cookie) { // Iben: Get the current cookies const currentCookies = CookieConsent.getCookie('data') || {}; // Iben: Remove the cookie from the currently set cookies const { [cookie]: _removedValue, ...value } = currentCookies; // Iben: Remove the cookie from the cookies holder const isSet = CookieConsent.setCookieData({ value, mode: 'overwrite' }); // Iben: If the cookie was not set, we return if (!isSet) { return; } // Iben: Update the subject so we can notify listeners this.cookiesChangedSubject.next(CookieConsent.getCookie('data')); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgxCookieService, deps: [{ token: i1.NgxWindowService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgxCookieService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgxCookieService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.NgxWindowService }] }); /** A token to provide a fallback component that needs to be rendered when a specific (set of) cookie(s) was not accepted. */ const NgxCookiesFallbackComponentToken = new InjectionToken('NgxCookiesFallbackComponent'); /** * A structural directive that provides a way to render UI elements based on whether a (set of) cookie(s) have been accepted by the user. */ class NgxHasCookieDirective { /** * A cookie or list of cookies the item should have */ set hasCookie(cookie) { this.cookies = Array.isArray(cookie) ? cookie : [cookie]; this.updateView(); } /** * The else template in case the cookie is not accepted */ set hasCookieElse(ngTemplate) { this.elseTemplateRef = ngTemplate; this.updateView(); } constructor(templateRef, viewContainer, ngxCookieService, cdRef, component) { this.viewContainer = viewContainer; this.ngxCookieService = ngxCookieService; this.cdRef = cdRef; this.component = component; /** * The needed templateRefs */ this.thenTemplateRef = null; this.elseTemplateRef = null; /** * The list of cookies we need to check */ this.cookies = []; this.thenTemplateRef = templateRef; } ngOnDestroy() { this.dispose(); } /** * Updates the view and hides/renders the template as needed */ updateView() { // Iben: Dispose the current subscription this.dispose(); // Iben: Create a new onDestroyed handler this.destroyed$ = new Subject(); // Iben: Render the views based on the correct state combineLatest( // Iben Check for each cookie if it is accepted this.cookies.map((cookie) => { // Iben: If no specific services were provided, we can just check the category if (!cookie.services) { return this.ngxCookieService.hasAcceptedCategory(cookie.category); } // Iben: If specific services were provided, we can just check each individual category return combineLatest(cookie.services.map((service) => { return this.ngxCookieService.hasAcceptedService(cookie.category, service); })); })) .pipe(map((hasCookies) => { return flatten(hasCookies).every((hasCookie) => hasCookie); }), tap((hasCookie) => { // Iben: Clear the current view this.viewContainer.clear(); // Iben: If there already is a component, destroy it so it can update correctly if (this.componentRef) { this.componentRef.destroy(); this.componentRef = undefined; } // Iben: Render the correct templates hasCookie ? this.renderThenTemplate() : this.renderElseTemplate(); // Iben: Detect the changes so that the view gets updated this.cdRef.detectChanges(); }), takeUntil(this.destroyed$)) .subscribe(); } /** * Dispose the current subscription */ dispose() { if (this.destroyed$) { this.destroyed$.next(); this.destroyed$.complete(); } } /** * Render the template on which the directive is set */ renderThenTemplate() { // Iben: If a thenTemplateRef is provided, render the template if (this.thenTemplateRef) { this.viewContainer.createEmbeddedView(this.thenTemplateRef); } } /** * Render the fallbackTemplate or fallBackComponent */ renderElseTemplate() { // Iben: If a custom template ref was provided, render the template and early exit if (this.elseTemplateRef) { this.viewContainer.createEmbeddedView(this.elseTemplateRef); return; } // Iben: If a component was provided as a fallback, we render that if (this.component) { // Iben: Render the provided component this.componentRef = this.viewContainer.createComponent(this.component); // Iben: Set the cookies of the component this.componentRef.setInput('cookies', this.cookies); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgxHasCookieDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: NgxCookieService }, { token: i0.ChangeDetectorRef }, { token: NgxCookiesFallbackComponentToken, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.3", type: NgxHasCookieDirective, isStandalone: true, selector: "[hasCookie]", inputs: { hasCookie: "hasCookie", hasCookieElse: "hasCookieElse" }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgxHasCookieDirective, decorators: [{ type: Directive, args: [{ selector: '[hasCookie]', standalone: true, }] }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: NgxCookieService }, { type: i0.ChangeDetectorRef }, { type: i0.Type, decorators: [{ type: Optional }, { type: Inject, args: [NgxCookiesFallbackComponentToken] }] }], propDecorators: { hasCookie: [{ type: Input }], hasCookieElse: [{ type: Input }] } }); /** *An abstract component that can optionally be used as a fallback to content that depends on accepted cookies */ class NgxCookiesFallBackComponent { constructor() { /** * The cookies that were required to show the original content */ this.cookies = []; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgxCookiesFallBackComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.3", type: NgxCookiesFallBackComponent, isStandalone: true, inputs: { cookies: "cookies" }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgxCookiesFallBackComponent, decorators: [{ type: Directive }], propDecorators: { cookies: [{ type: Input }] } }); /* * Public API Surface of ngx-cookies */ /** * Generated bundle index. Do not edit. */ export { NgxCookieService, NgxCookiesFallBackComponent, NgxCookiesFallbackComponentToken, NgxHasCookieDirective }; //# sourceMappingURL=studiohyperdrive-ngx-cookies.mjs.map