feat(mesh): device onboarding modal (backlog #6)
Guided prompt that pops up when a mesh radio is detected but not yet connected -- wraps the existing Connect action (mesh.configure with device_path) rather than building a new setup engine. Dismissible per device path (won't re-prompt for the same undismissed-but-ignored device on every poll tick). Not the whole-app identity/seed onboarding system (useOnboarding.ts) -- confirmed unrelated, this is mesh-specific only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
712df2278f
commit
46dae75a0f
@ -38,6 +38,13 @@ const sendError = ref('')
|
||||
const broadcasting = ref(false)
|
||||
const configuring = ref(false)
|
||||
const connectingDevice = ref<string | null>(null)
|
||||
// Onboarding modal (#6): guides a first-time connect for a freshly-detected,
|
||||
// not-yet-connected device — a friendlier wrapper around the same Connect
|
||||
// action the "Detected USB devices" list already offers, not a new setup
|
||||
// engine. `onboardingDismissed` remembers paths the user closed without
|
||||
// connecting, so it doesn't reappear every poll tick for the same device.
|
||||
const showOnboardingModal = ref(false)
|
||||
const onboardingDismissed = ref<Set<string>>(new Set())
|
||||
const chatScrollEl = ref<HTMLElement | null>(null)
|
||||
const mobileShowChat = ref(false)
|
||||
// Device status panel starts collapsed on mobile (expandable via its header).
|
||||
@ -1014,11 +1021,34 @@ async function handleConnectDevice(devicePath: string) {
|
||||
connectingDevice.value = devicePath
|
||||
try {
|
||||
await mesh.configure({ enabled: true, device_path: devicePath } as Partial<import('@/stores/mesh').MeshStatus>)
|
||||
showOnboardingModal.value = false
|
||||
} finally {
|
||||
connectingDevice.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const undismissedDetectedDevices = computed(() =>
|
||||
(mesh.status?.detected_devices ?? []).filter((d) => !onboardingDismissed.value.has(d))
|
||||
)
|
||||
|
||||
function dismissOnboarding() {
|
||||
for (const d of undismissedDetectedDevices.value) onboardingDismissed.value.add(d)
|
||||
showOnboardingModal.value = false
|
||||
}
|
||||
|
||||
// Pop the onboarding modal the moment a device is detected but not yet
|
||||
// connected — same trigger condition the inline "Detected USB devices" list
|
||||
// already uses (mesh.status.detected_devices non-empty + not connected),
|
||||
// just surfaced as a guided prompt instead of requiring the user to notice
|
||||
// the collapsed Device card.
|
||||
watch(
|
||||
() => [mesh.status?.device_connected, undismissedDetectedDevices.value.length] as const,
|
||||
([connected, count]) => {
|
||||
if (!connected && count > 0) showOnboardingModal.value = true
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function signalBars(rssi: number | null): number {
|
||||
if (rssi === null) return 0
|
||||
if (rssi > -60) return 4
|
||||
@ -2308,6 +2338,32 @@ function isImageMime(mime?: string): boolean {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Onboarding modal (#6): guided first-connect prompt for a freshly
|
||||
detected, not-yet-connected mesh device — wraps the same Connect
|
||||
action the inline "Detected USB devices" list already offers. -->
|
||||
<div v-if="showOnboardingModal" class="mesh-transport-modal-backdrop" @click.self="dismissOnboarding">
|
||||
<div class="glass-card mesh-transport-modal">
|
||||
<h3 class="mesh-transport-title">📡 Mesh Device Found</h3>
|
||||
<p class="mesh-transport-sub">
|
||||
A radio was detected but isn't connected yet. Connect it to start using off-grid mesh chat.
|
||||
</p>
|
||||
<div class="mesh-transport-options">
|
||||
<button
|
||||
v-for="dev in undismissedDetectedDevices"
|
||||
:key="dev"
|
||||
class="mesh-transport-option"
|
||||
:disabled="connectingDevice !== null"
|
||||
@click="handleConnectDevice(dev)"
|
||||
>
|
||||
<span class="mesh-transport-icon">📡</span>
|
||||
<span class="mesh-transport-label">{{ dev }}</span>
|
||||
<span class="mesh-transport-meta">{{ connectingDevice === dev ? 'Connecting…' : 'Connect' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<button class="mesh-transport-cancel" @click="dismissOnboarding">Not now</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user