-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2501 from IDEMSInternational/feat/landscape-mode
Feat: landscape mode
- Loading branch information
Showing
9 changed files
with
175 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,15 @@ | ||
<ion-content [scrollEvents]="shouldEmitScrollEvents"> | ||
<plh-template-container | ||
*ngIf="templateName" | ||
[templatename]="templateName" | ||
></plh-template-container> | ||
<div *ngIf="!templateName" class="ion-padding"> | ||
@if (templateName) { | ||
<plh-template-container [templatename]="templateName"></plh-template-container> | ||
} @else { | ||
<div class="ion-padding"> | ||
<h3>Select a Template</h3> | ||
<ion-searchbar [(ngModel)]="filterTerm" (ionInput)="search()"></ion-searchbar> | ||
<ion-list> | ||
<ion-item | ||
*ngFor="let template of filteredTemplates; trackBy: trackByFn" | ||
[routerLink]="template.flow_name" | ||
>{{template.flow_name}}</ion-item | ||
> | ||
@for(template of filteredTemplates; track $index) { | ||
<ion-item [routerLink]="template.flow_name">{{template.flow_name}}</ion-item> | ||
} | ||
</ion-list> | ||
</div> | ||
} | ||
</ion-content> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
src/app/shared/components/template/services/template-metadata.service.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { TestBed } from "@angular/core/testing"; | ||
|
||
import { TemplateMetadataService } from "./template-metadata.service"; | ||
|
||
describe("TemplateMetadataService", () => { | ||
let service: TemplateMetadataService; | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({}); | ||
service = TestBed.inject(TemplateMetadataService); | ||
}); | ||
|
||
it("should be created", () => { | ||
expect(service).toBeTruthy(); | ||
}); | ||
}); |
45 changes: 45 additions & 0 deletions
45
src/app/shared/components/template/services/template-metadata.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { computed, effect, Injectable, signal } from "@angular/core"; | ||
import { SyncServiceBase } from "src/app/shared/services/syncService.base"; | ||
import { TemplateService } from "./template.service"; | ||
import { FlowTypes } from "src/app/shared/model"; | ||
import { Router } from "@angular/router"; | ||
import { toSignal } from "@angular/core/rxjs-interop"; | ||
import { ngRouterMergedSnapshot$ } from "src/app/shared/utils/angular.utils"; | ||
import { isEqual } from "packages/shared/src/utils/object-utils"; | ||
|
||
/** | ||
* Service responsible for handling metadata of the current top-level template, | ||
* i.e. parameters authored through the template's parameter_list in a contents list | ||
*/ | ||
@Injectable({ | ||
providedIn: "root", | ||
}) | ||
export class TemplateMetadataService extends SyncServiceBase { | ||
/** Utility snapshot used to get router snapshot from service (outside render context) */ | ||
private snapshot = toSignal(ngRouterMergedSnapshot$(this.router)); | ||
|
||
/** Name of current template provide by route param */ | ||
private templateName = computed<string | undefined>(() => this.snapshot().params.templateName); | ||
|
||
/** List of parameterList provided with current template */ | ||
public parameterList = signal<FlowTypes.Template["parameter_list"]>({}, { equal: isEqual }); | ||
|
||
constructor( | ||
private templateService: TemplateService, | ||
private router: Router | ||
) { | ||
super("TemplateMetadata"); | ||
|
||
// subscribe to template name changes and load corresponding template parameter list on change | ||
effect( | ||
async () => { | ||
const templateName = this.templateName(); | ||
const parameterList = templateName | ||
? await this.templateService.getTemplateMetadata(templateName) | ||
: {}; | ||
this.parameterList.set(parameterList); | ||
}, | ||
{ allowSignalWrites: true } | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 48 additions & 16 deletions
64
src/app/shared/services/screen-orientation/screen-orientation.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,69 @@ | ||
import { Injectable } from "@angular/core"; | ||
import { SyncServiceBase } from "../syncService.base"; | ||
import { ScreenOrientation, OrientationLockType } from "@capacitor/screen-orientation"; | ||
import { effect, Injectable } from "@angular/core"; | ||
import { ScreenOrientation } from "@capacitor/screen-orientation"; | ||
import { TemplateActionRegistry } from "../../components/template/services/instance/template-action.registry"; | ||
import { Capacitor } from "@capacitor/core"; | ||
import { TemplateMetadataService } from "../../components/template/services/template-metadata.service"; | ||
import { SyncServiceBase } from "../syncService.base"; | ||
import { environment } from "src/environments/environment"; | ||
|
||
const ORIENTATION_TYPES: OrientationLockType[] = ["portrait", "landscape"]; | ||
/** List of possible orientations provided by authors */ | ||
const SCREEN_ORIENTATIONS = ["portrait", "landscape", "unlock"] as const; | ||
|
||
type IScreenOrientation = (typeof SCREEN_ORIENTATIONS)[number]; | ||
|
||
@Injectable({ | ||
providedIn: "root", | ||
}) | ||
export class ScreenOrientationService extends SyncServiceBase { | ||
constructor(private templateActionRegistry: TemplateActionRegistry) { | ||
/** Actively locked screen orientation */ | ||
private lockedOrientation: IScreenOrientation | undefined; | ||
|
||
constructor( | ||
private templateActionRegistry: TemplateActionRegistry, | ||
private templateMetadataService: TemplateMetadataService | ||
) { | ||
super("Screen Orientation Service"); | ||
this.initialise(); | ||
} | ||
|
||
initialise() { | ||
this.registerTemplateActionHandlers(); | ||
// TODO: expose a property at deployment config level to enable "landscape_mode" to avoid unnecessary checks | ||
// AND/OR: check on init if any templates actually use screen orientation metadata? | ||
const isEnabled = Capacitor.isNativePlatform() || !environment.production; | ||
|
||
if (isEnabled) { | ||
// Add handlers to set orientation on action | ||
this.registerTemplateActionHandlers(); | ||
// Set orientation when template parameter orientation changes | ||
effect(async () => { | ||
const { orientation } = this.templateMetadataService.parameterList(); | ||
this.setOrientation(orientation); | ||
}); | ||
} | ||
} | ||
|
||
private registerTemplateActionHandlers() { | ||
this.templateActionRegistry.register({ | ||
screen_orientation: async ({ args }) => { | ||
const [targetOrientation] = args; | ||
if (ORIENTATION_TYPES.includes(targetOrientation)) { | ||
this.setOrientation(targetOrientation); | ||
} else { | ||
console.error(`[SCREEN ORIENTATION] - Invalid orientation: ${targetOrientation}`); | ||
} | ||
this.setOrientation(targetOrientation); | ||
}, | ||
}); | ||
} | ||
|
||
private async setOrientation(orientation: OrientationLockType) { | ||
return await ScreenOrientation.lock({ orientation }); | ||
private async setOrientation(orientation: IScreenOrientation) { | ||
// avoid re-locking same orientation | ||
if (orientation === this.lockedOrientation) return; | ||
|
||
this.lockedOrientation = orientation; | ||
|
||
if (orientation && orientation !== "unlock") { | ||
if (SCREEN_ORIENTATIONS.includes(orientation)) { | ||
console.log(`[SCREEN ORIENTATION] - Lock ${orientation}`); | ||
return ScreenOrientation.lock({ orientation }); | ||
} else { | ||
console.error(`[SCREEN ORIENTATION] - Invalid orientation: ${orientation}`); | ||
} | ||
} else { | ||
console.log(`[SCREEN ORIENTATION] - Unlock`); | ||
return ScreenOrientation.unlock(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { NavigationEnd } from "@angular/router"; | ||
import type { ActivatedRoute, ActivatedRouteSnapshot, Router } from "@angular/router"; | ||
import { filter, map, startWith } from "rxjs"; | ||
|
||
/** | ||
* When accessing ActivatedRoute from a provider router hierarchy includes all routers, not just | ||
* current view router (as identified when using from within a component) | ||
* | ||
* Workaround to check all nested routers for params and combined. Adapted from: | ||
* https://medium.com/simars/ngrx-router-store-reduce-select-route-params-6baff607dd9 | ||
*/ | ||
function mergeRouterSnapshots(router: Router) { | ||
const merged: Partial<ActivatedRouteSnapshot> = { data: {}, params: {}, queryParams: {} }; | ||
let route: ActivatedRoute | undefined = router.routerState.root; | ||
while (route !== undefined) { | ||
const { data, params, queryParams } = route.snapshot; | ||
merged.data = { ...merged.data, ...data }; | ||
merged.params = { ...merged.params, ...params }; | ||
merged.queryParams = { ...merged.queryParams, ...queryParams }; | ||
route = route.children.find((child) => child.outlet === "primary"); | ||
} | ||
return merged as ActivatedRouteSnapshot; | ||
} | ||
|
||
/** | ||
* Subscribe to snapshot across all active routers | ||
* This may be useful in cases where a service wants to subscribe to route parameter changes | ||
* (default behaviour would only detect changes to top-most route) | ||
* Adapted from https://github.com/angular/angular/issues/46891#issuecomment-1190590046 | ||
*/ | ||
export function ngRouterMergedSnapshot$(router: Router) { | ||
return router.events.pipe( | ||
filter((e) => e instanceof NavigationEnd), | ||
map(() => mergeRouterSnapshots(router)), | ||
startWith(mergeRouterSnapshots(router)) | ||
); | ||
} |