adding loading spinner to desktop callback.

make sure modal popup is closed in sources window when app is redirected.
This commit is contained in:
Jason Kulatunga 2023-09-07 16:07:46 -07:00
parent 0ce1b3f20f
commit 7dc0318931
8 changed files with 69 additions and 37 deletions

View File

@ -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")

View File

@ -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>

View File

@ -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,
})
})
}
}

View File

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

View File

@ -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<any> {
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<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)
})
)

View File

@ -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',

View File

@ -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");
}

View File

@ -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