3 Commits

Author SHA1 Message Date
isra el
83d0c3803a fix(web): stop the SupportHQ widget rebuilding itself on every session refetch
The effect depended on the whole session object. SessionProvider returns a
new object on every refetch, and React compares deps by reference, so an
identical session still ran the cleanup and the body again: the widget was
destroyed and its script re-injected even though nothing about the user
had changed, closing any chat the user had open.

It now depends on the individual strings the effect actually reads, which
compare by value, so it only re-initialises when the metadata genuinely
changes.

Two related leaks fixed at the same time:

- cleanup called destroy() but left the script tag in the document, so
  every re-run added another one for the life of the page. It is now
  removed.
- a script whose src is already cached can finish loading after cleanup
  ran, and initialising then left a widget behind that nothing would
  destroy. That late load is now ignored.

Tests cover all four behaviours and fail against the previous version.

Also gives the API guide page focus before the clipboard test: writeText
rejects on an unfocused document, which is where a page sits while other
workers run, and granting clipboard permissions does not cover it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 14:45:40 +03:00
isra el
3eba8d3444 perf(web): register only the Prism languages used, scope Google OAuth
The API guide imported react-syntax-highlighter's default entry, which is
the highlight.js build with every language compiled in. It also applied
Prism themes to it, and the two use different class names, so the samples
were largely uncoloured on top of being heavy. Switch to PrismLight and
register the six languages the guide actually renders.

GoogleOAuthProvider wrapped the whole app but only the login and register
pages use it, so the Google Identity SDK initialised on every dashboard
page. Move it inside LoginWithGoogle, which is where both consumers go
through.

Total client chunks drop from 3.1M to 2.3M.

Also fixes four e2e tests that were relying on page.goto returning a
fully loaded page. Adding loading.tsx introduced a short skeleton state
that did not exist before, and each test measured or interacted during
it. They now wait on real signals instead of timing:

- bulk send waits for the dropzone row cap, which comes from
  useSubscription inside that page's own hook. The previous wait on the
  devices response was wrong: the dashboard layout requests the same
  query key, so it can resolve before the page hydrates.
- the mobile overflow sweep and the API guide replace networkidle, which
  needs a 500ms window with zero connections that link prefetching keeps
  pushing out of reach.
- the footer/tab-bar check re-scrolls as the page grows, instead of
  scrolling once while the skeleton is still short.
- the overflow sweep measures the billing tab rather than
  /dashboard/account, a redirect stub with no layout of its own whose
  client-side redirect tore down the measurement.

A new test asserts the samples carry Prism token markup in all five
languages, so a revert to the highlight.js entry fails instead of
silently dropping the colours.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 14:36:34 +03:00
isra el
1979e2f0b5 feat: drop recent activity, correct usage wording, rebuild the API guide
Dashboard:
- Removed the recent activity section.
- Corrected the usage card wording. The number was labelled "sent", but
  getCurrentSubscription counts SMS documents with no type filter, and the
  same model stores both SMSType.SENT and SMSType.RECEIVED. Inbound messages
  consume the quota too, so the cards now say "messages" and carry an explicit
  note that sends and receipts both count against the limit.
- "This month" is now "Last 30 days". The backend counts from setMonth(-1), a
  rolling window, not the calendar month.

Devices:
- Removed the battery and signal indicators. Neither the device schema nor the
  Android app reports either value, so they only ever rendered "unknown" and
  "-" beside an icon that implied real telemetry. Also dropped the phantom
  batteryLevel field from the Device type so nothing is built on it later.

API guide, rebuilt:
- It rendered a Collapsible that defaulted to closed. Once it moved to its own
  route the page looked empty until you found the chevron. It is a page now,
  so nothing is collapsed.
- Samples are generated from the user's real device id, with a device picker,
  so they run after pasting one key. Every sample previously said
  YOUR_DEVICE_ID, so nothing on the page was runnable as-is.
- Code blocks follow the theme. The old ones hardcoded a slate-800 background,
  so they were the only dark element on a light page.
- Languages went from 3 to 5 (cURL, Node, Python, PHP, Go), picked once and
  applied to every endpoint, replacing a hand-rolled inline Python SVG icon.
- Endpoints now cover bulk send and received messages, not just send and two
  lookups. Response examples use the real lowercase status values from the SMS
  schema instead of invented uppercase ones, and the batch example is no
  longer invalid JSON (it had a trailing comma).
- Keys the copy state per block rather than by index arithmetic over
  Object.entries order.

The mount guard usually needed for next-themes is not used in CodeBlock:
resolvedTheme is undefined on the server and first client render, so both
produce the light style. Verified with a console sweep across nine pages in
both colour schemes, zero messages.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 21:07:38 +03:00