diff --git a/ts/services/releaseNotesFetcher.ts b/ts/services/releaseNotesFetcher.ts index 9ed2687c9..882c00cb5 100644 --- a/ts/services/releaseNotesFetcher.ts +++ b/ts/services/releaseNotesFetcher.ts @@ -106,20 +106,51 @@ export class ReleaseNotesFetcher { } const { uuid, ctaId, link } = note; - const result = await window.textsecure.server.getReleaseNote({ - uuid, - }); - strictAssert( - result.uuid === uuid, - 'UUID of localized release note should match requested UUID' - ); + const globalLocale = new Intl.Locale(window.SignalContext.getI18nLocale()); + const localesToTry = [ + globalLocale.toString(), + globalLocale.language.toString(), + 'en', + ].map(locale => locale.toLocaleLowerCase().replace('-', '_')); - return { - ...result, - uuid, - ctaId, - link, - }; + for (const localeToTry of localesToTry) { + try { + // eslint-disable-next-line no-await-in-loop + const hash = await window.textsecure.server.getReleaseNoteHash({ + uuid, + locale: localeToTry, + }); + + if (hash === undefined) { + continue; + } + + // eslint-disable-next-line no-await-in-loop + const result = await window.textsecure.server.getReleaseNote({ + uuid, + locale: localeToTry, + }); + + strictAssert( + result.uuid === uuid, + 'UUID of localized release note should match requested UUID' + ); + + return { + ...result, + uuid, + ctaId, + link, + }; + } catch { + // If either request fails, try the next locale + continue; + } + } + + throw new Error( + `Could not fetch release note with any locale for UUID ${uuid}` + ); } private async processReleaseNotes( diff --git a/ts/textsecure/WebAPI.ts b/ts/textsecure/WebAPI.ts index 6e856da50..31eb343d0 100644 --- a/ts/textsecure/WebAPI.ts +++ b/ts/textsecure/WebAPI.ts @@ -1216,6 +1216,7 @@ export type GetBackupInfoResponseType = z.infer< export type GetReleaseNoteOptionsType = Readonly<{ uuid: string; + locale: string; }>; export const releaseNoteSchema = z.object({ @@ -1408,6 +1409,9 @@ export type WebAPIType = { getReleaseNote: ( options: GetReleaseNoteOptionsType ) => Promise; + getReleaseNoteHash: ( + options: GetReleaseNoteOptionsType + ) => Promise; getReleaseNotesManifest: () => Promise; getReleaseNotesManifestHash: () => Promise; getSticker: (packId: string, stickerId: number) => Promise; @@ -1880,6 +1884,7 @@ export function initialize({ getProfileUnauth, getProvisioningResource, getReleaseNote, + getReleaseNoteHash, getReleaseNotesManifest, getReleaseNotesManifestHash, getTransferArchive, @@ -2174,15 +2179,43 @@ export function initialize({ languages: Record>; }; } + + async function getReleaseNoteHash({ + uuid, + locale, + }: { + uuid: string; + locale: string; + }): Promise { + const { response } = await _ajax({ + call: 'releaseNotes', + host: resourcesUrl, + httpType: 'HEAD', + urlParameters: `/${uuid}/${locale}.json`, + responseType: 'byteswithdetails', + }); + + const etag = response.headers.get('etag'); + + if (etag == null) { + return undefined; + } + + return etag; + } async function getReleaseNote({ uuid, - }: GetReleaseNoteOptionsType): Promise { + locale, + }: { + uuid: string; + locale: string; + }): Promise { const rawRes = await _ajax({ call: 'releaseNotes', host: resourcesUrl, httpType: 'GET', responseType: 'json', - urlParameters: `/${uuid}/en.json`, + urlParameters: `/${uuid}/${locale}.json`, }); return parseUnknown(releaseNoteSchema, rawRes); }