diff --git a/app/javascript/dashboard/components/widgets/conversation/bubble/ImageAudioVideo.vue b/app/javascript/dashboard/components/widgets/conversation/bubble/ImageAudioVideo.vue
index ec021e0b9..9a0640bf3 100644
--- a/app/javascript/dashboard/components/widgets/conversation/bubble/ImageAudioVideo.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/bubble/ImageAudioVideo.vue
@@ -2,6 +2,7 @@
import { mapGetters } from 'vuex';
import { hasPressedCommand } from 'shared/helpers/KeyboardHelpers';
import GalleryView from '../components/GalleryView.vue';
+import { timeStampAppendedURL } from 'dashboard/helper/URLHelper';
const ALLOWED_FILE_TYPES = {
IMAGE: 'image',
@@ -42,6 +43,9 @@ export default {
isAudio() {
return this.attachment.file_type === ALLOWED_FILE_TYPES.AUDIO;
},
+ timeStampURL() {
+ return timeStampAppendedURL(this.dataUrl);
+ },
attachmentTypeClasses() {
return {
image: this.isImage,
@@ -108,7 +112,7 @@ export default {
@click="onClick"
/>
{
return false;
}
};
+
+export const timeStampAppendedURL = dataUrl => {
+ const url = new URL(dataUrl);
+ if (!url.searchParams.has('t')) {
+ url.searchParams.append('t', Date.now());
+ }
+
+ return url.toString();
+};
diff --git a/app/javascript/dashboard/helper/specs/URLHelper.spec.js b/app/javascript/dashboard/helper/specs/URLHelper.spec.js
index d137c367c..d0dda11e8 100644
--- a/app/javascript/dashboard/helper/specs/URLHelper.spec.js
+++ b/app/javascript/dashboard/helper/specs/URLHelper.spec.js
@@ -5,6 +5,7 @@ import {
conversationListPageURL,
getArticleSearchURL,
hasValidAvatarUrl,
+ timeStampAppendedURL,
} from '../URLHelper';
describe('#URL Helpers', () => {
@@ -190,4 +191,51 @@ describe('#URL Helpers', () => {
expect(hasValidAvatarUrl()).toBe(false);
});
});
+
+ describe('timeStampAppendedURL', () => {
+ const FIXED_TIMESTAMP = 1234567890000;
+
+ beforeEach(() => {
+ vi.spyOn(Date, 'now').mockImplementation(() => FIXED_TIMESTAMP);
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it('should append timestamp to a URL without query parameters', () => {
+ const input = 'https://example.com/audio.mp3';
+ const expected = `https://example.com/audio.mp3?t=${FIXED_TIMESTAMP}`;
+ expect(timeStampAppendedURL(input)).toBe(expected);
+ });
+
+ it('should append timestamp to a URL with existing query parameters', () => {
+ const input = 'https://example.com/audio.mp3?volume=50';
+ const expected = `https://example.com/audio.mp3?volume=50&t=${FIXED_TIMESTAMP}`;
+ expect(timeStampAppendedURL(input)).toBe(expected);
+ });
+
+ it('should not append timestamp if it already exists', () => {
+ const input = 'https://example.com/audio.mp3?t=9876543210';
+ expect(timeStampAppendedURL(input)).toBe(input);
+ });
+
+ it('should handle URLs with hash fragments', () => {
+ const input = 'https://example.com/audio.mp3#section1';
+ const expected = `https://example.com/audio.mp3?t=${FIXED_TIMESTAMP}#section1`;
+ expect(timeStampAppendedURL(input)).toBe(expected);
+ });
+
+ it('should handle complex URLs', () => {
+ const input =
+ 'https://example.com/path/to/audio.mp3?key1=value1&key2=value2#fragment';
+ const expected = `https://example.com/path/to/audio.mp3?key1=value1&key2=value2&t=${FIXED_TIMESTAMP}#fragment`;
+ expect(timeStampAppendedURL(input)).toBe(expected);
+ });
+
+ it('should throw an error for invalid URLs', () => {
+ const input = 'not a valid url';
+ expect(() => timeStampAppendedURL(input)).toThrow();
+ });
+ });
});