diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 00c23b9e..d914c927 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -30,7 +30,7 @@ export class AppComponent implements OnInit { modifyHeader(event) { if (event instanceof NavigationEnd) { - if (event.url?.startsWith('/auth')) { + if (event.url?.startsWith('/auth') || event.url?.startsWith('/desktop')) { this.showHeader = false; } else { // console.log("NU") diff --git a/frontend/src/app/pages/desktop-callback/desktop-callback.component.html b/frontend/src/app/pages/desktop-callback/desktop-callback.component.html index 2b98cb57..b6e49612 100644 --- a/frontend/src/app/pages/desktop-callback/desktop-callback.component.html +++ b/frontend/src/app/pages/desktop-callback/desktop-callback.component.html @@ -1 +1,5 @@ -

desktop-callback works!

+
+
+ +
+
diff --git a/frontend/src/app/pages/desktop-callback/desktop-callback.component.ts b/frontend/src/app/pages/desktop-callback/desktop-callback.component.ts index 76501e12..91967ca1 100644 --- a/frontend/src/app/pages/desktop-callback/desktop-callback.component.ts +++ b/frontend/src/app/pages/desktop-callback/desktop-callback.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import {ActivatedRoute} from '@angular/router'; @Component({ selector: 'app-desktop-callback', @@ -8,14 +9,14 @@ import { Component, OnInit } from '@angular/core'; export class DesktopCallbackComponent implements OnInit { //This component is used to redirect the user to the desktop app after they have authenticated with a source - constructor() { } + constructor(private activatedRoute : ActivatedRoute) { } ngOnInit(): void { - wails.Event.Emit({ - name: "wails:fasten-lighthouse:success", - data: - - ) + this.activatedRoute.queryParams.subscribe(values => { + wails.Events.Emit({ + name: "wails:fasten-lighthouse:response", + data: values, + }) + }) } - } diff --git a/frontend/src/app/pages/medical-sources/medical-sources.component.ts b/frontend/src/app/pages/medical-sources/medical-sources.component.ts index a9534f90..f5850894 100644 --- a/frontend/src/app/pages/medical-sources/medical-sources.component.ts +++ b/frontend/src/app/pages/medical-sources/medical-sources.component.ts @@ -5,7 +5,7 @@ import {LighthouseSourceMetadata} from '../../models/lighthouse/lighthouse-sourc import {Source} from '../../models/fasten/source'; import {MetadataSource} from '../../models/fasten/metadata-source'; import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; -import {ActivatedRoute} from '@angular/router'; +import {ActivatedRoute, Router, UrlSerializer} from '@angular/router'; import {environment} from '../../../environments/environment'; import {BehaviorSubject, forkJoin, Observable, of, Subject} from 'rxjs'; import { @@ -17,7 +17,7 @@ import {debounceTime, distinctUntilChanged, pairwise, startWith} from 'rxjs/oper import {MedicalSourcesFilter, MedicalSourcesFilterService} from '../../services/medical-sources-filter.service'; import {FormControl, FormGroup} from '@angular/forms'; import * as _ from 'lodash'; -// If you dont import this angular will import the wrong "Location" +import {Location} from '@angular/common'; export const sourceConnectWindowTimeout = 24*5000 //wait 2 minutes (5 * 24 = 120) @@ -79,6 +79,9 @@ export class MedicalSourcesComponent implements OnInit { private activatedRoute: ActivatedRoute, private filterService: MedicalSourcesFilterService, private modalService: NgbModal, + private router: Router, + private urlSerializer: UrlSerializer, + private location: Location, ) { this.filterService.filterChanges.subscribe((filterInfo) => { @@ -284,8 +287,31 @@ export class MedicalSourcesComponent implements OnInit { let authorizationUrl = await this.lighthouseApi.generateSourceAuthorizeUrl(sourceType, sourceMetadata) console.log('authorize url:', authorizationUrl.toString()); - // redirect to lighthouse with uri's - this.lighthouseApi.redirectWithOriginAndDestination(authorizationUrl.toString(), sourceType, sourceMetadata.redirect_uri) + // redirect to lighthouse with uri's (or open a new window in desktop mode) + this.lighthouseApi.redirectWithOriginAndDestination(authorizationUrl.toString(), sourceType, sourceMetadata.redirect_uri).subscribe((codeData) => { + //Note: this code will only run in Desktop mode (with popups) + //in non-desktop environments, the user is redirected in the same window, and this code is never executed. + + //always close the modal + this.modalService.dismissAll() + + if(!codeData){ + //if we redirected completely, no callback data will be present. + return + } + + //User was shown a popup, which was closed, and data was returned using events + //redirect to callback page with code + + let urlTree = this.router.createUrlTree( + ['/sources/callback/' + sourceType], + { queryParams: codeData, } + ); + + let absUrl = this.location.prepareExternalUrl(this.urlSerializer.serialize(urlTree)) + console.log(absUrl); + window.location.replace(absUrl) + }) }); } diff --git a/frontend/src/app/services/lighthouse.service.ts b/frontend/src/app/services/lighthouse.service.ts index d4c90a72..3565a546 100644 --- a/frontend/src/app/services/lighthouse.service.ts +++ b/frontend/src/app/services/lighthouse.service.ts @@ -1,6 +1,6 @@ import {Inject, Injectable} from '@angular/core'; import {HttpClient} from '@angular/common/http'; -import {Observable, of, throwError, fromEvent } from 'rxjs'; +import {Observable, of, throwError, bindCallback} from 'rxjs'; import {environment} from '../../environments/environment'; import {concatMap, delay, retryWhen, timeout, first, map, filter, catchError, tap} from 'rxjs/operators'; import {ResponseWrapper} from '../models/response-wrapper'; @@ -146,7 +146,7 @@ export class LighthouseService { * dest_url - https://patient360la.anthem.com/.../connect/authorize?redirect_uri=https://lighthouse.fastenhealth.com/callback/anthem * redirect_url - lighthouse.fastenhealth.com/sandbox/redirect/anthem?origin_url=...&dest_url=... */ - redirectWithOriginAndDestination(destUrl: string, sourceType: string, callbackUri: string): void { + redirectWithOriginAndDestination(destUrl: string, sourceType: string, callbackUri: string): Observable { const originUrlParts = new URL(window.location.href) if(environment.environment_desktop){ @@ -171,23 +171,15 @@ export class LighthouseService { if(environment.environment_desktop && environment.popup_source_auth){ //@ts-ignore - OpenExternalLink(redirectUrlParts.toString(), environment.environment_desktop) - // let openedWindow = window.runtime.BrowserOpenURL(redirectUrlParts.toString()); + OpenExternalLink(redirectUrlParts.toString(), environment.environment_desktop, sourceType) - wails.Event.Once("wails:fasten-lighthouse:success", (code: string) => { - console.log("GOT CODE FROM DESKTOP", code) - - }) - - this.waitForDesktopCodeOrTimeout(null, sourceType).subscribe(async (codeData) => { - //TODO: redirect to the callback url with the code. - console.log("DONE WAITING FOR CODE") - }) + return this.waitForDesktopCodeOrTimeout(sourceType) //now wait for response from the opened window } else { //redirect to the url in the same window window.location.href = redirectUrlParts.toString(); + return of(null) //should never happen } } @@ -260,26 +252,32 @@ export class LighthouseService { return parts.join(separator); } - private waitForDesktopCodeOrTimeout(openedWindow: Window, sourceType: string): Observable { - console.log(`waiting for postMessage notification from ${sourceType} window`) + private waitForDesktopCodeOrTimeout(sourceType: string): Observable { + console.log(`waiting for wails Event notification from window`) + + if(typeof wails == "undefined"){ + return throwError("wails is not defined, this is likely because you're running in a browser.") + } + + let fromWailsEvent = bindCallback(wails.Events.Once) //new code to listen to post message - return fromEvent(window, 'message') + return fromWailsEvent('wails:fasten-lighthouse:response') .pipe( //throw an error if we wait more than 2 minutes (this will close the window) timeout(sourceConnectDesktopTimeout), //make sure we're only listening to events from the "opened" window. - filter((event: MessageEvent) => event.source == openedWindow), + filter((eventPayload: any ) => eventPayload.sender == sourceType), //after filtering, we should only have one event to handle. first(), map((event) => { - console.log(`received postMessage notification from ${sourceType} window & sending acknowledgment`, event) + console.log(`received wails event notification from ${sourceType} window & sending acknowledgment`, event) // @ts-ignore - event.source.postMessage(JSON.stringify({close:true}), event.origin); + return event.data }), catchError((err) => { console.warn(`timed out waiting for notification from ${sourceType} (${sourceConnectDesktopTimeout/1000}s), closing window`) - openedWindow.self.close() + wails.Application.GetWindowByName(sourceType).Window.Close() return throwError(err) }) ) diff --git a/frontend/src/environments/environment.desktop_prod.ts b/frontend/src/environments/environment.desktop_prod.ts index 2c27c05e..3a16b67c 100644 --- a/frontend/src/environments/environment.desktop_prod.ts +++ b/frontend/src/environments/environment.desktop_prod.ts @@ -3,7 +3,7 @@ export const environment = { environment_cloud: false, environment_desktop: true, environment_name: "desktop_prod", - popup_source_auth: false, + popup_source_auth: true, lighthouse_api_endpoint_base: 'https://lighthouse.fastenhealth.com/v1', diff --git a/frontend/src/lib/utils/external_link.ts b/frontend/src/lib/utils/external_link.ts index f1a23198..168ac708 100644 --- a/frontend/src/lib/utils/external_link.ts +++ b/frontend/src/lib/utils/external_link.ts @@ -1,4 +1,4 @@ -export function OpenExternalLink(url: string, desktopMode: boolean){ +export function OpenExternalLink(url: string, desktopMode: boolean, windowId?: string){ //check if url starts with https, and if not, prepend it (external links are never relative) if(!url.startsWith("https://") && !url.startsWith("http://")){ url = "https://" + url; @@ -6,7 +6,7 @@ export function OpenExternalLink(url: string, desktopMode: boolean){ //check if wails exists and is defined if(typeof wails !== "undefined" && desktopMode){ - wails.CallByName("pkg.AppService.BrowserOpenURL", url) + wails.CallByName("pkg.AppService.BrowserOpenURL", url, windowId || 'external') } else{ window.open(url, "_blank"); } diff --git a/frontend/src/polyfills.ts b/frontend/src/polyfills.ts index 054eaa50..28528659 100644 --- a/frontend/src/polyfills.ts +++ b/frontend/src/polyfills.ts @@ -66,7 +66,10 @@ declare global { // let wails: any let wails: { - Event: { + Application: { + GetWindowByName: (sourceType: string) => any + } + Events: { Emit: (event: any) => void Once: (eventName, callback) => void On: (eventName, callback) => void