Merge branch 'develop' of github.com:chatwoot/chatwoot into feature/cw-1605
This commit is contained in:
@@ -13,15 +13,19 @@ import videojs from 'video.js';
|
||||
import alertMixin from '../../../../shared/mixins/alertMixin';
|
||||
|
||||
import Recorder from 'opus-recorder';
|
||||
|
||||
// Workers to record Audio .ogg and .wav
|
||||
import encoderWorker from 'opus-recorder/dist/encoderWorker.min';
|
||||
import waveWorker from 'opus-recorder/dist/waveWorker.min';
|
||||
|
||||
import WaveSurfer from 'wavesurfer.js';
|
||||
import MicrophonePlugin from 'wavesurfer.js/dist/plugin/wavesurfer.microphone.js';
|
||||
import 'videojs-wavesurfer/dist/videojs.wavesurfer.js';
|
||||
|
||||
import 'videojs-wavesurfer/dist/videojs.wavesurfer.js';
|
||||
import 'videojs-record/dist/videojs.record.js';
|
||||
import 'videojs-record/dist/plugins/videojs.record.opus-recorder.js';
|
||||
|
||||
import OpusRecorderEngine from 'videojs-record/dist/plugins/videojs.record.opus-recorder.js';
|
||||
|
||||
import { format, addSeconds } from 'date-fns';
|
||||
import { AUDIO_FORMATS } from 'shared/constants/messages';
|
||||
|
||||
@@ -33,7 +37,7 @@ export default {
|
||||
props: {
|
||||
audioRecordFormat: {
|
||||
type: String,
|
||||
default: AUDIO_FORMATS.WEBM,
|
||||
default: AUDIO_FORMATS.WAV,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
@@ -79,24 +83,18 @@ export default {
|
||||
maxLength: 900,
|
||||
timeSlice: 1000,
|
||||
maxFileSize: 15 * 1024 * 1024,
|
||||
...(this.audioRecordFormat === AUDIO_FORMATS.WEBM && {
|
||||
monitorGain: 0,
|
||||
recordingGain: 1,
|
||||
numberOfChannels: 1,
|
||||
encoderSampleRate: 16000,
|
||||
originalSampleRateOverride: 16000,
|
||||
streamPages: true,
|
||||
maxFramesPerPage: 1,
|
||||
encoderFrameSize: 1,
|
||||
encoderPath: waveWorker,
|
||||
displayMilliseconds: false,
|
||||
audioChannels: 1,
|
||||
audioSampleRate: 48000,
|
||||
audioBitRate: 128,
|
||||
audioEngine: 'opus-recorder',
|
||||
...(this.audioRecordFormat === AUDIO_FORMATS.WAV && {
|
||||
audioMimeType: 'audio/wav',
|
||||
audioWorkerURL: waveWorker,
|
||||
}),
|
||||
...(this.audioRecordFormat === AUDIO_FORMATS.OGG && {
|
||||
displayMilliseconds: false,
|
||||
audioEngine: 'opus-recorder',
|
||||
audioMimeType: 'audio/ogg',
|
||||
audioWorkerURL: encoderWorker,
|
||||
audioChannels: 1,
|
||||
audioSampleRate: 48000,
|
||||
audioBitRate: 128,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -134,6 +132,11 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
deviceReady() {
|
||||
if (this.player.record().engine instanceof OpusRecorderEngine) {
|
||||
if (this.audioRecordFormat === AUDIO_FORMATS.WAV) {
|
||||
this.player.record().engine.audioType = 'audio/wav';
|
||||
}
|
||||
}
|
||||
this.player.record().start();
|
||||
},
|
||||
startRecord() {
|
||||
|
||||
@@ -501,10 +501,10 @@ export default {
|
||||
return `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
||||
},
|
||||
audioRecordFormat() {
|
||||
if (this.isAWebWidgetInbox) {
|
||||
return AUDIO_FORMATS.WEBM;
|
||||
if (this.isAWhatsAppChannel) {
|
||||
return AUDIO_FORMATS.OGG;
|
||||
}
|
||||
return AUDIO_FORMATS.OGG;
|
||||
return AUDIO_FORMATS.WAV;
|
||||
},
|
||||
messageVariables() {
|
||||
const variables = getMessageVariables({
|
||||
|
||||
@@ -404,6 +404,33 @@ describe('#actions', () => {
|
||||
expect(commit.mock.calls).toEqual([[types.CLEAR_CONVERSATION_FILTERS]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#updateConversationLastActivity', () => {
|
||||
it('sends correct action', async () => {
|
||||
await actions.updateConversationLastActivity(
|
||||
{ commit },
|
||||
{ conversationId: 1, lastActivityAt: 12121212 }
|
||||
);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[
|
||||
'UPDATE_CONVERSATION_LAST_ACTIVITY',
|
||||
{ conversationId: 1, lastActivityAt: 12121212 },
|
||||
],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setChatSortFilter', () => {
|
||||
it('sends correct action', async () => {
|
||||
await actions.setChatSortFilter(
|
||||
{ commit },
|
||||
{ data: 'sort_on_created_at' }
|
||||
);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
['CHANGE_CHAT_SORT_FILTER', { data: 'sort_on_created_at' }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#deleteMessage', () => {
|
||||
|
||||
@@ -6,43 +6,127 @@ commonHelpers();
|
||||
|
||||
describe('#getters', () => {
|
||||
describe('#getAllConversations', () => {
|
||||
it('order conversations based on last message date', () => {
|
||||
it('order conversations based on last activity', () => {
|
||||
const state = {
|
||||
allConversations: [
|
||||
{
|
||||
id: 1,
|
||||
messages: [
|
||||
{
|
||||
created_at: 1466424480,
|
||||
content: 'test1',
|
||||
},
|
||||
],
|
||||
created_at: 2466424490,
|
||||
last_activity_at: 2466424490,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
messages: [
|
||||
{
|
||||
created_at: 2466424490,
|
||||
},
|
||||
],
|
||||
messages: [{ content: 'test2' }],
|
||||
created_at: 1466424480,
|
||||
last_activity_at: 1466424480,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getters.getAllConversations(state)).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
messages: [
|
||||
{
|
||||
content: 'test1',
|
||||
},
|
||||
],
|
||||
created_at: 2466424490,
|
||||
last_activity_at: 2466424490,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
messages: [{ content: 'test2' }],
|
||||
created_at: 1466424480,
|
||||
last_activity_at: 1466424480,
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('order conversations based on created at', () => {
|
||||
const state = {
|
||||
allConversations: [
|
||||
{
|
||||
id: 1,
|
||||
messages: [
|
||||
{
|
||||
content: 'test1',
|
||||
},
|
||||
],
|
||||
created_at: 1683645801, // Tuesday, 9 May 2023
|
||||
last_activity_at: 2466424490,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
messages: [{ content: 'test2' }],
|
||||
created_at: 1652109801, // Monday, 9 May 2022
|
||||
last_activity_at: 1466424480,
|
||||
},
|
||||
],
|
||||
chatSortFilter: 'sort_on_created_at',
|
||||
};
|
||||
|
||||
expect(getters.getAllConversations(state)).toEqual([
|
||||
{
|
||||
id: 2,
|
||||
messages: [
|
||||
{
|
||||
created_at: 2466424490,
|
||||
},
|
||||
],
|
||||
messages: [{ content: 'test2' }],
|
||||
created_at: 1652109801,
|
||||
last_activity_at: 1466424480,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
messages: [
|
||||
{
|
||||
created_at: 1466424480,
|
||||
content: 'test1',
|
||||
},
|
||||
],
|
||||
created_at: 1683645801,
|
||||
last_activity_at: 2466424490,
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('order conversations based on default order', () => {
|
||||
const state = {
|
||||
allConversations: [
|
||||
{
|
||||
id: 1,
|
||||
messages: [
|
||||
{
|
||||
content: 'test1',
|
||||
},
|
||||
],
|
||||
created_at: 2466424490,
|
||||
last_activity_at: 2466424490,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
messages: [{ content: 'test2' }],
|
||||
created_at: 1466424480,
|
||||
last_activity_at: 1466424480,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getters.getAllConversations(state)).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
messages: [
|
||||
{
|
||||
content: 'test1',
|
||||
},
|
||||
],
|
||||
created_at: 2466424490,
|
||||
last_activity_at: 2466424490,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
messages: [{ content: 'test2' }],
|
||||
created_at: 1466424480,
|
||||
last_activity_at: 1466424480,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -39,6 +39,19 @@ describe('#mutations', () => {
|
||||
|
||||
describe('#ASSIGN_TEAM', () => {
|
||||
it('clears current chat window', () => {
|
||||
const state = { allConversations: [{ id: 1, meta: {} }] };
|
||||
mutations[types.UPDATE_CONVERSATION_LAST_ACTIVITY](state, {
|
||||
lastActivityAt: 1602256198,
|
||||
conversationId: 1,
|
||||
});
|
||||
|
||||
expect(state.allConversations).toEqual([
|
||||
{ id: 1, meta: {}, last_activity_at: 1602256198 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('#UPDATE_CONVERSATION_LAST_ACTIVITY', () => {
|
||||
it('update conversation last activity', () => {
|
||||
const state = { allConversations: [{ id: 1, meta: {} }] };
|
||||
mutations[types.ASSIGN_TEAM](state, {
|
||||
team: { id: 1, name: 'Team 1' },
|
||||
@@ -50,6 +63,16 @@ describe('#mutations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#CHANGE_CHAT_SORT_FILTER', () => {
|
||||
it('update conversation sort filter', () => {
|
||||
const state = { chatSortFilter: 'latest' };
|
||||
mutations[types.CHANGE_CHAT_SORT_FILTER](state, {
|
||||
data: 'sort_on_created_at',
|
||||
});
|
||||
expect(state.chatSortFilter).toEqual({ data: 'sort_on_created_at' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('#SET_CURRENT_CHAT_WINDOW', () => {
|
||||
it('set current chat window', () => {
|
||||
const state = { selectedChatId: 1 };
|
||||
|
||||
@@ -84,6 +84,7 @@ export const CSAT_RATINGS = [
|
||||
export const AUDIO_FORMATS = {
|
||||
WEBM: 'audio/webm',
|
||||
OGG: 'audio/ogg',
|
||||
WAV: 'audio/wav',
|
||||
};
|
||||
|
||||
export const MESSAGE_VARIABLES = [
|
||||
|
||||
@@ -195,6 +195,10 @@ class Conversation < ApplicationRecord
|
||||
messages.chat.last(5)
|
||||
end
|
||||
|
||||
def csat_survey_link
|
||||
"#{ENV.fetch('FRONTEND_URL', nil)}/survey/responses/#{uuid}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def execute_after_update_commit_callbacks
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
# index_messages_on_additional_attributes_campaign_id (((additional_attributes -> 'campaign_id'::text))) USING gin
|
||||
# index_messages_on_content (content) USING gin
|
||||
# index_messages_on_conversation_id (conversation_id)
|
||||
# index_messages_on_created_at (created_at)
|
||||
# index_messages_on_inbox_id (inbox_id)
|
||||
# index_messages_on_sender_type_and_sender_id (sender_type,sender_id)
|
||||
# index_messages_on_source_id (source_id)
|
||||
|
||||
@@ -5,18 +5,26 @@
|
||||
<% @messages.each do |message| %>
|
||||
<tr>
|
||||
<td>
|
||||
<b><%= message.incoming? ? 'You' : message.sender&.available_name || message.sender&.name %></b>
|
||||
<b><%= message.incoming? ? 'You' : message.sender&.available_name || message.sender&.name || 'Bot' %></b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 10px 20px; margin: 5px 0 20px 0; background: #F2F3F7; border-radius: 10px; display: inline-block; font-family: 'Helvetica Neue',Tahoma,Arial,sans-serif; text-align: start; unicode-bidi: plaintext;">
|
||||
<% if message.content %>
|
||||
<td style="padding: 0px 16px; margin: 4px 0 8px 0; background: #F5FAFF; border-radius: 5px; display: inline-block; font-family: 'Helvetica Neue',Tahoma,Arial,sans-serif; text-align: start; unicode-bidi: plaintext;">
|
||||
<% if (message.content_type == 'input_csat' && message.message_type == 'template') %>
|
||||
<p>Click <a href="<%= message.conversation.csat_survey_link %>" _target="blank">here</a> to rate the conversation.</p>
|
||||
<% elsif message.content.present? %>
|
||||
<%= CommonMarker.render_html(message.content).html_safe %>
|
||||
<% end %>
|
||||
<% if message.attachments %>
|
||||
<% message.attachments.each do |attachment| %>
|
||||
Attachment [<a href="<%= attachment.file_url %>" _target="blank">Click here to view</a>]
|
||||
<% end %>
|
||||
<% if message.attachments.count.positive? %>
|
||||
<p>
|
||||
<% if message.content.present? %>
|
||||
<hr style="border: 0; border-bottom: 1px solid #AEC3D5;"/>
|
||||
<% end %>
|
||||
This message contains <%= message.attachments.count > 1 ? 'attachments' : 'an attachment' %>.
|
||||
<% message.attachments.each do |attachment| %>
|
||||
<br />- View the attachment <a href="<%= attachment.file_url %>" _target="blank">here</a>.
|
||||
<% end %>
|
||||
</p>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
class AddIndexToMessagesCreatedAt < ActiveRecord::Migration[7.0]
|
||||
disable_ddl_transaction!
|
||||
def change
|
||||
add_index :messages, [:created_at], name: 'index_messages_on_created_at', algorithm: :concurrently
|
||||
end
|
||||
end
|
||||
+2
-1
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_04_26_130150) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_05_09_101256) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
enable_extension "pg_trgm"
|
||||
@@ -667,6 +667,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_04_26_130150) do
|
||||
t.index ["account_id"], name: "index_messages_on_account_id"
|
||||
t.index ["content"], name: "index_messages_on_content", opclass: :gin_trgm_ops, using: :gin
|
||||
t.index ["conversation_id"], name: "index_messages_on_conversation_id"
|
||||
t.index ["created_at"], name: "index_messages_on_created_at"
|
||||
t.index ["inbox_id"], name: "index_messages_on_inbox_id"
|
||||
t.index ["sender_type", "sender_id"], name: "index_messages_on_sender_type_and_sender_id"
|
||||
t.index ["source_id"], name: "index_messages_on_source_id"
|
||||
|
||||
Reference in New Issue
Block a user