Merge branch 'develop' into fix/next-message-bubbles
This commit is contained in:
@@ -19,7 +19,7 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- node/install:
|
||||
node-version: '20.12'
|
||||
node-version: '23.7'
|
||||
- node/install-pnpm
|
||||
- node/install-packages:
|
||||
pkg-manager: pnpm
|
||||
|
||||
@@ -5,30 +5,30 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
base:
|
||||
base:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: .devcontainer/Dockerfile.base
|
||||
args:
|
||||
VARIANT: "ubuntu-22.04"
|
||||
NODE_VERSION: "20.9.0"
|
||||
RUBY_VERSION: "3.3.3"
|
||||
VARIANT: 'ubuntu-22.04'
|
||||
NODE_VERSION: '23.7.0'
|
||||
RUBY_VERSION: '3.3.3'
|
||||
# On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000.
|
||||
USER_UID: "1000"
|
||||
USER_GID: "1000"
|
||||
USER_UID: '1000'
|
||||
USER_GID: '1000'
|
||||
image: base:latest
|
||||
|
||||
|
||||
app:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: .devcontainer/Dockerfile
|
||||
args:
|
||||
VARIANT: "ubuntu-22.04"
|
||||
NODE_VERSION: "20.9.0"
|
||||
RUBY_VERSION: "3.3.3"
|
||||
VARIANT: 'ubuntu-22.04'
|
||||
NODE_VERSION: '23.7.0'
|
||||
RUBY_VERSION: '3.3.3'
|
||||
# On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000.
|
||||
USER_UID: "1000"
|
||||
USER_GID: "1000"
|
||||
USER_UID: '1000'
|
||||
USER_GID: '1000'
|
||||
|
||||
volumes:
|
||||
- ..:/workspace:cached
|
||||
|
||||
@@ -23,12 +23,10 @@ jobs:
|
||||
bundler-cache: true
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.3.0
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 23
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install pnpm dependencies
|
||||
|
||||
@@ -38,7 +38,6 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
|
||||
@@ -48,7 +47,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 23
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install pnpm dependencies
|
||||
|
||||
@@ -19,13 +19,11 @@ jobs:
|
||||
with:
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 9.3.0
|
||||
- uses: pnpm/action-setup@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 23
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: pnpm
|
||||
@@ -39,7 +37,7 @@ jobs:
|
||||
- name: setup env
|
||||
run: |
|
||||
cp .env.example .env
|
||||
|
||||
|
||||
- name: Run asset compile
|
||||
run: bundle exec rake assets:precompile
|
||||
env:
|
||||
@@ -47,5 +45,3 @@ jobs:
|
||||
|
||||
- name: Size Check
|
||||
run: pnpm run size
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
3.1.0
|
||||
3.2.0
|
||||
|
||||
@@ -2,7 +2,7 @@ class Api::V1::Accounts::Contacts::ConversationsController < Api::V1::Accounts::
|
||||
def index
|
||||
@conversations = Current.account.conversations.includes(
|
||||
:assignee, :contact, :inbox, :taggings
|
||||
).where(inbox_id: inbox_ids, contact_id: @contact.id).order(id: :desc).limit(20)
|
||||
).where(inbox_id: inbox_ids, contact_id: @contact.id).order(last_activity_at: :desc).limit(20)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
+3
-2
@@ -77,7 +77,7 @@ const toggleMessageSignature = () => {
|
||||
setSignature();
|
||||
};
|
||||
|
||||
// Added this watch to dynamically set signature.
|
||||
// Added this watch to dynamically set signature on target inbox change.
|
||||
// Only targetInbox has value and is Advance Editor(used by isEmailOrWebWidgetInbox)
|
||||
// Set the signature only if the inbox based flag is true
|
||||
watch(
|
||||
@@ -86,7 +86,8 @@ watch(
|
||||
nextTick(() => {
|
||||
if (newValue && props.isEmailOrWebWidgetInbox) setSignature();
|
||||
});
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const onClickInsertEmoji = emoji => {
|
||||
|
||||
@@ -15,18 +15,14 @@ import { useCamelCase } from 'dashboard/composables/useTransformKeys';
|
||||
* @property {Array} messages - Array of all messages [These are not in camelcase]
|
||||
*/
|
||||
const props = defineProps({
|
||||
readMessages: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
unReadMessages: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
currentUserId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
firstUnreadId: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
isAnEmailChannel: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@@ -41,12 +37,8 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const unread = computed(() => {
|
||||
return useCamelCase(props.unReadMessages, { deep: true });
|
||||
});
|
||||
|
||||
const read = computed(() => {
|
||||
return useCamelCase(props.readMessages, { deep: true });
|
||||
const allMessages = computed(() => {
|
||||
return useCamelCase(props.messages, { deep: true });
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -123,31 +115,22 @@ const getMessageSpacingClass = (message, messages, index) => {
|
||||
<template>
|
||||
<ul class="px-4 bg-n-background">
|
||||
<slot name="beforeAll" />
|
||||
<template v-for="(message, index) in read" :key="message.id">
|
||||
<template v-for="(message, index) in allMessages" :key="message.id">
|
||||
<slot
|
||||
v-if="firstUnreadId && message.id === firstUnreadId"
|
||||
name="unreadBadge"
|
||||
/>
|
||||
<Message
|
||||
v-bind="message"
|
||||
:is-email-inbox="isAnEmailChannel"
|
||||
:in-reply-to="getInReplyToMessage(message)"
|
||||
:group-with-next="shouldGroupWithNext(index, read)"
|
||||
:group-with-next="shouldGroupWithNext(index, allMessages)"
|
||||
:inbox-supports-reply-to="inboxSupportsReplyTo"
|
||||
:current-user-id="currentUserId"
|
||||
:class="getMessageSpacingClass(message, read, index)"
|
||||
data-clarity-mask="True"
|
||||
/>
|
||||
</template>
|
||||
<slot name="beforeUnread" />
|
||||
<template v-for="(message, index) in unread" :key="message.id">
|
||||
<Message
|
||||
v-bind="message"
|
||||
:in-reply-to="getInReplyToMessage(message)"
|
||||
:group-with-next="shouldGroupWithNext(index, unread)"
|
||||
:inbox-supports-reply-to="inboxSupportsReplyTo"
|
||||
:current-user-id="currentUserId"
|
||||
:is-email-inbox="isAnEmailChannel"
|
||||
:class="getMessageSpacingClass(message, unread, index)"
|
||||
data-clarity-mask="True"
|
||||
/>
|
||||
</template>
|
||||
<slot name="after" />
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
@@ -26,7 +26,18 @@ const ccEmail = computed(() => {
|
||||
});
|
||||
|
||||
const senderName = computed(() => {
|
||||
return sender.value.name ?? '';
|
||||
const fromEmailAddress = fromEmail.value[0] ?? '';
|
||||
const senderEmail = sender.value.email ?? '';
|
||||
|
||||
if (!fromEmailAddress && !senderEmail) return null;
|
||||
|
||||
// if the sender of the conversation and the sender of this particular
|
||||
// email are the same, only then we return the sender name
|
||||
if (fromEmailAddress === senderEmail) {
|
||||
return sender.value.name;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
const bccEmail = computed(() => {
|
||||
@@ -59,11 +70,19 @@ const showMeta = computed(() => {
|
||||
:class="hasError ? 'text-n-ruby-11' : 'text-n-slate-11'"
|
||||
>
|
||||
<template v-if="showMeta">
|
||||
<div v-if="fromEmail[0]">
|
||||
<span :class="hasError ? 'text-n-ruby-11' : 'text-n-slate-12'">
|
||||
{{ senderName }}
|
||||
</span>
|
||||
<{{ fromEmail[0] }}>
|
||||
<div
|
||||
v-if="fromEmail[0]"
|
||||
:class="hasError ? 'text-n-ruby-11' : 'text-n-slate-12'"
|
||||
>
|
||||
<template v-if="senderName">
|
||||
<span>
|
||||
{{ senderName }}
|
||||
</span>
|
||||
<{{ fromEmail[0] }}>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ fromEmail[0] }}
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="toEmail.length">
|
||||
{{ $t('EMAIL_HEADER.TO') }}: {{ toEmail.join(', ') }}
|
||||
|
||||
@@ -44,9 +44,10 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
const formatTime = time => {
|
||||
if (!time || Number.isNaN(time)) return '00:00';
|
||||
const minutes = Math.floor(time / 60);
|
||||
const seconds = Math.floor(time % 60);
|
||||
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const toggleMute = () => {
|
||||
@@ -55,7 +56,7 @@ const toggleMute = () => {
|
||||
};
|
||||
|
||||
const onTimeUpdate = () => {
|
||||
currentTime.value = audioPlayer.value.currentTime;
|
||||
currentTime.value = audioPlayer.value?.currentTime;
|
||||
};
|
||||
|
||||
const seek = event => {
|
||||
|
||||
@@ -243,6 +243,15 @@ export default {
|
||||
unreadMessageCount() {
|
||||
return this.currentChat.unread_count || 0;
|
||||
},
|
||||
unreadMessageLabel() {
|
||||
const count =
|
||||
this.unreadMessageCount > 9 ? '9+' : this.unreadMessageCount;
|
||||
const label =
|
||||
this.unreadMessageCount > 1
|
||||
? 'CONVERSATION.UNREAD_MESSAGES'
|
||||
: 'CONVERSATION.UNREAD_MESSAGE';
|
||||
return `${count} ${this.$t(label)}`;
|
||||
},
|
||||
isInstagramDM() {
|
||||
return this.conversationType === 'instagram_direct_message';
|
||||
},
|
||||
@@ -492,12 +501,11 @@ export default {
|
||||
<NextMessageList
|
||||
v-if="showNextBubbles"
|
||||
class="conversation-panel"
|
||||
:read-messages="readMessages"
|
||||
:un-read-messages="unReadMessages"
|
||||
:current-user-id="currentUserId"
|
||||
:first-unread-id="unReadMessages[0]?.id"
|
||||
:is-an-email-channel="isAnEmailChannel"
|
||||
:inbox-supports-reply-to="inboxSupportsReplyTo"
|
||||
:messages="currentChat ? currentChat.messages : []"
|
||||
:messages="getMessages"
|
||||
>
|
||||
<template #beforeAll>
|
||||
<transition name="slide-up">
|
||||
@@ -507,15 +515,10 @@ export default {
|
||||
</li>
|
||||
</transition>
|
||||
</template>
|
||||
<template #beforeUnread>
|
||||
<template #unreadBadge>
|
||||
<li v-show="unreadMessageCount != 0" class="unread--toast">
|
||||
<span>
|
||||
{{ unreadMessageCount > 9 ? '9+' : unreadMessageCount }}
|
||||
{{
|
||||
unreadMessageCount > 1
|
||||
? $t('CONVERSATION.UNREAD_MESSAGES')
|
||||
: $t('CONVERSATION.UNREAD_MESSAGE')
|
||||
}}
|
||||
{{ unreadMessageLabel }}
|
||||
</span>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
@@ -99,6 +99,9 @@
|
||||
},
|
||||
"fallback": {
|
||||
"CONTENT": "has shared a url"
|
||||
},
|
||||
"contact": {
|
||||
"CONTENT": "Shared contact"
|
||||
}
|
||||
},
|
||||
"CHAT_SORT_BY_FILTER": {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"READ_MORE": "Read more",
|
||||
"WROTE": "wrote:",
|
||||
"FROM": "from",
|
||||
"EMAIL": "email"
|
||||
"EMAIL": "email",
|
||||
"EMAIL_SUBJECT": "subject"
|
||||
}
|
||||
}
|
||||
|
||||
+32
-14
@@ -35,6 +35,10 @@ const props = defineProps({
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
emailSubject: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const navigateTo = computed(() => {
|
||||
@@ -49,6 +53,28 @@ const navigateTo = computed(() => {
|
||||
});
|
||||
|
||||
const createdAtTime = dynamicTime(props.createdAt);
|
||||
|
||||
const infoItems = computed(() => [
|
||||
{
|
||||
label: 'SEARCH.FROM',
|
||||
value: props.name,
|
||||
show: !!props.name,
|
||||
},
|
||||
{
|
||||
label: 'SEARCH.EMAIL',
|
||||
value: props.email,
|
||||
show: !!props.email,
|
||||
},
|
||||
{
|
||||
label: 'SEARCH.EMAIL_SUBJECT',
|
||||
value: props.emailSubject,
|
||||
show: !!props.emailSubject,
|
||||
},
|
||||
]);
|
||||
|
||||
const visibleInfoItems = computed(() =>
|
||||
infoItems.value.filter(item => item.show)
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -86,26 +112,18 @@ const createdAtTime = dynamicTime(props.createdAt);
|
||||
{{ createdAtTime }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<div class="flex flex-wrap gap-x-2 gap-y-1.5">
|
||||
<h5
|
||||
v-if="name"
|
||||
class="m-0 text-sm min-w-0 truncate text-n-slate-12 dark:text-n-slate-12"
|
||||
>
|
||||
<span class="text-xs font-norma text-n-slate-11 dark:text-n-slate-11">
|
||||
{{ $t('SEARCH.FROM') }}:
|
||||
</span>
|
||||
{{ name }}
|
||||
</h5>
|
||||
<h5
|
||||
v-if="email"
|
||||
class="m-0 overflow-hidden text-sm text-n-slate-12 dark:text-n-slate-12 truncate"
|
||||
v-for="item in visibleInfoItems"
|
||||
:key="item.label"
|
||||
class="m-0 text-sm min-w-0 text-n-slate-12 dark:text-n-slate-12 truncate"
|
||||
>
|
||||
<span
|
||||
class="text-xs font-normal text-n-slate-11 dark:text-n-slate-11"
|
||||
>
|
||||
{{ $t('SEARCH.EMAIL') }}:
|
||||
{{ $t(item.label) }}:
|
||||
</span>
|
||||
{{ email }}
|
||||
{{ item.value }}
|
||||
</h5>
|
||||
</div>
|
||||
<slot />
|
||||
|
||||
+14
-2
@@ -1,9 +1,10 @@
|
||||
<script setup>
|
||||
import { defineProps, computed } from 'vue';
|
||||
import { useMapGetter } from 'dashboard/composables/store.js';
|
||||
import SearchResultSection from './SearchResultSection.vue';
|
||||
import SearchResultConversationItem from './SearchResultConversationItem.vue';
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
conversations: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
@@ -23,6 +24,13 @@ defineProps({
|
||||
});
|
||||
|
||||
const accountId = useMapGetter('getCurrentAccountId');
|
||||
|
||||
const conversationsWithSubject = computed(() => {
|
||||
return props.conversations.map(conversation => ({
|
||||
...conversation,
|
||||
mail_subject: conversation.additional_attributes?.mail_subject || '',
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -34,7 +42,10 @@ const accountId = useMapGetter('getCurrentAccountId');
|
||||
:is-fetching="isFetching"
|
||||
>
|
||||
<ul v-if="conversations.length" class="space-y-1.5 list-none">
|
||||
<li v-for="conversation in conversations" :key="conversation.id">
|
||||
<li
|
||||
v-for="conversation in conversationsWithSubject"
|
||||
:key="conversation.id"
|
||||
>
|
||||
<SearchResultConversationItem
|
||||
:id="conversation.id"
|
||||
:name="conversation.contact.name"
|
||||
@@ -42,6 +53,7 @@ const accountId = useMapGetter('getCurrentAccountId');
|
||||
:account-id="accountId"
|
||||
:inbox="conversation.inbox"
|
||||
:created-at="conversation.created_at"
|
||||
:email-subject="conversation.mail_subject"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -43,6 +43,7 @@ class Telegram::IncomingMessageService
|
||||
def process_message_attachments
|
||||
attach_location
|
||||
attach_files
|
||||
attach_contact
|
||||
end
|
||||
|
||||
def update_contact_avatar
|
||||
@@ -136,6 +137,16 @@ class Telegram::IncomingMessageService
|
||||
)
|
||||
end
|
||||
|
||||
def attach_contact
|
||||
return unless contact_card
|
||||
|
||||
@message.attachments.new(
|
||||
account_id: @message.account_id,
|
||||
file_type: :contact,
|
||||
fallback_title: contact_card['phone_number'].to_s
|
||||
)
|
||||
end
|
||||
|
||||
def file
|
||||
@file ||= visual_media_params || params[:message][:voice].presence || params[:message][:audio].presence || params[:message][:document].presence
|
||||
end
|
||||
@@ -154,6 +165,10 @@ class Telegram::IncomingMessageService
|
||||
@location ||= params.dig(:message, :location).presence
|
||||
end
|
||||
|
||||
def contact_card
|
||||
@contact_card ||= params.dig(:message, :contact).presence
|
||||
end
|
||||
|
||||
def visual_media_params
|
||||
params[:message][:photo].presence&.last || params.dig(:message, :sticker, :thumb).presence || params[:message][:video].presence
|
||||
end
|
||||
|
||||
@@ -16,6 +16,8 @@ json.payload do
|
||||
json.agent do
|
||||
json.partial! 'agent', formats: [:json], agent: conversation.assignee if conversation.try(:assignee).present?
|
||||
end
|
||||
|
||||
json.additional_attributes conversation.additional_attributes
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -70,7 +70,9 @@
|
||||
Finish Setup
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Description: Install and manage a Chatwoot installation.
|
||||
# OS: Ubuntu 20.04 LTS, 22.04 LTS, 24.04 LTS
|
||||
# Script Version: 3.1.0
|
||||
# Script Version: 3.2.0
|
||||
# Run this script as root
|
||||
|
||||
set -eu -o errexit -o pipefail -o noclobber -o nounset
|
||||
@@ -19,7 +19,7 @@ fi
|
||||
# option --output/-o requires 1 argument
|
||||
LONGOPTS=console,debug,help,install,Install:,logs:,restart,ssl,upgrade,webserver,version
|
||||
OPTIONS=cdhiI:l:rsuwv
|
||||
CWCTL_VERSION="3.1.0"
|
||||
CWCTL_VERSION="3.2.0"
|
||||
pg_pass=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 15 ; echo '')
|
||||
CHATWOOT_HUB_URL="https://hub.2.chatwoot.com/events"
|
||||
|
||||
@@ -177,7 +177,7 @@ function install_dependencies() {
|
||||
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
|
||||
mkdir -p /etc/apt/keyrings
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||
NODE_MAJOR=20
|
||||
NODE_MAJOR=23
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
|
||||
echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg 16" > /etc/apt/sources.list.d/pgdg.list
|
||||
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||
@@ -779,15 +779,15 @@ function upgrade_node() {
|
||||
# Parse major version number
|
||||
major_version=$(echo "$current_version" | cut -d. -f1)
|
||||
|
||||
if [ "$major_version" -ge 20 ]; then
|
||||
echo "Node.js is already version $current_version (>= 20.x). Skipping Node.js upgrade."
|
||||
if [ "$major_version" -ge 23 ]; then
|
||||
echo "Node.js is already version $current_version (>= 23.x). Skipping Node.js upgrade."
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Upgrading Node.js version to v20.x"
|
||||
echo "Upgrading Node.js version to v23.x"
|
||||
mkdir -p /etc/apt/keyrings
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||
NODE_MAJOR=20
|
||||
NODE_MAJOR=23
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
|
||||
|
||||
apt-get update
|
||||
|
||||
+31
-12
@@ -1,6 +1,12 @@
|
||||
# pre-build stage
|
||||
FROM node:23-alpine as node
|
||||
FROM ruby:3.3.3-alpine3.19 AS pre-builder
|
||||
|
||||
ARG NODE_VERSION="23.7.0"
|
||||
ARG PNPM_VERSION="10.2.0"
|
||||
ENV NODE_VERSION=${NODE_VERSION}
|
||||
ENV PNPM_VERSION=${PNPM_VERSION}
|
||||
|
||||
# ARG default to production settings
|
||||
# For development docker-compose file overrides ARGS
|
||||
ARG BUNDLE_WITHOUT="development:test"
|
||||
@@ -25,18 +31,24 @@ RUN apk update && apk add --no-cache \
|
||||
tzdata \
|
||||
postgresql-dev \
|
||||
postgresql-client \
|
||||
nodejs=20.15.1-r0 \
|
||||
git \
|
||||
curl \
|
||||
xz \
|
||||
&& mkdir -p /var/app \
|
||||
&& gem install bundler
|
||||
|
||||
# Install pnpm and configure environment
|
||||
RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL="$(which sh)" sh - \
|
||||
&& echo 'export PNPM_HOME="/root/.local/share/pnpm"' >> /root/.shrc \
|
||||
&& echo 'export PATH="$PNPM_HOME:$PATH"' >> /root/.shrc \
|
||||
&& export PNPM_HOME="/root/.local/share/pnpm" \
|
||||
&& export PATH="$PNPM_HOME:$PATH" \
|
||||
&& pnpm --version
|
||||
COPY --from=node /usr/local/bin/node /usr/local/bin/
|
||||
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
|
||||
RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
|
||||
&& ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx
|
||||
|
||||
RUN npm install -g pnpm@${PNPM_VERSION}
|
||||
|
||||
RUN echo 'export PNPM_HOME="/root/.local/share/pnpm"' >> /root/.shrc \
|
||||
&& echo 'export PATH="$PNPM_HOME:$PATH"' >> /root/.shrc \
|
||||
&& export PNPM_HOME="/root/.local/share/pnpm" \
|
||||
&& export PATH="$PNPM_HOME:$PATH" \
|
||||
&& pnpm --version
|
||||
|
||||
# Persist the environment variables in Docker
|
||||
ENV PNPM_HOME="/root/.local/share/pnpm"
|
||||
@@ -86,6 +98,10 @@ RUN rm -rf /gems/ruby/3.3.0/cache/*.gem \
|
||||
# final build stage
|
||||
FROM ruby:3.3.3-alpine3.19
|
||||
|
||||
ARG NODE_VERSION="23.7.0"
|
||||
ARG PNPM_VERSION="10.2.0"
|
||||
ENV NODE_VERSION=${NODE_VERSION}
|
||||
ENV PNPM_VERSION=${PNPM_VERSION}
|
||||
|
||||
ARG BUNDLE_WITHOUT="development:test"
|
||||
ENV BUNDLE_WITHOUT ${BUNDLE_WITHOUT}
|
||||
@@ -114,11 +130,14 @@ RUN apk update && apk add --no-cache \
|
||||
vips \
|
||||
&& gem install bundler
|
||||
|
||||
COPY --from=node /usr/local/bin/node /usr/local/bin/
|
||||
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
|
||||
|
||||
RUN if [ "$RAILS_ENV" != "production" ]; then \
|
||||
apk add --no-cache nodejs=20.15.1-r0; \
|
||||
# Install pnpm and configure environment
|
||||
wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL="$(which sh)" sh - \
|
||||
&& source /root/.shrc \
|
||||
apk add --no-cache curl \
|
||||
&& ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
|
||||
&& ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \
|
||||
&& npm install -g pnpm@${PNPM_VERSION} \
|
||||
&& pnpm --version; \
|
||||
fi
|
||||
|
||||
|
||||
+9
-8
@@ -111,7 +111,7 @@
|
||||
"@iconify-json/ri": "^1.2.3",
|
||||
"@iconify-json/teenyicons": "^1.2.1",
|
||||
"@size-limit/file": "^8.2.4",
|
||||
"@vitest/coverage-v8": "2.0.1",
|
||||
"@vitest/coverage-v8": "3.0.5",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8.57.0",
|
||||
@@ -134,13 +134,13 @@
|
||||
"prosemirror-model": "^1.22.3",
|
||||
"size-limit": "^8.2.4",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"vite": "^5.4.8",
|
||||
"vite": "^5.4.12",
|
||||
"vite-plugin-ruby": "^5.0.0",
|
||||
"vitest": "2.0.1"
|
||||
"vitest": "3.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20.x",
|
||||
"pnpm": "9.x"
|
||||
"node": "23.x",
|
||||
"pnpm": "10.x"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
@@ -150,8 +150,8 @@
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"vite-node": "2.0.1",
|
||||
"vite": "5.4.8",
|
||||
"vitest": "2.0.1"
|
||||
"vite": "5.4.12",
|
||||
"vitest": "3.0.5"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
@@ -162,5 +162,6 @@
|
||||
"*.scss": [
|
||||
"scss-lint"
|
||||
]
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@10.2.0+sha512.0d27364e0139c6aadeed65ada153135e0ca96c8da42123bd50047f961339dc7a758fc2e944b428f52be570d1bd3372455c1c65fa2e7aa0bfbf931190f9552001"
|
||||
}
|
||||
|
||||
Generated
+322
-367
File diff suppressed because it is too large
Load Diff
@@ -309,5 +309,22 @@ describe Telegram::IncomingMessageService do
|
||||
expect(telegram_channel.inbox.messages.first.content).to eq('Option 1')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when valid contact message params' do
|
||||
it 'creates appropriate conversations, message and contacts' do
|
||||
params = {
|
||||
'update_id' => 2_342_342_343_242,
|
||||
'message' => {
|
||||
'contact': {
|
||||
'phone_number': '+918660944581'
|
||||
}
|
||||
}.merge(message_params)
|
||||
}.with_indifferent_access
|
||||
described_class.new(inbox: telegram_channel.inbox, params: params).perform
|
||||
expect(telegram_channel.inbox.conversations.count).not_to eq(0)
|
||||
expect(Contact.all.first.name).to eq('Sojan Jose')
|
||||
expect(telegram_channel.inbox.messages.first.attachments.first.file_type).to eq('contact')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user