mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 20:18:08 +00:00
fix: Instagram audio rendering issues (#9957)
We are using `audio` component for rendering audio files in dashboard.
```
<audio v-else-if="isAudio" controls>
<source :src="`${dataUrl}?t=${Date.now()}`" />
</audio>
```
We have added the timestamp for every audio URL for cache busting. For
Instagram, we are getting a signature URL. When we add any value and
access the URL, it results in an "Invalid signature. If I remove the
timestamp, the audio is rendering properly. This PR will change the
logic to construct the URL properly instead of direct string
manipulation.
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { hasPressedCommand } from 'shared/helpers/KeyboardHelpers';
|
import { hasPressedCommand } from 'shared/helpers/KeyboardHelpers';
|
||||||
import GalleryView from '../components/GalleryView.vue';
|
import GalleryView from '../components/GalleryView.vue';
|
||||||
|
import { timeStampAppendedURL } from 'dashboard/helper/URLHelper';
|
||||||
|
|
||||||
const ALLOWED_FILE_TYPES = {
|
const ALLOWED_FILE_TYPES = {
|
||||||
IMAGE: 'image',
|
IMAGE: 'image',
|
||||||
@@ -42,6 +43,9 @@ export default {
|
|||||||
isAudio() {
|
isAudio() {
|
||||||
return this.attachment.file_type === ALLOWED_FILE_TYPES.AUDIO;
|
return this.attachment.file_type === ALLOWED_FILE_TYPES.AUDIO;
|
||||||
},
|
},
|
||||||
|
timeStampURL() {
|
||||||
|
return timeStampAppendedURL(this.dataUrl);
|
||||||
|
},
|
||||||
attachmentTypeClasses() {
|
attachmentTypeClasses() {
|
||||||
return {
|
return {
|
||||||
image: this.isImage,
|
image: this.isImage,
|
||||||
@@ -108,7 +112,7 @@ export default {
|
|||||||
@click="onClick"
|
@click="onClick"
|
||||||
/>
|
/>
|
||||||
<audio v-else-if="isAudio" controls class="skip-context-menu mb-0.5">
|
<audio v-else-if="isAudio" controls class="skip-context-menu mb-0.5">
|
||||||
<source :src="`${dataUrl}?t=${Date.now()}`" />
|
<source :src="timeStampURL" />
|
||||||
</audio>
|
</audio>
|
||||||
<GalleryView
|
<GalleryView
|
||||||
v-if="show"
|
v-if="show"
|
||||||
|
|||||||
@@ -108,3 +108,12 @@ export const hasValidAvatarUrl = avatarUrl => {
|
|||||||
return false;
|
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();
|
||||||
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
conversationListPageURL,
|
conversationListPageURL,
|
||||||
getArticleSearchURL,
|
getArticleSearchURL,
|
||||||
hasValidAvatarUrl,
|
hasValidAvatarUrl,
|
||||||
|
timeStampAppendedURL,
|
||||||
} from '../URLHelper';
|
} from '../URLHelper';
|
||||||
|
|
||||||
describe('#URL Helpers', () => {
|
describe('#URL Helpers', () => {
|
||||||
@@ -190,4 +191,51 @@ describe('#URL Helpers', () => {
|
|||||||
expect(hasValidAvatarUrl()).toBe(false);
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user