Skip to content

Commit

Permalink
adding loading spinner to desktop callback.
Browse files Browse the repository at this point in the history
make sure modal popup is closed in sources window when app is redirected.
  • Loading branch information
AnalogJ committed Sep 7, 2023
1 parent 0ce1b3f commit 7dc0318
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 37 deletions.
2 changes: 1 addition & 1 deletion frontend/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
<p>desktop-callback works!</p>
<div class="row">
<div class="col-12">
<app-loading-spinner [loadingTitle]="'Please wait, completing connection...'"></app-loading-spinner>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component({
selector: 'app-desktop-callback',
Expand All @@ -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,
})
})
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)

Expand Down Expand Up @@ -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) => {

Expand Down Expand Up @@ -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)
})
});
}

Expand Down
38 changes: 18 additions & 20 deletions frontend/src/app/services/lighthouse.service.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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<any> {
const originUrlParts = new URL(window.location.href)

if(environment.environment_desktop){
Expand All @@ -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
}

}
Expand Down Expand Up @@ -260,26 +252,32 @@ export class LighthouseService {
return parts.join(separator);
}

private waitForDesktopCodeOrTimeout(openedWindow: Window, sourceType: string): Observable<any> {
console.log(`waiting for postMessage notification from ${sourceType} window`)
private waitForDesktopCodeOrTimeout(sourceType: string): Observable<any> {
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)
})
)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/environments/environment.desktop_prod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/lib/utils/external_link.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
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;
}

//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");
}
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/polyfills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7dc0318

Please sign in to comment.