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(); + }); + }); });