Browse Source

Show AI button based on settings.

pull/1034/head
Sebastian 3 years ago
parent
commit
43c207b443
  1. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
  2. 2
      backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj
  3. 12
      backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
  4. 20
      backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs
  5. 58
      backend/src/Squidex/Config/Domain/FontendServices.cs
  6. 22
      backend/src/Squidex/Squidex.csproj
  7. 1
      backend/src/Squidex/Startup.cs
  8. 6
      frontend/src/app/features/apps/pages/apps-page.component.ts
  9. 8
      frontend/src/app/features/content/pages/content/editor/content-field.component.html
  10. 9
      frontend/src/app/features/content/pages/content/editor/content-field.component.ts
  11. 7
      frontend/src/app/features/content/pages/schemas/schemas-page.component.ts
  12. 4
      frontend/src/app/features/content/shared/forms/array-editor.component.html
  13. 3
      frontend/src/app/features/content/shared/forms/array-editor.component.ts
  14. 1
      frontend/src/app/features/content/shared/forms/array-item.component.html
  15. 3
      frontend/src/app/features/content/shared/forms/array-item.component.ts
  16. 1
      frontend/src/app/features/content/shared/forms/component-section.component.html
  17. 3
      frontend/src/app/features/content/shared/forms/component-section.component.ts
  18. 1
      frontend/src/app/features/content/shared/forms/component.component.html
  19. 3
      frontend/src/app/features/content/shared/forms/component.component.ts
  20. 5
      frontend/src/app/features/content/shared/forms/field-editor.component.html
  21. 3
      frontend/src/app/features/content/shared/forms/field-editor.component.ts
  22. 8
      frontend/src/app/features/content/shared/references/references-checkboxes.component.ts
  23. 11
      frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts
  24. 22
      frontend/src/app/framework/configurations.ts
  25. 28
      frontend/src/app/shared/components/forms/geolocation-editor.component.ts
  26. 7
      frontend/src/app/shared/components/notifo.component.ts
  27. 9
      frontend/src/app/shared/guards/must-be-authenticated.guard.ts
  28. 7
      frontend/src/app/shared/guards/must-be-not-authenticated.guard.ts
  29. 7
      frontend/src/app/shared/state/resolvers.ts
  30. 9
      frontend/src/app/shared/state/tour.state.ts
  31. 7
      frontend/src/app/shell/pages/home/home-page.component.ts
  32. 9
      frontend/src/app/shell/pages/internal/feedback-menu.component.ts
  33. 8
      frontend/src/app/shell/pages/internal/internal-area.component.ts
  34. 2
      frontend/src/app/shell/pages/internal/notifications-menu.component.ts
  35. 8
      frontend/src/app/shell/pages/internal/profile-menu.component.ts

2
backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj

@ -28,7 +28,7 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="NJsonSchema" Version="10.9.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="5.18.0" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="5.19.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Collections.Immutable" Version="7.0.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />

2
backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj

@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="Notifo.SDK" Version="1.7.4" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.CLI.Core" Version="11.1.0" />
<PackageReference Include="Squidex.CLI.Core" Version="11.2.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Collections.Immutable" Version="7.0.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />

12
backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj

@ -24,12 +24,12 @@
<PackageReference Include="NodaTime" Version="3.1.9" />
<PackageReference Include="OpenTelemetry.Api" Version="1.5.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.Assets" Version="5.18.0" />
<PackageReference Include="Squidex.Caching" Version="5.18.0" />
<PackageReference Include="Squidex.Hosting.Abstractions" Version="5.18.0" />
<PackageReference Include="Squidex.Log" Version="5.18.0" />
<PackageReference Include="Squidex.Messaging" Version="5.18.0" />
<PackageReference Include="Squidex.Text" Version="5.18.0" />
<PackageReference Include="Squidex.Assets" Version="5.19.0" />
<PackageReference Include="Squidex.Caching" Version="5.19.0" />
<PackageReference Include="Squidex.Hosting.Abstractions" Version="5.19.0" />
<PackageReference Include="Squidex.Log" Version="5.19.0" />
<PackageReference Include="Squidex.Messaging" Version="5.19.0" />
<PackageReference Include="Squidex.Text" Version="5.19.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Collections.Immutable" Version="7.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />

20
backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs

@ -10,8 +10,6 @@ using System.Globalization;
using System.Text.Json;
using Microsoft.Extensions.Options;
using Squidex.Areas.Api.Controllers.UI;
using Squidex.Domain.Apps.Entities.History;
using Squidex.Web;
namespace Squidex.Areas.Frontend.Middlewares;
@ -39,28 +37,12 @@ public static class IndexExtensions
{
var clonedOptions = uiOptions with
{
More = new Dictionary<string, object>
More = new Dictionary<string, object>(uiOptions.More)
{
["culture"] = CultureInfo.CurrentUICulture.Name
}
};
var jsonOptions = httpContext.RequestServices.GetRequiredService<JsonSerializerOptions>();
using var jsonDocument = JsonSerializer.SerializeToDocument(uiOptions, jsonOptions);
if (httpContext.RequestServices.GetService<ExposedValues>() is ExposedValues values)
{
clonedOptions.More["info"] = values.ToString();
}
var notifo = httpContext.RequestServices!.GetService<IOptions<NotifoOptions>>()?.Value;
if (notifo?.IsConfigured() == true)
{
clonedOptions.More["notifoApi"] = notifo.ApiUrl;
}
var options = httpContext.Features.Get<OptionsFeature>();
if (options != null)

58
backend/src/Squidex/Config/Domain/FontendServices.cs

@ -0,0 +1,58 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Text.Json;
using Microsoft.Extensions.Options;
using Squidex.Areas.Api.Controllers.UI;
using Squidex.Domain.Apps.Entities.History;
using Squidex.Text.ChatBots;
using Squidex.Text.Translations;
using Squidex.Web;
namespace Squidex.Config.Domain;
public static class FontendServices
{
public static void AddSquidexFrontend(this IServiceCollection services)
{
services.Configure<MyUIOptions>((services, options) =>
{
var jsonOptions = services.GetRequiredService<JsonSerializerOptions>();
using var jsonDocument = JsonSerializer.SerializeToDocument(options, jsonOptions);
if (services.GetService<ExposedValues>() is ExposedValues values)
{
options.More["info"] = values.ToString();
}
});
services.Configure<MyUIOptions>((services, options) =>
{
var notifo = services.GetRequiredService<IOptions<NotifoOptions>>().Value;
if (notifo.IsConfigured())
{
options.More["notifoApi"] = notifo.ApiUrl;
}
});
services.Configure<MyUIOptions>((services, options) =>
{
var translator = services.GetRequiredService<ITranslator>();
options.More["canUseTranslator"] = translator.IsConfigured;
});
services.Configure<MyUIOptions>((services, options) =>
{
var chatBot = services.GetRequiredService<IChatBot>();
options.More["canUseChatBot"] = chatBot.IsConfigured;
});
}
}

22
backend/src/Squidex/Squidex.csproj

@ -62,18 +62,18 @@
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc7" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="ReportGenerator" Version="5.1.22" PrivateAssets="all" />
<PackageReference Include="Squidex.Assets.Azure" Version="5.18.0" />
<PackageReference Include="Squidex.Assets.GoogleCloud" Version="5.18.0" />
<PackageReference Include="Squidex.Assets.FTP" Version="5.18.0" />
<PackageReference Include="Squidex.Assets.ImageMagick" Version="5.18.0" />
<PackageReference Include="Squidex.Assets.ImageSharp" Version="5.18.0" />
<PackageReference Include="Squidex.Assets.Mongo" Version="5.18.0" />
<PackageReference Include="Squidex.Assets.S3" Version="5.18.0" />
<PackageReference Include="Squidex.Assets.TusAdapter" Version="5.18.0" />
<PackageReference Include="Squidex.Assets.Azure" Version="5.19.0" />
<PackageReference Include="Squidex.Assets.GoogleCloud" Version="5.19.0" />
<PackageReference Include="Squidex.Assets.FTP" Version="5.19.0" />
<PackageReference Include="Squidex.Assets.ImageMagick" Version="5.19.0" />
<PackageReference Include="Squidex.Assets.ImageSharp" Version="5.19.0" />
<PackageReference Include="Squidex.Assets.Mongo" Version="5.19.0" />
<PackageReference Include="Squidex.Assets.S3" Version="5.19.0" />
<PackageReference Include="Squidex.Assets.TusAdapter" Version="5.19.0" />
<PackageReference Include="Squidex.ClientLibrary" Version="16.1.0" />
<PackageReference Include="Squidex.Hosting" Version="5.18.0" />
<PackageReference Include="Squidex.Messaging.All" Version="5.18.0" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="5.18.0" />
<PackageReference Include="Squidex.Hosting" Version="5.19.0" />
<PackageReference Include="Squidex.Messaging.All" Version="5.19.0" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="5.19.0" />
<PackageReference Include="Squidex.OpenIddict.MongoDb" Version="4.0.1-dev" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
</ItemGroup>

1
backend/src/Squidex/Startup.cs

@ -50,6 +50,7 @@ public sealed class Startup
services.AddSquidexContents(config);
services.AddSquidexControllerServices(config);
services.AddSquidexEventSourcing(config);
services.AddSquidexFrontend();
services.AddSquidexGraphQL();
services.AddSquidexHealthChecks(config);
services.AddSquidexHistory(config);

6
frontend/src/app/features/apps/pages/apps-page.component.ts

@ -68,8 +68,8 @@ export class AppsPageComponent implements OnInit {
private readonly tourState: TourState,
private readonly uiOptions: UIOptions,
) {
if (uiOptions.get('showInfo')) {
this.info = uiOptions.get('info');
if (uiOptions.value.showInfo) {
this.info = uiOptions.value.info;
}
}
@ -87,7 +87,7 @@ export class AppsPageComponent implements OnInit {
this.tourState.complete();
}
if (!this.uiOptions.get('hideNews')) {
if (!this.uiOptions.value.hideNews) {
const newsVersion = this.localStore.getInt(Settings.Local.NEWS_VERSION);
this.newsService.getFeatures(newsVersion)

8
frontend/src/app/features/content/pages/content/editor/content-field.component.html

@ -15,7 +15,7 @@
<sqx-field-copy-button [formModel]="formModel" [languages]="languages"></sqx-field-copy-button>
<button *ngIf="!formModel.field.isDisabled && isTranslatable" type="button" class="btn btn-sm btn-outline-secondary force no-focus-shadow ms-1" title="i18n:contents.autotranslate" (click)="translate()" tabindex="-1">
<button *ngIf="isTranslatable" type="button" [disabled]="formModel.field.isDisabled" class="btn btn-sm btn-outline-secondary force no-focus-shadow ms-1" title="i18n:contents.autotranslate" (click)="translate()" tabindex="-1">
<i class="icon-translate"></i>
</button>
</div>
@ -32,6 +32,7 @@
[formLevel]="formLevel"
[formModel]="formModel.get(language)"
[isComparing]="!!formModelCompare"
[hasChatBot]="hasChatBot"
[language]="language"
[languages]="languages">
</sqx-field-editor>
@ -46,6 +47,7 @@
[formLevel]="formLevel"
[formModel]="getControl()"
[isComparing]="!!formModelCompare"
[hasChatBot]="hasChatBot"
[language]="language"
[languages]="languages">
</sqx-field-editor>
@ -85,6 +87,7 @@
[formLevel]="formLevel"
[formModel]="formModelCompare.get(language)"
[isComparing]="!!formModelCompare"
[hasChatBot]="hasChatBot"
[language]="language"
[languages]="languages">
</sqx-field-editor>
@ -99,7 +102,8 @@
[formModel]="getControlCompare()!"
[language]="language"
[languages]="languages"
[isComparing]="!!formModelCompare">
[isComparing]="!!formModelCompare"
[hasChatBot]="hasChatBot">
</sqx-field-editor>
</ng-template>
</div>

9
frontend/src/app/features/content/pages/content/editor/content-field.component.ts

@ -5,9 +5,9 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { booleanAttribute, Component, EventEmitter, HostBinding, Input, numberAttribute, Output } from '@angular/core';
import { booleanAttribute, Component, EventEmitter, HostBinding, inject, Input, numberAttribute, Output } from '@angular/core';
import { Observable } from 'rxjs';
import { AppLanguageDto, AppsState, changed$, disabled$, EditContentForm, FieldForm, invalid$, LocalStoreService, SchemaDto, Settings, TranslationsService, TypedSimpleChanges } from '@app/shared';
import { AppLanguageDto, AppsState, changed$, disabled$, EditContentForm, FieldForm, invalid$, LocalStoreService, SchemaDto, Settings, TranslationsService, TypedSimpleChanges, UIOptions } from '@app/shared';
@Component({
selector: 'sqx-content-field',
@ -54,6 +54,9 @@ export class ContentFieldComponent {
public isInvalid?: Observable<boolean>;
public isDisabled?: Observable<boolean>;
public readonly hasTranslator = inject(UIOptions).value.canUseTranslator;
public readonly hasChatBot = inject(UIOptions).value.canUseChatBot;
@HostBinding('class')
public get class() {
return this.isHalfWidth ? 'col-6 half-field' : 'col-12';
@ -64,7 +67,7 @@ export class ContentFieldComponent {
}
public get isTranslatable() {
return this.formModel.field.properties.fieldType === 'String' && this.formModel.field.isLocalizable && this.languages.length > 1;
return this.formModel.field.properties.fieldType === 'String' && this.hasTranslator && this.formModel.field.isLocalizable && this.languages.length > 1;
}
constructor(

7
frontend/src/app/features/content/pages/schemas/schemas-page.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
@ -19,7 +19,7 @@ import { AppsState, getCategoryTree, SchemaCategory, SchemasState, Settings, UIO
export class SchemasPageComponent {
public schemasFilter = new UntypedFormControl();
public isEmbedded = false;
public readonly isEmbedded = inject(UIOptions).value.embedded;
public schemas =
this.schemasState.schemas.pipe(
@ -43,11 +43,10 @@ export class SchemasPageComponent {
return getCategoryTree(schemas, categories, filter);
});
constructor(uiOptions: UIOptions,
constructor(
public readonly schemasState: SchemasState,
private readonly appsState: AppsState,
) {
this.isEmbedded = uiOptions.get('embedded');
}
public trackByCategory(_index: number, category: SchemaCategory) {

4
frontend/src/app/features/content/shared/forms/array-editor.component.html

@ -24,7 +24,8 @@
[isFirst]="isFirst"
[isLast]="isLast"
(itemRemove)="removeItem(i)"
(itemMove)="move(itemForm, $event)"
(itemMove)="move(itemForm, $event)"
[hasChatBot]="hasChatBot"
[language]="language"
[languages]="languages">
<i cdkDragHandle class="icon-drag2" [class.hidden]="isDisabled | async"></i>
@ -53,6 +54,7 @@
(itemExpanded)="scroll.invalidateCachedMeasurementAtIndex(scroll.viewPortInfo.startIndexWithBuffer + i)"
(itemRemove)="removeItem(scroll.viewPortInfo.startIndexWithBuffer + i)"
(itemMove)="move(itemForm, $event)"
[hasChatBot]="hasChatBot"
[language]="language"
[languages]="languages">
</sqx-array-item>

3
frontend/src/app/features/content/shared/forms/array-editor.component.ts

@ -20,6 +20,9 @@ import { ArrayItemComponent } from './array-item.component';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ArrayEditorComponent {
@Input({ required: true })
public hasChatBot!: boolean;
@Input({ required: true })
public form!: EditContentForm;

1
frontend/src/app/features/content/shared/forms/array-item.component.html

@ -52,6 +52,7 @@
[formSection]="$any(section)"
[index]="index"
[isComparing]="isComparing"
[hasChatBot]="hasChatBot"
[language]="language"
[languages]="languages">
</sqx-component-section>

3
frontend/src/app/features/content/shared/forms/array-item.component.ts

@ -30,6 +30,9 @@ export class ArrayItemComponent {
@Output()
public clone = new EventEmitter();
@Input({ required: true })
public hasChatBot!: boolean;
@Input({ required: true })
public form!: EditContentForm;

1
frontend/src/app/features/content/shared/forms/component-section.component.html

@ -19,6 +19,7 @@
[formModel]="child"
[index]="index"
[isComparing]="isComparing"
[hasChatBot]="hasChatBot"
[language]="language"
[languages]="languages">
</sqx-field-editor>

3
frontend/src/app/features/content/shared/forms/component-section.component.ts

@ -16,6 +16,9 @@ import { FieldEditorComponent } from './field-editor.component';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ComponentSectionComponent {
@Input({ required: true })
public hasChatBot!: boolean;
@Input({ required: true })
public form!: EditContentForm;

1
frontend/src/app/features/content/shared/forms/component.component.html

@ -12,6 +12,7 @@
[formLevel]="formLevel + 1"
[formSection]="$any(section)"
[isComparing]="isComparing"
[hasChatBot]="hasChatBot"
[language]="language"
[languages]="languages">
</sqx-component-section>

3
frontend/src/app/features/content/shared/forms/component.component.ts

@ -17,6 +17,9 @@ import { ComponentSectionComponent } from './component-section.component';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ComponentComponent extends ResourceOwner {
@Input({ required: true })
public hasChatBot!: boolean;
@Input({ transform: booleanAttribute })
public canUnset?: boolean | null;

5
frontend/src/app/features/content/shared/forms/field-editor.component.html

@ -1,7 +1,7 @@
<div class="field" [class.expanded]="isExpanded" *ngIf="formModel">
<div class="buttons-container" *ngIf="canUnset">
<div class="buttons">
<button type="button" class="btn btn-sm btn-outline-secondary force no-focus-shadow" (click)="chatDialog.show()" tabindex="-1">
<button *ngIf="hasChatBot" type="button" class="btn btn-sm btn-outline-secondary force no-focus-shadow" (click)="chatDialog.show()" tabindex="-1">
AI
</button>
@ -54,6 +54,7 @@
[formContext]="formContext"
[isComparing]="isComparing"
[isExpanded]="isExpanded"
[hasChatBot]="hasChatBot"
[language]="language"
[languages]="languages">
</sqx-array-editor>
@ -82,6 +83,7 @@
[formLevel]="formLevel"
[formModel]="$any(formModel)"
[isComparing]="isComparing"
[hasChatBot]="hasChatBot"
[language]="language"
[languages]="languages">
</sqx-component>
@ -95,6 +97,7 @@
[formContext]="formContext"
[isComparing]="isComparing"
[isExpanded]="isExpanded"
[hasChatBot]="hasChatBot"
[language]="language"
[languages]="languages">
</sqx-array-editor>

3
frontend/src/app/features/content/shared/forms/field-editor.component.ts

@ -21,6 +21,9 @@ export class FieldEditorComponent {
@Output()
public expandedChange = new EventEmitter();
@Input({ required: true })
public hasChatBot!: boolean;
@Input({ required: true })
public form!: EditContentForm;

8
frontend/src/app/features/content/shared/references/references-checkboxes.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input } from '@angular/core';
import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, inject, Input } from '@angular/core';
import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms';
import { AppsState, ContentDto, ContentsService, LanguageDto, LocalizerService, StatefulControlComponent, TypedSimpleChanges, UIOptions } from '@app/shared/internal';
import { ReferencesTagsConverter } from './references-tag-converter';
@ -31,7 +31,7 @@ const NO_EMIT = { emitEvent: false };
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ReferencesCheckboxesComponent extends StatefulControlComponent<State, ReadonlyArray<string>> {
private readonly itemCount: number;
private readonly itemCount: number = inject(UIOptions).value.referencesDropdownItemCount;
private contentItems: ReadonlyArray<ContentDto> | null = null;
@Input({ required: true })
@ -51,15 +51,13 @@ export class ReferencesCheckboxesComponent extends StatefulControlComponent<Stat
return !!this.schemaId && !!this.language;
}
constructor(uiOptions: UIOptions,
constructor(
private readonly appsState: AppsState,
private readonly contentsService: ContentsService,
private readonly localizer: LocalizerService,
) {
super({ converter: new ReferencesTagsConverter(null!, [], localizer) });
this.itemCount = uiOptions.get('referencesDropdownItemCount');
this.own(
this.control.valueChanges
.subscribe((value: string[]) => {

11
frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, forwardRef, inject, Input, OnInit, Output, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms';
import * as Pikaday from 'pikaday/pikaday';
import { DateHelper, DateTime, StatefulControlComponent, UIOptions } from '@app/framework/internal';
@ -34,8 +34,8 @@ interface State {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DateTimeEditorComponent extends StatefulControlComponent<State, string | null> implements OnInit, AfterViewInit, FocusComponent {
private readonly hideDateButtonsSettings: boolean;
private readonly hideDateTimeModeButtonSetting: boolean;
private readonly hideDateButtonsSettings: boolean = !!inject(UIOptions).value.hideDateButtons;
private readonly hideDateTimeModeButtonSetting: boolean = !!inject(UIOptions).value.hideDateTimeModeButton;
private picker: any;
private dateTime?: DateTime | null;
private suppressEvents = false;
@ -88,11 +88,8 @@ export class DateTimeEditorComponent extends StatefulControlComponent<State, str
return !!this.dateTime;
}
constructor(uiOptions: UIOptions) {
constructor() {
super({ isLocal: true });
this.hideDateButtonsSettings = !!uiOptions.get('hideDateButtons');
this.hideDateTimeModeButtonSetting = !!uiOptions.get('hideDateTimeModeButton');
}
public ngOnInit() {

22
frontend/src/app/framework/configurations.ts

@ -10,28 +10,6 @@ export class UIOptions {
public readonly value: any,
) {
}
public get(path: string) {
if (!path) {
return undefined;
}
let value = this.value;
if (value) {
const parts = path.split('.');
for (const part of parts) {
value = value[part];
if (!value) {
break;
}
}
}
return value;
}
}
export class ApiUrlConfig {

28
frontend/src/app/shared/components/forms/geolocation-editor.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, forwardRef, Input, ViewChild } from '@angular/core';
import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, forwardRef, inject, Input, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms';
import { ExtendedFormGroup, LocalStoreService, ResourceLoaderService, Settings, StatefulControlComponent, Types, UIOptions, ValidatorsEx } from '@app/shared/internal';
@ -38,20 +38,23 @@ type UpdateOptions = { reset?: boolean; pan?: true; fire?: boolean };
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class GeolocationEditorComponent extends StatefulControlComponent<State, Geolocation> implements AfterViewInit {
private readonly googleMapsKey = inject(UIOptions).value.map.googleMaps.key;
private marker: any;
private map: any;
private value: Geolocation | null = null;
@ViewChild('editor', { static: false })
public editor!: ElementRef<HTMLElement>;
@ViewChild('searchBox', { static: false })
public searchBoxInput!: ElementRef<HTMLInputElement>;
@Input({ transform: booleanAttribute })
public set disabled(value: boolean | undefined | null) {
this.setDisabledState(value === true);
}
public readonly isGoogleMaps: boolean;
public get hasValue() {
return !!this.value;
}
public readonly isGoogleMaps = inject(UIOptions).value.map.type !== 'OSM';
public geolocationForm =
new ExtendedFormGroup({
@ -63,23 +66,18 @@ export class GeolocationEditorComponent extends StatefulControlComponent<State,
),
});
@ViewChild('editor', { static: false })
public editor!: ElementRef<HTMLElement>;
@ViewChild('searchBox', { static: false })
public searchBoxInput!: ElementRef<HTMLInputElement>;
public get hasValue() {
return !!this.value;
}
constructor(localStore: LocalStoreService,
private readonly resourceLoader: ResourceLoaderService,
private readonly uiOptions: UIOptions,
) {
super({ isMapHidden: localStore.getBoolean(Settings.Local.HIDE_MAP) });
this.project(x => x.isMapHidden).subscribe(isMapHidden => {
localStore.setBoolean(Settings.Local.HIDE_MAP, isMapHidden);
});
this.isGoogleMaps = uiOptions.get('map.type') !== 'OSM';
}
public hideMap(isMapHidden: boolean) {
@ -161,7 +159,7 @@ export class GeolocationEditorComponent extends StatefulControlComponent<State,
if (!this.isGoogleMaps) {
this.ngAfterViewInitOSM();
} else {
this.ngAfterViewInitGoogle(this.uiOptions.get('map.googleMaps.key'));
this.ngAfterViewInitGoogle(this.googleMapsKey);
}
}

7
frontend/src/app/shared/components/notifo.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, OnDestroy, Renderer2, ViewChild } from '@angular/core';
import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, inject, Input, OnDestroy, Renderer2, ViewChild } from '@angular/core';
import { ResourceLoaderService, TypedSimpleChanges, UIOptions } from '@app/framework';
import { AuthService } from '@app/shared/internal';
@ -16,7 +16,7 @@ import { AuthService } from '@app/shared/internal';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NotifoComponent implements AfterViewInit, OnDestroy {
private readonly notifoApiUrl: string;
private readonly notifoApiUrl: string = inject(UIOptions).value.notifoApi;
private readonly notifoApiKey: string | undefined;
@Input()
@ -36,11 +36,10 @@ export class NotifoComponent implements AfterViewInit, OnDestroy {
return !!this.notifoApiUrl && !!this.topic;
}
constructor(resourceLoader: ResourceLoaderService, uiOptions: UIOptions, authService: AuthService,
constructor(resourceLoader: ResourceLoaderService, authService: AuthService,
private readonly renderer: Renderer2,
) {
this.notifoApiKey = authService.user?.notifoToken;
this.notifoApiUrl = uiOptions.get('notifoApi');
if (this.isConfigured) {
if (this.notifoApiUrl.indexOf('localhost:5002') >= 0) {

9
frontend/src/app/shared/guards/must-be-authenticated.guard.ts

@ -6,7 +6,7 @@
*/
import { Location } from '@angular/common';
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { map, take, tap } from 'rxjs/operators';
@ -15,17 +15,16 @@ import { AuthService } from './../services/auth.service';
@Injectable()
export class MustBeAuthenticatedGuard {
private readonly redirectToLogin = inject(UIOptions).value.redirectToLogin;
constructor(
private readonly authService: AuthService,
private readonly location: Location,
private readonly router: Router,
private readonly uiOptions: UIOptions,
) {
}
public canActivate(): Observable<boolean> {
const redirect = this.uiOptions.get('redirectToLogin');
return this.authService.userChanges.pipe(
take(1),
tap(user => {
@ -35,7 +34,7 @@ export class MustBeAuthenticatedGuard {
const redirectPath = this.location.path(true);
if (redirect) {
if (this.redirectToLogin) {
this.authService.loginRedirect(redirectPath);
} else {
this.router.navigate([''], { queryParams: { redirectPath } });

7
frontend/src/app/shared/guards/must-be-not-authenticated.guard.ts

@ -6,7 +6,7 @@
*/
import { Location } from '@angular/common';
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { map, take, tap } from 'rxjs/operators';
@ -15,16 +15,17 @@ import { AuthService } from './../services/auth.service';
@Injectable()
export class MustBeNotAuthenticatedGuard {
private readonly redirectToLogin = inject(UIOptions).value.redirectToLogin;
constructor(
private readonly authService: AuthService,
private readonly location: Location,
private readonly router: Router,
private readonly uiOptions: UIOptions,
) {
}
public canActivate(snapshot: ActivatedRouteSnapshot): Observable<boolean> {
const redirect = this.uiOptions.get('redirectToLogin') && !snapshot.queryParams.logout;
const redirect = this.redirectToLogin && !snapshot.queryParams.logout;
return this.authService.userChanges.pipe(
take(1),

7
frontend/src/app/shared/state/resolvers.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { from, Observable, of, shareReplay } from 'rxjs';
import { UIOptions } from '@app/framework';
import { AssetDto, AssetsDto, AssetsService } from './../services/assets.service';
@ -105,16 +105,13 @@ abstract class ResolverBase<T extends { id: string }, TResult extends { items: R
@Injectable()
export class ResolveContents extends ResolverBase<ContentDto, ContentsDto> {
private readonly schemas: { [name: string]: Observable<ContentsDto> } = {};
private readonly itemCount;
private readonly itemCount = inject(UIOptions).value.referencesDropdownItemCount;
constructor(
uiOptions: UIOptions,
private readonly appsState: AppsState,
private readonly contentsService: ContentsService,
) {
super();
this.itemCount = uiOptions.get('referencesDropdownItemCount');
}
public resolveAll(schema: string) {

9
frontend/src/app/shared/state/tour.state.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Inject, Injectable } from '@angular/core';
import { inject, Inject, Injectable } from '@angular/core';
import { filter, skip, take } from 'rxjs';
import { debug, State, TourService, UIOptions } from '@app/framework';
import { TASK_CONFIGURATION, TaskConfiguration, TaskDefinition } from './tour.tasks';
@ -32,6 +32,8 @@ interface Snapshot {
@Injectable()
export class TourState extends State<Snapshot> {
private readonly isDisabled = inject(UIOptions).value.hideOnboarding;
public completedTasks =
this.project(x => x.completedTasks);
@ -48,7 +50,6 @@ export class TourState extends State<Snapshot> {
@Inject(TASK_CONFIGURATION) private readonly definition: TaskConfiguration,
private readonly tourService: TourService,
private readonly uiState: UIState,
private readonly uiOptions: UIOptions,
) {
super({});
@ -114,10 +115,6 @@ export class TourState extends State<Snapshot> {
private disableHintCore(key: string) {
this.next(s => ({ ...s, shownHints: { ...s.shownHints || {}, [key]: true } }), 'Disable Hint');
}
private get isDisabled() {
return this.uiOptions.get('hideOnboarding');
}
}
const LoadedEvent = 'Loaded';

7
frontend/src/app/shell/pages/home/home-page.component.ts

@ -6,7 +6,7 @@
*/
import { Location } from '@angular/common';
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AuthService, UIOptions } from '@app/shared';
@ -16,6 +16,8 @@ import { AuthService, UIOptions } from '@app/shared';
templateUrl: './home-page.component.html',
})
export class HomePageComponent {
private readonly redirectToLogin = inject(UIOptions).value.redirectToLogin;
public showLoginError = false;
constructor(
@ -23,7 +25,6 @@ export class HomePageComponent {
private readonly location: Location,
private readonly route: ActivatedRoute,
private readonly router: Router,
private readonly uiOptions: UIOptions,
) {
}
@ -32,7 +33,7 @@ export class HomePageComponent {
this.route.snapshot.queryParams.redirectPath ||
this.location.path();
if (this.isInternetExplorer() || this.uiOptions.get('redirectToLogin')) {
if (this.isInternetExplorer() || this.redirectToLogin) {
this.authService.loginRedirect(redirectPath);
return;
}

9
frontend/src/app/shell/pages/internal/feedback-menu.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, inject, OnDestroy, OnInit } from '@angular/core';
import markerSDK, { MarkerSdk } from '@marker.io/browser';
import { UIOptions } from '@app/shared';
@ -18,12 +18,7 @@ import { UIOptions } from '@app/shared';
export class FeedbackMenuComponent implements OnInit, OnDestroy {
private widget?: MarkerSdk;
public markerProject = '';
constructor(uiOptions: UIOptions,
) {
this.markerProject = uiOptions.get('markerProject');
}
public readonly markerProject = inject(UIOptions).value.markerProject;
public ngOnDestroy() {
this.widget?.unload();

8
frontend/src/app/shell/pages/internal/internal-area.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, OnInit } from '@angular/core';
import { Component, inject, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { DialogService, LoadingService, Notification, ResourceOwner, UIOptions } from '@app/shared';
@ -15,16 +15,14 @@ import { DialogService, LoadingService, Notification, ResourceOwner, UIOptions }
templateUrl: './internal-area.component.html',
})
export class InternalAreaComponent extends ResourceOwner implements OnInit {
public isEmbedded = false;
public readonly isEmbedded = inject(UIOptions).value.embedded;
constructor(uiOptions: UIOptions,
constructor(
public readonly loadingService: LoadingService,
private readonly dialogs: DialogService,
private readonly route: ActivatedRoute,
) {
super();
this.isEmbedded = !!uiOptions.get('embedded');
}
public ngOnInit() {

2
frontend/src/app/shell/pages/internal/notifications-menu.component.ts

@ -20,7 +20,7 @@ export class NotificationsMenuComponent {
constructor(authService: AuthService, uiOptions: UIOptions,
) {
const notifoApiKey = authService.user?.notifoToken;
const notifoApiUrl = uiOptions.get('notifoApi');
const notifoApiUrl = uiOptions.value.notifoAPi;
this.isNotifoConfigured = !!notifoApiKey && !!notifoApiUrl;
}

8
frontend/src/app/shell/pages/internal/profile-menu.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core';
import { ApiUrlConfig, AuthService, Cookies, ModalModel, StatefulComponent, UILanguages, UIOptions, UIState } from '@app/shared';
interface State {
@ -32,10 +32,10 @@ interface State {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProfileMenuComponent extends StatefulComponent<State> implements OnInit {
public modalMenu = new ModalModel();
public readonly modalMenu = new ModalModel();
public language = this.uiOptions.get('culture');
public languages = UILanguages.ALL;
public readonly language = inject(UIOptions).value.culture;
public readonly languages = UILanguages.ALL;
constructor(apiUrl: ApiUrlConfig,
public readonly uiState: UIState,

Loading…
Cancel
Save