diff --git a/server/app.js b/server/app.js index 14c57af..e03c582 100644 --- a/server/app.js +++ b/server/app.js @@ -67,6 +67,9 @@ const TEMP_FOLDER = 'temp'; const REMOTE_USER = 'web-configurator'; let rc2Model = new RC2Model(); +// Against 304 +// app.disable('etag'); + app.get('/server/api', (req, res, next) => { let url = req.headers.destinationurl; let headers = {} @@ -150,6 +153,7 @@ app.post('/server/api', (req, res, next) => { } catch (err) { console.error('Error parsing response', err, proxyres?.body); } + console.log('Proxy post answer', url, resBody); res.status(200).json(resBody); }).catch(error => { errorHandler(error, req, res, next); diff --git a/src/app/pages/pages.component.html b/src/app/pages/pages.component.html index ae8204b..842bff0 100644 --- a/src/app/pages/pages.component.html +++ b/src/app/pages/pages.component.html @@ -1,4 +1,5 @@ + diff --git a/src/app/pages/pages.component.ts b/src/app/pages/pages.component.ts index d9d7901..ef558d0 100644 --- a/src/app/pages/pages.component.ts +++ b/src/app/pages/pages.component.ts @@ -38,10 +38,20 @@ import {InputTextModule} from "primeng/inputtext"; import {TagModule} from "primeng/tag"; import {RemoteOperationsComponent} from "../remote-operations/remote-operations.component"; import {saveAs} from "file-saver-es"; +import {RemoteWidgetComponent} from "../remote-widget/remote-widget.component"; + + +enum ModificationType { + ModifyPage, + DeletePage, + AddPage +} + interface ModifiedPages { profile: Profile; page: Page; + type: ModificationType; } @Component({ @@ -67,7 +77,8 @@ interface ModifiedPages { DialogModule, InputTextModule, TagModule, - RemoteOperationsComponent + RemoteOperationsComponent, + RemoteWidgetComponent ], templateUrl: './pages.component.html', styleUrl: './pages.component.css', @@ -78,6 +89,7 @@ interface ModifiedPages { export class PagesComponent implements OnInit { menuItems: MenuItem[] = [ {label: 'Home', routerLink: '/home', icon: 'pi pi-home'}, + {label: 'Load Remote data', command: () => this.loadRemote(), icon: 'pi pi-cloud-download', block: true}, {label: 'Save pages to remote', command: () => this.updateRemote(), icon: 'pi pi-cloud-upload'}, {label: 'Restore profile to remote', command: () => this.input_file?.nativeElement.click(), icon: 'pi pi-upload'}, ] @@ -174,7 +186,7 @@ export class PagesComponent implements OnInit { const entry = this.modifiedProfiles.find(item => item.profile.profile_id === profile.profile_id && item.page.page_id === page.page_id); if (!entry) { - this.modifiedProfiles.push({profile, page}); + this.modifiedProfiles.push({profile, page, type: ModificationType.ModifyPage}); this.cdr.detectChanges(); } } @@ -183,7 +195,7 @@ export class PagesComponent implements OnInit { const index = profile.pages.indexOf($event); if (index && index > -1) { profile.pages.splice(index, 1); - this.updatePendingModifications(profile,$event); + this.modifiedProfiles.push({profile, page: $event, type: ModificationType.DeletePage}); this.cdr.detectChanges(); } } @@ -191,6 +203,16 @@ export class PagesComponent implements OnInit { private updateRemote() { this.remoteOperations = []; this.modifiedProfiles.forEach(item => { + if (item.type === ModificationType.DeletePage) + { + this.remoteOperations.push({status: OperationStatus.Todo, + message: `Delete page ${item.page.name} from profile ${item.profile.name}`, + method: "DELETE", + api: `/api/profiles/${item.profile.profile_id}/pages/${item.page.page_id}`, + body: {} + }); + return; + } if (!item.page.items) item.page.items = []; this.remoteOperations.push({status: OperationStatus.Todo, message: `Update page ${item.page.name} from profile ${item.profile.name}`, @@ -207,6 +229,9 @@ export class PagesComponent implements OnInit { }); if (this.remoteOperations.length > 0) this.showOperations = true; + else { + this.messageService.add({severity: "warn", summary: "No pending operations", key: "pages"}); + } this.cdr.detectChanges(); } @@ -272,7 +297,7 @@ export class PagesComponent implements OnInit { const operation: RemoteOperation = {status: OperationStatus.Todo, message: `Add group ${group.name}`, method: "POST", - api: `/api/profiles/${profile.profile_id}/group`, + api: `/api/profiles/${profile.profile_id}/groups`, body}; this.remoteOperations.push(operation); mappedGroup.push({fieldName: "group_id", contentKey: group.group_id, linkedOperation: operation}); @@ -293,7 +318,7 @@ export class PagesComponent implements OnInit { const createProfileoperation: RemoteOperation = {status: OperationStatus.Todo, message: `Create new profile ${profile.name}`, method: "POST", - api: `/api/profiles/`, + api: `/api/profiles`, body: { name: profile.name, restricted: profile.restricted @@ -304,13 +329,15 @@ export class PagesComponent implements OnInit { const body = {...group}; delete (body as any).group_id; delete (body as any).profile_id; - group.profile_id = ""; + body.profile_id = ""; const operation: RemoteOperation = {status: OperationStatus.Todo, message: `Add group ${group.name}`, method: "POST", - api: `/api/profiles/${profile.profile_id}/group`, + api: `/api/profiles//groups`, body, - resultFields: [{fieldName: "profile_id", linkedOperation: createProfileoperation, keyName: ""}],}; + resultFields: [{fieldName: "profile_id", linkedOperation: createProfileoperation, keyName: ""}, + {fieldName: "profile_id", contentKey: '', linkedOperation: createProfileoperation} + ],}; this.remoteOperations.push(operation); mappedGroup.push({fieldName: "group_id", contentKey: group.group_id, linkedOperation: operation}); }); @@ -319,12 +346,13 @@ export class PagesComponent implements OnInit { const body = {...page}; delete (body as any).page_id; delete (body as any).profile_id; - page.profile_id = ""; + body.profile_id = ""; this.remoteOperations.push({status: OperationStatus.Todo, message: `Add page ${page.name}`, method: "POST", - api: `/api/profiles/${profile.profile_id}/pages`, + api: `/api/profiles//pages`, body, resultFields: [...mappedGroup, + {fieldName: "profile_id", contentKey: '', linkedOperation: createProfileoperation}, {fieldName: "profile_id", linkedOperation: createProfileoperation, keyName: ""}]}); }); } @@ -344,4 +372,25 @@ export class PagesComponent implements OnInit { } fileReader.readAsText(file); } + + loadRemote() + { + if (!this.remoteLoader) return; + this.blockedMenu = true; + this.progress = true; + this.cdr.detectChanges(); + this.remoteLoader.loadRemoteData().subscribe({next: value => { + this.blockedMenu = false; + this.progress = false; + // if (value?.profiles) + // this.profiles = value.profiles; + this.cdr.detectChanges(); + }, error: error => { + console.error("Error during remote extraction", error); + this.blockedMenu = false; + this.progress = false; + this.messageService.add({severity:'error', summary:'Error during remote extraction'}); + this.cdr.detectChanges(); + }}); + } } diff --git a/src/app/remote-registration/remote-registration.component.html b/src/app/remote-registration/remote-registration.component.html index d079f87..66d9fe6 100644 --- a/src/app/remote-registration/remote-registration.component.html +++ b/src/app/remote-registration/remote-registration.component.html @@ -34,7 +34,7 @@       - +   {{remote.remote_name}} (selected) diff --git a/src/app/remote-widget/remote-widget.component.html b/src/app/remote-widget/remote-widget.component.html index df10728..ac21474 100644 --- a/src/app/remote-widget/remote-widget.component.html +++ b/src/app/remote-widget/remote-widget.component.html @@ -25,9 +25,9 @@ - +
- diff --git a/src/app/remote-widget/remote-widget.component.ts b/src/app/remote-widget/remote-widget.component.ts index a94e5ef..199a432 100644 --- a/src/app/remote-widget/remote-widget.component.ts +++ b/src/app/remote-widget/remote-widget.component.ts @@ -48,14 +48,13 @@ import {DropdownOverComponent} from "../controls/dropdown-over/dropdown-over.com export class RemoteWidgetComponent implements OnInit { @Input() visible = true; @Input() scale = 0.8; - protected readonly Math = Math; - - minimized = false; + @Input() minimized = false; + @Input() remote: Remote | undefined; remoteState: RemoteState | undefined; mediaEntity: MediaEntityState | undefined; mediaEntities: MediaEntityState[] = []; - selectedRemote: Remote | undefined; activities: Activity[] = []; + protected readonly Math = Math; constructor(private server:ServerService, protected remoteWebsocketService: RemoteWebsocketService, private cdr:ChangeDetectorRef) { } @@ -76,8 +75,8 @@ export class RemoteWidgetComponent implements OnInit { this.cdr.detectChanges(); }); this.server.remote$.subscribe(remote => { - this.selectedRemote = remote; - this.server.getRemoteBattery(this.selectedRemote).subscribe(batteryInfo => { + this.remote = remote; + this.server.getRemoteBattery(this.remote).subscribe(batteryInfo => { this.remoteState = {batteryInfo}; this.cdr.detectChanges(); }) @@ -88,14 +87,14 @@ export class RemoteWidgetComponent implements OnInit { loadActivities() { - if (!this.selectedRemote) return; - this.server.getRemoteActivities(this.selectedRemote).subscribe(activities => { + if (!this.remote) return; + this.server.getRemoteActivities(this.remote).subscribe(activities => { this.activities = activities; activities.forEach(activity => { if (activity.attributes?.state && activity.attributes.state === "ON" - && this.selectedRemote && activity.entity_id) + && this.remote && activity.entity_id) { - this.server.getRemoteActivity(this.selectedRemote, activity.entity_id).subscribe(activity => { + this.server.getRemoteActivity(this.remote, activity.entity_id).subscribe(activity => { const existingActivity = this.activities.find(item => item.entity_id === activity.entity_id); if (!existingActivity) this.activities.push(activity); @@ -105,7 +104,7 @@ export class RemoteWidgetComponent implements OnInit { activity.options?.included_entities?.forEach(entity => { if (entity.entity_type !== "media_player") return; //TODO add other entities if (this.mediaEntities.find(item => item.entity_id === entity.entity_id)) return; - if (this.selectedRemote && entity.entity_id) + if (this.remote && entity.entity_id) { this.remoteWebsocketService.updateEntity(entity.entity_id); } diff --git a/src/app/server.service.ts b/src/app/server.service.ts index 968c218..a013b45 100644 --- a/src/app/server.service.ts +++ b/src/app/server.service.ts @@ -296,32 +296,27 @@ export class ServerService { getRemoteProfiles(remote: Remote): Observable { - const profiles: Profile[] = []; - let obs = this.http.get(`/api/remote/${remote.address}/profiles`).pipe( + return this.http.get(`/api/remote/${remote.address}/profiles`).pipe( mergeMap(profiles => { - return from(profiles); - }), - mergeMap(profile => { - return forkJoin([ - this.http.get(`/api/remote/${remote.address}/profiles/${profile.profile_id}/pages`).pipe(map(pages => { - profile.pages = pages; - })), - this.http.get(`/api/remote/${remote.address}/profiles/${profile.profile_id}/groups`).pipe(map(groups => { - profile.groups = groups; - })), - ]).pipe(map(groups => { - return profile; + console.log("Profiles", profiles); + return from(profiles).pipe(mergeMap(profile => { + return forkJoin([ + this.http.get(`/api/remote/${remote.address}/profiles/${profile.profile_id}/pages`).pipe(map(pages => { + profile.pages = pages; + })), + this.http.get(`/api/remote/${remote.address}/profiles/${profile.profile_id}/groups`).pipe(map(groups => { + profile.groups = groups; + })), + ]).pipe(map(groups => { + console.log("Update profile", profile); + return profiles; + })) })) - }), - map(profileData => { - profiles.push(profileData); - return profileData; - })) - return forkJoin([obs]).pipe(map(results => { + })).pipe(map(profiles => { this.profiles = profiles; this.profiles$.next(profiles); - return results; - })) + return profiles; + })); } getRemoteMacros(remote: Remote): Observable