livechat 0.5.0 → 0.5.2
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 +18 -0
- data/app/controllers/livechat/attachments_controller.rb +4 -4
- data/app/helpers/livechat/inbox_helper.rb +5 -2
- data/app/models/livechat/message.rb +5 -3
- data/app/views/layouts/livechat/application.html.erb +10 -0
- data/app/views/livechat/conversations/show.html.erb +10 -1
- data/config/locales/livechat.ar.yml +14 -0
- data/config/locales/livechat.bg.yml +14 -0
- data/config/locales/livechat.bn.yml +14 -0
- data/config/locales/livechat.de.yml +14 -0
- data/config/locales/livechat.el.yml +14 -0
- data/config/locales/livechat.en.yml +14 -0
- data/config/locales/livechat.es.yml +14 -0
- data/config/locales/livechat.fr.yml +14 -0
- data/config/locales/livechat.hi.yml +14 -0
- data/config/locales/livechat.hr.yml +14 -0
- data/config/locales/livechat.id.yml +14 -0
- data/config/locales/livechat.it.yml +14 -0
- data/config/locales/livechat.ja.yml +14 -0
- data/config/locales/livechat.ko.yml +14 -0
- data/config/locales/livechat.lb.yml +14 -0
- data/config/locales/livechat.nl.yml +14 -0
- data/config/locales/livechat.pl.yml +14 -0
- data/config/locales/livechat.pt.yml +14 -0
- data/config/locales/livechat.ro.yml +14 -0
- data/config/locales/livechat.ru.yml +14 -0
- data/config/locales/livechat.th.yml +14 -0
- data/config/locales/livechat.tr.yml +14 -0
- data/config/locales/livechat.uk.yml +14 -0
- data/config/locales/livechat.ur.yml +14 -0
- data/config/locales/livechat.vi.yml +14 -0
- data/config/locales/livechat.zh-CN.yml +14 -0
- data/lib/livechat/dashboard.js +185 -2
- data/lib/livechat/version.rb +1 -1
- data/lib/livechat/widget.js +270 -10
- data/lib/livechat/widget.rb +8 -0
- metadata +1 -1
|
@@ -18,6 +18,13 @@ pt:
|
|
|
18
18
|
error_rate_limited: "Muitas mensagens. Aguarde um momento e tente novamente."
|
|
19
19
|
unread_aria: "mensagens não lidas"
|
|
20
20
|
attach: "Anexar arquivos"
|
|
21
|
+
record_audio: "Record audio"
|
|
22
|
+
stop_recording: "Stop recording"
|
|
23
|
+
cancel_recording: "Cancel recording"
|
|
24
|
+
recording: "Recording %{time}"
|
|
25
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
26
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
27
|
+
microphone_error: "Could not start audio recording."
|
|
21
28
|
remove_file: "Remover arquivo"
|
|
22
29
|
attachment_error: "Não foi possível enviar alguns arquivos."
|
|
23
30
|
expand: "Ampliar"
|
|
@@ -46,6 +53,13 @@ pt:
|
|
|
46
53
|
reply_placeholder: "Escreva sua resposta…"
|
|
47
54
|
send: "Enviar"
|
|
48
55
|
attach: "Anexar arquivos"
|
|
56
|
+
record_audio: "Record audio"
|
|
57
|
+
stop_recording: "Stop recording"
|
|
58
|
+
cancel_recording: "Cancel recording"
|
|
59
|
+
recording: "Recording %{time}"
|
|
60
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
61
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
62
|
+
microphone_error: "Could not start audio recording."
|
|
49
63
|
newer: "Mais recentes"
|
|
50
64
|
older: "Mais antigas"
|
|
51
65
|
mail:
|
|
@@ -18,6 +18,13 @@ ro:
|
|
|
18
18
|
error_rate_limited: "Prea multe mesaje. Așteptați puțin și încercați din nou."
|
|
19
19
|
unread_aria: "mesaje necitite"
|
|
20
20
|
attach: "Atașează fișiere"
|
|
21
|
+
record_audio: "Record audio"
|
|
22
|
+
stop_recording: "Stop recording"
|
|
23
|
+
cancel_recording: "Cancel recording"
|
|
24
|
+
recording: "Recording %{time}"
|
|
25
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
26
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
27
|
+
microphone_error: "Could not start audio recording."
|
|
21
28
|
remove_file: "Elimină fișierul"
|
|
22
29
|
attachment_error: "Unele fișiere nu au putut fi trimise."
|
|
23
30
|
expand: "Mărește"
|
|
@@ -46,6 +53,13 @@ ro:
|
|
|
46
53
|
reply_placeholder: "Scrieți răspunsul…"
|
|
47
54
|
send: "Trimite"
|
|
48
55
|
attach: "Atașează fișiere"
|
|
56
|
+
record_audio: "Record audio"
|
|
57
|
+
stop_recording: "Stop recording"
|
|
58
|
+
cancel_recording: "Cancel recording"
|
|
59
|
+
recording: "Recording %{time}"
|
|
60
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
61
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
62
|
+
microphone_error: "Could not start audio recording."
|
|
49
63
|
newer: "Mai noi"
|
|
50
64
|
older: "Mai vechi"
|
|
51
65
|
mail:
|
|
@@ -18,6 +18,13 @@ ru:
|
|
|
18
18
|
error_rate_limited: "Слишком много сообщений. Подождите немного и попробуйте ещё раз."
|
|
19
19
|
unread_aria: "непрочитанные сообщения"
|
|
20
20
|
attach: "Прикрепить файлы"
|
|
21
|
+
record_audio: "Record audio"
|
|
22
|
+
stop_recording: "Stop recording"
|
|
23
|
+
cancel_recording: "Cancel recording"
|
|
24
|
+
recording: "Recording %{time}"
|
|
25
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
26
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
27
|
+
microphone_error: "Could not start audio recording."
|
|
21
28
|
remove_file: "Удалить файл"
|
|
22
29
|
attachment_error: "Некоторые файлы не удалось отправить."
|
|
23
30
|
expand: "Развернуть"
|
|
@@ -46,6 +53,13 @@ ru:
|
|
|
46
53
|
reply_placeholder: "Напишите ответ…"
|
|
47
54
|
send: "Отправить"
|
|
48
55
|
attach: "Прикрепить файлы"
|
|
56
|
+
record_audio: "Record audio"
|
|
57
|
+
stop_recording: "Stop recording"
|
|
58
|
+
cancel_recording: "Cancel recording"
|
|
59
|
+
recording: "Recording %{time}"
|
|
60
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
61
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
62
|
+
microphone_error: "Could not start audio recording."
|
|
49
63
|
newer: "Новее"
|
|
50
64
|
older: "Старше"
|
|
51
65
|
mail:
|
|
@@ -18,6 +18,13 @@ th:
|
|
|
18
18
|
error_rate_limited: "ข้อความมากเกินไป โปรดรอสักครู่แล้วลองอีกครั้ง"
|
|
19
19
|
unread_aria: "ข้อความที่ยังไม่ได้อ่าน"
|
|
20
20
|
attach: "แนบไฟล์"
|
|
21
|
+
record_audio: "Record audio"
|
|
22
|
+
stop_recording: "Stop recording"
|
|
23
|
+
cancel_recording: "Cancel recording"
|
|
24
|
+
recording: "Recording %{time}"
|
|
25
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
26
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
27
|
+
microphone_error: "Could not start audio recording."
|
|
21
28
|
remove_file: "นำไฟล์ออก"
|
|
22
29
|
attachment_error: "ไม่สามารถส่งไฟล์บางไฟล์ได้"
|
|
23
30
|
expand: "ขยาย"
|
|
@@ -46,6 +53,13 @@ th:
|
|
|
46
53
|
reply_placeholder: "เขียนคำตอบของคุณ…"
|
|
47
54
|
send: "ส่ง"
|
|
48
55
|
attach: "แนบไฟล์"
|
|
56
|
+
record_audio: "Record audio"
|
|
57
|
+
stop_recording: "Stop recording"
|
|
58
|
+
cancel_recording: "Cancel recording"
|
|
59
|
+
recording: "Recording %{time}"
|
|
60
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
61
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
62
|
+
microphone_error: "Could not start audio recording."
|
|
49
63
|
newer: "ใหม่กว่า"
|
|
50
64
|
older: "เก่ากว่า"
|
|
51
65
|
mail:
|
|
@@ -18,6 +18,13 @@ tr:
|
|
|
18
18
|
error_rate_limited: "Çok fazla mesaj. Lütfen biraz bekleyip tekrar deneyin."
|
|
19
19
|
unread_aria: "okunmamış mesaj"
|
|
20
20
|
attach: "Dosya ekle"
|
|
21
|
+
record_audio: "Record audio"
|
|
22
|
+
stop_recording: "Stop recording"
|
|
23
|
+
cancel_recording: "Cancel recording"
|
|
24
|
+
recording: "Recording %{time}"
|
|
25
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
26
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
27
|
+
microphone_error: "Could not start audio recording."
|
|
21
28
|
remove_file: "Dosyayı kaldır"
|
|
22
29
|
attachment_error: "Bazı dosyalar gönderilemedi."
|
|
23
30
|
expand: "Büyüt"
|
|
@@ -46,6 +53,13 @@ tr:
|
|
|
46
53
|
reply_placeholder: "Yanıtınızı yazın…"
|
|
47
54
|
send: "Gönder"
|
|
48
55
|
attach: "Dosya ekle"
|
|
56
|
+
record_audio: "Record audio"
|
|
57
|
+
stop_recording: "Stop recording"
|
|
58
|
+
cancel_recording: "Cancel recording"
|
|
59
|
+
recording: "Recording %{time}"
|
|
60
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
61
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
62
|
+
microphone_error: "Could not start audio recording."
|
|
49
63
|
newer: "Daha yeni"
|
|
50
64
|
older: "Daha eski"
|
|
51
65
|
mail:
|
|
@@ -18,6 +18,13 @@ uk:
|
|
|
18
18
|
error_rate_limited: "Забагато повідомлень. Зачекайте трохи та спробуйте ще раз."
|
|
19
19
|
unread_aria: "непрочитані повідомлення"
|
|
20
20
|
attach: "Прикріпити файли"
|
|
21
|
+
record_audio: "Record audio"
|
|
22
|
+
stop_recording: "Stop recording"
|
|
23
|
+
cancel_recording: "Cancel recording"
|
|
24
|
+
recording: "Recording %{time}"
|
|
25
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
26
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
27
|
+
microphone_error: "Could not start audio recording."
|
|
21
28
|
remove_file: "Видалити файл"
|
|
22
29
|
attachment_error: "Деякі файли не вдалося надіслати."
|
|
23
30
|
expand: "Розгорнути"
|
|
@@ -46,6 +53,13 @@ uk:
|
|
|
46
53
|
reply_placeholder: "Напишіть відповідь…"
|
|
47
54
|
send: "Надіслати"
|
|
48
55
|
attach: "Прикріпити файли"
|
|
56
|
+
record_audio: "Record audio"
|
|
57
|
+
stop_recording: "Stop recording"
|
|
58
|
+
cancel_recording: "Cancel recording"
|
|
59
|
+
recording: "Recording %{time}"
|
|
60
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
61
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
62
|
+
microphone_error: "Could not start audio recording."
|
|
49
63
|
newer: "Новіші"
|
|
50
64
|
older: "Старіші"
|
|
51
65
|
mail:
|
|
@@ -18,6 +18,13 @@ ur:
|
|
|
18
18
|
error_rate_limited: "بہت زیادہ پیغامات۔ براہ کرم تھوڑا انتظار کریں اور دوبارہ کوشش کریں۔"
|
|
19
19
|
unread_aria: "غیر پڑھے گئے پیغامات"
|
|
20
20
|
attach: "فائلیں منسلک کریں"
|
|
21
|
+
record_audio: "Record audio"
|
|
22
|
+
stop_recording: "Stop recording"
|
|
23
|
+
cancel_recording: "Cancel recording"
|
|
24
|
+
recording: "Recording %{time}"
|
|
25
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
26
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
27
|
+
microphone_error: "Could not start audio recording."
|
|
21
28
|
remove_file: "فائل ہٹائیں"
|
|
22
29
|
attachment_error: "کچھ فائلیں نہیں بھیجی جا سکیں۔"
|
|
23
30
|
expand: "بڑا کریں"
|
|
@@ -46,6 +53,13 @@ ur:
|
|
|
46
53
|
reply_placeholder: "اپنا جواب لکھیں…"
|
|
47
54
|
send: "بھیجیں"
|
|
48
55
|
attach: "فائلیں منسلک کریں"
|
|
56
|
+
record_audio: "Record audio"
|
|
57
|
+
stop_recording: "Stop recording"
|
|
58
|
+
cancel_recording: "Cancel recording"
|
|
59
|
+
recording: "Recording %{time}"
|
|
60
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
61
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
62
|
+
microphone_error: "Could not start audio recording."
|
|
49
63
|
newer: "نئے"
|
|
50
64
|
older: "پرانے"
|
|
51
65
|
mail:
|
|
@@ -18,6 +18,13 @@ vi:
|
|
|
18
18
|
error_rate_limited: "Quá nhiều tin nhắn. Vui lòng đợi một lát rồi thử lại."
|
|
19
19
|
unread_aria: "tin nhắn chưa đọc"
|
|
20
20
|
attach: "Đính kèm tệp"
|
|
21
|
+
record_audio: "Record audio"
|
|
22
|
+
stop_recording: "Stop recording"
|
|
23
|
+
cancel_recording: "Cancel recording"
|
|
24
|
+
recording: "Recording %{time}"
|
|
25
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
26
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
27
|
+
microphone_error: "Could not start audio recording."
|
|
21
28
|
remove_file: "Xóa tệp"
|
|
22
29
|
attachment_error: "Không thể gửi một số tệp."
|
|
23
30
|
expand: "Mở rộng"
|
|
@@ -46,6 +53,13 @@ vi:
|
|
|
46
53
|
reply_placeholder: "Viết câu trả lời của bạn…"
|
|
47
54
|
send: "Gửi"
|
|
48
55
|
attach: "Đính kèm tệp"
|
|
56
|
+
record_audio: "Record audio"
|
|
57
|
+
stop_recording: "Stop recording"
|
|
58
|
+
cancel_recording: "Cancel recording"
|
|
59
|
+
recording: "Recording %{time}"
|
|
60
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
61
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
62
|
+
microphone_error: "Could not start audio recording."
|
|
49
63
|
newer: "Mới hơn"
|
|
50
64
|
older: "Cũ hơn"
|
|
51
65
|
mail:
|
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
error_rate_limited: "消息过多,请稍等片刻再重试。"
|
|
19
19
|
unread_aria: "条未读消息"
|
|
20
20
|
attach: "附加文件"
|
|
21
|
+
record_audio: "Record audio"
|
|
22
|
+
stop_recording: "Stop recording"
|
|
23
|
+
cancel_recording: "Cancel recording"
|
|
24
|
+
recording: "Recording %{time}"
|
|
25
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
26
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
27
|
+
microphone_error: "Could not start audio recording."
|
|
21
28
|
remove_file: "移除文件"
|
|
22
29
|
attachment_error: "部分文件无法发送。"
|
|
23
30
|
expand: "放大"
|
|
@@ -46,6 +53,13 @@
|
|
|
46
53
|
reply_placeholder: "输入您的回复…"
|
|
47
54
|
send: "发送"
|
|
48
55
|
attach: "附加文件"
|
|
56
|
+
record_audio: "Record audio"
|
|
57
|
+
stop_recording: "Stop recording"
|
|
58
|
+
cancel_recording: "Cancel recording"
|
|
59
|
+
recording: "Recording %{time}"
|
|
60
|
+
microphone_unsupported: "Audio recording is not supported in this browser."
|
|
61
|
+
microphone_denied: "Microphone access was blocked. Allow microphone access in your browser and try again."
|
|
62
|
+
microphone_error: "Could not start audio recording."
|
|
49
63
|
newer: "较新"
|
|
50
64
|
older: "较旧"
|
|
51
65
|
mail:
|
data/lib/livechat/dashboard.js
CHANGED
|
@@ -37,6 +37,15 @@
|
|
|
37
37
|
|
|
38
38
|
input.hidden = true;
|
|
39
39
|
var canEdit = typeof DataTransfer !== "undefined"; // per-file removal needs it
|
|
40
|
+
var labels = {
|
|
41
|
+
recordAudio: form.dataset.recordAudio || "Record audio",
|
|
42
|
+
stopRecording: form.dataset.stopRecording || "Stop recording",
|
|
43
|
+
cancelRecording: form.dataset.cancelRecording || "Cancel recording",
|
|
44
|
+
recording: form.dataset.recording || "Recording %{time}",
|
|
45
|
+
microphoneUnsupported: form.dataset.microphoneUnsupported || "Audio recording is not supported in this browser.",
|
|
46
|
+
microphoneDenied: form.dataset.microphoneDenied || "Microphone access was blocked. Allow microphone access in your browser and try again.",
|
|
47
|
+
microphoneError: form.dataset.microphoneError || "Could not start audio recording."
|
|
48
|
+
};
|
|
40
49
|
|
|
41
50
|
var button = document.createElement("button");
|
|
42
51
|
button.type = "button";
|
|
@@ -53,7 +62,23 @@
|
|
|
53
62
|
button.addEventListener("click", function () { input.click(); });
|
|
54
63
|
// The paperclip lives in the toolbar (left of send); chips sit at the top
|
|
55
64
|
// of the box — so it reads as one composer, like the visitor widget.
|
|
56
|
-
|
|
65
|
+
var tools = form.querySelector(".reply-tools") || form;
|
|
66
|
+
tools.appendChild(button);
|
|
67
|
+
|
|
68
|
+
var recordButton = null;
|
|
69
|
+
if (canEdit) {
|
|
70
|
+
recordButton = document.createElement("button");
|
|
71
|
+
recordButton.type = "button";
|
|
72
|
+
recordButton.className = "attach record";
|
|
73
|
+
recordButton.setAttribute("aria-label", labels.recordAudio);
|
|
74
|
+
recordButton.title = labels.recordAudio;
|
|
75
|
+
recordButton.innerHTML =
|
|
76
|
+
'<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">' +
|
|
77
|
+
'<path fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" ' +
|
|
78
|
+
'stroke-linejoin="round" d="M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3Z' +
|
|
79
|
+
'M5 11a7 7 0 0 0 14 0M12 18v3M8.5 21h7"/></svg>';
|
|
80
|
+
tools.appendChild(recordButton);
|
|
81
|
+
}
|
|
57
82
|
|
|
58
83
|
var chips = document.createElement("div");
|
|
59
84
|
chips.className = "chips";
|
|
@@ -61,6 +86,7 @@
|
|
|
61
86
|
form.insertBefore(chips, form.firstChild);
|
|
62
87
|
|
|
63
88
|
input.addEventListener("change", render);
|
|
89
|
+
if (recordButton) enhanceAudioRecorder();
|
|
64
90
|
|
|
65
91
|
function render() {
|
|
66
92
|
var files = Array.prototype.slice.call(input.files);
|
|
@@ -71,7 +97,7 @@
|
|
|
71
97
|
chip.className = "chip";
|
|
72
98
|
var name = document.createElement("span");
|
|
73
99
|
name.className = "chip-name";
|
|
74
|
-
name.textContent = file.name;
|
|
100
|
+
name.textContent = file.name || "voice-message.webm";
|
|
75
101
|
chip.appendChild(name);
|
|
76
102
|
if (canEdit) {
|
|
77
103
|
var remove = document.createElement("button");
|
|
@@ -94,6 +120,154 @@
|
|
|
94
120
|
input.files = kept.files;
|
|
95
121
|
render();
|
|
96
122
|
}
|
|
123
|
+
|
|
124
|
+
function enhanceAudioRecorder() {
|
|
125
|
+
var recorder = null;
|
|
126
|
+
var stream = null;
|
|
127
|
+
var chunks = [];
|
|
128
|
+
var startedAt = 0;
|
|
129
|
+
var timer = null;
|
|
130
|
+
var cancelled = false;
|
|
131
|
+
|
|
132
|
+
var bar = document.createElement("div");
|
|
133
|
+
bar.className = "recording";
|
|
134
|
+
bar.hidden = true;
|
|
135
|
+
var status = document.createElement("span");
|
|
136
|
+
status.className = "recording-status";
|
|
137
|
+
var dot = document.createElement("span");
|
|
138
|
+
dot.className = "recording-dot";
|
|
139
|
+
var time = document.createElement("span");
|
|
140
|
+
status.appendChild(dot);
|
|
141
|
+
status.appendChild(time);
|
|
142
|
+
bar.appendChild(status);
|
|
143
|
+
var actions = document.createElement("div");
|
|
144
|
+
actions.className = "recording-actions";
|
|
145
|
+
var cancel = document.createElement("button");
|
|
146
|
+
cancel.type = "button";
|
|
147
|
+
cancel.textContent = labels.cancelRecording;
|
|
148
|
+
var stop = document.createElement("button");
|
|
149
|
+
stop.type = "button";
|
|
150
|
+
stop.className = "stop-recording";
|
|
151
|
+
stop.textContent = labels.stopRecording;
|
|
152
|
+
actions.appendChild(cancel);
|
|
153
|
+
actions.appendChild(stop);
|
|
154
|
+
bar.appendChild(actions);
|
|
155
|
+
form.appendChild(bar);
|
|
156
|
+
|
|
157
|
+
recordButton.addEventListener("click", start);
|
|
158
|
+
stop.addEventListener("click", function () {
|
|
159
|
+
if (recorder && recorder.state !== "inactive") recorder.stop();
|
|
160
|
+
});
|
|
161
|
+
cancel.addEventListener("click", function () {
|
|
162
|
+
cancelled = true;
|
|
163
|
+
if (recorder && recorder.state !== "inactive") recorder.stop();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
function start() {
|
|
167
|
+
if (recorder) return;
|
|
168
|
+
if (!recordingSupported()) {
|
|
169
|
+
alert(labels.microphoneUnsupported);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
recordButton.disabled = true;
|
|
174
|
+
navigator.mediaDevices.getUserMedia({ audio: true })
|
|
175
|
+
.then(function (mediaStream) {
|
|
176
|
+
var options = preferredAudioOptions();
|
|
177
|
+
chunks = [];
|
|
178
|
+
cancelled = false;
|
|
179
|
+
stream = mediaStream;
|
|
180
|
+
try {
|
|
181
|
+
recorder = options ? new MediaRecorder(mediaStream, options) : new MediaRecorder(mediaStream);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
stopTracks();
|
|
184
|
+
throw e;
|
|
185
|
+
}
|
|
186
|
+
recorder.addEventListener("dataavailable", function (event) {
|
|
187
|
+
if (event.data && event.data.size) chunks.push(event.data);
|
|
188
|
+
});
|
|
189
|
+
recorder.addEventListener("stop", finish);
|
|
190
|
+
recorder.start();
|
|
191
|
+
startedAt = Date.now();
|
|
192
|
+
recordButton.classList.add("recording-on");
|
|
193
|
+
bar.hidden = false;
|
|
194
|
+
tick();
|
|
195
|
+
timer = setInterval(tick, 500);
|
|
196
|
+
})
|
|
197
|
+
.catch(function (error) {
|
|
198
|
+
recordButton.disabled = false;
|
|
199
|
+
var denied = error && (error.name === "NotAllowedError" || error.name === "SecurityError");
|
|
200
|
+
alert(denied ? labels.microphoneDenied : labels.microphoneError);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function finish() {
|
|
205
|
+
var mimeType = (recorder && recorder.mimeType) || "audio/webm";
|
|
206
|
+
stopTracks();
|
|
207
|
+
clearInterval(timer);
|
|
208
|
+
timer = null;
|
|
209
|
+
bar.hidden = true;
|
|
210
|
+
recordButton.disabled = false;
|
|
211
|
+
recordButton.classList.remove("recording-on");
|
|
212
|
+
|
|
213
|
+
if (!cancelled && chunks.length && typeof DataTransfer !== "undefined") {
|
|
214
|
+
var kept = new DataTransfer();
|
|
215
|
+
Array.prototype.slice.call(input.files).forEach(function (file) { kept.items.add(file); });
|
|
216
|
+
kept.items.add(audioFile(chunks, mimeType));
|
|
217
|
+
input.files = kept.files;
|
|
218
|
+
render();
|
|
219
|
+
}
|
|
220
|
+
recorder = null;
|
|
221
|
+
chunks = [];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function tick() {
|
|
225
|
+
time.textContent = labels.recording.replace("%{time}", formatDuration(Date.now() - startedAt));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function stopTracks() {
|
|
229
|
+
if (!stream) return;
|
|
230
|
+
stream.getTracks().forEach(function (track) { track.stop(); });
|
|
231
|
+
stream = null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function recordingSupported() {
|
|
237
|
+
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia && window.MediaRecorder);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function preferredAudioOptions() {
|
|
241
|
+
var types = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4", "audio/ogg;codecs=opus"];
|
|
242
|
+
if (!window.MediaRecorder || !MediaRecorder.isTypeSupported) return null;
|
|
243
|
+
for (var i = 0; i < types.length; i++) {
|
|
244
|
+
if (MediaRecorder.isTypeSupported(types[i])) return { mimeType: types[i] };
|
|
245
|
+
}
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function audioFile(chunks, mimeType) {
|
|
250
|
+
var blob = new Blob(chunks, { type: mimeType });
|
|
251
|
+
var name = "voice-message-" + new Date().toISOString().replace(/[:.]/g, "-") + "." + audioExtension(mimeType);
|
|
252
|
+
try {
|
|
253
|
+
return new File([blob], name, { type: mimeType });
|
|
254
|
+
} catch (e) {
|
|
255
|
+
blob.name = name;
|
|
256
|
+
return blob;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function audioExtension(mimeType) {
|
|
261
|
+
if (/mp4|mpeg|m4a/.test(mimeType)) return "m4a";
|
|
262
|
+
if (/ogg/.test(mimeType)) return "ogg";
|
|
263
|
+
return "webm";
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function formatDuration(ms) {
|
|
267
|
+
var seconds = Math.max(0, Math.floor(ms / 1000));
|
|
268
|
+
var minutes = Math.floor(seconds / 60);
|
|
269
|
+
seconds = seconds % 60;
|
|
270
|
+
return minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
|
|
97
271
|
}
|
|
98
272
|
|
|
99
273
|
// The conversation list keeps itself fresh: when the server's token moves
|
|
@@ -220,6 +394,15 @@
|
|
|
220
394
|
img.loading = "lazy";
|
|
221
395
|
img.addEventListener("load", keepBottom); // a live image grows the thread
|
|
222
396
|
link.appendChild(img);
|
|
397
|
+
} else if (att.audio) {
|
|
398
|
+
var audio = document.createElement("audio");
|
|
399
|
+
audio.className = "att-audio";
|
|
400
|
+
audio.controls = true;
|
|
401
|
+
audio.preload = "metadata";
|
|
402
|
+
audio.src = att.url;
|
|
403
|
+
audio.setAttribute("aria-label", att.name);
|
|
404
|
+
wrap.appendChild(audio);
|
|
405
|
+
return;
|
|
223
406
|
} else {
|
|
224
407
|
link.className = "att-file";
|
|
225
408
|
link.textContent = att.name;
|
data/lib/livechat/version.rb
CHANGED