livechat 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f475396740f88ad5b62ea7f30889dd7e3b36699834f061d2a7d09cdf341b88a
4
- data.tar.gz: cb58318b0d8ac30a316d59bca48e8eca3bbe63b697c0d744a2404820d9327344
3
+ metadata.gz: b796f38ce15d2bbedc3b49cceeb0bfc8c5ee26610a6841d956aa3711c18da550
4
+ data.tar.gz: d163445e0442f0f155227210a12a2bdd3f87ec01523a96f29bb898de0550a079
5
5
  SHA512:
6
- metadata.gz: e261a7ad6751071cafb5546af26cc27fe2191ff7425c8aa001d52dc7ce996d0a7176cce69aecae2b7f2e09dccf38b6536dabc1c71af06f790f80b536c45de37a
7
- data.tar.gz: 443fe470e3d66f164a4f970ea4d0e3fdfdd41e42d98d5a05674ac9783e97f4c128ea5dfe97866ad034e4a71eb3cc412e4ec8cabfaf3449c7a727e903925328c2
6
+ metadata.gz: 3a0947576d889216cf1b1330093baa73c5dcae8b55f10b682f19953a02d8e1ac071d02d38bc51ae23cd11352bec7dbba9f77520d199167b1553d9ccf75598894
7
+ data.tar.gz: 59c6ad9b5a10cc2b2a2c13ce50693e4d9c492982e098c6ec42edb84c3b343c96154c988a4dc356d8e259bebc433295c10571a216cc5f451d102a653a26c81f2d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ - Unread count badges on `data-livechat-open` elements, so launcher-less
6
+ setups (`show_launcher = false`) still surface waiting replies.
7
+ - Message prefill: `data-livechat-message="…"` on any opener, or
8
+ `window.Livechat.open("…")` — seeds the message box for contextual asks,
9
+ never overwriting a visitor's draft.
10
+
3
11
  ## 0.1.0
4
12
 
5
13
  - Initial release: floating chat widget (guests + signed-in visitors, one
data/README.md CHANGED
@@ -134,8 +134,14 @@ reply. Gated by `config.authorize_agent` (development-only until you set it).
134
134
 
135
135
  ## Widget API
136
136
 
137
- - `window.Livechat.open()` / `window.Livechat.close()`
138
- - Any element with `data-livechat-open` opens the panel on click
137
+ - `window.Livechat.open()` / `window.Livechat.close()`
138
+ `open("Hi, I need help with…")` prefills the message box (never over a
139
+ visitor's draft)
140
+ - Any element with `data-livechat-open` opens the panel on click; add
141
+ `data-livechat-message="…"` to prefill — great for contextual buttons
142
+ ("Request verification", "Ask about billing")
143
+ - While replies are unread, every `data-livechat-open` element carries a
144
+ small count badge — so hiding the launcher doesn't hide the answer
139
145
  - `<%= livechat_button %>` renders a plain, unstyled opener button
140
146
  - `config.show_launcher = false` hides the bubble entirely — bring your own
141
147
  entry point
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Livechat
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -83,6 +83,7 @@
83
83
  injectStyles();
84
84
  mount();
85
85
  applyBrandColor();
86
+ syncOpenerBadges(); // a Turbo visit brings fresh openers — restamp them
86
87
 
87
88
  if (sessionGet("livechat_open") === "1") {
88
89
  openPanel();
@@ -100,7 +101,7 @@
100
101
  if (!opener) return;
101
102
  event.preventDefault();
102
103
  event.stopPropagation();
103
- openPanel();
104
+ openPanel(opener.getAttribute("data-livechat-message"));
104
105
  }
105
106
 
106
107
  // --- mounting ---------------------------------------------------------------
@@ -287,24 +288,33 @@
287
288
 
288
289
  // --- open / close -----------------------------------------------------------
289
290
 
290
- function openPanel() {
291
+ // prefill (optional string, from data-livechat-message or
292
+ // Livechat.open("…")) seeds the input — but never over a visitor's draft.
293
+ function openPanel(prefill) {
291
294
  mount();
292
- if (isOpen) return;
293
- isOpen = true;
294
- sessionSet("livechat_open", "1");
295
-
296
- // The panel is built once and then hidden/shown — closing must never
297
- // throw away the rendered thread or a half-written message.
298
- var panel = document.getElementById("lvc-panel");
299
- if (!panel) {
300
- panel = buildPanel();
301
- root.appendChild(panel);
295
+ if (!isOpen) {
296
+ isOpen = true;
297
+ sessionSet("livechat_open", "1");
298
+
299
+ // The panel is built once and then hidden/shown — closing must never
300
+ // throw away the rendered thread or a half-written message.
301
+ var panel = document.getElementById("lvc-panel");
302
+ if (!panel) {
303
+ panel = buildPanel();
304
+ root.appendChild(panel);
305
+ }
306
+ panel.hidden = false;
307
+ setUnread(0);
308
+ poll();
309
+ schedulePoll();
310
+ }
311
+ if (inputEl) {
312
+ if (typeof prefill === "string" && prefill && !inputEl.value.trim()) {
313
+ inputEl.value = prefill;
314
+ autogrow();
315
+ }
316
+ inputEl.focus();
302
317
  }
303
- panel.hidden = false;
304
- setUnread(0);
305
- poll();
306
- schedulePoll();
307
- if (inputEl) inputEl.focus();
308
318
  }
309
319
 
310
320
  function closePanel() {
@@ -356,10 +366,35 @@
356
366
  .catch(function () { fetching = false; });
357
367
  }
358
368
 
369
+ var unreadCount = 0;
370
+
359
371
  function setUnread(count) {
360
- if (!badgeEl) return;
361
- badgeEl.hidden = !(count > 0);
362
- badgeEl.textContent = count > 9 ? "9+" : String(count);
372
+ unreadCount = count;
373
+ if (badgeEl) {
374
+ badgeEl.hidden = !(count > 0);
375
+ badgeEl.textContent = count > 9 ? "9+" : String(count);
376
+ }
377
+ syncOpenerBadges();
378
+ }
379
+
380
+ // Hosts that hide the launcher still get an unread indicator: every
381
+ // data-livechat-open element carries a small count badge while replies
382
+ // are waiting, and loses it the moment the panel opens.
383
+ function syncOpenerBadges() {
384
+ var openers = document.querySelectorAll("[data-livechat-open]");
385
+ for (var i = 0; i < openers.length; i++) {
386
+ var badge = openers[i].querySelector(".lvc-opener-badge");
387
+ if (unreadCount > 0) {
388
+ if (!badge) {
389
+ badge = document.createElement("span");
390
+ badge.className = "lvc-opener-badge";
391
+ openers[i].appendChild(badge);
392
+ }
393
+ badge.textContent = unreadCount > 9 ? "9+" : String(unreadCount);
394
+ } else if (badge) {
395
+ badge.remove();
396
+ }
397
+ }
363
398
  }
364
399
 
365
400
  function syncEmailRow() {
@@ -553,7 +588,12 @@
553
588
  "color:var(--lvc-accent-text);width:40px;min-width:40px;height:40px;align-self:flex-end;" +
554
589
  "display:flex;align-items:center;justify-content:center;cursor:pointer}" +
555
590
  "#lvc-form button:disabled{opacity:.6;cursor:default}" +
556
- "#lvc-root[dir=rtl] #lvc-form button svg{transform:scaleX(-1)}";
591
+ "#lvc-root[dir=rtl] #lvc-form button svg{transform:scaleX(-1)}" +
592
+ // Lives in host DOM (on data-livechat-open elements), so no #lvc-root prefix.
593
+ ".lvc-opener-badge{display:inline-flex;min-width:18px;height:18px;padding:0 5px;" +
594
+ "margin-inline-start:6px;border-radius:999px;background:#dc2626;color:#fff;" +
595
+ "font-size:11px;font-weight:700;line-height:18px;align-items:center;" +
596
+ "justify-content:center;vertical-align:middle}";
557
597
 
558
598
  var style = document.createElement("style");
559
599
  style.id = "lvc-styles";
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: livechat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov
@@ -118,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
118
  - !ruby/object:Gem::Version
119
119
  version: '0'
120
120
  requirements: []
121
- rubygems_version: 4.0.6
121
+ rubygems_version: 3.6.9
122
122
  specification_version: 4
123
123
  summary: 'Open-source live chat for Rails: a drop-in support widget plus a team inbox,
124
124
  in your own database.'