尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Stop scrolling. Start living.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Anti-doomscrolling · Stop scrolling, start living.
Click Web extends the same philosophy as the mobile app: fewer infinite feeds, more real-world presence. The dashboard and APIs exist to support verified connection, venue intelligence, and voice—not to recreate doomscroll loops in the browser.
The insights, graph validation, and web-calling companion for Click.檢視完整 README ↗Anti-doomscrolling · Stop scrolling, start living.
Click Web extends the same philosophy as the mobile app: fewer infinite feeds, more real-world presence. The dashboard and APIs exist to support verified connection, venue intelligence, and voice—not to recreate doomscroll loops in the browser.
The insights, graph validation, and web-calling companion for Click.
Click Web is the Next.js companion to the Kotlin Multiplatform (KMP) mobile app. It brings authenticated users a dashboard with real-time chat, optional WebRTC voice through LiveKit, B2B analytics for venues and promoters, and secure auth redirects (including email verification).
<@) so “is this set of user IDs a fully connected clique under verified edges?” stays a single indexed lookup, not an exploratory graph crawl.| Area | What it does |
|---|---|
| Web-based VoIP (dormant) | Calling controls are removed from the web UI (as on iOS). The LiveKit token route, livekit-client integration, and push payload shapes remain in place for future work. |
| Real-time chat | Chat UI syncs with Supabase-backed data; API routes under app/api/chat/ support messages, reactions, and related flows. |
| Home recap | GET /api/me/recap?window=day|week returns a zeroed 200 payload when the caller has no active handshake records in the window (archived / hidden connections and missing columns do not 500). |
| Map beacons | GET/POST /api/beacons, plus POST /api/beacons/image for unencrypted beacon photos (max 2 MB; optional, as on mobile). Pin/popup and card identity uses lib/ui/generateCardVisual.ts + components/ui/CardVisualSurface.tsx (mirrors mobile). |
| Events | Public /events directory and /e/{id} microsite (guest RSVP, no account). Create from dashboard Events tab, /events/new, or Insights Events (not Vibe Radar deploy). |
| Business insights & heatmaps | Insights under app/insights/ (heatmap, live metrics, vibe stream, tribes, social activity) with APIs under app/api/insights/. |
| Secure auth callbacks | Email verification, PKCE, token-hash, and legacy hash flows—see app/auth/callback/page.tsx and app/api/auth/callback/route.ts. |
Click for Business is the monetization layer for local venues, promoters, and event operators who care about who actually showed up together, not just raw door counts.
Because Click verifies physical group cliques (not assumed friend graphs), venues see verified social factions—cohorts that arrived and connected as real groups. Example narrative: “30% of attendees arrived as part of a verified Indie Rock micro-community.” That is foot traffic plus social topology, grounded in handshake integrity.
The dashboard surfaces a Social Sticky Score: composite metrics for connection density (how often verified cliques form on-site) and connection survival (whether those connections persist and re-engage after the event). It is designed to prove real-world community ROI—sticky social outcomes venues can cite beyond impressions or check-ins.
"use client" only where needed (hooks, window, LiveKit, Framer Motion).@supabase/ssr, @supabase/supabase-js), Postgres, Edge Functions (push, proximity clustering, validation-adjacent workflows).app/api/livekit/token/route.ts). Custom React on the JS client.Other libraries include Framer Motion, MapLibre GL, Recharts, SWR, Lucide, and Radix.
app/ — App Router pages, layouts, and Route Handlers (app/api/**).components/ — Dashboard, chat, insights, modals.lib/ — Supabase clients, auth helpers, config.middleware.ts — Session refresh / auth cookies.cd click-web
npm install
Create .env.local in the click-web directory (never commit secrets). .env.example and .env.local.example list every documented variable and are committed so GitHub Actions and Cloudflare Workers Builds can materialize a complete env (scripts/materialize-ci-env.sh) without empty keys. Overlay real values from repo/dashboard secrets when they are set.
Required (core app + Supabase client)
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Supabase anonymous (public) key |
Server-only (recommended for production APIs)
| Variable | Purpose |
|---|---|
SUPABASE_SERVICE_ROLE_KEY | Elevated access for select API routes when anon scope is insufficient |
LiveKit (Web calling)
| Variable | Purpose |
|---|---|
LIVEKIT_API_KEY | LiveKit API key |
LIVEKIT_API_SECRET | LiveKit API secret |
LIVEKIT_WS_URL or LIVEKIT_URL | WebSocket URL for the LiveKit server. Host-only values from the LiveKit Cloud dashboard (click-….livekit.cloud) are normalized to wss://. |
These must be set on the Cloudflare Worker click-web (Workers dashboard → Settings → Variables and secrets), not GitHub Actions Variables. GitHub Actions env does not reach production. Deploy with npm run deploy (opennextjs-cloudflare deploy -- --keep-vars) so dashboard secrets are not wiped.
Unauthenticated POST /api/livekit/token returns 401 and does not prove these vars are set. After deploy:
GET /api/health/env with x-click-health-secret: <HEALTH_DIAGNOSTICS_SECRET> — keys.LIVEKIT_API_KEY / LIVEKIT_API_SECRET / LIVEKIT_WS_URL should be true (presence only; values are never returned). Without that header the endpoint returns coarse liveness only.curl -i -X POST https://joinclick.co/api/livekit/token \
-H "Authorization: Bearer <SUPABASE_ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"connection_id":"<id>","room_name":"click-<id>-x","participant_name":"probe"}'
500 LiveKit environment is not configured means the Worker still cannot see the vars (wrong surface, or deploy without --keep-vars). 200 + JWT means minting works; remaining call failures are client-side.
Optional
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_BASE_URL | Canonical site URL (https://joinclick.co); falls back to VERCEL_URL in some routes |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | Google OAuth if enabled |
NEXT_PUBLIC_IOS_STORE_URL / NEXT_PUBLIC_ANDROID_STORE_URL | Store links (lib/config.ts) |
NEXT_PUBLIC_APP_LAUNCHED | Set to true when the app is publicly launched |
HEALTH_DIAGNOSTICS_SECRET | Protects detailed /api/health/env diagnostics; without the matching x-click-health-secret header callers receive coarse liveness only |
CLICK_ENABLE_SIMULATOR_MOCK + CLICK_APP_ENV | Nonproduction-only proximity fixture gate. Set CLICK_ENABLE_SIMULATOR_MOCK=true only when CLICK_APP_ENV is not production. |
npm run dev
Open http://localhost:3000.
Unit / component (Jest):
npm test
End-to-end UI (Maestro, Chromium; Beta). Install the CLI (Java 17+), start the app, then:
curl -fsSL "https://get.maestro.mobile.dev" | bash
npm run dev # or npm run build && npm start
npm run test:e2e
Flows set a literal url: http://localhost:3000 (Maestro 2.8 leaves ${BASE_URL} unexpanded in the url: field, which opens Chromium at data:). They then launchApp.appId=${BASE_URL} (same-tab CDP navigation). Do not use openLink on web (second window; Maestro reads the leftover blank tab) or launchApp.clearState (wipes the tab back to data:). Text asserts must match accessibility-tree nodes: styled spans split strings such as "About Click", so smoke flows use unsplit copy (Stop scrolling. Start living.) and heading ids.
Override the origin after that with -e BASE_URL=https://joinclick.co (or a preview URL). Authenticated dashboard chrome:
maestro test .maestro/auth --include-tags auth \
-e BASE_URL=http://localhost:3000 \
-e [email protected] \
-e TEST_PASSWORD=secret
Flows live in .maestro/. Jest stays the unit suite; Maestro covers real browser journeys (landing — including not showing Loading your connections for anonymous / — legal pages, playground). CI: .github/workflows/e2e.yml pins Chrome 152 to match Maestro's bundled ChromeDriver, wraps /usr/bin/google-chrome with prepended --no-sandbox, and runs Maestro headed under Xvfb (maestro test --headless makes Chrome --headless=new, which leaves Maestro querying an empty CDP target). Local npm run test:e2e stays headed. Do not Maestro LiveKit rooms. Cloudflare deploys need npm run build:worker. GitHub build-worker proves the artifact. Workers Builds production uses dashboard npm run deploy (empty build command). npm run build must stay next build because OpenNext invokes it; do not switch it to OpenNext when WORKERS_CI=1.
click-web is the browser companion to the KMP mobile app (click/). Mobile owns in-room discovery (Tri-Factor, sensors, native push); web owns the authenticated dashboard, B2B Click Insights, and HTTP BFF routes mobile calls over CLICK_WEB_BASE_URL.
Yes for most post-connection experiences: connections inbox, map, timeline, E2EE chat (pins, mutes, plans, scheduled messages), event chat, QR identity, availability intents, stats, and collaboration hooks (components/DashboardView.tsx).
No for hardware-native flows: BLE + ultrasonic + progressive GPS orchestration, Multi-Tap initiation in a physical room, sensor capture at handshake time, CallKit/PushKit/FCM wake, App Clip, and device calendar integration. Web can display Memory Capsule data captured on mobile but cannot produce the same sensor readings.
click (KMP) ──Tri-Factor/QR──► Supabase Edge + Postgres
│ ▲
│ CLICK_WEB_BASE_URL │ aggregates
▼ │
click-web ──dashboard/chat/insights──┘
Mobile is the primary data producer for B2B insights (connections, encounters, availability intents with include_in_business_insights). Web is the operator surface for /insights/* and the consumer dashboard for browser users.
| Category | Mobile | click-web |
|---|---|---|
| Tri-Factor / Multi-Tap initiation | Primary | Not achievable (hardware) |
| QR connect | Via web HTTP APIs | Full parity |
| Dashboard (table, map, timeline) | Yes | Full parity — lib/dashboard/README.md |
| E2EE chat | MessageCrypto | Full parity — lib/chat/README.md |
| Voice/video | LiveKit native | Web parity — /api/livekit/token |
| Availability intents | Yes | Partial (UI yes; match pushes mobile-first) |
| Memory Capsules | Capture + display | Display only |
| Prior Connections | On-device contact hashing + skippable onboarding | Discover / request / respond APIs + profile badge |
| Community Hubs | Mobile UI | Hubs tab: discovery, create/join/leave, text and encrypted attachments — lib/hub/README.md |
| Home connection insights | ReconnectHelper | Insights, reconnect suggestions and archive warning |
| Home recap and saved events | Day/week recap + bookmarks | Day/week recap and saved-event links |
| B2B Click Insights | widget-vibe consumer | Web-only — lib/insights/README.md |
Detailed matrices live in colocated module READMEs: lib/dashboard/README.md, lib/connections/README.md, lib/insights/README.md. Mobile companion context: click/README.md § Monorepo note.
Web actions that wake mobile or mirror app contracts—especially incoming call push payloads—must stay aligned with the KMP app. Example: DashboardView.tsx documents parity with CallPushNotifier.kt for the send-push-notification Edge Function and incoming_call data shape. E2EE wire format must match KMP MessageCrypto (lib/chat/README.md). When changing call, notification, or crypto flows, update both clients and any Edge Function schema. See AI.md §2 for field-name rules.
The browser calls Supabase Edge Functions with supabase.functions.invoke(). CORS applies. Each invoked function must respond correctly to OPTIONS preflight and return appropriate Access-Control-* headers for your web origin.
Production is the Cloudflare Worker click-web. Set runtime variables on the Workers dashboard (not GitHub Actions Variables). npm run deploy uses --keep-vars so dashboard secrets survive the upload. Ensure Supabase Auth → URL configuration (site URL, redirect URLs) includes https://joinclick.co.
This package is part of the broader Click Platforms workspace. For the KMP client and shared product context, see click/README.md and any workspace root docs.