livechat 0.4.0 → 0.4.1
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/app/views/layouts/livechat/application.html.erb +13 -1
- data/app/views/livechat/conversations/show.html.erb +3 -1
- data/lib/livechat/dashboard.js +70 -0
- data/lib/livechat/version.rb +1 -1
- data/lib/livechat/widget.js +4 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5996e3979cd73dab0019abd50f021bdec6abcf3953794dff646c056ecc2b3e92
|
|
4
|
+
data.tar.gz: 747930bfc368d5ba79683b8ef0890065415c3489f6180204b60fe7879294b916
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a3158ae2981632e866fa0d7abbed03446a5f3cb64edc3d82dd0a12bb8c562b87b7804c5bf17770b10b8ce82bcd206972582c99aaf6545f8e0c35cbdd78c19ccd
|
|
7
|
+
data.tar.gz: 2a1eec1c1dc53d0a5d40ed8ac4f047132f2047e4e7ea9e8e1f103683411c9e21eb0ce38e57e4f98528927f040786f50b50ab1df4a5e9cc588cd601f5cc6ab824
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.1
|
|
4
|
+
|
|
5
|
+
- Consistent attachment UI across both sides. The inbox reply now shows the
|
|
6
|
+
same quiet paperclip + file chips (with per-file remove) as the visitor
|
|
7
|
+
widget, instead of the browser's raw "Choose Files" control — progressively
|
|
8
|
+
enhanced, so the native input still works without JavaScript. Also fixed the
|
|
9
|
+
widget's attach button rendering as a solid-blue second button (a CSS
|
|
10
|
+
specificity slip); it's now a quiet paperclip, with the accent reserved for
|
|
11
|
+
Send.
|
|
12
|
+
|
|
3
13
|
## 0.4.0
|
|
4
14
|
|
|
5
15
|
- **File attachments.** Visitors and agents can attach images and documents to
|
|
@@ -98,7 +98,19 @@
|
|
|
98
98
|
.reply textarea { resize: vertical; min-height: 44px; max-height: 200px; padding: 8px 10px;
|
|
99
99
|
border: 1px solid var(--border); border-radius: 10px; font: inherit;
|
|
100
100
|
background: var(--bg); color: var(--text); }
|
|
101
|
-
.reply input[type=file] { font-size: 12px; color: var(--muted); }
|
|
101
|
+
.reply input[type=file].reply-file { font-size: 12px; color: var(--muted); }
|
|
102
|
+
.reply button.attach { align-self: flex-end; width: 42px; min-width: 42px; height: 42px; padding: 0;
|
|
103
|
+
display: flex; align-items: center; justify-content: center; color: var(--muted);
|
|
104
|
+
background: var(--surface); border: 1px solid var(--border); border-radius: 10px; }
|
|
105
|
+
.reply button.attach:hover { color: var(--text); background: var(--bg); }
|
|
106
|
+
.reply .chips { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
107
|
+
.reply .chip { display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
|
|
108
|
+
padding: 4px 6px 4px 10px; border: 1px solid var(--border); border-radius: 999px;
|
|
109
|
+
background: var(--bg); font-size: 12px; }
|
|
110
|
+
.reply .chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
|
|
111
|
+
.reply .chip-x { padding: 0 2px; border: none; background: none; color: var(--muted);
|
|
112
|
+
font-size: 16px; line-height: 1; cursor: pointer; }
|
|
113
|
+
.reply .chip-x:hover { color: var(--text); }
|
|
102
114
|
.reply button.send { align-self: flex-end; width: 42px; min-width: 42px; height: 42px; padding: 0;
|
|
103
115
|
display: flex; align-items: center; justify-content: center;
|
|
104
116
|
background: var(--accent); border-color: var(--accent); color: var(--accent-text);
|
|
@@ -65,8 +65,10 @@
|
|
|
65
65
|
<div class="compose">
|
|
66
66
|
<%= form.text_area :body, rows: 2, autofocus: true, required: !Livechat.attachments_enabled?,
|
|
67
67
|
placeholder: t('livechat.dashboard.reply_placeholder', default: 'Write your reply…') %>
|
|
68
|
+
<%# dashboard.js hides this and swaps in a paperclip button + file chips.
|
|
69
|
+
Left plain as a no-JS fallback so agents can still attach without it. %>
|
|
68
70
|
<% if Livechat.attachments_enabled? %>
|
|
69
|
-
<%= form.file_field :files, multiple: true,
|
|
71
|
+
<%= form.file_field :files, multiple: true, class: 'reply-file',
|
|
70
72
|
aria: { label: t('livechat.dashboard.attach', default: 'Attach files') } %>
|
|
71
73
|
<% end %>
|
|
72
74
|
</div>
|
data/lib/livechat/dashboard.js
CHANGED
|
@@ -23,8 +23,78 @@
|
|
|
23
23
|
ready(function () {
|
|
24
24
|
watchIndex();
|
|
25
25
|
watchThread();
|
|
26
|
+
enhanceReplyAttachments();
|
|
26
27
|
});
|
|
27
28
|
|
|
29
|
+
// Progressive enhancement for the reply form's file input: hide the raw
|
|
30
|
+
// control and swap in a quiet paperclip plus file chips, matching the
|
|
31
|
+
// visitor widget. Without JS the native input stays and still works.
|
|
32
|
+
function enhanceReplyAttachments() {
|
|
33
|
+
var form = document.querySelector("form.reply");
|
|
34
|
+
if (!form) return;
|
|
35
|
+
var input = form.querySelector("input[type=file]");
|
|
36
|
+
if (!input) return;
|
|
37
|
+
|
|
38
|
+
input.hidden = true;
|
|
39
|
+
var canEdit = typeof DataTransfer !== "undefined"; // per-file removal needs it
|
|
40
|
+
|
|
41
|
+
var button = document.createElement("button");
|
|
42
|
+
button.type = "button";
|
|
43
|
+
button.className = "attach";
|
|
44
|
+
var label = input.getAttribute("aria-label") || "Attach files";
|
|
45
|
+
button.setAttribute("aria-label", label);
|
|
46
|
+
button.title = label;
|
|
47
|
+
// Paperclip (Heroicons, MIT) — same icon as the widget.
|
|
48
|
+
button.innerHTML =
|
|
49
|
+
'<svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">' +
|
|
50
|
+
'<path fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" ' +
|
|
51
|
+
'stroke-linejoin="round" d="M18.4 8.6 9.9 17a3.5 3.5 0 0 1-5-5l8.5-8.4a2.3 2.3 0 0 1 ' +
|
|
52
|
+
'3.3 3.3l-8.5 8.4a1.1 1.1 0 0 1-1.6-1.6l7.8-7.8"/></svg>';
|
|
53
|
+
button.addEventListener("click", function () { input.click(); });
|
|
54
|
+
form.insertBefore(button, form.firstChild);
|
|
55
|
+
|
|
56
|
+
var chips = document.createElement("div");
|
|
57
|
+
chips.className = "chips";
|
|
58
|
+
chips.hidden = true;
|
|
59
|
+
var compose = form.querySelector(".compose") || form;
|
|
60
|
+
compose.insertBefore(chips, compose.firstChild);
|
|
61
|
+
|
|
62
|
+
input.addEventListener("change", render);
|
|
63
|
+
|
|
64
|
+
function render() {
|
|
65
|
+
var files = Array.prototype.slice.call(input.files);
|
|
66
|
+
chips.textContent = "";
|
|
67
|
+
chips.hidden = files.length === 0;
|
|
68
|
+
files.forEach(function (file, index) {
|
|
69
|
+
var chip = document.createElement("span");
|
|
70
|
+
chip.className = "chip";
|
|
71
|
+
var name = document.createElement("span");
|
|
72
|
+
name.className = "chip-name";
|
|
73
|
+
name.textContent = file.name;
|
|
74
|
+
chip.appendChild(name);
|
|
75
|
+
if (canEdit) {
|
|
76
|
+
var remove = document.createElement("button");
|
|
77
|
+
remove.type = "button";
|
|
78
|
+
remove.className = "chip-x";
|
|
79
|
+
remove.setAttribute("aria-label", "Remove file");
|
|
80
|
+
remove.innerHTML = "×";
|
|
81
|
+
remove.addEventListener("click", function () { removeAt(index); });
|
|
82
|
+
chip.appendChild(remove);
|
|
83
|
+
}
|
|
84
|
+
chips.appendChild(chip);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function removeAt(index) {
|
|
89
|
+
var kept = new DataTransfer();
|
|
90
|
+
Array.prototype.slice.call(input.files).forEach(function (file, i) {
|
|
91
|
+
if (i !== index) kept.items.add(file);
|
|
92
|
+
});
|
|
93
|
+
input.files = kept.files;
|
|
94
|
+
render();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
28
98
|
// The conversation list keeps itself fresh: when the server's token moves
|
|
29
99
|
// (new message, new thread, resolve/reopen), just reload — unless the
|
|
30
100
|
// agent is mid-search, whose typing must never be eaten.
|
data/lib/livechat/version.rb
CHANGED
data/lib/livechat/widget.js
CHANGED
|
@@ -829,10 +829,12 @@
|
|
|
829
829
|
"#lvc-form button:disabled{opacity:.6;cursor:default}" +
|
|
830
830
|
"#lvc-root[dir=rtl] #lvc-form button svg{transform:scaleX(-1)}" +
|
|
831
831
|
// Attach button sits alongside the send button, quieter (it's secondary).
|
|
832
|
-
|
|
832
|
+
// Selector repeats the id twice so the quiet look outranks the shared
|
|
833
|
+
// `#lvc-form button` accent-fill rule (else the paperclip goes solid blue).
|
|
834
|
+
"#lvc-root #lvc-attach{border:none;background:none;color:var(--lvc-muted);width:38px;min-width:38px;" +
|
|
833
835
|
"height:40px;align-self:flex-end;display:flex;align-items:center;justify-content:center;" +
|
|
834
836
|
"cursor:pointer;border-radius:10px}" +
|
|
835
|
-
"#lvc-attach:hover{background:var(--lvc-bg);color:var(--lvc-text)}" +
|
|
837
|
+
"#lvc-root #lvc-attach:hover{background:var(--lvc-bg);color:var(--lvc-text)}" +
|
|
836
838
|
// Pending-file chips above the composer.
|
|
837
839
|
"#lvc-files{display:flex;flex-wrap:wrap;gap:6px;padding:8px 12px 0}" +
|
|
838
840
|
"#lvc-files[hidden]{display:none}" +
|