Merge branch 'develop' into feat/reporting-events-rollup
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<script setup>
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
defineProps({
|
||||
@@ -28,7 +29,11 @@ const handleButtonClick = () => {
|
||||
{{ headerTitle }}
|
||||
</span>
|
||||
<div
|
||||
v-on-clickaway="() => emit('close')"
|
||||
v-on-click-outside="[
|
||||
() => emit('close'),
|
||||
// This will prevent closing the modal when the editor Create link popup is open
|
||||
{ ignore: ['dialog.ProseMirror-prompt-backdrop'] },
|
||||
]"
|
||||
class="relative group/campaign-button"
|
||||
>
|
||||
<Button
|
||||
|
||||
@@ -277,7 +277,7 @@ useKeyboardEvents(keyboardEvents);
|
||||
handleClickOutside,
|
||||
// Fixed and edge case https://github.com/chatwoot/chatwoot/issues/10785
|
||||
// This will prevent closing the compose conversation modal when the editor Create link popup is open
|
||||
{ ignore: ['div.ProseMirror-prompt'] },
|
||||
{ ignore: ['dialog.ProseMirror-prompt-backdrop'] },
|
||||
]"
|
||||
class="relative"
|
||||
:class="{
|
||||
|
||||
@@ -96,6 +96,17 @@ const close = () => {
|
||||
isOpen.value = false;
|
||||
};
|
||||
|
||||
// Only close if the close event originated from this dialog,
|
||||
// not from a child dialog (e.g. ProseMirror prompt) bubbling up.
|
||||
const handleDialogClose = e => e.target === dialogRef.value && close();
|
||||
|
||||
// Only close on click-outside if this dialog is the topmost one.
|
||||
// If another dialog (e.g. ProseMirror prompt) is open on top, ignore.
|
||||
const handleClickOutside = () => {
|
||||
const dialogs = document.querySelectorAll('dialog[open]');
|
||||
if (dialogs[dialogs.length - 1] === dialogRef.value) close();
|
||||
};
|
||||
|
||||
const confirm = () => {
|
||||
emit('confirm');
|
||||
};
|
||||
@@ -113,9 +124,9 @@ defineExpose({ open, close });
|
||||
positionClass,
|
||||
overflowYAuto ? 'overflow-y-auto' : 'overflow-visible',
|
||||
]"
|
||||
@close="close"
|
||||
@close.prevent="handleDialogClose"
|
||||
>
|
||||
<OnClickOutside @trigger="close">
|
||||
<OnClickOutside @trigger="handleClickOutside">
|
||||
<form
|
||||
ref="dialogContentRef"
|
||||
class="flex flex-col w-full h-auto gap-6 p-6 overflow-visible text-start align-middle transition-all duration-300 ease-in-out transform bg-n-alpha-3 backdrop-blur-[100px] shadow-xl rounded-xl"
|
||||
|
||||
@@ -978,11 +978,15 @@ useEmitter(BUS_EVENTS.INSERT_INTO_RICH_EDITOR, insertContentIntoEditor);
|
||||
@apply overflow-auto min-h-[5rem] max-h-[7.5rem];
|
||||
}
|
||||
|
||||
.ProseMirror-prompt-backdrop::backdrop {
|
||||
@apply bg-n-alpha-black1 backdrop-blur-[4px];
|
||||
}
|
||||
|
||||
.ProseMirror-prompt {
|
||||
@apply z-[9999] bg-n-alpha-3 backdrop-blur-[100px] border border-n-strong p-6 shadow-xl rounded-xl;
|
||||
@apply bg-n-alpha-3 border border-n-strong p-6 shadow-xl rounded-xl w-96 !important;
|
||||
|
||||
h5 {
|
||||
@apply text-n-slate-12 mb-1.5;
|
||||
@apply text-n-slate-12 mb-3;
|
||||
}
|
||||
|
||||
.ProseMirror-prompt-buttons {
|
||||
|
||||
@@ -326,26 +326,4 @@ export default {
|
||||
max-height: 7.5rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.ProseMirror-prompt {
|
||||
@apply z-[9999] bg-n-alpha-3 min-w-80 backdrop-blur-[100px] border border-n-strong p-6 shadow-xl rounded-xl;
|
||||
|
||||
h5 {
|
||||
@apply text-n-slate-12 mb-1.5;
|
||||
}
|
||||
|
||||
.ProseMirror-prompt-buttons {
|
||||
button {
|
||||
@apply h-8 px-3;
|
||||
|
||||
&[type='submit'] {
|
||||
@apply bg-n-brand text-white hover:bg-n-brand/90;
|
||||
}
|
||||
|
||||
&[type='button'] {
|
||||
@apply bg-n-slate-9/10 text-n-slate-12 hover:bg-n-slate-9/20;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,7 +6,12 @@ const RECONNECT_INTERVAL = 1000;
|
||||
class BaseActionCableConnector {
|
||||
static isDisconnected = false;
|
||||
|
||||
constructor(app, pubsubToken, websocketHost = '') {
|
||||
constructor(
|
||||
app,
|
||||
pubsubToken,
|
||||
websocketHost = '',
|
||||
presenceInterval = PRESENCE_INTERVAL
|
||||
) {
|
||||
const websocketURL = websocketHost ? `${websocketHost}/cable` : undefined;
|
||||
|
||||
this.consumer = createConsumer(websocketURL);
|
||||
@@ -37,7 +42,7 @@ class BaseActionCableConnector {
|
||||
setTimeout(() => {
|
||||
this.subscription.updatePresence();
|
||||
this.triggerPresenceInterval();
|
||||
}, PRESENCE_INTERVAL);
|
||||
}, presenceInterval);
|
||||
};
|
||||
this.triggerPresenceInterval();
|
||||
}
|
||||
|
||||
@@ -13,9 +13,11 @@ const isMessageInActiveConversation = (getters, message) => {
|
||||
return activeConversationId && conversationId !== activeConversationId;
|
||||
};
|
||||
|
||||
const WIDGET_PRESENCE_INTERVAL = 60000;
|
||||
|
||||
class ActionCableConnector extends BaseActionCableConnector {
|
||||
constructor(app, pubsubToken) {
|
||||
super(app, pubsubToken);
|
||||
super(app, pubsubToken, '', WIDGET_PRESENCE_INTERVAL);
|
||||
this.events = {
|
||||
'message.created': this.onMessageCreated,
|
||||
'message.updated': this.onMessageUpdated,
|
||||
|
||||
@@ -3,7 +3,7 @@ class Internal::RemoveStaleRedisKeysService
|
||||
|
||||
def perform
|
||||
Rails.logger.info "Removing redis stale keys for account #{@account_id}"
|
||||
range_start = (Time.zone.now - OnlineStatusTracker::PRESENCE_DURATION).to_i
|
||||
range_start = (Time.zone.now - OnlineStatusTracker::CONTACT_PRESENCE_DURATION).to_i
|
||||
# exclusive minimum score is specified by prefixing (
|
||||
# we are clearing old records because this could clogg up the sorted set
|
||||
::Redis::Alfred.zremrangebyscore(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
class OnlineStatusTracker
|
||||
# NOTE: You can customise the environment variable to keep your agents/contacts as online for longer
|
||||
PRESENCE_DURATION = ENV.fetch('PRESENCE_DURATION', 20).to_i.seconds
|
||||
# Widget pings every 60s, so contacts need a longer presence window
|
||||
CONTACT_PRESENCE_DURATION = ENV.fetch('CONTACT_PRESENCE_DURATION', 90).to_i.seconds
|
||||
|
||||
# presence : sorted set with timestamp as the score & object id as value
|
||||
|
||||
@@ -11,7 +13,8 @@ class OnlineStatusTracker
|
||||
|
||||
def self.get_presence(account_id, obj_type, obj_id)
|
||||
connected_time = ::Redis::Alfred.zscore(presence_key(account_id, obj_type), obj_id)
|
||||
connected_time && connected_time > (Time.zone.now - PRESENCE_DURATION).to_i
|
||||
duration = obj_type == 'Contact' ? CONTACT_PRESENCE_DURATION : PRESENCE_DURATION
|
||||
connected_time && connected_time > (Time.zone.now - duration).to_i
|
||||
end
|
||||
|
||||
def self.presence_key(account_id, type)
|
||||
@@ -39,7 +42,7 @@ class OnlineStatusTracker
|
||||
end
|
||||
|
||||
def self.get_available_contact_ids(account_id)
|
||||
range_start = (Time.zone.now - PRESENCE_DURATION).to_i
|
||||
range_start = (Time.zone.now - CONTACT_PRESENCE_DURATION).to_i
|
||||
# exclusive minimum score is specified by prefixing (
|
||||
# we are clearing old records because this could clogg up the sorted set
|
||||
::Redis::Alfred.zremrangebyscore(presence_key(account_id, 'Contact'), '-inf', "(#{range_start}")
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@
|
||||
"@amplitude/analytics-browser": "^2.11.10",
|
||||
"@breezystack/lamejs": "^1.2.7",
|
||||
"@chatwoot/ninja-keys": "1.2.3",
|
||||
"@chatwoot/prosemirror-schema": "1.3.6",
|
||||
"@chatwoot/prosemirror-schema": "1.3.7",
|
||||
"@chatwoot/utils": "^0.0.52",
|
||||
"@formkit/core": "^1.6.7",
|
||||
"@formkit/vue": "^1.6.7",
|
||||
|
||||
Generated
+5
-5
@@ -23,8 +23,8 @@ importers:
|
||||
specifier: 1.2.3
|
||||
version: 1.2.3
|
||||
'@chatwoot/prosemirror-schema':
|
||||
specifier: 1.3.6
|
||||
version: 1.3.6
|
||||
specifier: 1.3.7
|
||||
version: 1.3.7
|
||||
'@chatwoot/utils':
|
||||
specifier: ^0.0.52
|
||||
version: 0.0.52
|
||||
@@ -454,8 +454,8 @@ packages:
|
||||
'@chatwoot/ninja-keys@1.2.3':
|
||||
resolution: {integrity: sha512-xM8d9P5ikDMZm2WbaCTk/TW5HFauylrU3cJ75fq5je6ixKwyhl/0kZbVN/vbbZN4+AUX/OaSIn6IJbtCgIF67g==}
|
||||
|
||||
'@chatwoot/prosemirror-schema@1.3.6':
|
||||
resolution: {integrity: sha512-sHRtWqbtiow9mVF1ixim0eGUXfhGK5tuLOdF9Vf53aepjJ+ngEiNVkxQT6FohlEOd886ZsdQxMvmI92IDaUXAQ==}
|
||||
'@chatwoot/prosemirror-schema@1.3.7':
|
||||
resolution: {integrity: sha512-N+Gicecp18TSEJQoRtGZXkp8R+kC0iPSms8ezu1k8U+ySY9FAENzFQQ1rBVSSC4hDFwb9/EbSI9IFqDjHGds7g==}
|
||||
|
||||
'@chatwoot/utils@0.0.52':
|
||||
resolution: {integrity: sha512-e57uVqyVW4tj1gql4YJPNMykqMJPkETn5Y9AmHdhc6Y7oxDXfRXBq27fZrrDadLkZdn5RYVCZjfIhXOumyYv2Q==}
|
||||
@@ -4999,7 +4999,7 @@ snapshots:
|
||||
hotkeys-js: 3.8.7
|
||||
lit: 2.2.6
|
||||
|
||||
'@chatwoot/prosemirror-schema@1.3.6':
|
||||
'@chatwoot/prosemirror-schema@1.3.7':
|
||||
dependencies:
|
||||
markdown-it-sup: 2.0.0
|
||||
prosemirror-commands: 1.6.0
|
||||
|
||||
@@ -42,7 +42,7 @@ describe OnlineStatusTracker do
|
||||
described_class.update_presence(account.id, 'Contact', online_contact.id)
|
||||
# creating a stale record for offline contact presence
|
||||
Redis::Alfred.zadd(format(Redis::Alfred::ONLINE_PRESENCE_CONTACTS, account_id: account.id),
|
||||
(Time.zone.now - (OnlineStatusTracker::PRESENCE_DURATION + 20)).to_i, offline_contact.id)
|
||||
(Time.zone.now - (OnlineStatusTracker::CONTACT_PRESENCE_DURATION + 20)).to_i, offline_contact.id)
|
||||
end
|
||||
|
||||
it 'returns only the online contact ids with presence' do
|
||||
|
||||
Reference in New Issue
Block a user