Skip to content

feat(chat): show Crisp's launcher on every page - #46

Merged
datlechin merged 1 commit into
mainfrom
feat/crisp-always-on
Oct 5, 2026
Merged

datlechin merged 1 commit into
mainfrom
feat/crisp-always-on

Conversation

@datlechin

Copy link
Copy Markdown
Member

Why

The chat launcher appeared only after a reader clicked "Live chat" in the footer or on the FAQ. The owner wants it on every page.

Change

  • lib/crisp.ts (shared): loadChatWhenIdle() adds Crisp's loader once the load event has fired and the browser is idle (requestIdleCallback, or 2 s on Safari). It is never in the server render and never competes with the first paint. openChat() opens the same widget, loading it first if a reader clicks early. Crisp is still given only the page's language.
  • The landing layout calls useLiveChat(), so every public page carries the launcher. The footer and FAQ "Live chat" buttons now open it.
  • Consent bar: on a phone the bar spans the width, so the launcher would cover its Decline button. While the bar reaches the bottom-right corner the launcher is hidden (chat:hide), and it comes back when the bar closes (chat:show). A chat the reader opened is never hidden. The bar carries data-consent-bar for this. On desktop the bar sits bottom-left and the launcher is untouched.
  • Privacy policy (EN and VI), updated 2026-10-05: Crisp loads on every page of the site and the account portal and sets its crisp-client/ cookies there. It receives the IP, browser details, the pages viewed and messages. The cookies' lawful basis is legitimate interest; conversations a reader starts stay under consent. The processors list and the summary are updated to match.
  • docs/architecture.md, docs/shared-files.md (new hashes), the Blade comment and test comments now describe the new behaviour.

Merge with TableProApp/license (same shared files).

Tests

  • tests/js/crisp.test.ts (10 cases):
    • nothing is loaded before load plus idle, and the loader is added once;
    • a load is cancelled when the reader leaves the page first;
    • nothing loads without a website ID;
    • the button opens the chat;
    • the launcher hides and returns around a full-width consent bar, and is untouched by a desktop bar or an open chat;
    • Crisp receives the language only.
  • ScriptsTest: openChat() is called only from click handlers and loadChatWhenIdle() only in an effect. No document carries a third-party script.
  • Full suite with SSR: 1425 passed, 1 skipped. npm run test:js: 181/181. Typecheck, build and Pint pass.
  • Headless Chrome against a local build with SSR, with Crisp's script blocked so its command queue stays readable:
    • no Crisp in the server HTML or at DOMContentLoaded;
    • the loader arrives after load plus idle, in en on / and vi on /vi/pricing;
    • at 390px with the consent bar open, the queue is chat:hide, and after answering it is chat:hide, chat:show;
    • no console errors.

The chat loaded only when a reader clicked "Live chat" in the footer or
on the FAQ. It now joins every page: the layout calls loadChatWhenIdle(),
which adds Crisp's loader after the load event once the browser is idle,
so it never delays the first paint and is never in the server render.
The "Live chat" buttons open the same widget.

While the consent bar covers the bottom-right corner (on a phone it spans
the width) the launcher is hidden, and it returns when the bar closes; a
chat the reader opened is never hidden. The bar carries data-consent-bar
for this.

The privacy policy, in English and Vietnamese, now says Crisp loads on
every page and sets its cookies there, under legitimate interest, with
conversations a reader starts still under consent. lib/crisp.ts and
consent-bar.tsx are shared with TableProApp/license, which ships the same
copies.
@datlechin
datlechin merged commit 6f7d1bf into main Oct 5, 2026
4 checks passed
@datlechin
datlechin deleted the feat/crisp-always-on branch October 5, 2026 12:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant