mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-30 18:47:51 +00:00 
			
		
		
		
	fix: Fix bubble design in RTL (#10683)
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
		| @@ -1,9 +1,11 @@ | ||||
| <script setup> | ||||
| import { computed, ref, toRefs } from 'vue'; | ||||
| import { onMounted, computed, ref, toRefs } from 'vue'; | ||||
| import { useTimeoutFn } from '@vueuse/core'; | ||||
| import { provideMessageContext } from './provider.js'; | ||||
| import { useTrack } from 'dashboard/composables'; | ||||
| import { emitter } from 'shared/helpers/mitt'; | ||||
| import { useI18n } from 'vue-i18n'; | ||||
| import { useRoute } from 'vue-router'; | ||||
| import { LocalStorage } from 'shared/helpers/localStorage'; | ||||
| import { ACCOUNT_EVENTS } from 'dashboard/helper/AnalyticsHelper/events'; | ||||
| import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage'; | ||||
| @@ -115,7 +117,7 @@ const props = defineProps({ | ||||
|   createdAt: { type: Number, required: true }, // eslint-disable-line vue/no-unused-properties | ||||
|   currentUserId: { type: Number, required: true }, | ||||
|   groupWithNext: { type: Boolean, default: false }, | ||||
|   inboxId: { type: Number, required: true }, // eslint-disable-line vue/no-unused-properties | ||||
|   inboxId: { type: Number, default: null }, // eslint-disable-line vue/no-unused-properties | ||||
|   inboxSupportsReplyTo: { type: Object, default: () => ({}) }, | ||||
|   inReplyTo: { type: Object, default: null }, // eslint-disable-line vue/no-unused-properties | ||||
|   isEmailInbox: { type: Boolean, default: false }, | ||||
| @@ -127,8 +129,10 @@ const props = defineProps({ | ||||
| }); | ||||
|  | ||||
| const contextMenuPosition = ref({}); | ||||
| const showBackgroundHighlight = ref(false); | ||||
| const showContextMenu = ref(false); | ||||
| const { t } = useI18n(); | ||||
| const route = useRoute(); | ||||
|  | ||||
| /** | ||||
|  * Computes the message variant based on props | ||||
| @@ -329,6 +333,22 @@ const contextMenuEnabledOptions = computed(() => { | ||||
|   }; | ||||
| }); | ||||
|  | ||||
| const shouldRenderMessage = computed(() => { | ||||
|   const hasAttachments = !!(props.attachments && props.attachments.length > 0); | ||||
|   const isEmailContentType = props.contentType === CONTENT_TYPES.INCOMING_EMAIL; | ||||
|   const isUnsupported = props.contentAttributes?.isUnsupported; | ||||
|   const isAnIntegrationMessage = | ||||
|     props.contentType === CONTENT_TYPES.INTEGRATIONS; | ||||
|  | ||||
|   return ( | ||||
|     hasAttachments || | ||||
|     props.content || | ||||
|     isEmailContentType || | ||||
|     isUnsupported || | ||||
|     isAnIntegrationMessage | ||||
|   ); | ||||
| }); | ||||
|  | ||||
| function openContextMenu(e) { | ||||
|   const shouldSkipContextMenu = | ||||
|     e.target?.classList.contains('skip-context-menu') || | ||||
| @@ -382,6 +402,20 @@ const avatarInfo = computed(() => { | ||||
|   }; | ||||
| }); | ||||
|  | ||||
| const setupHighlightTimer = () => { | ||||
|   if (Number(route.query.messageId) !== Number(props.id)) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   showBackgroundHighlight.value = true; | ||||
|   const HIGHLIGHT_TIMER = 1000; | ||||
|   useTimeoutFn(() => { | ||||
|     showBackgroundHighlight.value = false; | ||||
|   }, HIGHLIGHT_TIMER); | ||||
| }; | ||||
|  | ||||
| onMounted(setupHighlightTimer); | ||||
|  | ||||
| provideMessageContext({ | ||||
|   ...toRefs(props), | ||||
|   isPrivate: computed(() => props.private), | ||||
| @@ -392,14 +426,19 @@ provideMessageContext({ | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <!-- eslint-disable-next-line vue/no-root-v-if --> | ||||
| <template> | ||||
|   <div | ||||
|     v-if="shouldRenderMessage" | ||||
|     :id="`message${props.id}`" | ||||
|     class="flex w-full message-bubble-container" | ||||
|     :data-message-id="props.id" | ||||
|     :class="[ | ||||
|       flexOrientationClass, | ||||
|       shouldGroupWithNext ? 'group-with-next mb-2' : 'mb-4', | ||||
|       { | ||||
|         'bg-n-alpha-1': showBackgroundHighlight, | ||||
|       }, | ||||
|     ]" | ||||
|   > | ||||
|     <div v-if="variant === MESSAGE_VARIANTS.ACTIVITY"> | ||||
| @@ -428,7 +467,7 @@ provideMessageContext({ | ||||
|       <div | ||||
|         class="[grid-area:bubble] flex" | ||||
|         :class="{ | ||||
|           'pl-9 justify-end': orientation === ORIENTATION.RIGHT, | ||||
|           'ltr:pl-9 rtl:pl-0 justify-end': orientation === ORIENTATION.RIGHT, | ||||
|           'min-w-0': variant === MESSAGE_VARIANTS.EMAIL, | ||||
|         }" | ||||
|         @contextmenu="openContextMenu($event)" | ||||
| @@ -461,11 +500,11 @@ provideMessageContext({ | ||||
| <style lang="scss"> | ||||
| .group-with-next + .message-bubble-container { | ||||
|   .left-bubble { | ||||
|     @apply rounded-tl-sm; | ||||
|     @apply ltr:rounded-tl-sm rtl:rounded-tr-sm; | ||||
|   } | ||||
|  | ||||
|   .right-bubble { | ||||
|     @apply rounded-tr-sm; | ||||
|     @apply ltr:rounded-tr-sm rtl:rounded-tl-sm; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -28,8 +28,8 @@ const { t } = useI18n(); | ||||
|       <div | ||||
|         class="absolute bg-n-alpha-3 px-4 py-3 border rounded-xl border-n-strong text-n-slate-12 bottom-6 w-52 text-xs backdrop-blur-[100px] shadow-[0px_0px_24px_0px_rgba(0,0,0,0.12)] opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all" | ||||
|         :class="{ | ||||
|           'left-0': orientation === ORIENTATION.LEFT, | ||||
|           'right-0': orientation === ORIENTATION.RIGHT, | ||||
|           'ltr:left-0 rtl:right-0': orientation === ORIENTATION.LEFT, | ||||
|           'ltr:right-0 rtl:left-0': orientation === ORIENTATION.RIGHT, | ||||
|         }" | ||||
|       > | ||||
|         {{ error }} | ||||
|   | ||||
| @@ -13,13 +13,10 @@ const readableTime = computed(() => | ||||
|  | ||||
| <template> | ||||
|   <BaseBubble | ||||
|     class="px-2 py-0.5 !rounded-full flex items-center gap-2" | ||||
|     v-tooltip.top="readableTime" | ||||
|     class="px-2 py-0.5 !rounded-full flex min-w-0 items-center gap-2" | ||||
|     data-bubble-name="activity" | ||||
|   > | ||||
|     <span v-dompurify-html="content" :title="content" class="truncate" /> | ||||
|     <div v-if="readableTime" class="w-px h-3 rounded-full bg-n-slate-7" /> | ||||
|     <time class="text-n-slate-10 truncate flex-shrink" :title="readableTime"> | ||||
|       {{ readableTime }} | ||||
|     </time> | ||||
|     <span v-dompurify-html="content" :title="content" /> | ||||
|   </BaseBubble> | ||||
| </template> | ||||
|   | ||||
| @@ -29,8 +29,10 @@ const varaintBaseMap = { | ||||
| }; | ||||
|  | ||||
| const orientationMap = { | ||||
|   [ORIENTATION.LEFT]: 'left-bubble rounded-xl rounded-bl-sm', | ||||
|   [ORIENTATION.RIGHT]: 'right-bubble rounded-xl rounded-br-sm', | ||||
|   [ORIENTATION.LEFT]: | ||||
|     'left-bubble rounded-xl ltr:rounded-bl-sm rtl:rounded-br-sm', | ||||
|   [ORIENTATION.RIGHT]: | ||||
|     'right-bubble rounded-xl ltr:rounded-br-sm rtl:rounded-bl-sm', | ||||
|   [ORIENTATION.CENTER]: 'rounded-md', | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -55,7 +55,7 @@ const showMeta = computed(() => { | ||||
| <template> | ||||
|   <section | ||||
|     v-show="showMeta" | ||||
|     class="space-y-1 pr-9 border-b border-n-strong text-sm" | ||||
|     class="space-y-1 rtl:pl-9 ltr:pr-9 border-b border-n-strong text-sm break-words" | ||||
|     :class="hasError ? 'text-n-ruby-11' : 'text-n-slate-11'" | ||||
|   > | ||||
|     <template v-if="showMeta"> | ||||
|   | ||||
| @@ -109,7 +109,10 @@ const textToShow = computed(() => { | ||||
|         </button> | ||||
|       </div> | ||||
|     </section> | ||||
|     <section v-if="attachments.length" class="px-4 pb-4 space-y-2"> | ||||
|     <section | ||||
|       v-if="Array.isArray(attachments) && attachments.length" | ||||
|       class="px-4 pb-4 space-y-2" | ||||
|     > | ||||
|       <AttachmentChips :attachments="attachments" class="gap-1" /> | ||||
|     </section> | ||||
|   </BaseBubble> | ||||
|   | ||||
| @@ -14,7 +14,7 @@ const isTemplate = computed(() => { | ||||
| }); | ||||
|  | ||||
| const isEmpty = computed(() => { | ||||
|   return !content.value && !attachments.value.length; | ||||
|   return !content.value && !attachments.value?.length; | ||||
| }); | ||||
| </script> | ||||
|  | ||||
|   | ||||
| @@ -109,15 +109,31 @@ const tailwindConfig = { | ||||
|             'ul li': { | ||||
|               margin: '0 0 0.5em 1em', | ||||
|               listStyleType: 'disc', | ||||
|               '[dir="rtl"] &': { | ||||
|                 margin: '0 1em 0.5em 0', | ||||
|               }, | ||||
|             }, | ||||
|             'ol li': { | ||||
|               margin: '0 0 0.5em 1em', | ||||
|               listStyleType: 'decimal', | ||||
|               '[dir="rtl"] &': { | ||||
|                 margin: '0 1em 0.5em 0', | ||||
|               }, | ||||
|             }, | ||||
|             blockquote: { | ||||
|               color: 'rgb(var(--slate-11))', | ||||
|               borderLeft: `4px solid rgb(var(--black-alpha-1))`, | ||||
|               paddingLeft: '1em', | ||||
|               '[dir="rtl"] &': { | ||||
|                 borderLeft: 'none', | ||||
|                 paddingLeft: '0', | ||||
|                 borderRight: `4px solid rgb(var(--black-alpha-1))`, | ||||
|                 paddingRight: '1em', | ||||
|               }, | ||||
|               '[dir="ltr"] &': { | ||||
|                 borderRight: 'none', | ||||
|                 paddingRight: '0', | ||||
|               }, | ||||
|             }, | ||||
|             code: { | ||||
|               backgroundColor: 'rgb(var(--alpha-3))', | ||||
| @@ -146,7 +162,7 @@ const tailwindConfig = { | ||||
|               padding: '0.75em', | ||||
|               color: 'rgb(var(--slate-12))', | ||||
|               border: `none`, | ||||
|               textAlign: 'left', | ||||
|               textAlign: 'start', | ||||
|               fontWeight: '600', | ||||
|             }, | ||||
|             tr: { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Shivam Mishra
					Shivam Mishra