2026-01-14 11:05:27 +01:00

135 lines
3.6 KiB
TypeScript

import { useEffect, useState, useCallback } from 'react'
import { Button, Card } from './ui'
import { getAlbyService } from '@/lib/alby'
interface AlbyInstallerProps {
onInstalled?: () => void
}
function InfoIcon(): React.ReactElement {
return (
<svg
className="h-5 w-5 text-neon-cyan"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
clipRule="evenodd"
/>
</svg>
)
}
interface InstallerActionsProps {
onInstalled?: () => void
markInstalled: () => void
}
function InstallerActions({ onInstalled, markInstalled }: InstallerActionsProps): React.ReactElement {
const connect = useCallback(() => {
const alby = getAlbyService()
void alby.enable().then(() => {
markInstalled()
onInstalled?.()
})
}, [markInstalled, onInstalled])
return (
<div className="mt-4">
<div className="flex flex-col sm:flex-row gap-2">
<a
href="https://getalby.com/"
target="_blank"
rel="noopener noreferrer"
>
<Button variant="primary" size="small">
Install Alby
</Button>
</a>
<Button
variant="secondary"
size="small"
onClick={() => {
void connect()
}}
>
Already installed? Connect
</Button>
</div>
</div>
)
}
function InstallerBody({ onInstalled, markInstalled }: InstallerActionsProps): React.ReactElement {
return (
<div className="ml-3 flex-1">
<h3 className="text-sm font-medium text-neon-cyan">Alby Extension Required</h3>
<div className="mt-2 text-sm text-cyber-accent">
<p>To make Lightning payments, please install the Alby browser extension.</p>
</div>
<InstallerActions
markInstalled={markInstalled}
{...(onInstalled ? { onInstalled } : {})}
/>
<div className="mt-3 text-xs text-cyber-accent/70">
<p>Alby is a Lightning wallet that enables instant Bitcoin payments in your browser.</p>
</div>
</div>
)
}
function useAlbyStatus(onInstalled?: () => void): { isInstalled: boolean; isChecking: boolean; markInstalled: () => void } {
const [isInstalled, setIsInstalled] = useState(false)
const [isChecking, setIsChecking] = useState(true)
useEffect(() => {
const checkAlby = (): void => {
try {
const alby = getAlbyService()
const installed = alby.isEnabled()
setIsInstalled(installed)
if (installed) {
onInstalled?.()
}
} catch (e) {
console.error('Error checking Alby:', e)
setIsInstalled(false)
} finally {
setIsChecking(false)
}
}
checkAlby()
}, [onInstalled])
const markInstalled = (): void => {
setIsInstalled(true)
}
return { isInstalled, isChecking, markInstalled }
}
export function AlbyInstaller({ onInstalled }: AlbyInstallerProps): React.ReactElement | null {
const { isInstalled, isChecking, markInstalled } = useAlbyStatus(onInstalled)
if (isChecking || isInstalled) {
return null
}
return (
<Card variant="default" className="bg-cyber-dark/50 mb-4">
<div className="flex items-start">
<div className="flex-shrink-0">
<InfoIcon />
</div>
<InstallerBody
markInstalled={markInstalled}
{...(onInstalled ? { onInstalled } : {})}
/>
</div>
</Card>
)
}