Skip to content

Commit

Permalink
adjust app code
Browse files Browse the repository at this point in the history
  • Loading branch information
frontendphil committed Feb 6, 2025
1 parent ec2c3cb commit 86c6580
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 84 deletions.
1 change: 1 addition & 0 deletions deployables/app/app/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export { AvatarInput } from './AvatarInput'
export { ChainSelect } from './ChainSelect'
export * from './navigation'
export { Page } from './Page'
export * from './pilotStatus'
export * from './wallet'
export { ZodiacMod } from './ZodiacMod'

Expand Down
49 changes: 49 additions & 0 deletions deployables/app/app/components/pilotStatus/PilotStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { GhostButton } from '@zodiac/ui'
import { Power, PowerOff } from 'lucide-react'
import { useState } from 'react'
import { useConnectChangeOnPilotEvents } from './useConnectChnageOnPilotEvents'
import { useDisconnectWhenUnreachable } from './useDisconnectWhenUnreachable'
import { usePingWhileDisconnected } from './usePingWhileDisconnected'

export const PilotStatus = () => {
const connected = useConnected()

if (connected) {
return (
<div className="flex flex-col gap-2">
<div className="leading-0 flex items-center gap-2 text-xs font-semibold uppercase">
<Power className="text-green-500" size={16} />
Connected
</div>
</div>
)
}

return (
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2 text-xs font-semibold uppercase">
<PowerOff className="text-red-700" size={16} />
Disconnected
</div>

<GhostButton id="ZODIAC-PILOT::open-panel-button" size="tiny">
Open Pilot
</GhostButton>
</div>
)
}

const useConnected = () => {
const [connected, setConnected] = useState(false)

useConnectChangeOnPilotEvents({
onConnect: () => setConnected(true),
onDisconnect: () => setConnected(false),
})
usePingWhileDisconnected(connected, { onConnect: () => setConnected(true) })
useDisconnectWhenUnreachable(connected, {
onDisconnect: () => setConnected(false),
})

return connected
}
1 change: 1 addition & 0 deletions deployables/app/app/components/pilotStatus/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { PilotStatus } from './PilotStatus'
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { PilotMessageType, type Message } from '@zodiac/messages'
import { useStableHandler } from '@zodiac/ui'
import { useEffect } from 'react'

type ConnectChangeOnPilotEventOptions = {
onConnect: () => void
onDisconnect: () => void
}

export const useConnectChangeOnPilotEvents = ({
onConnect,
onDisconnect,
}: ConnectChangeOnPilotEventOptions) => {
const onConnectRef = useStableHandler(onConnect)
const onDisconnectRef = useStableHandler(onDisconnect)

useEffect(() => {
const handleMessage = (event: MessageEvent<Message>) => {
if (event.data == null) {
return
}

switch (event.data.type) {
case PilotMessageType.PILOT_CONNECT: {
onConnectRef.current()

break
}

case PilotMessageType.PILOT_DISCONNECT: {
onDisconnectRef.current()
}
}
}

window.addEventListener('message', handleMessage)

return () => {
window.removeEventListener('message', handleMessage)
}
}, [onConnectRef, onDisconnectRef])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
CompanionAppMessageType,
PilotMessageType,
type CompanionAppMessage,
type Message,
} from '@zodiac/messages'
import { useEffect, useRef } from 'react'

type DisconnectWhenUnreachableOptions = {
onDisconnect: () => void
}

export const useDisconnectWhenUnreachable = (
connected: boolean,
{ onDisconnect }: DisconnectWhenUnreachableOptions,
) => {
const onDisconnectRef = useRef(onDisconnect)

useEffect(() => {
onDisconnectRef.current = onDisconnect
}, [onDisconnect])

useEffect(() => {
if (connected === false) {
return
}

let cancelled = false

const probeConnection = () => {
window.postMessage(
{ type: CompanionAppMessageType.PING } satisfies CompanionAppMessage,
'*',
)

const disconnectTimeout = setTimeout(() => {
if (cancelled) {
return
}

onDisconnectRef.current()
}, 1000)

const handlePong = (event: MessageEvent<Message>) => {
if (event.data == null) {
return
}

if (event.data.type !== PilotMessageType.PONG) {
return
}

clearTimeout(disconnectTimeout)
}

window.addEventListener('message', handlePong)
}

const interval = setInterval(probeConnection, 1000)

return () => {
cancelled = true

clearInterval(interval)
}
}, [connected])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
CompanionAppMessageType,
PilotMessageType,
type CompanionAppMessage,
type Message,
} from '@zodiac/messages'
import { useStableHandler } from '@zodiac/ui'
import { useEffect } from 'react'

type PingWhileDisconnectedOptions = {
onConnect: () => void
}

export const usePingWhileDisconnected = (
connected: boolean,
{ onConnect }: PingWhileDisconnectedOptions,
) => {
const onConnectRef = useStableHandler(onConnect)

useEffect(() => {
if (connected) {
return
}

const interval = setInterval(() => {
window.postMessage({
type: CompanionAppMessageType.PING,
} satisfies CompanionAppMessage)
}, 500)

const handlePong = (event: MessageEvent<Message>) => {
if (event.data == null) {
return
}

if (event.data.type !== PilotMessageType.PONG) {
return
}

onConnectRef.current()
}

window.addEventListener('message', handlePong)

return () => {
window.removeEventListener('message', handlePong)
clearInterval(interval)
}
}, [connected, onConnectRef])
}
87 changes: 3 additions & 84 deletions deployables/app/app/routes/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
import { Navigation } from '@/components'
import {
CompanionAppMessageType,
PilotMessageType,
type CompanionAppMessage,
type Message,
} from '@zodiac/messages'
import { GhostButton, PilotType, ZodiacOsPlain } from '@zodiac/ui'
import {
ArrowUpFromLine,
Edit,
Landmark,
Plus,
Power,
PowerOff,
} from 'lucide-react'
import { useEffect, useState } from 'react'
import { Navigation, PilotStatus } from '@/components'
import { PilotType, ZodiacOsPlain } from '@zodiac/ui'
import { ArrowUpFromLine, Edit, Landmark, Plus } from 'lucide-react'
import { Outlet } from 'react-router'

const Sidebar = () => {
Expand Down Expand Up @@ -63,70 +49,3 @@ const Sidebar = () => {
}

export default Sidebar

const PilotStatus = () => {
const [connected, setConnected] = useState(false)

useEffect(() => {
const handleMessage = (event: MessageEvent<Message>) => {
if (event.data == null) {
return
}

if (
event.data.type !== PilotMessageType.PILOT_CONNECT &&
event.data.type !== PilotMessageType.PONG
) {
return
}

setConnected(true)
}

window.addEventListener('message', handleMessage)

return () => {
window.removeEventListener('message', handleMessage)
}
}, [])

useEffect(() => {
if (connected) {
return
}

const interval = setInterval(() => {
window.postMessage({
type: CompanionAppMessageType.PING,
} satisfies CompanionAppMessage)
}, 500)

return () => {
clearInterval(interval)
}
}, [connected])

if (connected) {
return (
<div className="flex flex-col gap-2">
<div className="leading-0 flex items-center gap-2 text-xs font-semibold uppercase">
<Power className="text-green-500" size={16} />
Connected
</div>
</div>
)
}

return (
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2 text-xs font-semibold uppercase">
<PowerOff className="text-red-700" size={16} />
Disconnected
</div>

<GhostButton id="ZODIAC-PILOT::open-panel-button" size="tiny">
Open Pilot
</GhostButton>
</div>
)
}
1 change: 1 addition & 0 deletions packages/ui/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useStableHandler } from './useStableHandler'
11 changes: 11 additions & 0 deletions packages/ui/src/hooks/useStableHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useEffect, useRef } from 'react'

export const useStableHandler = <T>(handler: T) => {
const handlerRef = useRef(handler)

useEffect(() => {
handlerRef.current = handler
}, [handler])

return handlerRef
}
1 change: 1 addition & 0 deletions packages/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export { ConfirmationModal, useConfirmationModal } from './ConfirmationModal'
export { CopyToClipboard } from './CopyToClipboard'
export { Divider } from './Divider'
export { Form } from './Form'
export * from './hooks'
export { InlineForm } from './InlineForm'
export type { InlineFormContext } from './InlineForm'
export * from './inputs'
Expand Down

0 comments on commit 86c6580

Please sign in to comment.