43 lines
1.6 KiB
TypeScript
43 lines
1.6 KiB
TypeScript
"use client"
|
|
|
|
// The notes page's way into the feedback survey (feedback.guide.ts): a button that asks the guides
|
|
// module to start `notes.feedback` through the shared surface coordinator (`guides.start`, joint
|
|
// contract §12), the same command the support chat's help offers send. It shows only while the guides
|
|
// module serves that command, so an app without guides renders nothing here; `kit template:prune
|
|
// --without guides` removes it with this folder.
|
|
|
|
import type { SurfaceCoordinator } from "@conusai-kit/core"
|
|
import { useSurfaceCoordinator } from "@conusai-kit/ui"
|
|
import { type ReactElement, useState, useSyncExternalStore } from "react"
|
|
import type { FeedbackButtonLabels } from "./feedback-labels.js"
|
|
|
|
const never = () => () => undefined
|
|
|
|
export function FeedbackButton({ labels }: { labels: FeedbackButtonLabels }): ReactElement | null {
|
|
const coordinator = useSurfaceCoordinator<SurfaceCoordinator>()
|
|
const served = useSyncExternalStore(
|
|
coordinator ? (listener) => coordinator.subscribe(listener) : never,
|
|
() => coordinator?.serves("guides.start") ?? false,
|
|
() => false,
|
|
)
|
|
const [unavailable, setUnavailable] = useState(false)
|
|
if (!coordinator || !served) return null
|
|
const start = async () => {
|
|
const result = await coordinator.dispatch({
|
|
version: 1,
|
|
requestId: globalThis.crypto.randomUUID(),
|
|
type: "guides.start",
|
|
guideKey: "notes.feedback",
|
|
})
|
|
setUnavailable(result.status !== "ok")
|
|
}
|
|
return (
|
|
<p>
|
|
<button type="button" onClick={() => void start()}>
|
|
{labels.open}
|
|
</button>{" "}
|
|
<span role="status">{unavailable ? labels.unavailable : ""}</span>
|
|
</p>
|
|
)
|
|
}
|