Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
223892a
feat: add ODOMETER_DRAFT onyx key for Save for later functionality
jakubkalinski0 Apr 12, 2026
f65f2ab
feat: add convertFileObjectOrUriToBase64DataURL util
jakubkalinski0 Apr 12, 2026
bb29bb0
feat: add utils for updating/reading odometer draft onyx object
jakubkalinski0 Apr 12, 2026
e4b8429
feat: add odometer draft support to Odometer screen
jakubkalinski0 Apr 12, 2026
64238fd
feat: add odometer draft regression tests for save-for-later flow
jakubkalinski0 Apr 12, 2026
6267ad5
chore: fix failing eslint check
jakubkalinski0 Apr 12, 2026
ae0f31d
Merge branch 'main' into jakubkalinski0/Odometer_add_save_for_later_f…
jakubkalinski0 Apr 13, 2026
4f2dedd
Merge branch 'main' into jakubkalinski0/Odometer_add_save_for_later_f…
jakubkalinski0 Apr 17, 2026
41ecc54
test: remove odometer draft tests from IOUTest
jakubkalinski0 Apr 17, 2026
7c6d616
Merge branch 'main' into jakubkalinski0/Odometer_add_save_for_later_f…
jakubkalinski0 Apr 21, 2026
ce36dc9
fix: remove redundant image type check
jakubkalinski0 Apr 21, 2026
878dce8
fix: adjust placement of Save for later button and add sentry label t…
jakubkalinski0 Apr 22, 2026
2039333
fix: close RHP instead of navigating back after pressing Save for lat…
jakubkalinski0 Apr 22, 2026
646bc5b
Merge branch 'main' into jakubkalinski0/Odometer_add_save_for_later_f…
jakubkalinski0 Apr 22, 2026
71be80b
chore: restore SageIntacct files accidentally deleted by previous mai…
jakubkalinski0 Apr 22, 2026
299c1bd
Merge branch 'main' into jakubkalinski0/Odometer_add_save_for_later_f…
jakubkalinski0 Apr 22, 2026
30d3702
Merge branch 'main' into jakubkalinski0/Odometer_add_save_for_later_f…
jakubkalinski0 Apr 22, 2026
4bbba81
fix: restore odometer images on re-open after Save for Later
jakubkalinski0 Apr 22, 2026
4fbd413
fix: replace setShouldEnableDiscardConfirmation with shouldBypassDisc…
jakubkalinski0 Apr 22, 2026
ddb1c92
fix: revoke stale odometer blob URLs before hydrating draft images
jakubkalinski0 Apr 22, 2026
6b36409
fix: clear odometer draft after successful track expense submission
jakubkalinski0 Apr 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9200,6 +9200,7 @@ const CONST = {
DISTANCE_MAP_NEXT_BUTTON: 'IOURequestStep-DistanceMapNextButton',
DISTANCE_MANUAL_NEXT_BUTTON: 'IOURequestStep-DistanceManualNextButton',
DISTANCE_ODOMETER_NEXT_BUTTON: 'IOURequestStep-DistanceOdometerNextButton',
DISTANCE_ODOMETER_SAVE_FOR_LATER_BUTTON: 'IOURequestStep-DistanceOdometerSaveForLaterButton',
ODOMETER_CHOOSE_FILE_BUTTON: 'IOURequestStep-OdometerChooseFileButton',
GPS_START_STOP_BUTTON: 'IOURequestStep-GPSStartStopButton',
GPS_DISCARD_BUTTON: 'IOURequestStep-GPSDiscardButton',
Expand Down
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ const ONYXKEYS = {
/** GPS points stored for the GPS distance expense before they're accepted by the user */
GPS_DRAFT_DETAILS: 'gpsDraftDetails',

/** Odometer draft stored for the Save for later flow */
ODOMETER_DRAFT: 'odometerDraft',

/** Contains all the info for Tasks */
TASK: 'task',

Expand Down Expand Up @@ -1327,6 +1330,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.IS_OPEN_APP_FAILURE_MODAL_OPEN]: boolean;
[ONYXKEYS.IS_GPS_IN_PROGRESS_MODAL_OPEN]: boolean;
[ONYXKEYS.GPS_DRAFT_DETAILS]: OnyxTypes.GpsDraftDetails;
[ONYXKEYS.ODOMETER_DRAFT]: OnyxTypes.OdometerDraft;
[ONYXKEYS.FULLSCREEN_VISIBILITY]: boolean;
[ONYXKEYS.NETWORK]: OnyxTypes.Network;
[ONYXKEYS.NEW_GROUP_CHAT_DRAFT]: OnyxTypes.NewGroupChatDraft;
Expand Down
9 changes: 8 additions & 1 deletion src/hooks/useResetIOUType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {useRef} from 'react';
import {Keyboard} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import type {IOURequestType} from '@userActions/IOU';
import {initMoneyRequest} from '@userActions/IOU';
import {hydrateOdometerDraftToTransaction, initMoneyRequest} from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Policy, Report, Transaction} from '@src/types/onyx';
Expand Down Expand Up @@ -63,6 +63,7 @@ function useResetIOUType({
const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES);
const [currentDate] = useOnyx(ONYXKEYS.CURRENT_DATE);
const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector});
const [odometerDraft] = useOnyx(ONYXKEYS.ODOMETER_DRAFT);

const personalPolicy = usePersonalPolicy();
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
Expand All @@ -78,6 +79,8 @@ function useResetIOUType({

const isFromGlobalCreate = !report?.reportID;

// Capture before initMoneyRequest wipes the transaction via Onyx.set
const previousComment = transaction?.comment;
initMoneyRequest({
reportID,
policy,
Expand All @@ -95,6 +98,10 @@ function useResetIOUType({
hasOnlyPersonalPolicies: hasOnlyPersonalPolicies ?? true,
draftTransactionIDs,
});
// Re-queue hydration so it runs after initMoneyRequest's Onyx.set and restores any saved images
if (newIOUType === CONST.IOU.REQUEST_TYPE.DISTANCE_ODOMETER && odometerDraft) {
hydrateOdometerDraftToTransaction(CONST.IOU.OPTIMISTIC_TRANSACTION_ID, true, odometerDraft, previousComment);
}
};

const tabSelectedTypeRef = useRef<IOURequestType | null>(null);
Expand Down
9 changes: 9 additions & 0 deletions src/libs/actions/IOU/TrackExpense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2149,6 +2149,7 @@ function shareTrackedExpense(trackedExpenseParams: TrackedExpenseParams) {
| typeof ONYXKEYS.NVP_LAST_DISTANCE_EXPENSE_TYPE
| typeof ONYXKEYS.GPS_DRAFT_DETAILS
| typeof ONYXKEYS.SELF_DM_REPORT_ID
| typeof ONYXKEYS.ODOMETER_DRAFT
> = {
optimisticData: trackedExpenseOnyxData?.optimisticData ?? [],
successData: trackedExpenseOnyxData?.successData ?? [],
Expand Down Expand Up @@ -2443,6 +2444,14 @@ function trackExpense(params: CreateTrackExpenseParams) {
});
}

if (odometerStart !== undefined || odometerEnd !== undefined) {
onyxData?.successData?.push({
onyxMethod: Onyx.METHOD.SET,
key: ONYXKEYS.ODOMETER_DRAFT,
value: null,
});
}

const mileageRate = isCustomUnitRateIDForP2P(transaction) ? undefined : customUnitRateID;
if (shouldPlaySound) {
playSound(SOUNDS.DONE);
Expand Down
142 changes: 139 additions & 3 deletions src/libs/actions/IOU/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
import {deferOrExecuteWrite} from '@libs/deferredLayoutWrite';
import DistanceRequestUtils from '@libs/DistanceRequestUtils';
import {getMicroSecondOnyxErrorObject, getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
import {isLocalFile} from '@libs/fileDownload/FileUtils';
import {base64ToFile, convertFileObjectOrUriToBase64DataURL, isLocalFile} from '@libs/fileDownload/FileUtils';
import type {MinimalTransaction} from '@libs/Formula';
import getPlatform from '@libs/getPlatform';
import {getGPSRoutes, getGPSWaypoints} from '@libs/GPSDraftDetailsUtils';
import {calculateAmount as calculateIOUAmount, formatCurrentUserToAttendee, updateIOUOwnerAndTotal} from '@libs/IOUUtils';
import {formatPhoneNumber} from '@libs/LocalePhoneNumber';
Expand All @@ -29,7 +30,7 @@
import {buildNextStepNew, buildOptimisticNextStep} from '@libs/NextStepUtils';
import {roundToTwoDecimalPlaces} from '@libs/NumberUtils';
import * as NumberUtils from '@libs/NumberUtils';
import revokeOdometerImageUri from '@libs/OdometerImageUtils';
import revokeOdometerImageUri, {getOdometerImageUri} from '@libs/OdometerImageUtils';
import {getManagerMcTestParticipant} from '@libs/OptionsListUtils';
import {getCustomUnitID} from '@libs/PerDiemRequestUtils';
import {addSMSDomainIfPhoneNumber} from '@libs/PhoneNumber';
Expand Down Expand Up @@ -118,6 +119,13 @@

type IOURequestType = ValueOf<typeof CONST.IOU.REQUEST_TYPE>;

type SaveOdometerDraftParams = {
startReading?: number;
endReading?: number;
startImage?: FileObject | string | null;
endImage?: FileObject | string | null;
};

type OneOnOneIOUReport = OnyxTypes.Report | undefined | null;

type MoneyRequestInformation = {
Expand Down Expand Up @@ -374,7 +382,7 @@
};

let allPersonalDetails: OnyxTypes.PersonalDetailsList = {};
Onyx.connect({

Check warning on line 385 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
allPersonalDetails = value ?? {};
Expand Down Expand Up @@ -426,7 +434,7 @@
};

let allTransactions: NonNullable<OnyxCollection<OnyxTypes.Transaction>> = {};
Onyx.connect({

Check warning on line 437 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -440,7 +448,7 @@
});

let allTransactionDrafts: NonNullable<OnyxCollection<OnyxTypes.Transaction>> = {};
Onyx.connect({

Check warning on line 451 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION_DRAFT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -449,7 +457,7 @@
});

let allTransactionViolations: NonNullable<OnyxCollection<OnyxTypes.TransactionViolations>> = {};
Onyx.connect({

Check warning on line 460 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -463,7 +471,7 @@
});

let allPolicyTags: OnyxCollection<OnyxTypes.PolicyTagLists> = {};
Onyx.connect({

Check warning on line 474 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY_TAGS,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -476,7 +484,7 @@
});

let allReports: OnyxCollection<OnyxTypes.Report>;
Onyx.connect({

Check warning on line 487 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -485,7 +493,7 @@
});

let allReportNameValuePairs: OnyxCollection<OnyxTypes.ReportNameValuePairs>;
Onyx.connect({

Check warning on line 496 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -495,7 +503,7 @@

let deprecatedUserAccountID = -1;
let deprecatedCurrentUserEmail = '';
Onyx.connect({

Check warning on line 506 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (value) => {
deprecatedCurrentUserEmail = value?.email ?? '';
Expand All @@ -504,7 +512,7 @@
});

let deprecatedCurrentUserPersonalDetails: OnyxEntry<OnyxTypes.PersonalDetails>;
Onyx.connect({

Check warning on line 515 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
deprecatedCurrentUserPersonalDetails = value?.[deprecatedUserAccountID] ?? undefined;
Expand All @@ -512,7 +520,7 @@
});

let allReportActions: OnyxCollection<OnyxTypes.ReportActions>;
Onyx.connect({

Check warning on line 523 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
waitForCollectionCallback: true,
callback: (actions) => {
Expand Down Expand Up @@ -1112,6 +1120,117 @@
Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {comment: {customUnit: {quantity: distanceAsFloat, distanceUnit}}});
}

function clearOdometerDraft() {
Onyx.set(ONYXKEYS.ODOMETER_DRAFT, null);
}

async function serializeOdometerDraftImage(image: FileObject | string | null | undefined): Promise<string | undefined> {
if (!image) {
return undefined;
}

const imageURI = getOdometerImageUri(image);
if (!imageURI) {
return undefined;
}

if (getPlatform() !== CONST.PLATFORM.WEB) {
return imageURI;
}

try {
return await convertFileObjectOrUriToBase64DataURL(image);
} catch (error) {
Log.warn('Failed to serialize odometer draft image to base64', {error});
return imageURI;
}
}

function deserializeOdometerDraftImage(image: string | undefined, transactionID: string, imageType: OdometerImageType): FileObject | string | undefined {
if (!image) {
return undefined;
}

if (getPlatform() !== CONST.PLATFORM.WEB || !image.startsWith('data:')) {
return image;
}

try {
const file = base64ToFile(image, `odometer-${imageType}-${transactionID}.png`);
return {
uri: file.uri,
name: file.name,
type: file.type,
size: file.size,
};
} catch (error) {
Log.warn('Failed to deserialize odometer draft image from base64', {error});
return image;
}
}

async function saveOdometerDraft({startReading, endReading, startImage, endImage}: SaveOdometerDraftParams): Promise<void> {
const [serializedStartImage, serializedEndImage] = await Promise.all([serializeOdometerDraftImage(startImage), serializeOdometerDraftImage(endImage)]);
const hasDraftData = startReading !== undefined || endReading !== undefined || !!serializedStartImage || !!serializedEndImage;

if (!hasDraftData) {
clearOdometerDraft();
return;
}

const odometerDraft: OnyxTypes.OdometerDraft = {
...(startReading !== undefined && {odometerStartReading: startReading}),
...(endReading !== undefined && {odometerEndReading: endReading}),
...(serializedStartImage && {odometerStartImage: serializedStartImage}),
...(serializedEndImage && {odometerEndImage: serializedEndImage}),
};

Onyx.set(ONYXKEYS.ODOMETER_DRAFT, odometerDraft);
}

function hydrateOdometerDraftToTransaction(
transactionID: string,
isDraft: boolean,
odometerDraft: OnyxEntry<OnyxTypes.OdometerDraft>,
currentComment?: Partial<Comment>,
): Partial<Comment> | undefined {
if (!odometerDraft) {
return;
}

const commentUpdate: Partial<Comment> = {};

if (odometerDraft.odometerStartReading !== undefined) {
commentUpdate.odometerStart = odometerDraft.odometerStartReading;
}

if (odometerDraft.odometerEndReading !== undefined) {
commentUpdate.odometerEnd = odometerDraft.odometerEndReading;
}

const startImage = deserializeOdometerDraftImage(odometerDraft.odometerStartImage, transactionID, CONST.IOU.ODOMETER_IMAGE_TYPE.START);
if (startImage !== undefined) {
revokeOdometerImageUri(currentComment?.odometerStartImage, startImage);
commentUpdate.odometerStartImage = startImage;
}

Comment thread
jakubkalinski0 marked this conversation as resolved.
const endImage = deserializeOdometerDraftImage(odometerDraft.odometerEndImage, transactionID, CONST.IOU.ODOMETER_IMAGE_TYPE.END);
if (endImage !== undefined) {
revokeOdometerImageUri(currentComment?.odometerEndImage, endImage);
commentUpdate.odometerEndImage = endImage;
}

if (Object.keys(commentUpdate).length === 0) {
return;
}

Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {
comment: commentUpdate,
});

return commentUpdate;
}

/**
* Set the odometer readings for a transaction
*/
Expand Down Expand Up @@ -3223,7 +3342,13 @@
: receipt;

let parameters: CreateDistanceRequestParams;
let onyxData: OnyxData<BuildOnyxDataForMoneyRequestKeys | typeof ONYXKEYS.NVP_LAST_DISTANCE_EXPENSE_TYPE | typeof ONYXKEYS.NVP_RECENT_WAYPOINTS | typeof ONYXKEYS.GPS_DRAFT_DETAILS>;
let onyxData: OnyxData<
| BuildOnyxDataForMoneyRequestKeys
| typeof ONYXKEYS.NVP_LAST_DISTANCE_EXPENSE_TYPE
| typeof ONYXKEYS.NVP_RECENT_WAYPOINTS
| typeof ONYXKEYS.GPS_DRAFT_DETAILS
| typeof ONYXKEYS.ODOMETER_DRAFT
>;
let distanceIouReport: OnyxInputValue<OnyxTypes.Report> = null;
const sanitizedWaypoints = !isManualDistanceRequest ? sanitizeWaypointsForAPI(validWaypoints) : null;
if (iouType === CONST.IOU.TYPE.SPLIT) {
Expand Down Expand Up @@ -3407,6 +3532,14 @@
};
}

if (odometerStart !== undefined || odometerEnd !== undefined) {
onyxData?.successData?.push({
onyxMethod: Onyx.METHOD.SET,
key: ONYXKEYS.ODOMETER_DRAFT,
value: null,
Comment thread
jakubkalinski0 marked this conversation as resolved.
});
}

const recentServerValidatedWaypoints = recentWaypoints.filter((item) => !item.pendingAction);
onyxData?.failureData?.push({
onyxMethod: Onyx.METHOD.SET,
Expand Down Expand Up @@ -3733,6 +3866,7 @@

export {
clearMoneyRequest,
clearOdometerDraft,
createDistanceRequest,
createDraftTransaction,
getIOURequestPolicyID,
Expand All @@ -3753,8 +3887,10 @@
setMoneyRequestDescription,
setMoneyRequestDistance,
setMoneyRequestDistanceRate,
saveOdometerDraft,
setMoneyRequestOdometerReading,
setMoneyRequestOdometerImage,
hydrateOdometerDraftToTransaction,
removeMoneyRequestOdometerImage,
setMoneyRequestMerchant,
setMoneyRequestParticipants,
Expand Down
1 change: 1 addition & 0 deletions src/libs/actions/IOU/types/TrackedExpenseParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type TrackedExpenseParams = {
| typeof ONYXKEYS.NVP_LAST_DISTANCE_EXPENSE_TYPE
| typeof ONYXKEYS.GPS_DRAFT_DETAILS
| typeof ONYXKEYS.SELF_DM_REPORT_ID
| typeof ONYXKEYS.ODOMETER_DRAFT
>;
reportInformation: TrackedExpenseReportInformation;
transactionParams: TrackedExpenseTransactionParams;
Expand Down
49 changes: 49 additions & 0 deletions src/libs/fileDownload/FileUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,54 @@ function base64ToFile(base64: string, filename: string): File {
return file;
}

/**
* Converts a file-like input to a data URL string.
* Accepts either a `FileObject` (including `File`) or a URI string and returns
* a base64 data URL payload suitable for persisted storage.
*/
function convertFileObjectOrUriToBase64DataURL(fileObjectOrUri: FileObject | string): Promise<string> {
return new Promise((resolve, reject) => {
const source = typeof fileObjectOrUri === 'string' ? fileObjectOrUri : fileObjectOrUri.uri;
if (!source && !(typeof File !== 'undefined' && fileObjectOrUri instanceof File)) {
reject(new Error('No valid source to convert to base64'));
return;
}

if (source?.startsWith('data:')) {
resolve(source);
return;
}

const convertBlobToDataURL = (blob: Blob) => {
if (typeof FileReader === 'undefined') {
reject(new Error('FileReader is not available'));
return;
}

const reader = new FileReader();
reader.onloadend = () => {
if (typeof reader.result === 'string') {
resolve(reader.result);
return;
}
reject(new Error('Failed to convert blob to base64'));
};
reader.onerror = () => reject(new Error('Failed to read blob as base64'));
reader.readAsDataURL(blob);
};

if (typeof File !== 'undefined' && fileObjectOrUri instanceof File) {
convertBlobToDataURL(fileObjectOrUri);
return;
}

fetch(source ?? '')
.then((response) => response.blob())
.then((blob) => convertBlobToDataURL(blob))
.catch((error) => reject(error));
});
}

function validateImageForCorruption(file: FileObject): Promise<{width: number; height: number} | void> {
if (!Str.isImage(file.name ?? '') || !file.uri) {
return Promise.resolve();
Expand Down Expand Up @@ -884,6 +932,7 @@ export {
truncateFileNameToSafeLengthOnAndroid,
readFileAsync,
base64ToFile,
convertFileObjectOrUriToBase64DataURL,
isLocalFile,
validateImageForCorruption,
isImage,
Expand Down
Loading
Loading