Files
chatwoot/app/javascript/widget/components/ChatFooter.vue
Sivin Varghese faf35738b3 fix: Prevent reopening a resolved conversation (#11168)
# Pull Request Template

## Description

This PR addresses the issue where navigating back and starting a new
conversation incorrectly shows the previous messages or message screen.

Fixes
https://linear.app/chatwoot/issue/CW-4169/prevent-continue-conversation-in-previously-resolved-conversation

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

### Loom video
**Before**

https://www.loom.com/share/18172a3b26ff4e8faf8e1c3c1a0ba279?sid=ffbda52a-93e1-400f-bedc-17b925bae4d3

**After**

https://www.loom.com/share/584177d411424ad38c6812be868eb060?sid=fe5e771a-3faa-4c14-a5fe-918a3ccdb408

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-08-19 12:06:22 +05:30

150 lines
4.3 KiB
Vue
Executable File

<script>
import { mapActions, mapGetters } from 'vuex';
import { getContrastingTextColor } from '@chatwoot/utils';
import CustomButton from 'shared/components/Button.vue';
import FooterReplyTo from 'widget/components/FooterReplyTo.vue';
import ChatInputWrap from 'widget/components/ChatInputWrap.vue';
import { BUS_EVENTS } from 'shared/constants/busEvents';
import { sendEmailTranscript } from 'widget/api/conversation';
import routerMixin from 'widget/mixins/routerMixin';
import { IFrameHelper } from '../helpers/utils';
import { CHATWOOT_ON_START_CONVERSATION } from '../constants/sdkEvents';
import { emitter } from 'shared/helpers/mitt';
export default {
components: {
ChatInputWrap,
CustomButton,
FooterReplyTo,
},
mixins: [routerMixin],
data() {
return {
inReplyTo: null,
};
},
computed: {
...mapGetters({
conversationAttributes: 'conversationAttributes/getConversationParams',
widgetColor: 'appConfig/getWidgetColor',
conversationSize: 'conversation/getConversationSize',
currentUser: 'contacts/getCurrentUser',
isWidgetStyleFlat: 'appConfig/isWidgetStyleFlat',
}),
textColor() {
return getContrastingTextColor(this.widgetColor);
},
hideReplyBox() {
const { allowMessagesAfterResolved } = window.chatwootWebChannel;
const { status } = this.conversationAttributes;
return !allowMessagesAfterResolved && status === 'resolved';
},
showEmailTranscriptButton() {
return this.hasEmail;
},
hasEmail() {
return this.currentUser && this.currentUser.has_email;
},
hasReplyTo() {
return (
this.inReplyTo && (this.inReplyTo.content || this.inReplyTo.attachments)
);
},
},
mounted() {
emitter.on(BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE, this.toggleReplyTo);
},
methods: {
...mapActions('conversation', ['sendMessage', 'sendAttachment']),
...mapActions('conversationAttributes', ['getAttributes']),
async handleSendMessage(content) {
await this.sendMessage({
content,
replyTo: this.inReplyTo ? this.inReplyTo.id : null,
});
// reset replyTo message after sending
this.inReplyTo = null;
// Update conversation attributes on new conversation
if (this.conversationSize === 0) {
this.getAttributes();
}
},
async handleSendAttachment(attachment) {
await this.sendAttachment({
attachment,
replyTo: this.inReplyTo ? this.inReplyTo.id : null,
});
this.inReplyTo = null;
},
startNewConversation() {
this.replaceRoute('prechat-form');
IFrameHelper.sendMessage({
event: 'onEvent',
eventIdentifier: CHATWOOT_ON_START_CONVERSATION,
data: { hasConversation: true },
});
},
toggleReplyTo(message) {
this.inReplyTo = message;
},
async sendTranscript() {
if (this.hasEmail) {
try {
await sendEmailTranscript();
emitter.emit(BUS_EVENTS.SHOW_ALERT, {
message: this.$t('EMAIL_TRANSCRIPT.SEND_EMAIL_SUCCESS'),
type: 'success',
});
} catch (error) {
emitter.$emit(BUS_EVENTS.SHOW_ALERT, {
message: this.$t('EMAIL_TRANSCRIPT.SEND_EMAIL_ERROR'),
});
}
}
},
},
};
</script>
<template>
<footer
v-if="!hideReplyBox"
class="relative z-50 mb-1"
:class="{
'rounded-lg': !isWidgetStyleFlat,
'pt-2.5 shadow-[0px_-20px_20px_1px_rgba(0,_0,_0,_0.05)] dark:shadow-[0px_-20px_20px_1px_rgba(0,_0,_0,_0.15)] rounded-t-none':
hasReplyTo,
}"
>
<FooterReplyTo
v-if="hasReplyTo"
:in-reply-to="inReplyTo"
@dismiss="inReplyTo = null"
/>
<ChatInputWrap
class="shadow-sm"
:on-send-message="handleSendMessage"
:on-send-attachment="handleSendAttachment"
/>
</footer>
<div v-else>
<CustomButton
class="font-medium"
block
:bg-color="widgetColor"
:text-color="textColor"
@click="startNewConversation"
>
{{ $t('START_NEW_CONVERSATION') }}
</CustomButton>
<CustomButton
v-if="showEmailTranscriptButton"
type="clear"
class="font-normal"
@click="sendTranscript"
>
{{ $t('EMAIL_TRANSCRIPT.BUTTON_TEXT') }}
</CustomButton>
</div>
</template>