From 04907df3768714f77cf99324e6a06ac2574bb26d Mon Sep 17 00:00:00 2001
From: Jatin Chugh <105338943+jatin2008@users.noreply.github.com>
Date: Mon, 20 Feb 2023 21:16:09 +0530
Subject: [PATCH] [MOBILEAPPS-1701] Custom URL schema issues fixed (#2998)
* Custom URL schema issues
* code implementation changes for checkForMobileAppFlag
* updated test cases
* requested changed addressed
* requested changed addressed
* cspell changes
* review comments addressed
---
cspell.json | 130 +++++++++---------
.../open-in-app/open-in-app.component.html | 6 +-
.../open-in-app/open-in-app.component.scss | 6 +-
.../open-in-app/open-in-app.component.spec.ts | 24 +++-
.../open-in-app/open-in-app.component.ts | 11 +-
.../aca-mobile-app-switcher.service.spec.ts | 45 +++---
.../aca-mobile-app-switcher.service.ts | 46 +++++--
.../src/lib/services/app.service.ts | 4 +-
projects/aca-shared/src/lib/shared.module.ts | 5 +-
9 files changed, 169 insertions(+), 108 deletions(-)
diff --git a/cspell.json b/cspell.json
index 980839774..59e7d6583 100644
--- a/cspell.json
+++ b/cspell.json
@@ -2,82 +2,84 @@
"version": "0.1",
"language": "en",
"words": [
- "Kerberos",
- "succes",
- "sharedlinks",
- "Redistributable",
- "fullscreen",
- "LGPL",
- "mincount",
- "QNAME",
- "PNAME",
"ADDFEATURES",
- "CHECKIN",
- "thumbnailed",
- "superadmin",
- "SUPERADMIN",
- "hruser",
- "salesuser",
- "networkidle",
- "domcontentloaded",
- "mimetype",
-
- "ngrx",
- "ngstack",
- "sidenav",
- "injectable",
- "iosamw",
- "truthy",
- "cryptodoc",
- "mysites",
"afts",
"androidamw",
+ "cardview",
+ "CHECKIN",
"classlist",
- "folderlink",
- "filelink",
- "formcontrolname",
- "datetimepicker",
+ "cryptodoc",
"datatable",
- "repo",
- "snackbar",
- "promisify",
- "xdescribe",
- "unfavorite",
+ "dateitem",
+ "datetimepicker",
+ "denysvuika",
"devtools",
- "gitter",
- "jira",
- "markdownlint",
- "uploader",
- "nginx",
"docx",
- "SOLR",
- "simpletask",
- "titlecase",
-
- "unshare",
- "qshare",
- "validators",
+ "domcontentloaded",
+ "errored",
+ "erroredSpy",
+ "exif",
+ "filelink",
+ "folderlink",
+ "formcontrolname",
+ "fullscreen",
+ "gitter",
"guid",
+ "hruser",
+ "injectable",
+ "iosamw",
+ "jira",
+ "jsonp",
+ "Kerberos",
+ "keycodes",
+ "LGPL",
+ "markdownlint",
+ "mimetype",
+ "mincount",
+ "mobileapps",
+ "mysites",
+ "networkidle",
+ "nginx",
+ "ngrx",
+ "ngstack",
+ "noderef",
+ "pdfjs",
+ "PNAME",
"polyfill",
"polyfills",
- "jsonp",
- "pdfjs",
- "xpath",
- "tooltip",
- "tooltips",
- "unindent",
- "exif",
- "cardview",
- "webm",
- "keycodes",
- "denysvuika",
+ "promisify",
+ "QNAME",
+ "qshare",
+ "Redistributable",
+ "repo",
+ "salesuser",
+ "sharedlinks",
+ "sidenav",
+ "simpletask",
+ "snackbar",
+ "SOLR",
"submenu",
"submenus",
- "dateitem",
+ "succes",
+ "superadmin",
+ "thumbnailed",
+ "titlecase",
+ "tooltip",
+ "tooltips",
+ "truthy",
+ "unfavorite",
+ "unindent",
+ "unshare",
+ "uploader",
+ "validators",
"versionable",
- "erroredSpy",
- "errored",
- "noderef"
+ "webm",
+ "xdescribe",
+ "xpath"
],
- "dictionaries": ["html", "en-gb", "en_US"]
+ "dictionaries": [
+ "html",
+ "en-gb",
+ "en_US"
+ ]
}
diff --git a/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.html b/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.html
index d3697ea2c..4433ab6b3 100644
--- a/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.html
+++ b/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.html
@@ -1,8 +1,8 @@
-
diff --git a/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.scss b/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.scss
index 35a050dfa..115858f5e 100644
--- a/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.scss
+++ b/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.scss
@@ -6,8 +6,12 @@
}
.mat-dialog-container{
- padding: 12px;
+ padding: 5px;
border-radius: 36px;
background-color: var(--theme-blue-button-color);
color: var(--theme-about-panel-background-color);
}
+
+.open-in-app.mat-button {
+ overflow-x: hidden;
+}
diff --git a/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.spec.ts b/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.spec.ts
index 4f4540ed3..35b7807f7 100644
--- a/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.spec.ts
+++ b/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.spec.ts
@@ -1,16 +1,29 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { By } from '@angular/platform-browser';
import { OpenInAppComponent } from './open-in-app.component';
+import { initialState, LibTestingModule } from '../../testing/lib-testing-module';
+import { provideMockStore } from '@ngrx/store/testing';
+import { TranslateModule } from '@ngx-translate/core';
describe('OpenInAppComponent', () => {
let fixture: ComponentFixture;
let component: OpenInAppComponent;
+ const mockDialogRef = {
+ close: jasmine.createSpy('close'),
+ open: jasmine.createSpy('open')
+ };
+
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [OpenInAppComponent],
- providers: [{ provide: MAT_DIALOG_DATA, useValue: { redirectUrl: 'mockRedirectUrl' } }]
+ imports: [LibTestingModule, TranslateModule],
+ providers: [
+ provideMockStore({ initialState }),
+ { provide: MAT_DIALOG_DATA, useValue: { redirectUrl: 'mockRedirectUrl' } },
+ { provide: MatDialogRef, useValue: mockDialogRef }
+ ]
}).compileComponents();
fixture = TestBed.createComponent(OpenInAppComponent);
@@ -35,4 +48,11 @@ describe('OpenInAppComponent', () => {
expect(currentLocation).toBe('mockRedirectUrl');
});
+
+ it('should set the value `mobile_notification_expires_in` in session storage on dialog close', async () => {
+ sessionStorage.clear();
+ component.onCloseDialog();
+ expect(sessionStorage.getItem('mobile_notification_expires_in')).not.toBeNull();
+ expect(mockDialogRef.close).toHaveBeenCalled();
+ });
});
diff --git a/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.ts b/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.ts
index 14e53cd5c..b6f66824a 100644
--- a/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.ts
+++ b/projects/aca-shared/src/lib/components/open-in-app/open-in-app.component.ts
@@ -24,7 +24,7 @@
*/
import { Component, Inject, ViewEncapsulation } from '@angular/core';
-import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
export interface OpenInAppDialogOptions {
redirectUrl: string;
@@ -41,7 +41,8 @@ export class OpenInAppComponent {
constructor(
@Inject(MAT_DIALOG_DATA)
- public data: OpenInAppDialogOptions
+ public data: OpenInAppDialogOptions,
+ private dialog: MatDialogRef
) {
if (data) {
this.redirectUrl = data.redirectUrl;
@@ -51,4 +52,10 @@ export class OpenInAppComponent {
openInApp(): void {
this.window.location.href = this.redirectUrl;
}
+
+ onCloseDialog(): void {
+ const time: number = new Date().getTime();
+ sessionStorage.setItem('mobile_notification_expires_in', time.toString());
+ this.dialog.close();
+ }
}
diff --git a/projects/aca-shared/src/lib/services/aca-mobile-app-switcher.service.spec.ts b/projects/aca-shared/src/lib/services/aca-mobile-app-switcher.service.spec.ts
index f16c62085..dfd94195d 100644
--- a/projects/aca-shared/src/lib/services/aca-mobile-app-switcher.service.spec.ts
+++ b/projects/aca-shared/src/lib/services/aca-mobile-app-switcher.service.spec.ts
@@ -60,7 +60,7 @@ describe('AcaMobileAppSwitcherService', () => {
spyOnProperty(window.navigator, 'userAgent').and.returnValue('iphone');
const url: string = window.location.href;
const iphoneUrl: string = appConfig.config.mobileAppSwitch.iphoneUrl + url;
- service.showAppNotification();
+ service.identifyBrowserAndSetRedirectURL();
expect(service.redirectUrl).toEqual(iphoneUrl);
});
@@ -68,38 +68,49 @@ describe('AcaMobileAppSwitcherService', () => {
spyOnProperty(window.navigator, 'userAgent').and.returnValue('android');
const url: string = window.location.href;
const androidUrl: string = appConfig.config.mobileAppSwitch.androidUrlPart1 + url + appConfig.config.mobileAppSwitch.androidUrlPart2;
- service.showAppNotification();
+ service.identifyBrowserAndSetRedirectURL();
expect(service.redirectUrl).toEqual(androidUrl);
});
- it('should check if `showAppNotification` function is called', () => {
- const showAppNotificationSpy: jasmine.Spy<() => void> = spyOn(service, 'showAppNotification');
- service.checkForMobileApp();
- expect(showAppNotificationSpy).toHaveBeenCalled();
+ it('should check if `identifyBrowserAndSetRedirectURL` function is called', () => {
+ const identifyBrowserAndSetRedirectURLSpy: jasmine.Spy<() => void> = spyOn(service, 'identifyBrowserAndSetRedirectURL');
+ service.resolveExistenceOfDialog();
+ expect(identifyBrowserAndSetRedirectURLSpy).toHaveBeenCalled();
});
- it('should not display `openInApp` dialog box when timeDifference is less than the session time', () => {
- service.checkForMobileApp();
- const showAppNotificationSpy: jasmine.Spy<() => void> = spyOn(service, 'showAppNotification');
- service.checkForMobileApp();
- expect(showAppNotificationSpy).not.toHaveBeenCalled();
+ it('should not display `openInApp` dialog box after closing the same and time difference less than session time', () => {
+ const time: number = new Date().getTime();
+ sessionStorage.setItem('mobile_notification_expires_in', time.toString());
+ const identifyBrowserAndSetRedirectURLSpy: jasmine.Spy<() => void> = spyOn(service, 'identifyBrowserAndSetRedirectURL');
+ service.verifySessionExistsForDialog();
+ expect(identifyBrowserAndSetRedirectURLSpy).not.toHaveBeenCalled();
});
it('should check if `openInApp` dialog box is getting opened with `iphone` url', () => {
- const openInAppSpy: jasmine.Spy<(redirectUrl: string) => void> = spyOn(service, 'openInApp');
const url: string = window.location.href;
service.redirectUrl = appConfig.config.mobileAppSwitch.iphoneUrl + url;
- service.showAppNotification();
- expect(openInAppSpy).toHaveBeenCalled();
+ service.identifyBrowserAndSetRedirectURL();
expect(mockDialogRef.open).toHaveBeenCalled();
});
it('should check if `openInApp` dialog box is getting opened with `android` url', () => {
- const openInAppSpy: jasmine.Spy<(redirectUrl: string) => void> = spyOn(service, 'openInApp');
const url: string = window.location.href;
service.redirectUrl = appConfig.config.mobileAppSwitch.androidUrlPart1 + url + appConfig.config.mobileAppSwitch.androidUrlPart2;
- service.showAppNotification();
- expect(openInAppSpy).toHaveBeenCalled();
+ service.identifyBrowserAndSetRedirectURL();
expect(mockDialogRef.open).toHaveBeenCalled();
});
+
+ it('should not display Open in app dialog when the web application is opened within mobile application', () => {
+ spyOn(service, 'getCurrentUrl').and.returnValue('localhost?mobileapps=true');
+ const verifySessionExistsForDialogSpy: jasmine.Spy<() => void> = spyOn(service, 'verifySessionExistsForDialog');
+ service.resolveExistenceOfDialog();
+ expect(verifySessionExistsForDialogSpy).not.toHaveBeenCalled();
+ });
+
+ it('should display Open in app dialog when the web application is opened in mobile browser', () => {
+ spyOn(service, 'getCurrentUrl').and.returnValue('localhost');
+ const verifySessionExistsForDialogSpy: jasmine.Spy<() => void> = spyOn(service, 'verifySessionExistsForDialog');
+ service.resolveExistenceOfDialog();
+ expect(verifySessionExistsForDialogSpy).toHaveBeenCalled();
+ });
});
diff --git a/projects/aca-shared/src/lib/services/aca-mobile-app-switcher.service.ts b/projects/aca-shared/src/lib/services/aca-mobile-app-switcher.service.ts
index 24feeb8c5..0105f662d 100644
--- a/projects/aca-shared/src/lib/services/aca-mobile-app-switcher.service.ts
+++ b/projects/aca-shared/src/lib/services/aca-mobile-app-switcher.service.ts
@@ -46,31 +46,42 @@ export class AcaMobileAppSwitcherService {
this.mobileAppSwitchConfig = this.config.get('mobileAppSwitch');
}
- checkForMobileApp(): void {
- const currentTime: number = new Date().getTime();
- const sessionTime: string = sessionStorage.getItem('mobile_notification_expires_in');
+ resolveExistenceOfDialog(): void {
+ const url: string = this.getCurrentUrl();
+ const queryParams: string = url.split('?')[1];
+ let queryParamsList = [];
+ let hideBanner = false;
+ if (queryParams !== null && queryParams !== undefined) {
+ queryParamsList = queryParams.split('&');
+ hideBanner = queryParamsList.some((param) => param.split('=')[0] === 'mobileapps' && param.split('=')[1] === 'true');
+ }
+ if (!hideBanner) {
+ this.verifySessionExistsForDialog();
+ }
+ }
+ verifySessionExistsForDialog(): void {
+ const sessionTime: string = sessionStorage.getItem('mobile_notification_expires_in');
if (sessionTime !== null) {
+ const currentTime: number = new Date().getTime();
const sessionConvertedTime: number = parseFloat(sessionTime);
const timeDifference: number = (currentTime - sessionConvertedTime) / (1000 * 60 * 60);
const sessionTimeForOpenAppDialogDisplay: number = parseFloat(this.mobileAppSwitchConfig.sessionTimeForOpenAppDialogDisplay);
if (timeDifference > sessionTimeForOpenAppDialogDisplay) {
- this.showAppNotification();
+ this.clearSessionExpireTime();
+ this.identifyBrowserAndSetRedirectURL();
}
} else {
- this.showAppNotification();
+ this.identifyBrowserAndSetRedirectURL();
}
}
- showAppNotification(): void {
+ identifyBrowserAndSetRedirectURL(): void {
const ua: string = navigator.userAgent.toLowerCase();
const isAndroid: boolean = ua.indexOf('android') > -1;
const isIOS: boolean = ua.indexOf('iphone') > -1 || ua.indexOf('ipad') > -1 || ua.indexOf('ipod') > -1;
- const currentUrl: string = window.location.href;
- const time: number = new Date().getTime();
-
- sessionStorage.setItem('mobile_notification_expires_in', time.toString());
+ const currentUrl: string = this.getCurrentUrl();
if (isIOS === true) {
this.redirectUrl = this.mobileAppSwitchConfig.iphoneUrl + currentUrl;
@@ -79,22 +90,27 @@ export class AcaMobileAppSwitcherService {
}
if (this.redirectUrl !== undefined && this.redirectUrl !== null) {
- this.openInApp(this.redirectUrl);
+ this.openDialog(this.redirectUrl);
}
}
- openInApp(redirectUrl: string): void {
+ openDialog(redirectUrl: string): void {
this.dialog.open(OpenInAppComponent, {
data: {
redirectUrl
},
- width: '75%',
+ hasBackdrop: false,
+ width: 'auto',
role: 'dialog',
- position: { bottom: '50px' }
+ position: { bottom: '20px' }
});
}
- reset(): void {
+ clearSessionExpireTime(): void {
sessionStorage.removeItem('mobile_notification_expires_in');
}
+
+ getCurrentUrl(): string {
+ return window.location.href;
+ }
}
diff --git a/projects/aca-shared/src/lib/services/app.service.ts b/projects/aca-shared/src/lib/services/app.service.ts
index 5ba628e35..bc442c91f 100644
--- a/projects/aca-shared/src/lib/services/app.service.ts
+++ b/projects/aca-shared/src/lib/services/app.service.ts
@@ -182,9 +182,9 @@ export class AppService implements OnDestroy {
const isMobileSwitchEnabled: boolean = this.config.get('mobileAppSwitch.enabled', false);
if (isMobileSwitchEnabled) {
- this.acaMobileAppSwitcherService.checkForMobileApp();
+ this.acaMobileAppSwitcherService.resolveExistenceOfDialog();
} else {
- this.acaMobileAppSwitcherService.reset();
+ this.acaMobileAppSwitcherService.clearSessionExpireTime();
}
}
diff --git a/projects/aca-shared/src/lib/shared.module.ts b/projects/aca-shared/src/lib/shared.module.ts
index 16e53b0d8..1378be197 100644
--- a/projects/aca-shared/src/lib/shared.module.ts
+++ b/projects/aca-shared/src/lib/shared.module.ts
@@ -32,10 +32,11 @@ import { OpenInAppComponent } from './components/open-in-app/open-in-app.compone
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatDialogModule } from '@angular/material/dialog';
+import { TranslateModule } from '@ngx-translate/core';
@NgModule({
- imports: [ContextActionsModule, MatButtonModule, MatIconModule, MatDialogModule],
- exports: [ContextActionsModule, MatButtonModule, MatIconModule, MatDialogModule],
+ imports: [ContextActionsModule, MatButtonModule, MatIconModule, MatDialogModule, TranslateModule],
+ exports: [ContextActionsModule, MatButtonModule, MatIconModule, MatDialogModule, TranslateModule],
declarations: [OpenInAppComponent]
})
export class SharedModule {