Skip to content

Commit

Permalink
Keep global state of native app connection
Browse files Browse the repository at this point in the history
  • Loading branch information
kuba2k2 committed Sep 22, 2023
1 parent 0b32025 commit 1d5bff2
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 52 deletions.
12 changes: 12 additions & 0 deletions src/background.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { choosePort, listPortsNative } from "./messaging"
import { getNativeParamsFromBackground } from "./messaging/native"
import {
extendPromiseFromBackground,
rejectPromiseFromBackground,
Expand All @@ -13,6 +14,17 @@ import {
import { BackgroundRequest } from "./utils/types"

class MessageHandler {
/**
* Get native app state & parameters.
*
* ACCESS:
* - Page Script (via Content Script)
* - Popup Script
*/
async getNativeParams() {
return await getNativeParamsFromBackground()
}

/**
* List authorized ports.
*
Expand Down
5 changes: 5 additions & 0 deletions src/messaging/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import { sendToBackground } from "./background"
import { sendToNative } from "./native"
import { sendToPopup } from "./popup"
import { SerialPortData } from "../serial/types"
import { NativeParams } from "../utils/types"

export async function getNativeParams(): Promise<NativeParams> {
return await sendToBackground({ action: "getNativeParams", origin })
}

export async function getPorts(origin: string): Promise<SerialPortData[]> {
return await sendToBackground({ action: "getPorts", origin })
Expand Down
42 changes: 32 additions & 10 deletions src/messaging/native.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { debugLog, debugRx, debugTx } from "../utils/logging"
import { v4 } from "uuid"
import { clearAuthKeyCache, rejectPromise, resolvePromise } from "."
import { NativeRequest } from "../utils/types"
import { debugLog, debugRx, debugTx } from "../utils/logging"
import { NativeParams, NativeRequest } from "../utils/types"
import { catchIgnore } from "../utils/utils"
import { keepPromise } from "./promises"
import { v4 } from "uuid"

const NATIVE_PROTOCOL = 1

Expand All @@ -14,6 +15,10 @@ type RawNativeResponse = {
error?: number
}

let nativeParams: NativeParams = {
state: "checking",
}

async function getNativePort(): Promise<browser.runtime.Port> {
if (globalPort != undefined && globalPort.error == null) return globalPort

Expand All @@ -29,7 +34,12 @@ async function getNativePort(): Promise<browser.runtime.Port> {
}
newPort.postMessage(pingRequest)

// update native params
nativeParams = { state: "checking" }

globalPort = await new Promise((resolve, reject) => {
let isOutdated = false

newPort.onMessage.addListener(async (message: RawNativeResponse) => {
if (!message.id) {
if (message.data) debugRx("NATIVE", message.data)
Expand All @@ -42,17 +52,23 @@ async function getNativePort(): Promise<browser.runtime.Port> {
const version = message.data?.version
const protocol = message.data?.protocol
if (protocol !== NATIVE_PROTOCOL) {
newPort.disconnect()
const error = `Native protocol incompatible: expected v${NATIVE_PROTOCOL}, found v${protocol}`
debugLog("NATIVE", "onMessage", error)

nativeParams = { state: "outdated", version, protocol }
isOutdated = true
newPort.disconnect()

reject(new Error(error))
return
}
const wsPort = message.data?.wsPort
debugLog(
"NATIVE",
"onMessage",
`Connection successful: native v${version}`
`Connection successful: native v${version} @ port ${wsPort}`
)
nativeParams = { state: "connected", version, protocol, wsPort }
resolve(newPort)
return
}
Expand All @@ -67,12 +83,12 @@ async function getNativePort(): Promise<browser.runtime.Port> {
)
})

newPort.onDisconnect.addListener((port) => {
newPort.onDisconnect.addListener(async (port) => {
debugLog("NATIVE", "onDisconnect", "Disconnected:", port.error)
if (globalPort) {
globalPort = null
reject(port.error)
}
if (isOutdated) return
globalPort = null
nativeParams = { state: "error" }
reject(port.error)
})
})

Expand All @@ -87,3 +103,9 @@ export async function sendToNative(message: NativeRequest): Promise<any> {
port.postMessage(message)
return await promise
}

export async function getNativeParamsFromBackground(): Promise<NativeParams> {
// ignore errors, which are reflected in nativeParams instead
await catchIgnore(getNativePort())
return nativeParams
}
6 changes: 0 additions & 6 deletions src/ui/Common.tsx

This file was deleted.

39 changes: 39 additions & 0 deletions src/ui/components/Common.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import styled from "styled-components"
import { PopupRequest } from "../../utils/types"
import { Button } from "../controls/Button"

export type CommonProps = {
resolve: (value: any) => void
reject: (reason?: any) => void
} & PopupRequest

export const MessageContainer = styled.div`
position: relative;
min-height: 40px;
padding-right: 56px;
`

export const ReloadButton = styled(Button)`
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
`

export const ButtonContainer = styled.div`
display: flex;
position: absolute;
bottom: 12px;
left: 12px;
right: 12px;
`

export const ButtonSpacer = styled.span`
margin-right: 12px;
`

export const ButtonMessage = styled.div`
display: flex;
flex-grow: 1;
opacity: 0.5;
`
21 changes: 21 additions & 0 deletions src/ui/components/NativeInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react"
import { NativeParams } from "../../utils/types"

export class NativeInfo extends React.Component<NativeParams> {
render() {
switch (this.props.state) {
case "outdated":
return (
<small>
Native <b>outdated</b>: v{this.props.version}
</small>
)

case "connected":
return <small>Native connected: v{this.props.version}</small>

default:
return null
}
}
}
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createRoot } from "react-dom/client"
import { extendPromise, rejectPromise, resolvePromise } from "../messaging"
import { SerialPortData } from "../serial/types"
import { PopupRequest } from "../utils/types"
import { PortChooser } from "./PortChooser"
import { PortChooser } from "./pages/PortChooser"

async function renderAndWait<T>(
component: any,
Expand Down
72 changes: 37 additions & 35 deletions src/ui/PortChooser.tsx → src/ui/pages/PortChooser.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,21 @@
import React from "react"
import styled from "styled-components"
import { listAvailablePorts } from "../messaging"
import { SerialPortData } from "../serial/types"
import { CommonProps } from "./Common"
import { Button } from "./components/Button"
import { List } from "./components/List"

const MessageContainer = styled.div`
position: relative;
min-height: 40px;
padding-right: 56px;
`

const ReloadButton = styled(Button)`
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
`

const ButtonContainer = styled.div`
position: absolute;
bottom: 12px;
right: 12px;
`

const ButtonSpacer = styled.span`
margin-right: 12px;
`
import { getNativeParams, listAvailablePorts } from "../../messaging"
import { SerialPortData } from "../../serial/types"
import { NativeParams } from "../../utils/types"
import {
ButtonContainer,
ButtonMessage,
ButtonSpacer,
CommonProps,
MessageContainer,
ReloadButton,
} from "../components/Common"
import { NativeInfo } from "../components/NativeInfo"
import { Button } from "../controls/Button"
import { List } from "../controls/List"

type PortChooserState = {
params: NativeParams | null
ports: SerialPortData[] | null
error?: any
active: number | null
Expand All @@ -41,7 +27,7 @@ export class PortChooser extends React.Component<
> {
constructor(props: CommonProps) {
super(props)
this.state = { ports: null, active: null }
this.state = { params: null, ports: null, active: null }
this.handleItemClick = this.handleItemClick.bind(this)
this.handleRefresh = this.handleRefresh.bind(this)
this.handleOkClick = this.handleOkClick.bind(this)
Expand All @@ -53,13 +39,19 @@ export class PortChooser extends React.Component<
}

async handleRefresh() {
this.setState({ ports: null, active: null })
this.setState({ params: null, ports: null, active: null })
try {
const params = await getNativeParams()
this.setState({ params })
if (params.state !== "connected") {
this.setState({ ports: null })
return
}
const ports = await listAvailablePorts(
this.props.origin,
this.props.options
)
this.setState({ ports })
this.setState({ params, ports })
} catch (error) {
this.setState({ error })
}
Expand Down Expand Up @@ -99,6 +91,14 @@ export class PortChooser extends React.Component<

<hr />

{!this.state.params &&
!this.state.ports &&
!this.state.error && (
<small>Looking for serial ports...</small>
)}
{this.state.params && !this.state.ports && (
<small>Couldn't connect to native app</small>
)}
{this.state.ports && (
<List
items={this.state.ports.map(
Expand All @@ -111,9 +111,6 @@ export class PortChooser extends React.Component<
{this.state.ports && this.state.ports.length == 0 && (
<small>No serial ports found</small>
)}
{!this.state.ports && !this.state.error && (
<small>Looking for serial ports...</small>
)}
{this.state.error && (
<small>
Failed to enumerate serial ports:{" "}
Expand All @@ -122,6 +119,11 @@ export class PortChooser extends React.Component<
)}

<ButtonContainer>
<ButtonMessage>
{this.state.params && (
<NativeInfo {...this.state.params} />
)}
</ButtonMessage>
<Button text="Cancel" onClick={this.handleCancelClick} />
<ButtonSpacer />
<Button
Expand Down
8 changes: 8 additions & 0 deletions src/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export type BackgroundRequest = {
action:
| "getNativeParams"
| "getPorts"
| "requestPort"
| "listAvailablePorts"
Expand Down Expand Up @@ -29,3 +30,10 @@ export type PopupRequest = {
origin?: string
options?: SerialPortRequestOptions
}

export type NativeParams = {
state: "checking" | "not-installed" | "error" | "outdated" | "connected"
version?: string
protocol?: number
wsPort?: number
}

0 comments on commit 1d5bff2

Please sign in to comment.