1- import { ComponentProps , DisplayComponent , EventBus , FSComponent , VNode } from "@microsoft/msfs-sdk"
2- import { CancelToken } from "navigraph/auth"
3- import { packages } from "../Lib/navigraph"
4- import { AuthService } from "../Services/AuthService"
5- import "./InterfaceSample.css"
1+ import {
2+ ComponentProps ,
3+ DisplayComponent ,
4+ EventBus ,
5+ FSComponent ,
6+ MappedSubject ,
7+ Subject ,
8+ VNode ,
9+ } from "@microsoft/msfs-sdk"
610import {
711 DownloadProgressPhase ,
812 NavigraphEventType ,
913 NavigraphNavigationDataInterface ,
1014} from "@navigraph/msfs-navigation-data-interface"
15+ import { NavigationDataStatus } from "@navigraph/msfs-navigation-data-interface/types/meta"
16+ import { CancelToken } from "navigraph/auth"
17+ import { packages } from "../Lib/navigraph"
18+ import { AuthService } from "../Services/AuthService"
1119import { Dropdown } from "./Dropdown"
20+ import { Input } from "./Input"
21+ import "./InterfaceSample.css"
1222
1323interface InterfaceSampleProps extends ComponentProps {
1424 bus : EventBus
@@ -21,8 +31,15 @@ export class InterfaceSample extends DisplayComponent<InterfaceSampleProps> {
2131 private readonly qrCodeRef = FSComponent . createRef < HTMLImageElement > ( )
2232 private readonly dropdownRef = FSComponent . createRef < Dropdown > ( )
2333 private readonly downloadButtonRef = FSComponent . createRef < HTMLButtonElement > ( )
24- private readonly executeButtonRef = FSComponent . createRef < HTMLButtonElement > ( )
25- private readonly inputRef = FSComponent . createRef < HTMLInputElement > ( )
34+ private readonly icaoInputRef = FSComponent . createRef < Input > ( )
35+ private readonly executeIcaoButtonRef = FSComponent . createRef < HTMLButtonElement > ( )
36+ private readonly sqlInputRef = FSComponent . createRef < Input > ( )
37+ private readonly executeSqlButtonRef = FSComponent . createRef < HTMLButtonElement > ( )
38+ private readonly outputRef = FSComponent . createRef < HTMLPreElement > ( )
39+ private readonly loadingRef = FSComponent . createRef < HTMLDivElement > ( )
40+ private readonly authContainerRef = FSComponent . createRef < HTMLDivElement > ( )
41+
42+ private readonly navigationDataStatus = Subject . create < NavigationDataStatus | null > ( null )
2643
2744 private cancelSource = CancelToken . source ( )
2845
@@ -53,50 +70,123 @@ export class InterfaceSample extends DisplayComponent<InterfaceSampleProps> {
5370 } )
5471 }
5572
56- public render ( ) : VNode {
73+ public renderDatabaseStatus ( ) : VNode | void {
5774 return (
58- < div class = "auth-container" >
59- < div class = "horizontal" >
60- < div class = "vertical" >
61- < div ref = { this . textRef } > Loading</ div >
62- < div ref = { this . loginButtonRef } class = "button" />
63- < div ref = { this . navigationDataTextRef } />
64- < img ref = { this . qrCodeRef } class = "qr-code" />
75+ < >
76+ < div
77+ class = { MappedSubject . create ( ( [ status ] ) => {
78+ return status ? "vertical" : "hidden"
79+ } , this . navigationDataStatus ) }
80+ >
81+ < div > { this . navigationDataStatus . map ( s => `Install method: ${ s ?. status } ` ) } </ div >
82+ < div >
83+ { this . navigationDataStatus . map (
84+ s => `Installed format: ${ s ?. installedFormat } revision ${ s ?. installedRevision } ` ,
85+ ) }
6586 </ div >
66- < div class = "vertical" >
67- < Dropdown ref = { this . dropdownRef } />
68- < div ref = { this . downloadButtonRef } class = "button" >
69- Download
87+ < div > { this . navigationDataStatus . map ( s => `Installed path: ${ s ?. installedPath } ` ) } </ div >
88+ < div > { this . navigationDataStatus . map ( s => `Installed cycle: ${ s ?. installedCycle } ` ) } </ div >
89+ < div > { this . navigationDataStatus . map ( s => `Latest cycle: ${ s ?. latestCycle } ` ) } </ div >
90+ < div > { this . navigationDataStatus . map ( s => `Validity period: ${ s ?. validityPeriod } ` ) } </ div >
91+ </ div >
92+ < div class = { this . navigationDataStatus . map ( status => ( status ? "hidden" : "visible" ) ) } > Loading status...</ div >
93+ </ >
94+ )
95+ }
96+
97+ public render ( ) : VNode {
98+ return (
99+ < >
100+ < div class = "loading-container" ref = { this . loadingRef } >
101+ Waiting for navigation data interface to initialize... If building for the first time, this may take a few
102+ minutes
103+ </ div >
104+ < div class = "auth-container" ref = { this . authContainerRef } style = { { display : "none" } } >
105+ < div class = "horizontal" >
106+ < div class = "vertical" >
107+ < h4 > Step 1 - Sign in</ h4 >
108+ < div ref = { this . textRef } > Loading</ div >
109+ < div ref = { this . loginButtonRef } class = "button" />
110+ < div ref = { this . navigationDataTextRef } />
111+ < img ref = { this . qrCodeRef } class = "qr-code" />
112+ </ div >
113+ < div class = "vertical" >
114+ < h4 > Step 2 - Select Database</ h4 >
115+ < Dropdown ref = { this . dropdownRef } />
116+ < div ref = { this . downloadButtonRef } class = "button" >
117+ Download
118+ </ div >
119+ { this . renderDatabaseStatus ( ) }
70120 </ div >
71- < input ref = { this . inputRef } type = "text" id = "sql" name = "sql" value = "ESSA" class = "text-field" />
72- < div ref = { this . executeButtonRef } class = "button" >
73- Execute SQL
121+ </ div >
122+
123+ < h4 style = "text-align: center;" > Step 3 - Query the database</ h4 >
124+ < div class = "horizontal" >
125+ < div class = "vertical" >
126+ < Input ref = { this . icaoInputRef } value = "TNCM" class = "text-field" />
127+ < div ref = { this . executeIcaoButtonRef } class = "button" >
128+ Fetch Airport
129+ </ div >
130+ < div style = "height:30px;" > </ div >
131+ < Input
132+ ref = { this . sqlInputRef }
133+ textarea
134+ value = "SELECT airport_name FROM tbl_airports WHERE airport_identifier = 'TNCM'"
135+ class = "text-field"
136+ />
137+ < div ref = { this . executeSqlButtonRef } class = "button" >
138+ Execute SQL
139+ </ div >
74140 </ div >
141+ < pre ref = { this . outputRef } id = "output" >
142+ The output of the query will show up here
143+ </ pre >
75144 </ div >
76145 </ div >
77- </ div >
146+ </ >
78147 )
79148 }
80149
81- public onBeforeRender ( ) : void {
82- super . onBeforeRender ( )
83- }
84-
85150 public onAfterRender ( node : VNode ) : void {
86151 super . onAfterRender ( node )
87152
153+ // Populate status when ready
154+ this . navigationDataInterface . onReady ( ( ) => {
155+ this . navigationDataInterface
156+ . get_navigation_data_install_status ( )
157+ . then ( status => this . navigationDataStatus . set ( status ) )
158+ . catch ( e => console . error ( e ) )
159+
160+ // show the auth container
161+ this . authContainerRef . instance . style . display = "block"
162+ this . loadingRef . instance . style . display = "none"
163+ } )
164+
88165 this . loginButtonRef . instance . addEventListener ( "click" , ( ) => this . handleClick ( ) )
89166 this . downloadButtonRef . instance . addEventListener ( "click" , ( ) => this . handleDownloadClick ( ) )
90167
91- this . executeButtonRef . instance . addEventListener ( "click" , ( ) => {
168+ this . executeIcaoButtonRef . instance . addEventListener ( "click" , ( ) => {
92169 console . time ( "query" )
93170 this . navigationDataInterface
94- . get_airport ( this . inputRef . instance . value )
171+ . get_airport ( this . icaoInputRef . instance . value )
95172 . then ( airport => {
96173 console . info ( airport )
97- console . timeEnd ( "query" )
174+ this . outputRef . instance . textContent = JSON . stringify ( airport , null , 2 )
98175 } )
99176 . catch ( e => console . error ( e ) )
177+ . finally ( ( ) => console . timeEnd ( "query" ) )
178+ } )
179+
180+ this . executeSqlButtonRef . instance . addEventListener ( "click" , ( ) => {
181+ console . time ( "query" )
182+ this . navigationDataInterface
183+ . execute_sql ( this . sqlInputRef . instance . value , [ ] )
184+ . then ( result => {
185+ console . info ( result )
186+ this . outputRef . instance . textContent = JSON . stringify ( result , null , 2 )
187+ } )
188+ . catch ( e => console . error ( e ) )
189+ . finally ( ( ) => console . timeEnd ( "query" ) )
100190 } )
101191
102192 AuthService . user . sub ( user => {
@@ -105,6 +195,7 @@ export class InterfaceSample extends DisplayComponent<InterfaceSampleProps> {
105195 this . qrCodeRef . instance . style . display = "none"
106196 this . loginButtonRef . instance . textContent = "Log out"
107197 this . textRef . instance . textContent = `Welcome, ${ user . preferred_username } `
198+ this . displayMessage ( "" )
108199
109200 this . handleLogin ( )
110201 } else {
@@ -120,6 +211,7 @@ export class InterfaceSample extends DisplayComponent<InterfaceSampleProps> {
120211 await AuthService . signOut ( )
121212 } else {
122213 this . cancelSource = CancelToken . source ( ) // Reset any previous cancellations
214+ this . displayMessage ( "Authenticating.. Scan code (or click it) to sign in" )
123215 await AuthService . signIn ( p => {
124216 if ( p ) {
125217 this . qrCodeRef . instance . src = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${ p . verification_uri_complete } `
@@ -131,6 +223,7 @@ export class InterfaceSample extends DisplayComponent<InterfaceSampleProps> {
131223 } , this . cancelSource . token )
132224 }
133225 } catch ( err ) {
226+ this . qrCodeRef . instance . style . display = "none"
134227 if ( err instanceof Error ) this . displayError ( err . message )
135228 else this . displayError ( `Unknown error: ${ String ( err ) } ` )
136229 }
@@ -160,6 +253,13 @@ export class InterfaceSample extends DisplayComponent<InterfaceSampleProps> {
160253
161254 // Download navigation data to work dir
162255 await this . navigationDataInterface . download_navigation_data ( pkg . file . url )
256+
257+ // Update navigation data status
258+ this . navigationDataInterface
259+ . get_navigation_data_install_status ( )
260+ . then ( status => this . navigationDataStatus . set ( status ) )
261+ . catch ( e => console . error ( e ) )
262+
163263 this . displayMessage ( "Navigation data downloaded" )
164264 } catch ( err ) {
165265 if ( err instanceof Error ) this . displayError ( err . message )
0 commit comments