Skip to content

Commit 3b59f87

Browse files
committed
fix: restore files that got deleted during refactor
1 parent 57af829 commit 3b59f87

File tree

5 files changed

+307
-0
lines changed

5 files changed

+307
-0
lines changed

samples/gauge/Components/Dropdown.css

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* Dropdown container */
2+
.dropdown {
3+
position: relative;
4+
display: inline-block;
5+
width: 300px;
6+
text-align: center;
7+
}
8+
9+
/* Dropdown button */
10+
.dropdown-toggle {
11+
background: white;
12+
color: black;
13+
padding: 10px;
14+
width: 100%;
15+
}
16+
17+
/* Dropdown menu */
18+
.dropdown-menu {
19+
display: none;
20+
position: absolute;
21+
background: white;
22+
color: black;
23+
list-style: none;
24+
padding: 0;
25+
margin: 0;
26+
width: 100%;
27+
}
28+
29+
.dropdown-menu li {
30+
padding: 10px;
31+
width: 100%;
32+
}

samples/gauge/Components/Dropdown.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { ComponentProps, ComputedSubject, DisplayComponent, FSComponent, Subject, VNode } from "@microsoft/msfs-sdk"
2+
import "./Dropdown.css"
3+
4+
export class Dropdown extends DisplayComponent<ComponentProps> {
5+
private readonly dropdownButtonRef = FSComponent.createRef<HTMLDivElement>()
6+
private readonly dropdownMenuRef = FSComponent.createRef<HTMLUListElement>()
7+
8+
private readonly dropdownButtonText = Subject.create<string>("Select an item")
9+
10+
private navdataFormat: null | string = null
11+
12+
public render(): VNode {
13+
return (
14+
<div class="dropdown">
15+
<div ref={this.dropdownButtonRef} class="dropdown-toggle">
16+
{this.dropdownButtonText}
17+
</div>
18+
<ul ref={this.dropdownMenuRef} class="dropdown-menu" />
19+
</div>
20+
)
21+
}
22+
23+
public onAfterRender(node: VNode): void {
24+
const dropdownButton = this.dropdownButtonRef.instance
25+
const dropdownMenu = this.dropdownMenuRef.instance
26+
27+
dropdownButton.addEventListener("click", function () {
28+
dropdownMenu.style.display = dropdownMenu.style.display === "block" ? "none" : "block"
29+
})
30+
31+
// Close the dropdown when clicking outside of it
32+
document.addEventListener("click", this.onDropdownItemClick.bind(this))
33+
}
34+
35+
public onDropdownItemClick(event: Event): void {
36+
const dropdownButton = this.dropdownButtonRef.instance
37+
const dropdownMenu = this.dropdownMenuRef.instance
38+
39+
const target = event.target as HTMLElement
40+
if (!target) {
41+
return
42+
}
43+
if (!dropdownMenu.contains(target) && !dropdownButton.contains(target)) {
44+
dropdownMenu.style.display = "none"
45+
} else if (dropdownMenu.contains(target)) {
46+
this.dropdownButtonText.set(target.textContent as string)
47+
const navdataFormat = target.dataset.navdataFormat
48+
if (navdataFormat) {
49+
this.navdataFormat = navdataFormat
50+
}
51+
}
52+
}
53+
54+
public addDropdownItem(text: string, format: string): void {
55+
const dropdownItem = document.createElement("li")
56+
dropdownItem.textContent = text
57+
dropdownItem.dataset.navdataFormat = format
58+
this.dropdownMenuRef.instance.appendChild(dropdownItem)
59+
}
60+
61+
public getNavdataFormat(): string | null {
62+
return this.navdataFormat
63+
}
64+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
.auth-container {
2+
font-size: x-large;
3+
width: 100%;
4+
height: 100%;
5+
position: relative;
6+
top: 100px;
7+
}
8+
9+
.button {
10+
width: 150px;
11+
height: 50px;
12+
font-size: x-large;
13+
margin-top: 5px;
14+
background: white;
15+
color: black;
16+
display: flex;
17+
justify-content: center;
18+
align-items: center;
19+
}
20+
21+
.qr-code {
22+
width: 300px;
23+
height: 300px;
24+
display: none;
25+
}
26+
27+
.horizontal {
28+
display: flex;
29+
flex-direction: row;
30+
justify-content: center;
31+
align-items: center;
32+
}
33+
34+
.vertical {
35+
display: flex;
36+
flex-direction: column;
37+
justify-content: center;
38+
align-items: center;
39+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { ComponentProps, DisplayComponent, EventBus, FSComponent, VNode } from "@microsoft/msfs-sdk"
2+
import { CancelToken, navigraphRequest } from "navigraph/auth"
3+
import { packages } from "../Lib/navigraph"
4+
import { AuthService } from "../Services/AuthService"
5+
import "./NavigraphLogin.css"
6+
import { Dropdown } from "./Dropdown"
7+
8+
interface NavigraphLoginProps extends ComponentProps {
9+
bus: EventBus
10+
}
11+
12+
export class NavigraphLogin extends DisplayComponent<NavigraphLoginProps> {
13+
private readonly textRef = FSComponent.createRef<HTMLDivElement>()
14+
private readonly navdataTextRef = FSComponent.createRef<HTMLDivElement>()
15+
private readonly loginButtonRef = FSComponent.createRef<HTMLButtonElement>()
16+
private readonly qrCodeRef = FSComponent.createRef<HTMLImageElement>()
17+
private readonly dropdownRef = FSComponent.createRef<Dropdown>()
18+
private readonly downloadButtonRef = FSComponent.createRef<HTMLButtonElement>()
19+
20+
private cancelSource = CancelToken.source()
21+
22+
private commBusListener: ViewListener.ViewListener
23+
24+
constructor(props: NavigraphLoginProps) {
25+
super(props)
26+
27+
this.commBusListener = RegisterViewListener("JS_LISTENER_COMM_BUS", () => {
28+
console.info("JS_LISTENER_COMM_BUS registered")
29+
})
30+
31+
this.commBusListener.on("NAVIGRAPH_NavdataDownloaded", () => {
32+
console.info("WASM downloaded navdata")
33+
this.navdataTextRef.instance.textContent = "Navdata downloaded!"
34+
})
35+
36+
this.commBusListener.on("NAVIGRAPH_UnzippedFilesRemaining", (jsonArgs: string) => {
37+
const args = JSON.parse(jsonArgs)
38+
console.info("WASM unzipping files", args)
39+
const percent = Math.round((args.unzipped / args.total) * 100)
40+
this.navdataTextRef.instance.textContent = `Unzipping files... ${percent}% complete`
41+
})
42+
}
43+
44+
public render(): VNode {
45+
return (
46+
<div class="auth-container">
47+
<div class="horizontal">
48+
<div class="vertical">
49+
<div ref={this.textRef} />
50+
<div ref={this.loginButtonRef} class="button" />
51+
<div ref={this.navdataTextRef} />
52+
<img ref={this.qrCodeRef} class="qr-code" />
53+
</div>
54+
<div class="vertical">
55+
<Dropdown ref={this.dropdownRef} />
56+
<div ref={this.downloadButtonRef} class="button">
57+
Download
58+
</div>
59+
</div>
60+
</div>
61+
</div>
62+
)
63+
}
64+
65+
public onBeforeRender(): void {
66+
super.onBeforeRender()
67+
}
68+
69+
public onAfterRender(node: VNode): void {
70+
super.onAfterRender(node)
71+
72+
this.loginButtonRef.instance.addEventListener("click", () => this.handleClick().catch(e => console.error(e)))
73+
this.downloadButtonRef.instance.addEventListener("click", () => this.handleDownloadClick())
74+
75+
AuthService.user.sub(user => {
76+
if (user) {
77+
this.qrCodeRef.instance.src = ""
78+
this.qrCodeRef.instance.style.display = "none"
79+
this.loginButtonRef.instance.textContent = "Log out"
80+
this.textRef.instance.textContent = `Welcome, ${user.preferred_username}`
81+
82+
this.handleLogin()
83+
} else {
84+
this.loginButtonRef.instance.textContent = "Sign in"
85+
this.textRef.instance.textContent = "Not signed in"
86+
}
87+
}, true)
88+
}
89+
90+
private async handleClick() {
91+
if (AuthService.getUser()) {
92+
await AuthService.signOut()
93+
} else {
94+
this.cancelSource = CancelToken.source() // Reset any previous cancellations
95+
AuthService.signIn(p => {
96+
if (p) {
97+
this.qrCodeRef.instance.src = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${p.verification_uri_complete}`
98+
this.qrCodeRef.instance.style.display = "block"
99+
console.info(p.verification_uri_complete)
100+
}
101+
}, this.cancelSource.token).catch(e => console.error("Failed to sign in!", e))
102+
}
103+
}
104+
105+
private handleLogin() {
106+
// Let's display all of our packages
107+
packages
108+
.listPackages()
109+
.then(pkgs => {
110+
for (const pkg of pkgs) {
111+
this.dropdownRef.instance.addDropdownItem(pkg.format, pkg.format)
112+
}
113+
})
114+
.catch(e => console.error(e))
115+
}
116+
117+
private handleDownloadClick() {
118+
packages
119+
.getPackage(this.dropdownRef.instance.getNavdataFormat() as string)
120+
.then(pkg => {
121+
const url = pkg.file.url
122+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
123+
this.commBusListener.call(
124+
"COMM_BUS_WASM_CALLBACK",
125+
"NAVIGRAPH_DownloadNavdata",
126+
JSON.stringify({
127+
url,
128+
folder: pkg.format,
129+
}),
130+
)
131+
this.navdataTextRef.instance.textContent = "Downloading navdata..."
132+
})
133+
.catch(e => console.error(e))
134+
}
135+
}

samples/gauge/lib/navigraph.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { DataStore } from "@microsoft/msfs-sdk"
2+
import { initializeApp, NavigraphApp, Scope } from "@navigraph/app"
3+
import { getAuth } from "@navigraph/auth"
4+
import { getPackagesAPI } from "@navigraph/packages"
5+
import { getChartsAPI } from "@navigraph/charts"
6+
7+
const config: NavigraphApp = {
8+
clientId: "YOUR_CLIENT_ID",
9+
clientSecret: "YOUR_CLIENT_SECRET",
10+
scopes: [Scope.FMSDATA],
11+
}
12+
13+
initializeApp(config)
14+
15+
// Wait 1s before accessing datastorage
16+
// This is a potential workaround for the issue where datastorage does not deliver credentials on startup.
17+
const dataStoreInit = new Promise(resolve => setTimeout(resolve, 1000))
18+
19+
const isNavigraphClient = config.clientId.includes("navigraph")
20+
const clientPrefix = isNavigraphClient ? "NG" : config.clientId.toUpperCase().replace("-", "_") + "_NG"
21+
22+
export const AUTH_STORAGE_KEYS = {
23+
accessToken: `${clientPrefix}_ACCESS_TOKEN`,
24+
refreshToken: `${clientPrefix}_REFRESH_TOKEN`,
25+
} as const
26+
27+
export const auth = getAuth({
28+
storage: {
29+
getItem: key => dataStoreInit.then(() => DataStore.get(key)?.toString() ?? null),
30+
setItem: (key, value) => DataStore.set(key, value),
31+
},
32+
keys: AUTH_STORAGE_KEYS,
33+
})
34+
35+
export const charts = getChartsAPI()
36+
37+
export const packages = getPackagesAPI()

0 commit comments

Comments
 (0)