diff --git a/src/Ombi/ClientApp/package.json b/src/Ombi/ClientApp/package.json index 9d3715eaf..d864e64b0 100644 --- a/src/Ombi/ClientApp/package.json +++ b/src/Ombi/ClientApp/package.json @@ -31,6 +31,7 @@ "@ngu/carousel": "^1.4.9-beta-2", "@ngx-translate/core": "^11.0.1", "@ngx-translate/http-loader": "^4.0.0", + "@ngxs/store": "^3.7.2", "@types/jquery": "^3.3.29", "@yellowspot/ng-truncate": "^1.4.0", "angular-bootstrap-md": "^7.5.4", diff --git a/src/Ombi/ClientApp/src/app/app.component.ts b/src/Ombi/ClientApp/src/app/app.component.ts index 64a59ea4a..8410dfd84 100644 --- a/src/Ombi/ClientApp/src/app/app.component.ts +++ b/src/Ombi/ClientApp/src/app/app.component.ts @@ -13,6 +13,7 @@ import { ICustomizationSettings, ICustomPage } from "./interfaces"; import { SignalRNotificationService } from './services/signlarnotification.service'; import { DOCUMENT } from '@angular/common'; +import { CustomizationFacade } from './state/customization'; @Component({ @@ -42,6 +43,7 @@ export class AppComponent implements OnInit { public authService: AuthService, private readonly router: Router, private readonly settingsService: SettingsService, + private customizationFacade: CustomizationFacade, public readonly translate: TranslateService, private readonly customPageService: CustomPageService, public overlayContainer: OverlayContainer, @@ -84,10 +86,9 @@ export class AppComponent implements OnInit { public ngOnInit() { // window["loading_screen"].finish(); - - this.settingsService.getCustomization().subscribe(x => { + this.customizationFacade.loadCustomziationSettings(); + this.customizationFacade.settings$().subscribe(x => { this.customizationSettings = x; - if (this.customizationSettings && this.customizationSettings.applicationName) { this.applicationName = this.customizationSettings.applicationName; this.document.getElementsByTagName('title')[0].innerText = this.applicationName; diff --git a/src/Ombi/ClientApp/src/app/app.module.ts b/src/Ombi/ClientApp/src/app/app.module.ts index ae4122eb7..5a733aa62 100644 --- a/src/Ombi/ClientApp/src/app/app.module.ts +++ b/src/Ombi/ClientApp/src/app/app.module.ts @@ -17,6 +17,7 @@ import { ConfirmDialogModule } from "primeng/confirmdialog"; import { CookieComponent } from "./auth/cookie.component"; import { CookieService } from "ng2-cookies"; import { CustomPageComponent } from "./custompage/custompage.component"; +import { CustomizationState } from "./state/customization/customization.state"; import { DataViewModule } from "primeng/dataview"; import { DialogModule } from "primeng/dialog"; import { JwtModule } from "@auth0/angular-jwt"; @@ -46,6 +47,7 @@ import { MatTooltipModule } from "@angular/material/tooltip"; import { MyNavComponent } from './my-nav/my-nav.component'; import { NavSearchComponent } from "./my-nav/nav-search.component"; import { NgModule } from "@angular/core"; +import { NgxsModule } from '@ngxs/store'; import { NotificationService } from "./services"; import { OverlayModule } from "@angular/cdk/overlay"; import { OverlayPanelModule } from "primeng/overlaypanel"; @@ -60,6 +62,7 @@ import { TokenResetPasswordComponent } from "./login/tokenresetpassword.componen import { TooltipModule } from "primeng/tooltip"; import { TranslateHttpLoader } from "@ngx-translate/http-loader"; import { UnauthorizedInterceptor } from "./auth/unauthorized.interceptor"; +import { environment } from "../environments/environment"; // Components @@ -185,7 +188,10 @@ export function JwtTokenGetter() { }, }), SidebarModule, - MatNativeDateModule, MatIconModule, MatSidenavModule, MatListModule, MatToolbarModule, LayoutModule, MatSlideToggleModule + MatNativeDateModule, MatIconModule, MatSidenavModule, MatListModule, MatToolbarModule, LayoutModule, MatSlideToggleModule, + NgxsModule.forRoot([CustomizationState], { + developmentMode: !environment.production, + }), ], declarations: [ AppComponent, diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts index 2f96e8760..e03e073eb 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts @@ -1,9 +1,10 @@ import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; import { Component, EventEmitter, Input, OnInit, Output, SimpleChanges } from '@angular/core'; -import { IUser, RequestType, UserType } from '../interfaces'; +import { ICustomizationSettings, IUser, RequestType, UserType } from '../interfaces'; import { SettingsService, SettingsStateService } from '../services'; import { AdvancedSearchDialogComponent } from '../shared/advanced-search-dialog/advanced-search-dialog.component'; +import { CustomizationFacade } from '../state/customization'; import { FilterService } from '../discover/services/filter-service'; import { ILocalUser } from '../auth/IUserLogin'; import { INavBar } from '../interfaces/ICommon'; @@ -53,8 +54,11 @@ export class MyNavComponent implements OnInit { public welcomeText: string; public RequestType = RequestType; + private customizationSettings: ICustomizationSettings; + constructor(private breakpointObserver: BreakpointObserver, private settingsService: SettingsService, + private customizationFacade: CustomizationFacade, private store: StorageService, private filterService: FilterService, private dialogService: MatDialog, @@ -74,8 +78,10 @@ export class MyNavComponent implements OnInit { this.issuesEnabled = await this.settingsService.issueEnabled().toPromise(); this.settingState.setIssue(this.issuesEnabled); - const customizationSettings = await this.settingsService.getCustomization().toPromise(); - console.log("issues enabled: " + this.issuesEnabled); + this.customizationFacade.settings$().subscribe(settings => { + this.customizationSettings = settings; + }); + this.theme = this.store.get("theme"); if (!this.theme) { this.store.save("theme", "dark"); @@ -92,7 +98,7 @@ export class MyNavComponent implements OnInit { { id: "nav-userManagement", name: "NavigationBar.UserManagement", icon: "fas fa-users", link: "/usermanagement", requiresAdmin: true, enabled: true }, //id: "", { name: "NavigationBar.Calendar", icon: "calendar_today", link: "/calendar", requiresAdmin: false, enabled: true }, { id: "nav-adminDonate", name: "NavigationBar.Donate", icon: "fas fa-dollar-sign", link: "https://www.paypal.me/PlexRequestsNet", externalLink: true, requiresAdmin: true, enabled: true, toolTip: true, style: "color:red;", toolTipMessage: 'NavigationBar.DonateTooltip' }, - { id: "nav-userDonate", name: "NavigationBar.Donate", icon: "fas fa-dollar-sign", link: customizationSettings.customDonationUrl, externalLink: true, requiresAdmin: false, enabled: customizationSettings.enableCustomDonations, toolTip: true, toolTipMessage: customizationSettings.customDonationMessage }, + { id: "nav-userDonate", name: "NavigationBar.Donate", icon: "fas fa-dollar-sign", link: this.customizationSettings.customDonationUrl, externalLink: true, requiresAdmin: false, enabled: this.customizationSettings.enableCustomDonations, toolTip: true, toolTipMessage: this.customizationSettings.customDonationMessage }, { id: "nav-featureSuggestion", name: "NavigationBar.FeatureSuggestion", icon: "far fa-lightbulb", link: "https://features.ombi.io/", externalLink: true, requiresAdmin: true, enabled: true, toolTip: true, toolTipMessage: 'NavigationBar.FeatureSuggestionTooltip'}, { id: "nav-settings", name: "NavigationBar.Settings", icon: "fas fa-cogs", link: "/Settings/About", requiresAdmin: true, enabled: true }, ]; @@ -145,7 +151,7 @@ export class MyNavComponent implements OnInit { const emailHash = md5.appendStr(email).end(); this.userProfileImageUrl = `https://www.gravatar.com/avatar/${emailHash}?d=404`;; } - else{ + else { this.userProfileImageUrl = this.getFallbackProfileImageUrl(); } } diff --git a/src/Ombi/ClientApp/src/app/state/customization/customization.actions.ts b/src/Ombi/ClientApp/src/app/state/customization/customization.actions.ts new file mode 100644 index 000000000..eb90980e4 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/customization/customization.actions.ts @@ -0,0 +1,5 @@ + + export class LoadSettings { + public static readonly type = '[Customization] LoadSettings'; + } + diff --git a/src/Ombi/ClientApp/src/app/state/customization/customization.facade.ts b/src/Ombi/ClientApp/src/app/state/customization/customization.facade.ts new file mode 100644 index 000000000..d6d3d9dba --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/customization/customization.facade.ts @@ -0,0 +1,18 @@ +import { CustomizationSelectors } from "./customization.selectors"; +import { ICustomizationSettings } from "../../interfaces"; +import { Injectable } from "@angular/core"; +import { LoadSettings } from "./customization.actions"; +import { Observable } from "rxjs"; +import { Store } from "@ngxs/store"; + +@Injectable({ + providedIn: 'root', +}) +export class CustomizationFacade { + + public constructor(private store: Store) {} + + public settings$ = (): Observable => this.store.select(CustomizationSelectors.customizationSettings); + + public loadCustomziationSettings = (): Observable => this.store.dispatch(new LoadSettings()); +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/customization/customization.selectors.ts b/src/Ombi/ClientApp/src/app/state/customization/customization.selectors.ts new file mode 100644 index 000000000..07f4ab24c --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/customization/customization.selectors.ts @@ -0,0 +1,11 @@ +import { CUSTOMIZATION_STATE_TOKEN } from "./types"; +import { ICustomizationSettings } from "../../interfaces"; +import { Selector } from "@ngxs/store"; + +export class CustomizationSelectors { + + @Selector([CUSTOMIZATION_STATE_TOKEN]) + public static customizationSettings(settings: ICustomizationSettings): ICustomizationSettings { + return settings; + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/customization/customization.state.ts b/src/Ombi/ClientApp/src/app/state/customization/customization.state.ts new file mode 100644 index 000000000..4007fa4c7 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/customization/customization.state.ts @@ -0,0 +1,24 @@ +import { Action, State, StateContext } from "@ngxs/store"; + +import { CUSTOMIZATION_STATE_TOKEN } from "./types"; +import { ICustomizationSettings } from "../../interfaces"; +import { Injectable } from "@angular/core"; +import { LoadSettings } from "./customization.actions"; +import { Observable } from "rxjs"; +import { SettingsService } from "../../services"; +import { tap } from "rxjs/operators"; + +@State({ + name: CUSTOMIZATION_STATE_TOKEN +}) +@Injectable() +export class CustomizationState { + constructor(private settingsService: SettingsService) { } + + @Action(LoadSettings) + public load({setState}: StateContext): Observable { + return this.settingsService.getCustomization().pipe( + tap(settings => setState(settings)) + ); + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/customization/index.ts b/src/Ombi/ClientApp/src/app/state/customization/index.ts new file mode 100644 index 000000000..e8fdeb050 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/customization/index.ts @@ -0,0 +1,4 @@ +export * from './customization.state'; +export * from './customization.actions'; +export * from './customization.facade'; +export * from './customization.selectors'; diff --git a/src/Ombi/ClientApp/src/app/state/customization/types.ts b/src/Ombi/ClientApp/src/app/state/customization/types.ts new file mode 100644 index 000000000..1aa108a4e --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/customization/types.ts @@ -0,0 +1,4 @@ +import { ICustomizationSettings } from "../../interfaces"; +import { StateToken } from "@ngxs/store"; + +export const CUSTOMIZATION_STATE_TOKEN = new StateToken('customization'); \ No newline at end of file diff --git a/src/Ombi/ClientApp/yarn.lock b/src/Ombi/ClientApp/yarn.lock index 52fde1cff..ef58a9282 100644 --- a/src/Ombi/ClientApp/yarn.lock +++ b/src/Ombi/ClientApp/yarn.lock @@ -1445,6 +1445,13 @@ dependencies: tslib "^1.9.0" +"@ngxs/store@^3.7.2": + version "3.7.2" + resolved "https://registry.yarnpkg.com/@ngxs/store/-/store-3.7.2.tgz#1088b0669adc382d36ca7ae8438c603e55879b42" + integrity sha512-1cnAjHOGCovfvhjtcAWBajrMXos97Un3c8ekKoS8FIHnq3aQOzY/ePspDRNi9kTcuBJ/r/xl097JC1ssEuNbyg== + dependencies: + tslib "^1.9.0" + "@nodelib/fs.scandir@2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69"