Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Chat] Fixed Image Placeholder Being Shown When Fetching Failed #4172

Merged
merged 8 commits into from
Feb 28, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "patch",
"area": "fix",
"workstream": "Inline Image",
"comment": "Fixed the issue where image placeholder being shown when fetching failed",
"packageName": "@azure/communication-react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "patch",
"area": "fix",
"workstream": "Inline Image",
"comment": "Fixed the issue where image placeholder being shown when fetching failed",
"packageName": "@azure/communication-react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ describe('ResourceDownloadQueue api functions', () => {

/* @conditional-compile-remove(teams-inline-images-and-file-sharing) */
describe('ResourceDownloadQueue api functions', () => {
// URL.createObjectURL is not available in jest-dom
// so we need to mock it in tests
if (typeof URL.createObjectURL === 'undefined') {
Object.defineProperty(window.URL, 'createObjectURL', {
value: () => {
return 'http://mocked-url';
}
});
}
test('should add a message to the queue and contains message', () => {
const context = new ChatContext();
const tokenCredential = stubCommunicationTokenCredential();
Expand Down
52 changes: 37 additions & 15 deletions packages/chat-stateful-client/src/ResourceDownloadQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,14 @@ export class ResourceDownloadQueue {
continue;
}

try {
if (options) {
const singleUrl = options.singleUrl;
message = await this.downloadSingleUrl(message, singleUrl, operation);
} else {
message = await this.downloadAllPreviewUrls(message, operation);
}
this._context.setChatMessage(threadId, message);
} catch (error) {
console.log('Downloading Resource error: ', error);
} finally {
this.isActive = false;
if (options) {
emlynmac marked this conversation as resolved.
Show resolved Hide resolved
const singleUrl = options.singleUrl;
message = await this.downloadSingleUrl(message, singleUrl, operation);
} else {
message = await this.downloadAllPreviewUrls(message, operation);
}
this._context.setChatMessage(threadId, message);
this.isActive = false;
emlynmac marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -79,7 +74,7 @@ export class ResourceDownloadQueue {
resourceUrl: string,
operation: ImageRequest
): Promise<ChatMessageWithStatus> {
const blobUrl = await operation(resourceUrl, this._credential);
const blobUrl = await this.downloadResource(operation, resourceUrl);
message = { ...message, resourceCache: { ...message.resourceCache, [resourceUrl]: blobUrl } };
return message;
}
Expand All @@ -95,14 +90,24 @@ export class ResourceDownloadQueue {
}
for (const attachment of attachments) {
if (attachment.previewUrl && attachment.attachmentType === 'image') {
const blobUrl = await operation(attachment.previewUrl, this._credential);
const blobUrl = await this.downloadResource(operation, attachment.previewUrl);
message.resourceCache[attachment.previewUrl] = blobUrl;
}
}
}

return message;
}

private async downloadResource(operation: ImageRequest, url: string): Promise<string> {
let blobUrl = URL.createObjectURL(new Blob());
try {
blobUrl = await operation(url, this._credential);
} catch (error) {
console.log('Downloading Resource error: ', error);
emlynmac marked this conversation as resolved.
Show resolved Hide resolved
}
return blobUrl;
}
}
/* @conditional-compile-remove(teams-inline-images-and-file-sharing) */
/**
Expand Down Expand Up @@ -141,11 +146,28 @@ export const fetchImageSource = async (src: string, credential: CommunicationTok
const headers = new Headers();
headers.append('Authorization', `Bearer ${token}`);
try {
return await fetch(url, { headers });
return await fetchWithTimeout(url, { headers });
} catch (err) {
throw new ChatError('ChatThreadClient.getMessage', err as Error);
}
}
async function fetchWithTimeout(
resource: string | URL | Request,
options: { timeout?: number; headers?: Headers }
): Promise<Response> {
// default timeout is 30 seconds
const { timeout = 30000 } = options;

const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);

const response = await fetch(resource, {
...options,
signal: controller.signal
});
clearTimeout(id);
return response;
}
const accessToken = await credential.getToken();
const response = await fetchWithAuthentication(src, accessToken.token);
const blob = await response.blob();
Expand Down
Loading