Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MOBILEAPPS-1707] Open in App Dialog design changes as per new design and visibility of dialog enabled after the login and is shown in case of private files as well #3225

Merged
merged 13 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/src/app.config.json.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"iphoneUrl": "iosamw://",
"androidUrlPart1": "intent:///",
"androidUrlPart2": "#Intent;scheme=androidamw;package=com.alfresco.content.app;end",
"sessionTimeForOpenAppDialogDisplay": "${APP_CONFIG_SESSION_TIME_FOR_OPEN_APP_DIALOG_DISPLAY_IN_HOURS}"
"sessionTimeForOpenAppDialogDisplay": "${APP_CONFIG_SESSION_TIME_FOR_OPEN_APP_DIALOG_DISPLAY_IN_HOURS}",
"appStoreUrl": "https://apps.apple.com/us/app/alfresco-mobile-workspace/id1514434480"
},
"plugins": {
"aosPlugin": ${APP_CONFIG_PLUGIN_AOS},
Expand Down
4 changes: 3 additions & 1 deletion projects/aca-content/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,9 @@
"NO_LABEL": "Cancel"
},
"MOBILE_APP": {
"MOBILE_APP_BUTTON_LABEL": "Open in App"
"MOBILE_APP_BUTTON_LABEL": "Open in App",
"DOWNLOAD_APP_BUTTON_LABEL": "Don't have the app? Download now.",
"OPEN_ALFRESCO_MOBILE_APP": "Open using Alfresco Mobile application?"
}
},
"DOCUMENT_LIST": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { SetSelectedNodesAction } from '@alfresco/aca-shared/store';
import { AppExtensionService } from '@alfresco/aca-shared';
import { AppExtensionService, AppService } from '@alfresco/aca-shared';

describe('SharedLinkViewComponent', () => {
let component: SharedLinkViewComponent;
Expand All @@ -41,6 +41,9 @@ describe('SharedLinkViewComponent', () => {
dispatch: jasmine.createSpy('dispatch'),
select: () => of({})
};
const appServiceMock = {
openMobileAppDialog: () => {}
};

beforeEach(() => {
TestBed.configureTestingModule({
Expand All @@ -55,6 +58,10 @@ describe('SharedLinkViewComponent', () => {
snapshot: { data: { preferencePrefix: 'prefix' } },
params: of({ id: '123' })
}
},
{
provide: AppService,
useValue: appServiceMock
}
],
schemas: [NO_ERRORS_SCHEMA]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { ActivatedRoute } from '@angular/router';
import { Store } from '@ngrx/store';
import { forkJoin, from, of, Subject } from 'rxjs';
import { catchError, mergeMap, takeUntil } from 'rxjs/operators';
import { AppExtensionService } from '@alfresco/aca-shared';
import { AppExtensionService, AppService } from '@alfresco/aca-shared';

@Component({
selector: 'app-shared-link-view',
Expand All @@ -50,7 +50,8 @@ export class SharedLinkViewComponent implements OnInit, OnDestroy {
private route: ActivatedRoute,
private store: Store<AppStore>,
private extensions: AppExtensionService,
private alfrescoApiService: AlfrescoApiService
private alfrescoApiService: AlfrescoApiService,
private appService: AppService
) {
this.sharedLinksApi = new SharedlinksApi(this.alfrescoApiService.getInstance());
}
Expand All @@ -65,6 +66,7 @@ export class SharedLinkViewComponent implements OnInit, OnDestroy {
.subscribe(([sharedEntry, sharedId]: [SharedLinkEntry, string]) => {
if (sharedEntry) {
this.store.dispatch(new SetSelectedNodesAction([sharedEntry as any]));
this.appService.openMobileAppDialog();
}
this.sharedLinkId = sharedId;
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
<div class="container">
<button mat-button (click)="openInApp()" data-automation-id="open-in-app-button" class="open-in-app">
<div class="alfresco-mobile-application-container">
<span>{{ 'APP.DIALOGS.MOBILE_APP.OPEN_ALFRESCO_MOBILE_APP' | translate }}</span>
<button mat-button class="cross-button" (click)="onCloseDialog()">
<mat-icon class="cross-icon">close</mat-icon>
</button>
</div>

<div class="open-in-app-container">
<button mat-button (click)="openInApp()" data-automation-id="open-in-app-button" class="open-in-app" cdkFocusInitial>
<span>{{ 'APP.DIALOGS.MOBILE_APP.MOBILE_APP_BUTTON_LABEL' | translate }}</span>
</button>
<button mat-button (click)="onCloseDialog()">
<mat-icon>close</mat-icon>
</div>

<div class="download-app-container" *ngIf="appStoreUrl">
<button mat-button data-automation-id="download-app-button" class="download-app-button" (click)="downloadIosApp()">
<span>{{ 'APP.DIALOGS.MOBILE_APP.DOWNLOAD_APP_BUTTON_LABEL' | translate }}</span>
</button>
</div>
AleksanderSklorz marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,21 +1,64 @@
.container{
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
aca-open-in-app {
.open-in-app-container {
display: flex;
place-content: center;
padding: 0;
border-radius: 8px;
background-color: var(--theme-primary-color);
color: var(--theme-about-panel-background-color);
margin-top: 12px;
}

.mat-dialog-container{
padding: 5px;
border-radius: 36px;
background-color: var(--theme-blue-button-color);
color: var(--theme-about-panel-background-color);
}
.open-in-app {
overflow-x: hidden;
font-size: 16px;
width: 100%;
padding: 0;
height: 48px;

.open-in-app.mat-button {
overflow-x: hidden;
}
&:focus-visible {
outline: none;
border-radius: unset;
}
}

.download-app-container {
display: flex;
place-content: center;
margin-top: 12px;
}

.alfresco-mobile-application-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
font-size: 14px;
padding: 6px 0;
}

.cross-button {
padding-right: 0;

&:focus-visible {
outline: none;
border-radius: unset;
}
}

.cross-icon {
font-weight: bold;
font-size: 21px;
height: 21px;
}

.download-app-button {
background: var(--theme-dialog-background-color);
color: var(--theme-primary-color);
font-size: 14px;
}

.open-in-app.mat-button.cdk-program-focused .mat-button-focus-overlay {
opacity: 0;
.mat-dialog-container {
padding: 8px 24px 24px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('OpenInAppComponent', () => {
imports: [LibTestingModule, SharedModule.forRoot(), MatIconTestingModule],
providers: [
provideMockStore({ initialState }),
{ provide: MAT_DIALOG_DATA, useValue: { redirectUrl: 'mockRedirectUrl' } },
{ provide: MAT_DIALOG_DATA, useValue: { redirectUrl: 'mockRedirectUrl', appStoreUrl: 'mockAppStoreUrl' } },
{ provide: MatDialogRef, useValue: mockDialogRef }
]
});
Expand Down Expand Up @@ -78,4 +78,22 @@ describe('OpenInAppComponent', () => {
expect(sessionStorage.getItem('mobile_notification_expires_in')).not.toBeNull();
expect(mockDialogRef.close).toHaveBeenCalled();
});

it('should redirect to App Store for downloading the app in case of Ios device', async () => {
let currentLocation: string | string[];
const windowStub: Window & typeof globalThis = {
location: {
set href(value: string | string[]) {
currentLocation = value;
}
}
} as Window & typeof globalThis;
component.window = windowStub;
const downloadAppButton = fixture.debugElement.query(By.css('[data-automation-id="download-app-button"]')).nativeElement;
downloadAppButton.dispatchEvent(new Event('click'));
fixture.detectChanges();
await fixture.whenStable();

expect(currentLocation).toBe('mockAppStoreUrl');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';

export interface OpenInAppDialogOptions {
redirectUrl: string;
appStoreUrl: string;
}
@Component({
selector: 'aca-open-in-app',
Expand All @@ -35,7 +36,8 @@ export interface OpenInAppDialogOptions {
encapsulation: ViewEncapsulation.None
})
export class OpenInAppComponent {
private readonly redirectUrl: string;
private redirectUrl: string;
public appStoreUrl: string;
public window: Window & typeof globalThis = window;

constructor(
Expand All @@ -45,13 +47,18 @@ export class OpenInAppComponent {
) {
if (data) {
this.redirectUrl = data.redirectUrl;
this.appStoreUrl = data.appStoreUrl;
}
}

openInApp(): void {
this.window.location.href = this.redirectUrl;
}

downloadIosApp(): void {
this.window.location.href = this.appStoreUrl;
}

onCloseDialog(): void {
const time: number = new Date().getTime();
sessionStorage.setItem('mobile_notification_expires_in', time.toString());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*!
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Alfresco Example Content Application
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/

import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { BrowserModule } from '@angular/platform-browser';
import { TranslateModule } from '@ngx-translate/core';
import { OpenInAppComponent } from './open-in-app.component';

@NgModule({
imports: [CommonModule, MatIconModule, TranslateModule, MatButtonModule, BrowserModule],
declarations: [OpenInAppComponent],
exports: [OpenInAppComponent]
})
export class OpenInAppModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

import { AppConfigService } from '@alfresco/adf-core';
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { OpenInAppComponent } from '../components/open-in-app/open-in-app.component';

export interface MobileAppSwitchConfigurationOptions {
Expand All @@ -33,13 +33,16 @@ export interface MobileAppSwitchConfigurationOptions {
androidUrlPart1: string;
androidUrlPart2: string;
sessionTimeForOpenAppDialogDisplay: string;
appStoreUrl: string;
}
@Injectable({
providedIn: 'root'
})
export class AcaMobileAppSwitcherService {
private mobileAppSwitchConfig: MobileAppSwitchConfigurationOptions;
public redirectUrl: string;
public appStoreUrl: string;
private dialogRef: MatDialogRef<OpenInAppComponent>;

constructor(private config: AppConfigService, private dialog: MatDialog) {
this.mobileAppSwitchConfig = this.config.get<MobileAppSwitchConfigurationOptions>('mobileAppSwitch');
Expand Down Expand Up @@ -79,30 +82,35 @@ export class AcaMobileAppSwitcherService {
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 isIPadInSafari = /Macintosh/i.test(ua) && navigator.maxTouchPoints && navigator.maxTouchPoints > 1;
const isIOS: boolean = ua.indexOf('iphone') > -1 || ua.indexOf('ipad') > -1 || ua.indexOf('ipod') > -1 || isIPadInSafari;
const currentUrl: string = this.getCurrentUrl();

if (isIOS === true) {
this.redirectUrl = this.mobileAppSwitchConfig.iphoneUrl + currentUrl;
this.appStoreUrl = this.mobileAppSwitchConfig.appStoreUrl;
} else if (isAndroid === true) {
this.redirectUrl = this.mobileAppSwitchConfig.androidUrlPart1 + currentUrl + this.mobileAppSwitchConfig.androidUrlPart2;
}

if (this.redirectUrl !== undefined && this.redirectUrl !== null) {
this.openDialog(this.redirectUrl);
this.openDialog(this.redirectUrl, this.appStoreUrl);
}
}

openDialog(redirectUrl: string): void {
this.dialog.open(OpenInAppComponent, {
data: {
redirectUrl
},
hasBackdrop: false,
width: 'auto',
role: 'dialog',
position: { bottom: '20px' }
});
openDialog(redirectUrl: string, appStoreUrl?: string): void {
if (!this.dialogRef) {
this.dialogRef = this.dialog.open(OpenInAppComponent, {
data: {
redirectUrl,
appStoreUrl
},
hasBackdrop: false,
width: '100%',
role: 'dialog',
position: { bottom: '0' }
});
}
}

clearSessionExpireTime(): void {
Expand All @@ -112,4 +120,11 @@ export class AcaMobileAppSwitcherService {
getCurrentUrl(): string {
return window.location.href;
}

closeDialog(): void {
if (this.dialogRef) {
this.dialog.closeAll();
this.dialogRef = null;
}
}
}
Loading