-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[MOB-7613] Remove extra height from iframe + refactor (#302)
* [MOB-7613] Unset iframe body margin if one is not explicitly already set * [MOB-7613] Detect img tags alongside image url paths * [MOB-7613] Set iframe height on iframe load * [MOB-7613] Add jwt generator url to .env.example * [MOB-7613] Cleaup utils * [MOB-7613] Move caching methods to separate file * [MOB-7613] Update usages of cache * [MOB-7613] Clean up comments in cache.ts * [MOB-7613] Remove added return * [MOB-7613] Clean up iframe width/height setter * [MOB-7613] Add comment for consume variable * [MOB-7613] Add env convenience variable for react sample app * [MOB-7613] Suppress console.warn lint warnings * [MOB-7613] Clean up types and remove template literals * [MOB-7613] Fix tests * [MOB-7613] Fix tests * [MOB-7613] Put localhost url as fallback for jwt generator
- Loading branch information
1 parent
52fed68
commit de72a62
Showing
14 changed files
with
377 additions
and
342 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,10 @@ | ||
# To make requests from this example app make sure you first create an .env file | ||
# and add the API key and JWT Secret to it like so (and uncomment the keys): | ||
|
||
# API_KEY=1234 | ||
# JWT_SECRET=1234 | ||
# JWT_SECRET=1234 | ||
|
||
# You can set the URL for the JWT generator here if needed | ||
# JWT_GENERATOR=http://localhost:5000/generate | ||
|
||
# Convenience variable to automatically set the login email during testing. | ||
# [email protected] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { setMany } from 'idb-keyval'; | ||
import { BrowserStorageEstimate, CachedMessage, InAppMessage } from './types'; | ||
|
||
/** | ||
* Detect amount of local storage remaining (quota) and used (usage). | ||
* If usageDetails exist (not supported in Safari), use this instead of usage. | ||
*/ | ||
export const determineRemainingStorageQuota = async () => { | ||
try { | ||
if (!('indexedDB' in window)) return 0; | ||
|
||
const storage: BrowserStorageEstimate | undefined = | ||
'storage' in navigator && 'estimate' in navigator.storage | ||
? await navigator.storage.estimate() | ||
: undefined; | ||
|
||
/** 50 MB is the lower common denominator on modern mobile browser caches. */ | ||
const mobileBrowserQuota = 52428800; | ||
/** Max quota of browser storage that in-apps will potentially fill */ | ||
const estimatedBrowserQuota = storage?.quota; | ||
/** | ||
* Determine lower max quota that can be used for message cache, set to | ||
* 60% of quota to leave space for other caching needs on that domain. | ||
*/ | ||
const messageQuota = | ||
((estimatedBrowserQuota && | ||
Math.min(estimatedBrowserQuota, mobileBrowserQuota)) ?? | ||
mobileBrowserQuota) * 0.6; | ||
|
||
/** How much local storage is being used. */ | ||
const usage = storage?.usageDetails?.indexedDB ?? storage?.usage; | ||
const remainingQuota = usage && messageQuota - usage; | ||
|
||
return remainingQuota ? remainingQuota : 0; | ||
} catch (err: any /* eslint-disable-line @typescript-eslint/no-explicit-any */) { | ||
// eslint-disable-next-line no-console | ||
console.warn( | ||
'Error determining remaining storage quota', | ||
err?.response?.data?.clientErrors ?? err | ||
); | ||
} | ||
/** Do not try to add to cache if we cannot determine storage space. */ | ||
return 0; | ||
}; | ||
|
||
/** | ||
* Deletes cached messages not present in latest getMessages fetch. | ||
* @param cachedMessages | ||
* @param fetchedMessages | ||
*/ | ||
export const getCachedMessagesToDelete = ( | ||
cachedMessages: CachedMessage[], | ||
fetchedMessages: Partial<InAppMessage>[] | ||
) => | ||
cachedMessages.reduce((deleteQueue: string[], [cachedMessageId]) => { | ||
const isCachedMessageInFetch = fetchedMessages.reduce( | ||
(isFound, { messageId }) => { | ||
if (messageId === cachedMessageId) isFound = true; | ||
return isFound; | ||
}, | ||
false | ||
); | ||
|
||
if (!isCachedMessageInFetch) deleteQueue.push(cachedMessageId); | ||
return deleteQueue; | ||
}, []); | ||
|
||
/** | ||
* Adds messages to cache only if they fit within the quota, starting with | ||
* oldest messages since newer messages can still be easily retrieved via | ||
* new requests while passing in latestCachedMessageId param. | ||
* @param messages | ||
* @param quota | ||
*/ | ||
export const addNewMessagesToCache = async ( | ||
messages: { messageId: string; message: InAppMessage }[] | ||
) => { | ||
const quota = await determineRemainingStorageQuota(); | ||
if (quota > 0) { | ||
/** | ||
* Determine total size (in bytes) of new messages to be added to cache | ||
* sorted oldest to newest (ascending createdAt property). | ||
*/ | ||
const messagesWithSizes: { | ||
messageId: string; | ||
message: InAppMessage; | ||
createdAt: number; | ||
size: number; | ||
}[] = messages | ||
.map(({ messageId, message }) => { | ||
const sizeInBytes = new Blob([ | ||
JSON.stringify(message).replace(/\[\[\],"\]/g, '') | ||
]).size; | ||
return { | ||
messageId, | ||
message, | ||
createdAt: message.createdAt, | ||
size: sizeInBytes | ||
}; | ||
}) | ||
.sort((a, b) => a.createdAt - b.createdAt); | ||
|
||
/** Only add messages that fit in cache, starting from oldest messages. */ | ||
let remainingQuota = quota; | ||
const messagesToAddToCache: [string, InAppMessage][] = []; | ||
messagesWithSizes.every(({ messageId, message, size }) => { | ||
if (remainingQuota - size < 0) return false; | ||
remainingQuota -= size; | ||
messagesToAddToCache.push([messageId, message]); | ||
return true; | ||
}); | ||
|
||
try { | ||
await setMany(messagesToAddToCache); | ||
} catch (err: any /* eslint-disable-line @typescript-eslint/no-explicit-any */) { | ||
// eslint-disable-next-line no-console | ||
console.warn( | ||
'Error adding new messages to the browser cache', | ||
err?.response?.data?.clientErrors ?? err | ||
); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { messages } from '../../__data__/inAppMessages'; | ||
import { getCachedMessagesToDelete } from '../cache'; | ||
import { CachedMessage, InAppMessage } from '../types'; | ||
|
||
describe('cache.ts', () => { | ||
describe('Caching', () => { | ||
const now = Date.now(); | ||
const allMessages = [...messages]; | ||
|
||
const cachedMessages: CachedMessage[] = allMessages.flatMap((msg) => [ | ||
[msg.messageId, msg] | ||
]); | ||
|
||
it('should delete cached messages that are expired', () => { | ||
const unexpiredMessages = allMessages.filter( | ||
(msg) => msg.expiresAt > now | ||
); | ||
const expiredMessageIds = allMessages.reduce( | ||
(allFetchedIds: string[], message) => { | ||
if (message.expiresAt < now) allFetchedIds.push(message.messageId); | ||
return allFetchedIds; | ||
}, | ||
[] | ||
); | ||
|
||
const messagesForDeletion = getCachedMessagesToDelete( | ||
cachedMessages, | ||
unexpiredMessages | ||
); | ||
expect(messagesForDeletion).toEqual(expiredMessageIds); | ||
}); | ||
|
||
it('should delete any cached messages not included in the fetch', () => { | ||
const validMessages: InAppMessage[] = []; | ||
const invalidMessages: InAppMessage[] = []; | ||
allMessages.forEach((msg) => | ||
msg.messageId === 'normalMessage!' | ||
? validMessages.push(msg) | ||
: invalidMessages.push(msg) | ||
); | ||
|
||
const messagesForDeletion = getCachedMessagesToDelete( | ||
cachedMessages, | ||
validMessages | ||
); | ||
expect(messagesForDeletion).toEqual( | ||
invalidMessages.map((msg) => msg.messageId) | ||
); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.