diff --git a/frontend/src/app/framework/internal.ts b/frontend/src/app/framework/internal.ts index 57d7c1c3cf..5ec5f89d73 100644 --- a/frontend/src/app/framework/internal.ts +++ b/frontend/src/app/framework/internal.ts @@ -10,6 +10,7 @@ export * from './angular/drag-helper'; export * from './angular/routers/router-utils'; export * from './angular/stateful.component'; export * from './configurations'; +export * from './services/analytics.service'; export * from './services/clipboard.service'; export * from './services/dialog.service'; export * from './services/loading.service'; diff --git a/frontend/src/app/framework/module.ts b/frontend/src/app/framework/module.ts index a0f2946ff1..08b7602272 100644 --- a/frontend/src/app/framework/module.ts +++ b/frontend/src/app/framework/module.ts @@ -7,12 +7,12 @@ import { CommonModule } from '@angular/common'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { ErrorHandler, ModuleWithProviders, NgModule } from '@angular/core'; +import { ErrorHandler, Injector, ModuleWithProviders, NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { ColorPickerModule } from 'ngx-color-picker'; import { TourService as BaseTourService } from 'ngx-ui-tour-core'; -import { AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, CompensateScrollbarDirective, ConfirmClickDirective, ControlErrorsComponent, ControlErrorsMessagesComponent, CopyDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DropdownMenuComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, GlobalErrorHandler, HighlightPipe, HoverBackgroundDirective, IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoaderComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, LongHoverDirective, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MonthPipe, PagerComponent, ParentLinkDirective, ProgressBarComponent, RadioGroupComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TourService, TourStepDirective, TourTemplateComponent, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations'; +import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, CompensateScrollbarDirective, ConfirmClickDirective, ControlErrorsComponent, ControlErrorsMessagesComponent, CopyDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DropdownMenuComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, GlobalErrorHandler, HighlightPipe, HoverBackgroundDirective, IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoaderComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, LongHoverDirective, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MonthPipe, PagerComponent, ParentLinkDirective, ProgressBarComponent, RadioGroupComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TourService, TourStepDirective, TourTemplateComponent, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations'; @NgModule({ imports: [ @@ -206,10 +206,15 @@ import { AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactiva ], }) export class SqxFrameworkModule { + constructor(injector: Injector) { + injector.get(AnalyticsService); + } + public static forRoot(): ModuleWithProviders { return { ngModule: SqxFrameworkModule, providers: [ + AnalyticsService, CanDeactivateGuard, ClipboardService, DialogService, diff --git a/frontend/src/app/framework/services/analytics.service.ts b/frontend/src/app/framework/services/analytics.service.ts new file mode 100644 index 0000000000..b334443f17 --- /dev/null +++ b/frontend/src/app/framework/services/analytics.service.ts @@ -0,0 +1,34 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Injectable } from '@angular/core'; +import { NavigationEnd, Router } from '@angular/router'; +import { Types } from './../internal'; + +type TrackEvent = (name: string, properties: any) => void; +type TrackPage = (url: string) => void; + +@Injectable() +export class AnalyticsService { + private readonly globalTrackEvent?: TrackEvent; + private readonly globalTrackPage?: TrackPage; + + constructor(router: Router) { + this.globalTrackEvent = (window as any)['trackEvent']; + this.globalTrackPage = (window as any)['trackEvent']; + + router.events.subscribe(event => { + if (Types.is(event, NavigationEnd)) { + this.globalTrackPage?.(event.urlAfterRedirects); + } + }); + } + + public trackEvent(name: string, properties: any) { + this.globalTrackEvent?.(name, properties); + } +} \ No newline at end of file diff --git a/frontend/src/app/framework/state.ts b/frontend/src/app/framework/state.ts index 90349244d9..5a34c24157 100644 --- a/frontend/src/app/framework/state.ts +++ b/frontend/src/app/framework/state.ts @@ -201,9 +201,13 @@ class Connector { state.changes .subscribe(change => { - this.state[slice] = change.snapshot; + const eventName = `${slice} - ${change.event}`; - this.devTools?.send(`${slice} - ${change.event}`, this.state); + if (this.devTools) { + this.state[slice] = change.snapshot; + + this.devTools?.send(eventName, this.state); + } }); } }