From e8c5c3373e500c179b7741a33fd3d49c34763136 Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Wed, 18 Dec 2024 14:26:04 +0100 Subject: [PATCH 01/13] fix cors issues in attachments --- src/CONST.ts | 3 + src/components/FullscreenLoadingIndicator.tsx | 5 +- .../HTMLRenderers/ImageRenderer.tsx | 3 +- src/components/Image/index.tsx | 106 ++++++++++++++---- src/components/Image/types.ts | 17 ++- src/components/ImageView/index.tsx | 5 + src/components/ImageWithSizeCalculation.tsx | 6 + src/components/Lightbox/index.tsx | 10 ++ src/libs/E2E/actions/e2eLogin.ts | 1 + src/libs/actions/Session/Reauthenticator.ts | 81 +++++++++++++ src/libs/actions/Session/index.ts | 15 ++- .../Session/updateSessionAuthTokens.ts | 2 +- src/types/onyx/Session.ts | 3 + 13 files changed, 221 insertions(+), 36 deletions(-) create mode 100644 src/libs/actions/Session/Reauthenticator.ts diff --git a/src/CONST.ts b/src/CONST.ts index 4bfaad7b6d1b..024679e4dcce 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1459,6 +1459,8 @@ const CONST = { UNKNOWN: 'unknown', }, }, + // the number of milliseconds for an idle session to expire + SESSION_EXPIRATION_TIME_MS: 2 * 3600 * 1000, // 2 hours WEEK_STARTS_ON: 1, // Monday DEFAULT_TIME_ZONE: {automatic: true, selected: 'America/Los_Angeles'}, DEFAULT_ACCOUNT_DATA: {errors: null, success: '', isLoading: false}, @@ -1574,6 +1576,7 @@ const CONST = { ATTACHMENT_PREVIEW_ATTRIBUTE: 'src', ATTACHMENT_ORIGINAL_FILENAME_ATTRIBUTE: 'data-name', ATTACHMENT_LOCAL_URL_PREFIX: ['blob:', 'file:'], + ATTACHMENT_OR_RECEIPT_LOCAL_URL: /^https:\/\/(www\.)?([a-z0-9-_]+\.)?expensify.com\/(chat-attachments|receipts)/, ATTACHMENT_THUMBNAIL_URL_ATTRIBUTE: 'data-expensify-thumbnail-url', ATTACHMENT_THUMBNAIL_WIDTH_ATTRIBUTE: 'data-expensify-width', ATTACHMENT_THUMBNAIL_HEIGHT_ATTRIBUTE: 'data-expensify-height', diff --git a/src/components/FullscreenLoadingIndicator.tsx b/src/components/FullscreenLoadingIndicator.tsx index bd3082db5fa4..4fa393789934 100644 --- a/src/components/FullscreenLoadingIndicator.tsx +++ b/src/components/FullscreenLoadingIndicator.tsx @@ -6,15 +6,16 @@ import useThemeStyles from '@hooks/useThemeStyles'; type FullScreenLoadingIndicatorProps = { style?: StyleProp; + flag51888test?: boolean; }; -function FullScreenLoadingIndicator({style}: FullScreenLoadingIndicatorProps) { +function FullScreenLoadingIndicator({style, flag51888test}: FullScreenLoadingIndicatorProps) { const theme = useTheme(); const styles = useThemeStyles(); return ( diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx index c27eef1de91e..b1fbb582b0f6 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx @@ -53,7 +53,8 @@ function ImageRenderer({tnode}: ImageRendererProps) { // Concierge responder attachments are uploaded to S3 without any access // control and thus require no authToken to verify access. // - const attachmentSourceAttribute = htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]; + const attachmentSourceAttribute = + htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE] ?? (new RegExp(CONST.ATTACHMENT_OR_RECEIPT_LOCAL_URL, 'i').test(htmlAttribs.src) ? htmlAttribs.src : null); const isAttachmentOrReceipt = !!attachmentSourceAttribute; // Files created/uploaded/hosted by App should resolve from API ROOT. Other URLs aren't modified diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index 5fe1ba306400..c4cc67c28083 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -1,14 +1,20 @@ -import React, {useCallback, useContext, useMemo, useState} from 'react'; -import {withOnyx} from 'react-native-onyx'; +import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; +import AttachmentCarouselPagerContext from '@components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import {useSession} from '@components/OnyxProvider'; +import {isExpiredSession} from '@libs/actions/Session'; +import activateReauthenticator from '@libs/actions/Session/Reauthenticator'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import BaseImage from './BaseImage'; import {ImageBehaviorContext} from './ImageBehaviorContextProvider'; -import type {ImageOnLoadEvent, ImageOnyxProps, ImageOwnProps, ImageProps} from './types'; +import type {ImageOnLoadEvent, ImageProps} from './types'; -function Image({source: propsSource, isAuthTokenRequired = false, session, onLoad, objectPosition = CONST.IMAGE_OBJECT_POSITION.INITIAL, style, ...forwardedProps}: ImageProps) { +function Image({source: propsSource, isAuthTokenRequired = false, onLoad, objectPosition = CONST.IMAGE_OBJECT_POSITION.INITIAL, style, ...forwardedProps}: ImageProps) { const [aspectRatio, setAspectRatio] = useState(null); const isObjectPositionTop = objectPosition === CONST.IMAGE_OBJECT_POSITION.TOP; + const session = useSession(); + // we just need to know this value once !!!!! 51888 + const isUsedInCarousel = !!useContext(AttachmentCarouselPagerContext)?.pagerRef; const {shouldSetAspectRatioInStyle} = useContext(ImageBehaviorContext); @@ -31,16 +37,57 @@ function Image({source: propsSource, isAuthTokenRequired = false, session, onLoa const handleLoad = useCallback( (event: ImageOnLoadEvent) => { const {width, height} = event.nativeEvent; - onLoad?.(event); updateAspectRatio(width, height); }, [onLoad, updateAspectRatio], ); + + // an accepted session is either received less than 60s after the previous + // or is the first valid session since previous session expired + const isAcceptedSession = useCallback((sessionCreationDateDiff: number, sessionCreationDate: number) => { + return sessionCreationDateDiff < 60000 || (sessionCreationDateDiff >= CONST.SESSION_EXPIRATION_TIME_MS && new Date().getTime() - sessionCreationDate < 60000); + }, []); + + /** + * trying to figure out if the current session is expired or fresh from a necessary reauthentication + */ + const previousSessionAge = useRef(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const validSessionAge: number | undefined = useMemo(() => { + // for performance gain, the processing is reserved to attachments images only + if (!isAuthTokenRequired) { + return undefined; + } + if (session?.creationDate) { + if (previousSessionAge.current) { + // most likely a reauthentication happens + // but unless the calculated source is different from the previous, the image wont reload + if (isAcceptedSession(session.creationDate - previousSessionAge.current, session.creationDate)) { + return session.creationDate; + } + return previousSessionAge.current; + } + if (isExpiredSession(session.creationDate)) { + return new Date().getTime(); + } + return session.creationDate; + } + return undefined; + }, [session, isAuthTokenRequired, isAcceptedSession]); + useEffect(() => { + if (!isAuthTokenRequired) { + return; + } + previousSessionAge.current = validSessionAge; + }); + /** * Check if the image source is a URL - if so the `encryptedAuthToken` is appended * to the source. */ + // source could be a result of require or a number or an object but all are expected so no unsafe-assignment + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const source = useMemo(() => { if (typeof propsSource === 'object' && 'uri' in propsSource) { if (typeof propsSource.uri === 'number') { @@ -48,47 +95,62 @@ function Image({source: propsSource, isAuthTokenRequired = false, session, onLoa } const authToken = session?.encryptedAuthToken ?? null; if (isAuthTokenRequired && authToken) { - return { - ...propsSource, - headers: { - [CONST.CHAT_ATTACHMENT_TOKEN_KEY]: authToken, - }, - }; + if (!!session?.creationDate && !isExpiredSession(session.creationDate)) { + // session valid + return { + ...propsSource, + headers: { + [CONST.CHAT_ATTACHMENT_TOKEN_KEY]: authToken, + }, + }; + } + if (session) { + activateReauthenticator(session, isUsedInCarousel); + } + return undefined; } } return propsSource; // The session prop is not required, as it causes the image to reload whenever the session changes. For more information, please refer to issue #26034. + // but we still need the image to reload sometimes (exemple : when the current session is expired) + // by forcing a recalculation of the source (which value could indeed change) through the modification of the variable validSessionAge // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [propsSource, isAuthTokenRequired]); + }, [propsSource, isAuthTokenRequired, validSessionAge]); + useEffect(() => { + if (!isAuthTokenRequired || source !== undefined) { + return; + } + forwardedProps?.waitForSession?.(); + }, [source, isAuthTokenRequired, forwardedProps]); /** * If the image fails to load and the object position is top, we should hide the image by setting the opacity to 0. */ const shouldOpacityBeZero = isObjectPositionTop && !aspectRatio; + if (source === undefined && !!forwardedProps?.waitForSession) { + return undefined; + } + if (source === undefined) { + return ; + } return ( ); } -function imagePropsAreEqual(prevProps: ImageOwnProps, nextProps: ImageOwnProps) { +function imagePropsAreEqual(prevProps: ImageProps, nextProps: ImageProps) { return prevProps.source === nextProps.source; } -const ImageWithOnyx = React.memo( - withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, - })(Image), - imagePropsAreEqual, -); +const ImageWithOnyx = React.memo(Image, imagePropsAreEqual); ImageWithOnyx.displayName = 'Image'; diff --git a/src/components/Image/types.ts b/src/components/Image/types.ts index 27964d8a6764..49e545799c3d 100644 --- a/src/components/Image/types.ts +++ b/src/components/Image/types.ts @@ -1,19 +1,12 @@ import type {ImageSource} from 'expo-image'; import type {ImageRequireSource, ImageResizeMode, ImageStyle, ImageURISource, StyleProp} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; -import type {Session} from '@src/types/onyx'; type ExpoImageSource = ImageSource | number | ImageSource[]; type ImageObjectPosition = ValueOf; -type ImageOnyxProps = { - /** Session info for the currently logged in user. */ - session: OnyxEntry; -}; - type ImageOnLoadEvent = { nativeEvent: { width: number; @@ -53,8 +46,14 @@ type ImageOwnProps = BaseImageProps & { /** The object position of image */ objectPosition?: ImageObjectPosition; + + /** the image should wait for a valid session to reload + * At the moment this function is called, the image is not in cache anymore + * cf issue#51888 + */ + waitForSession?: () => void; }; -type ImageProps = ImageOnyxProps & ImageOwnProps; +type ImageProps = ImageOwnProps; -export type {BaseImageProps, ImageOwnProps, ImageOnyxProps, ImageProps, ExpoImageSource, ImageOnLoadEvent, ImageObjectPosition}; +export type {BaseImageProps, ImageOwnProps, ImageProps, ExpoImageSource, ImageOnLoadEvent, ImageObjectPosition}; diff --git a/src/components/ImageView/index.tsx b/src/components/ImageView/index.tsx index 0bce2fd38432..45bc8aa4ce47 100644 --- a/src/components/ImageView/index.tsx +++ b/src/components/ImageView/index.tsx @@ -238,6 +238,11 @@ function ImageView({isAuthTokenRequired = false, url, fileName, onError}: ImageV resizeMode={RESIZE_MODES.contain} onLoadStart={imageLoadingStart} onLoad={imageLoad} + waitForSession={() => { + setIsLoading(true); + setZoomScale(0); + setIsZoomed(false); + }} onError={onError} /> diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index ebea1a90efca..bda028293490 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -111,6 +111,12 @@ function ImageWithSizeCalculation({url, altText, style, onMeasure, onLoadFailure }} onError={onError} onLoad={imageLoadedSuccessfully} + waitForSession={() => { + // at the moment this function is called the image is not in cache anymore + isLoadedRef.current = false; + setIsImageCached(false); + setIsLoading(true); + }} objectPosition={objectPosition} /> {isLoading && !isImageCached && !isOffline && } diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index 9e1b007321cc..017241405ecb 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -234,6 +234,16 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan updateContentSize(e); setLightboxImageLoaded(true); }} + waitForSession={() => { + // only active lightbox should call this function + if (!isActive || isFallbackVisible || !isLightboxVisible || isLightboxImageLoaded) { + return; + } + // we only work on visible lightbox/canvas so we shouldnt really care about the fallback image (status) + setLightboxImageLoaded(false); + setContentSize(cachedImageDimensions.get(uri)); + setCanvasSize(undefined); // to set isCanvasLoading to true + }} /> diff --git a/src/libs/E2E/actions/e2eLogin.ts b/src/libs/E2E/actions/e2eLogin.ts index 25aee3c43d83..e325332d7dc7 100644 --- a/src/libs/E2E/actions/e2eLogin.ts +++ b/src/libs/E2E/actions/e2eLogin.ts @@ -55,6 +55,7 @@ export default function (): Promise { .then((response) => { Onyx.merge(ONYXKEYS.SESSION, { authToken: response.authToken, + creationDate: new Date().getTime(), email: e2eUserCredentials.email, }); console.debug('[E2E] Signed in finished!'); diff --git a/src/libs/actions/Session/Reauthenticator.ts b/src/libs/actions/Session/Reauthenticator.ts new file mode 100644 index 000000000000..fcc5dceb0c87 --- /dev/null +++ b/src/libs/actions/Session/Reauthenticator.ts @@ -0,0 +1,81 @@ +import Onyx from 'react-native-onyx'; +import {reauthenticate} from '@libs/Authentication'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type Session from '@src/types/onyx/Session'; + +let isOffline = false; +let active = false; +let currentActiveSession: Session = {}; +let timer: NodeJS.Timeout; +// the delay before requesting a reauthentication once activated +// we think in that timing a "natural" reauthentication could happen (a session expired in the carousel is the only exception) +// and we wish not to overlap and make a double reauthentication +const TIMING_BEFORE_REAUTHENTICATION_MS = 5000; // 5s +// the delay before requesting a reauthentication once activated +// used in cases we know a "natural" reauthentification wont happen (example : in the carousel) +// we can then reautheticate quicker as we will not be overlapping with any other reauthentication +const TIMING_BEFORE_QUICK_REAUTHENTICATION_MS = 10; // 10ms + +// We subscribe to network's online/offline status +Onyx.connect({ + key: ONYXKEYS.NETWORK, + callback: (network) => { + if (!network) { + return; + } + isOffline = !!network.shouldForceOffline || !!network.isOffline; + // if offline we make sure of that by requesting the status 0.5s later + if (!isOffline) { + return; + } + setTimeout(() => { + isOffline = !!network.shouldForceOffline || !!network.isOffline; + }, 500); + }, +}); + +// We subscribe to sessions changes +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: (value) => { + if (!value || isSameSession(value) || !active) { + return; + } + deactivate(); + }, +}); + +function isSameSession(session: Session): boolean { + return currentActiveSession.authToken === session.authToken && currentActiveSession.encryptedAuthToken === session.encryptedAuthToken; +} + +function deactivate() { + active = false; + currentActiveSession = {}; + clearInterval(timer); +} + +/** + * the reauthenticator is currently only used by attachment images and only when the current session is expired + * it will only request reauthentification only once between two receptions of different sessions from Onyx + * @param session the current session + * @returns + */ +function activate(session: Session, useQuickMode: boolean) { + if (!session || isSameSession(session) || isOffline) { + return; + } + currentActiveSession = session; + active = true; + // no need to Timers.register() + timer = setTimeout(tryReauthenticate, useQuickMode ? TIMING_BEFORE_QUICK_REAUTHENTICATION_MS : TIMING_BEFORE_REAUTHENTICATION_MS); +} + +function tryReauthenticate() { + if (isOffline || !active) { + return; + } + reauthenticate(); +} + +export default activate; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 1dbb01b008dd..b9e770621ccc 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -66,6 +66,9 @@ Onyx.connect({ key: ONYXKEYS.SESSION, callback: (value) => { session = value ?? {}; + if (!session.creationDate) { + session.creationDate = new Date().getTime(); + } if (session.authToken && authPromiseResolver) { authPromiseResolver(true); authPromiseResolver = null; @@ -115,6 +118,7 @@ function setSupportAuthToken(supportAuthToken: string, email: string, accountID: authToken: supportAuthToken, email, accountID, + creationDate: new Date().getTime(), }).then(() => { Log.info('[Supportal] Authtoken set'); }); @@ -212,6 +216,14 @@ function hasAuthToken(): boolean { return !!session.authToken; } +/** + * Indicates if the session which creation date is in parameter is expired + * @param sessionCreationDate the session creation date timestamp + */ +function isExpiredSession(sessionCreationDate: number): boolean { + return new Date().getTime() - sessionCreationDate >= CONST.SESSION_EXPIRATION_TIME_MS; +} + function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSession?: boolean, killHybridApp = true) { Log.info('Redirecting to Sign In because signOut() was called'); hideContextMenu(false); @@ -764,7 +776,7 @@ function invalidateCredentials() { function invalidateAuthToken() { NetworkStore.setAuthToken('pizza'); - Onyx.merge(ONYXKEYS.SESSION, {authToken: 'pizza'}); + Onyx.merge(ONYXKEYS.SESSION, {authToken: 'pizza', encryptedAuthToken: 'pizza', creationDate: new Date().getTime()}); } /** @@ -1231,6 +1243,7 @@ export { validateTwoFactorAuth, waitForUserSignIn, hasAuthToken, + isExpiredSession, canAnonymousUserAccessRoute, signInWithSupportAuthToken, isSupportAuthToken, diff --git a/src/libs/actions/Session/updateSessionAuthTokens.ts b/src/libs/actions/Session/updateSessionAuthTokens.ts index 6f90b60ef06d..8f98cf2fc5dd 100644 --- a/src/libs/actions/Session/updateSessionAuthTokens.ts +++ b/src/libs/actions/Session/updateSessionAuthTokens.ts @@ -2,5 +2,5 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; export default function updateSessionAuthTokens(authToken?: string, encryptedAuthToken?: string) { - Onyx.merge(ONYXKEYS.SESSION, {authToken, encryptedAuthToken}); + Onyx.merge(ONYXKEYS.SESSION, {authToken, encryptedAuthToken, creationDate: new Date().getTime()}); } diff --git a/src/types/onyx/Session.ts b/src/types/onyx/Session.ts index 7cb47a380717..0a15455ee7b6 100644 --- a/src/types/onyx/Session.ts +++ b/src/types/onyx/Session.ts @@ -36,6 +36,9 @@ type Session = { /** User signed in with short lived token */ signedInWithShortLivedAuthToken?: boolean; + + /** timestamp of the session creation date */ + creationDate?: number; }; export default Session; From b3371ae29fb642baddd710578ef29b8d0758fe5e Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Wed, 18 Dec 2024 14:35:01 +0100 Subject: [PATCH 02/13] fix cors issues in attachments --- src/components/Image/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index c4cc67c28083..b71668d89497 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -13,7 +13,7 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object const [aspectRatio, setAspectRatio] = useState(null); const isObjectPositionTop = objectPosition === CONST.IMAGE_OBJECT_POSITION.TOP; const session = useSession(); - // we just need to know this value once !!!!! 51888 + // we just need to know this value once !!!!! @51888 const isUsedInCarousel = !!useContext(AttachmentCarouselPagerContext)?.pagerRef; const {shouldSetAspectRatioInStyle} = useContext(ImageBehaviorContext); From 28f7a27879d2c014e11958fbb3217012bdabe6f8 Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Thu, 19 Dec 2024 14:39:11 +0100 Subject: [PATCH 03/13] fix cors issues --- src/CONST.ts | 2 +- .../HTMLRenderers/ImageRenderer.tsx | 1 + src/components/Image/index.tsx | 25 ++++++++++++--- src/components/ImageWithSizeCalculation.tsx | 5 ++- src/components/Lightbox/index.tsx | 3 +- src/libs/Middleware/Reauthentication.ts | 3 +- src/libs/actions/Session/Reauthenticator.ts | 31 ++++++++++++++----- 7 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 024679e4dcce..6c25b00f7f20 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1576,7 +1576,7 @@ const CONST = { ATTACHMENT_PREVIEW_ATTRIBUTE: 'src', ATTACHMENT_ORIGINAL_FILENAME_ATTRIBUTE: 'data-name', ATTACHMENT_LOCAL_URL_PREFIX: ['blob:', 'file:'], - ATTACHMENT_OR_RECEIPT_LOCAL_URL: /^https:\/\/(www\.)?([a-z0-9-_]+\.)?expensify.com\/(chat-attachments|receipts)/, + ATTACHMENT_OR_RECEIPT_LOCAL_URL: /^https:\/\/(www\.)?([a-z0-9_-]+\.)*expensify.com(:[0-9]+)?\/(chat-attachments|receipts)/, ATTACHMENT_THUMBNAIL_URL_ATTRIBUTE: 'data-expensify-thumbnail-url', ATTACHMENT_THUMBNAIL_WIDTH_ATTRIBUTE: 'data-expensify-width', ATTACHMENT_THUMBNAIL_HEIGHT_ATTRIBUTE: 'data-expensify-height', diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx index b1fbb582b0f6..571b2f54e882 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx @@ -55,6 +55,7 @@ function ImageRenderer({tnode}: ImageRendererProps) { // const attachmentSourceAttribute = htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE] ?? (new RegExp(CONST.ATTACHMENT_OR_RECEIPT_LOCAL_URL, 'i').test(htmlAttribs.src) ? htmlAttribs.src : null); + console.log(`@51888 htmlAttribs `, htmlAttribs); const isAttachmentOrReceipt = !!attachmentSourceAttribute; // Files created/uploaded/hosted by App should resolve from API ROOT. Other URLs aren't modified diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index b71668d89497..bf35ec1cebb1 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -13,8 +13,10 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object const [aspectRatio, setAspectRatio] = useState(null); const isObjectPositionTop = objectPosition === CONST.IMAGE_OBJECT_POSITION.TOP; const session = useSession(); - // we just need to know this value once !!!!! @51888 - const isUsedInCarousel = !!useContext(AttachmentCarouselPagerContext)?.pagerRef; + + if (isAuthTokenRequired && session?.creationDate) { + console.log(`@51888 image initialized with session ${session.authToken?.substring(0, 10)} creationDate ${new Date(session.creationDate).toISOString()} `); + } const {shouldSetAspectRatioInStyle} = useContext(ImageBehaviorContext); @@ -64,13 +66,21 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object // most likely a reauthentication happens // but unless the calculated source is different from the previous, the image wont reload if (isAcceptedSession(session.creationDate - previousSessionAge.current, session.creationDate)) { + console.log( + `@51888 setting validSessionAge to accepted session ${session.authToken?.substring(0, 10)} creationDate ${new Date( + session.creationDate, + ).toISOString()}} received less than 60s ago or newer from 2H`, + ); return session.creationDate; } + console.log(`@51888 setting validSessionAge to unchanged`); return previousSessionAge.current; } if (isExpiredSession(session.creationDate)) { + console.log(`@51888 setting validSessionAge to now as session is expired`); return new Date().getTime(); } + console.log(`@51888 setting validSessionAge to current session ${session.authToken?.substring(0, 10)} ${new Date(session.creationDate).toISOString()}`); return session.creationDate; } return undefined; @@ -80,6 +90,7 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object return; } previousSessionAge.current = validSessionAge; + console.log(`@51888 useEffect setting previousSessionAge to ${validSessionAge ? new Date(validSessionAge).toISOString() : validSessionAge}`); }); /** @@ -96,6 +107,7 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object const authToken = session?.encryptedAuthToken ?? null; if (isAuthTokenRequired && authToken) { if (!!session?.creationDate && !isExpiredSession(session.creationDate)) { + console.log(`@51888 setting source with token and session ${session.authToken?.substring(0, 10)} creationDate ${new Date(session.creationDate).toISOString()} `); // session valid return { ...propsSource, @@ -104,8 +116,9 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object }, }; } + console.log(`@51888 source as spinner `); if (session) { - activateReauthenticator(session, isUsedInCarousel); + activateReauthenticator(session); } return undefined; } @@ -120,7 +133,11 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object if (!isAuthTokenRequired || source !== undefined) { return; } - forwardedProps?.waitForSession?.(); + if (forwardedProps?.waitForSession) { + forwardedProps.waitForSession(); + console.log(`@51888 forwardedProps.waitForSession() `); + } + //forwardedProps?.waitForSession?.(); }, [source, isAuthTokenRequired, forwardedProps]); /** diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index bda028293490..3c359a2c4b70 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -94,7 +94,7 @@ function ImageWithSizeCalculation({url, altText, style, onMeasure, onLoadFailure }, 200); return () => clearTimeout(timeout); }, [isLoading]); - + console.log(`@51888 ImageWitrhSize spinner status ${isLoading && !isImageCached && !isOffline} isLoading ${isLoading} !isImageCached ${!isImageCached} !isOffline ${!isOffline}`); return ( { if (isLoadedRef.current ?? isLoading) { + console.log(`@51888 ImageWitrhSize is not showing spinner`); return; } + console.log(`@51888 ImageWitrhSize should show spinner`); setIsLoading(true); }} onError={onError} onLoad={imageLoadedSuccessfully} waitForSession={() => { + console.log(`@51888 ImageWitrhSize waitForSession`); // at the moment this function is called the image is not in cache anymore isLoadedRef.current = false; setIsImageCached(false); diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index 017241405ecb..4a34841dbfb3 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -43,7 +43,7 @@ type LightboxProps = { function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChangedProp, onError, style, zoomRange = DEFAULT_ZOOM_RANGE}: LightboxProps) { const StyleUtils = useStyleUtils(); const styles = useThemeStyles(); - + console.log(`@51888 lightbox uri ${uri}`); /** * React hooks must be used in the render function of the component at top-level and unconditionally. * Therefore, in order to provide a default value for "isPagerScrolling" if the "AttachmentCarouselPagerContext" is not available, @@ -235,6 +235,7 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan setLightboxImageLoaded(true); }} waitForSession={() => { + console.log(`@51888 lightbox waitForSession`); // only active lightbox should call this function if (!isActive || isFallbackVisible || !isLightboxVisible || isLightboxImageLoaded) { return; diff --git a/src/libs/Middleware/Reauthentication.ts b/src/libs/Middleware/Reauthentication.ts index 9d95fa8af873..d271bbf01dcb 100644 --- a/src/libs/Middleware/Reauthentication.ts +++ b/src/libs/Middleware/Reauthentication.ts @@ -97,7 +97,7 @@ const Reauthentication: Middleware = (response, request, isFromSequentialQueue) MainQueue.replay(request); return data; } - + console.log(`@51888 middleware reauthentication has reauthenticate at ${new Date().toISOString()}`); return reauthenticate(request?.commandName) .then((authenticateResponse) => { if (isFromSequentialQueue || apiRequestType === CONST.API_REQUEST_TYPE.MAKE_REQUEST_WITH_SIDE_EFFECTS) { @@ -134,6 +134,7 @@ const Reauthentication: Middleware = (response, request, isFromSequentialQueue) return data; }) .catch((error) => { + console.log(`@51888 middleware reauthenttication failed reauthenticate`); // If the request is on the sequential queue, re-throw the error so we can decide to retry or not if (isFromSequentialQueue) { throw error; diff --git a/src/libs/actions/Session/Reauthenticator.ts b/src/libs/actions/Session/Reauthenticator.ts index fcc5dceb0c87..5626976c1913 100644 --- a/src/libs/actions/Session/Reauthenticator.ts +++ b/src/libs/actions/Session/Reauthenticator.ts @@ -10,11 +10,8 @@ let timer: NodeJS.Timeout; // the delay before requesting a reauthentication once activated // we think in that timing a "natural" reauthentication could happen (a session expired in the carousel is the only exception) // and we wish not to overlap and make a double reauthentication -const TIMING_BEFORE_REAUTHENTICATION_MS = 5000; // 5s -// the delay before requesting a reauthentication once activated -// used in cases we know a "natural" reauthentification wont happen (example : in the carousel) -// we can then reautheticate quicker as we will not be overlapping with any other reauthentication -const TIMING_BEFORE_QUICK_REAUTHENTICATION_MS = 10; // 10ms +const TIMING_BEFORE_REAUTHENTICATION_MS = 3500; // 3.5s + // We subscribe to network's online/offline status Onyx.connect({ @@ -30,6 +27,9 @@ Onyx.connect({ } setTimeout(() => { isOffline = !!network.shouldForceOffline || !!network.isOffline; + if (isOffline) { + console.log(`@51888 reauthenticator is Offline`); + } }, 500); }, }); @@ -38,9 +38,20 @@ Onyx.connect({ Onyx.connect({ key: ONYXKEYS.SESSION, callback: (value) => { + if (value?.creationDate) { + console.log( + `@51888 reauthenticator new session received ${value?.authToken?.substring(0, 10)} creationDate ${new Date( + value?.creationDate, + ).toISOString()} , active ${active} isOffline? ${isOffline} currentActiveSession ${currentActiveSession?.authToken?.substring(0, 10)} creationDate ${ + currentActiveSession?.creationDate ? new Date(currentActiveSession?.creationDate).toISOString() : '' + }`, + ); + } if (!value || isSameSession(value) || !active) { + console.log(`@51888 reauthenticator new session received but not doing anything`); return; } + console.log(`@51888 reauthenticator new session received, deactivating`); deactivate(); }, }); @@ -50,6 +61,7 @@ function isSameSession(session: Session): boolean { } function deactivate() { + console.log(`@51888 reauthenticator deactivating`); active = false; currentActiveSession = {}; clearInterval(timer); @@ -61,20 +73,23 @@ function deactivate() { * @param session the current session * @returns */ -function activate(session: Session, useQuickMode: boolean) { +function activate(session: Session) { if (!session || isSameSession(session) || isOffline) { + console.log(`@51888 reauthenticator activation requested but already active or offline`); return; - } + } + console.log(`@51888 reauthenticator activating`); currentActiveSession = session; active = true; // no need to Timers.register() - timer = setTimeout(tryReauthenticate, useQuickMode ? TIMING_BEFORE_QUICK_REAUTHENTICATION_MS : TIMING_BEFORE_REAUTHENTICATION_MS); + timer = setTimeout(tryReauthenticate, TIMING_BEFORE_REAUTHENTICATION_MS); } function tryReauthenticate() { if (isOffline || !active) { return; } + console.log(`@51888 reauthenticator reauthenticating at ${new Date().toISOString()}`); reauthenticate(); } From 88cf61ab767ab5a8a87cc44c4f54234fe64778df Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Thu, 19 Dec 2024 15:17:55 +0100 Subject: [PATCH 04/13] Update --- src/components/Image/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index b71668d89497..c4cc67c28083 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -13,7 +13,7 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object const [aspectRatio, setAspectRatio] = useState(null); const isObjectPositionTop = objectPosition === CONST.IMAGE_OBJECT_POSITION.TOP; const session = useSession(); - // we just need to know this value once !!!!! @51888 + // we just need to know this value once !!!!! 51888 const isUsedInCarousel = !!useContext(AttachmentCarouselPagerContext)?.pagerRef; const {shouldSetAspectRatioInStyle} = useContext(ImageBehaviorContext); From bc0c48b9e144f27f303aea21ace2f2e086cd5461 Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Thu, 19 Dec 2024 23:33:29 +0100 Subject: [PATCH 05/13] fix cors issues --- .../Attachments/AttachmentCarousel/CarouselItem.tsx | 2 +- .../AttachmentCarousel/extractAttachments.ts | 3 ++- .../Attachments/AttachmentCarousel/index.tsx | 3 +++ .../AttachmentView/AttachmentViewImage/index.tsx | 1 + src/components/Attachments/AttachmentView/index.tsx | 1 + src/components/Image/index.tsx | 1 - src/components/ImageView/index.tsx | 5 ++++- src/components/Lightbox/index.tsx | 11 +++++------ 8 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx b/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx index 5800e92cc4f4..9c4cc52e0e0b 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx @@ -69,7 +69,7 @@ function CarouselItem({item, onPress, isFocused, isModalHovered}: CarouselItemPr {children} ); } - + console.log(`@51888 carouselItem isAuthTokenRequired ${item.isAuthTokenRequired} previewSource ${item.previewSource} source ${item.source} fileName ${item.file?.name} `, item.source); return ( diff --git a/src/components/Attachments/AttachmentCarousel/extractAttachments.ts b/src/components/Attachments/AttachmentCarousel/extractAttachments.ts index c0010af468af..217d11743965 100644 --- a/src/components/Attachments/AttachmentCarousel/extractAttachments.ts +++ b/src/components/Attachments/AttachmentCarousel/extractAttachments.ts @@ -58,7 +58,8 @@ function extractAttachments( } if (name === 'img' && attribs.src) { - const expensifySource = attribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]; + console.log(`@51888 extractAttachments attribs `, attribs); + const expensifySource = attribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE] ?? (new RegExp(CONST.ATTACHMENT_OR_RECEIPT_LOCAL_URL, 'i').test(attribs.src) ? attribs.src : null); const source = tryResolveUrlFromApiRoot(expensifySource || attribs.src); const previewSource = tryResolveUrlFromApiRoot(attribs.src); const sourceLinkKey = `${source}|${currentLink}`; diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx index 50caaac3dd81..2bf856443493 100644 --- a/src/components/Attachments/AttachmentCarousel/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.tsx @@ -90,8 +90,10 @@ function AttachmentCarousel({report, source, onNavigate, setDownloadButtonVisibi let newAttachments: Attachment[] = []; if (type === CONST.ATTACHMENT_TYPE.NOTE && accountID) { newAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {privateNotes: report.privateNotes, accountID, report}); + console.log(`@51888 attachmentcarousel ${newAttachments?.length} newAttachments1 created `, newAttachments); } else { newAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.REPORT, {parentReportAction, reportActions: reportActions ?? undefined, report}); + console.log(`@51888 attachmentcarousel ${newAttachments?.length} newAttachments2 created `, newAttachments); } if (isEqual(attachments, newAttachments)) { @@ -130,6 +132,7 @@ function AttachmentCarousel({report, source, onNavigate, setDownloadButtonVisibi onNavigate(attachment); } } + console.log(`@51888 attachmentcarousel ${attachments?.length} attachments created `, attachments); }, [reportActions, parentReportActions, compareImage, attachments, setDownloadButtonVisibility, onNavigate, accountID, type, report]); // Scroll position is affected when window width is resized, so we readjust it on width changes diff --git a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.tsx b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.tsx index 0ec2bb5ef0b5..31bd0eda7c52 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.tsx @@ -18,6 +18,7 @@ type AttachmentViewImageProps = Pick(); const {isOffline} = useNetwork(); + console.log(`@51888 ImageView isAuthTokenRequired ${isAuthTokenRequired} url ${url} fileName ${fileName}`); const scrollableRef = useRef(null); const canUseTouchScreen = DeviceCapabilities.canUseTouchScreen(); @@ -49,7 +50,7 @@ function ImageView({isAuthTokenRequired = false, url, fileName, onError}: ImageV const newZoomScale = Math.min(newContainerWidth / newImageWidth, newContainerHeight / newImageHeight); setZoomScale(newZoomScale); }; - + const onContainerLayoutChanged = (e: LayoutChangeEvent) => { const {width, height} = e.nativeEvent.layout; setScale(width, height, imgWidth, imgHeight); @@ -204,6 +205,7 @@ function ImageView({isAuthTokenRequired = false, url, fileName, onError}: ImageV } if (canUseTouchScreen) { + console.log(`@51888 ImageView isAuthTokenRequired ${isAuthTokenRequired} displaying lightbox url ${url} fileName ${fileName}`); return ( ); } + console.log(`@51888 ImageView isAuthTokenRequired ${isAuthTokenRequired} displaying Image url ${url} fileName ${fileName}`); return ( { if (isLightboxVisible) { @@ -235,15 +235,14 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan setLightboxImageLoaded(true); }} waitForSession={() => { - console.log(`@51888 lightbox waitForSession`); // only active lightbox should call this function - if (!isActive || isFallbackVisible || !isLightboxVisible || isLightboxImageLoaded) { + if (!isActive || isFallbackVisible || !isLightboxVisible) { + console.log(`@51888 lightbox returns, waitForSession ${uri} isActive ${isActive} isFallbackVisible ${isFallbackVisible} isLightboxVisible ${isLightboxVisible} isLightboxImageLoaded ${isLightboxImageLoaded}`); return; } - // we only work on visible lightbox/canvas so we shouldnt really care about the fallback image (status) - setLightboxImageLoaded(false); + console.log(`@51888 HERE lightbox waitForSession ${uri}`); setContentSize(cachedImageDimensions.get(uri)); - setCanvasSize(undefined); // to set isCanvasLoading to true + setLightboxImageLoaded(false); }} /> From c7aab357e28ba0fc91bc27e6aeaadf4d47a25d79 Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Fri, 20 Dec 2024 15:23:22 +0100 Subject: [PATCH 06/13] fix cors issues --- .project | 11 +++++++++ src/ROUTES.ts | 2 +- .../AttachmentCarousel/CarouselItem.tsx | 2 +- .../AttachmentCarousel/extractAttachments.ts | 1 - .../Attachments/AttachmentCarousel/index.tsx | 3 --- .../AttachmentViewImage/index.tsx | 1 - .../Attachments/AttachmentView/index.tsx | 1 - src/components/EReceiptThumbnail.tsx | 1 - src/components/FullscreenLoadingIndicator.tsx | 5 ++-- .../HTMLRenderers/ImageRenderer.tsx | 5 ++-- src/components/Image/index.tsx | 23 ++----------------- src/components/ImageView/index.tsx | 4 +--- src/components/ImageWithSizeCalculation.tsx | 5 +--- src/components/Lightbox/index.tsx | 6 ++--- src/components/ShowContextMenuContext.ts | 4 ++-- .../VideoPlayerThumbnail.tsx | 2 +- src/libs/Middleware/Reauthentication.ts | 3 +-- src/libs/actions/Session/Reauthenticator.ts | 18 --------------- src/libs/actions/Session/index.ts | 2 +- 19 files changed, 28 insertions(+), 71 deletions(-) create mode 100644 .project diff --git a/.project b/.project new file mode 100644 index 000000000000..99a5444bf1b6 --- /dev/null +++ b/.project @@ -0,0 +1,11 @@ + + + App + + + + + + + + diff --git a/src/ROUTES.ts b/src/ROUTES.ts index f48a5cae92f0..1814bbb465a9 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -327,7 +327,7 @@ const ROUTES = { ATTACHMENTS: { route: 'attachment', getRoute: ( - reportID: string, + reportID: string | undefined, type: ValueOf, url: string, accountID?: number, diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx b/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx index 9c4cc52e0e0b..5800e92cc4f4 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx @@ -69,7 +69,7 @@ function CarouselItem({item, onPress, isFocused, isModalHovered}: CarouselItemPr {children} ); } - console.log(`@51888 carouselItem isAuthTokenRequired ${item.isAuthTokenRequired} previewSource ${item.previewSource} source ${item.source} fileName ${item.file?.name} `, item.source); + return ( diff --git a/src/components/Attachments/AttachmentCarousel/extractAttachments.ts b/src/components/Attachments/AttachmentCarousel/extractAttachments.ts index 217d11743965..62660980f56f 100644 --- a/src/components/Attachments/AttachmentCarousel/extractAttachments.ts +++ b/src/components/Attachments/AttachmentCarousel/extractAttachments.ts @@ -58,7 +58,6 @@ function extractAttachments( } if (name === 'img' && attribs.src) { - console.log(`@51888 extractAttachments attribs `, attribs); const expensifySource = attribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE] ?? (new RegExp(CONST.ATTACHMENT_OR_RECEIPT_LOCAL_URL, 'i').test(attribs.src) ? attribs.src : null); const source = tryResolveUrlFromApiRoot(expensifySource || attribs.src); const previewSource = tryResolveUrlFromApiRoot(attribs.src); diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx index 2bf856443493..50caaac3dd81 100644 --- a/src/components/Attachments/AttachmentCarousel/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.tsx @@ -90,10 +90,8 @@ function AttachmentCarousel({report, source, onNavigate, setDownloadButtonVisibi let newAttachments: Attachment[] = []; if (type === CONST.ATTACHMENT_TYPE.NOTE && accountID) { newAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {privateNotes: report.privateNotes, accountID, report}); - console.log(`@51888 attachmentcarousel ${newAttachments?.length} newAttachments1 created `, newAttachments); } else { newAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.REPORT, {parentReportAction, reportActions: reportActions ?? undefined, report}); - console.log(`@51888 attachmentcarousel ${newAttachments?.length} newAttachments2 created `, newAttachments); } if (isEqual(attachments, newAttachments)) { @@ -132,7 +130,6 @@ function AttachmentCarousel({report, source, onNavigate, setDownloadButtonVisibi onNavigate(attachment); } } - console.log(`@51888 attachmentcarousel ${attachments?.length} attachments created `, attachments); }, [reportActions, parentReportActions, compareImage, attachments, setDownloadButtonVisibility, onNavigate, accountID, type, report]); // Scroll position is affected when window width is resized, so we readjust it on width changes diff --git a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.tsx b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.tsx index 31bd0eda7c52..0ec2bb5ef0b5 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.tsx @@ -18,7 +18,6 @@ type AttachmentViewImageProps = Pick backgroundImages[colorCode], [colorCode]); const colorStyles = StyleUtils.getEReceiptColorStyles(colorCode); diff --git a/src/components/FullscreenLoadingIndicator.tsx b/src/components/FullscreenLoadingIndicator.tsx index 4fa393789934..bd3082db5fa4 100644 --- a/src/components/FullscreenLoadingIndicator.tsx +++ b/src/components/FullscreenLoadingIndicator.tsx @@ -6,16 +6,15 @@ import useThemeStyles from '@hooks/useThemeStyles'; type FullScreenLoadingIndicatorProps = { style?: StyleProp; - flag51888test?: boolean; }; -function FullScreenLoadingIndicator({style, flag51888test}: FullScreenLoadingIndicatorProps) { +function FullScreenLoadingIndicator({style}: FullScreenLoadingIndicatorProps) { const theme = useTheme(); const styles = useThemeStyles(); return ( diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx index 571b2f54e882..36b7085275e0 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx @@ -55,7 +55,6 @@ function ImageRenderer({tnode}: ImageRendererProps) { // const attachmentSourceAttribute = htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE] ?? (new RegExp(CONST.ATTACHMENT_OR_RECEIPT_LOCAL_URL, 'i').test(htmlAttribs.src) ? htmlAttribs.src : null); - console.log(`@51888 htmlAttribs `, htmlAttribs); const isAttachmentOrReceipt = !!attachmentSourceAttribute; // Files created/uploaded/hosted by App should resolve from API ROOT. Other URLs aren't modified @@ -107,14 +106,14 @@ function ImageRenderer({tnode}: ImageRendererProps) { } const attachmentLink = tnode.parent?.attributes?.href; - const route = ROUTES.ATTACHMENTS?.getRoute(reportID ?? '-1', type, source, accountID, isAttachmentOrReceipt, fileName, attachmentLink); + const route = ROUTES.ATTACHMENTS?.getRoute(reportID, type, source, accountID, isAttachmentOrReceipt, fileName, attachmentLink); Navigation.navigate(route); }} onLongPress={(event) => { if (isDisabled) { return; } - showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)); + showContextMenuForReport(event, anchor, report?.reportID, action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)); }} shouldUseHapticsOnLongPress accessibilityRole={CONST.ROLE.BUTTON} diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index 045f74d68908..c2a7c8b75ae5 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -13,10 +13,6 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object const isObjectPositionTop = objectPosition === CONST.IMAGE_OBJECT_POSITION.TOP; const session = useSession(); - if (isAuthTokenRequired && session?.creationDate) { - console.log(`@51888 image initialized with session ${session.authToken?.substring(0, 10)} creationDate ${new Date(session.creationDate).toISOString()} `); - } - const {shouldSetAspectRatioInStyle} = useContext(ImageBehaviorContext); const updateAspectRatio = useCallback( @@ -65,21 +61,13 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object // most likely a reauthentication happens // but unless the calculated source is different from the previous, the image wont reload if (isAcceptedSession(session.creationDate - previousSessionAge.current, session.creationDate)) { - console.log( - `@51888 setting validSessionAge to accepted session ${session.authToken?.substring(0, 10)} creationDate ${new Date( - session.creationDate, - ).toISOString()}} received less than 60s ago or newer from 2H`, - ); return session.creationDate; } - console.log(`@51888 setting validSessionAge to unchanged`); return previousSessionAge.current; } if (isExpiredSession(session.creationDate)) { - console.log(`@51888 setting validSessionAge to now as session is expired`); return new Date().getTime(); } - console.log(`@51888 setting validSessionAge to current session ${session.authToken?.substring(0, 10)} ${new Date(session.creationDate).toISOString()}`); return session.creationDate; } return undefined; @@ -89,7 +77,6 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object return; } previousSessionAge.current = validSessionAge; - console.log(`@51888 useEffect setting previousSessionAge to ${validSessionAge ? new Date(validSessionAge).toISOString() : validSessionAge}`); }); /** @@ -106,7 +93,6 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object const authToken = session?.encryptedAuthToken ?? null; if (isAuthTokenRequired && authToken) { if (!!session?.creationDate && !isExpiredSession(session.creationDate)) { - console.log(`@51888 setting source with token and session ${session.authToken?.substring(0, 10)} creationDate ${new Date(session.creationDate).toISOString()} `); // session valid return { ...propsSource, @@ -115,7 +101,6 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object }, }; } - console.log(`@51888 source as spinner `); if (session) { activateReauthenticator(session); } @@ -132,11 +117,7 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object if (!isAuthTokenRequired || source !== undefined) { return; } - if (forwardedProps?.waitForSession) { - forwardedProps.waitForSession(); - console.log(`@51888 forwardedProps.waitForSession() `); - } - //forwardedProps?.waitForSession?.(); + forwardedProps?.waitForSession?.(); }, [source, isAuthTokenRequired, forwardedProps]); /** @@ -148,7 +129,7 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object return undefined; } if (source === undefined) { - return ; + return ; } return ( (); const {isOffline} = useNetwork(); - console.log(`@51888 ImageView isAuthTokenRequired ${isAuthTokenRequired} url ${url} fileName ${fileName}`); const scrollableRef = useRef(null); const canUseTouchScreen = DeviceCapabilities.canUseTouchScreen(); @@ -205,7 +204,6 @@ function ImageView({isAuthTokenRequired = false, url, fileName, onError}: ImageV } if (canUseTouchScreen) { - console.log(`@51888 ImageView isAuthTokenRequired ${isAuthTokenRequired} displaying lightbox url ${url} fileName ${fileName}`); return ( ); } - console.log(`@51888 ImageView isAuthTokenRequired ${isAuthTokenRequired} displaying Image url ${url} fileName ${fileName}`); + return ( clearTimeout(timeout); }, [isLoading]); - console.log(`@51888 ImageWitrhSize spinner status ${isLoading && !isImageCached && !isOffline} isLoading ${isLoading} !isImageCached ${!isImageCached} !isOffline ${!isOffline}`); + return ( { if (isLoadedRef.current ?? isLoading) { - console.log(`@51888 ImageWitrhSize is not showing spinner`); return; } - console.log(`@51888 ImageWitrhSize should show spinner`); setIsLoading(true); }} onError={onError} onLoad={imageLoadedSuccessfully} waitForSession={() => { - console.log(`@51888 ImageWitrhSize waitForSession`); // at the moment this function is called the image is not in cache anymore isLoadedRef.current = false; setIsImageCached(false); diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index 0674a01f08aa..21fbf7e373cb 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -43,7 +43,7 @@ type LightboxProps = { function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChangedProp, onError, style, zoomRange = DEFAULT_ZOOM_RANGE}: LightboxProps) { const StyleUtils = useStyleUtils(); const styles = useThemeStyles(); - console.log(`@51888 lightbox uri ${uri}`); + /** * React hooks must be used in the render function of the component at top-level and unconditionally. * Therefore, in order to provide a default value for "isPagerScrolling" if the "AttachmentCarouselPagerContext" is not available, @@ -163,7 +163,7 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan const isFallbackStillLoading = isFallbackVisible && !isFallbackImageLoaded; const isLightboxStillLoading = isLightboxVisible && !isLightboxImageLoaded; const isLoading = isActive && (isCanvasLoading || isFallbackStillLoading || isLightboxStillLoading); - console.log(`@51888 lightbox ${uri} isActive ${isActive} isCanvasLoading ${isCanvasLoading} isFallbackStillLoading ${isFallbackStillLoading} isLightboxStillLoading ${isLightboxStillLoading} isLoading ${isLoading}`); + // Resets the lightbox when it becomes inactive useEffect(() => { if (isLightboxVisible) { @@ -237,10 +237,8 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan waitForSession={() => { // only active lightbox should call this function if (!isActive || isFallbackVisible || !isLightboxVisible) { - console.log(`@51888 lightbox returns, waitForSession ${uri} isActive ${isActive} isFallbackVisible ${isFallbackVisible} isLightboxVisible ${isLightboxVisible} isLightboxImageLoaded ${isLightboxImageLoaded}`); return; } - console.log(`@51888 HERE lightbox waitForSession ${uri}`); setContentSize(cachedImageDimensions.get(uri)); setLightboxImageLoaded(false); }} diff --git a/src/components/ShowContextMenuContext.ts b/src/components/ShowContextMenuContext.ts index 6fefa987fac3..3bd98e90eec3 100644 --- a/src/components/ShowContextMenuContext.ts +++ b/src/components/ShowContextMenuContext.ts @@ -44,7 +44,7 @@ ShowContextMenuContext.displayName = 'ShowContextMenuContext'; function showContextMenuForReport( event: GestureResponderEvent | MouseEvent, anchor: ContextMenuAnchor, - reportID: string, + reportID: string | undefined, action: OnyxEntry, checkIfContextMenuActive: () => void, isArchivedRoom = false, @@ -60,7 +60,7 @@ function showContextMenuForReport( anchor, reportID, action?.reportActionID, - ReportUtils.getOriginalReportID(reportID, action), + reportID ? ReportUtils.getOriginalReportID(reportID, action) : undefined, undefined, checkIfContextMenuActive, checkIfContextMenuActive, diff --git a/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx b/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx index 832b5eef45f0..7a5b2fafde98 100644 --- a/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx +++ b/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx @@ -30,7 +30,7 @@ type VideoPlayerThumbnailProps = { function VideoPlayerThumbnail({thumbnailUrl, onPress, accessibilityLabel, isDeleted}: VideoPlayerThumbnailProps) { const styles = useThemeStyles(); - + return ( {!!thumbnailUrl && ( diff --git a/src/libs/Middleware/Reauthentication.ts b/src/libs/Middleware/Reauthentication.ts index d271bbf01dcb..9d95fa8af873 100644 --- a/src/libs/Middleware/Reauthentication.ts +++ b/src/libs/Middleware/Reauthentication.ts @@ -97,7 +97,7 @@ const Reauthentication: Middleware = (response, request, isFromSequentialQueue) MainQueue.replay(request); return data; } - console.log(`@51888 middleware reauthentication has reauthenticate at ${new Date().toISOString()}`); + return reauthenticate(request?.commandName) .then((authenticateResponse) => { if (isFromSequentialQueue || apiRequestType === CONST.API_REQUEST_TYPE.MAKE_REQUEST_WITH_SIDE_EFFECTS) { @@ -134,7 +134,6 @@ const Reauthentication: Middleware = (response, request, isFromSequentialQueue) return data; }) .catch((error) => { - console.log(`@51888 middleware reauthenttication failed reauthenticate`); // If the request is on the sequential queue, re-throw the error so we can decide to retry or not if (isFromSequentialQueue) { throw error; diff --git a/src/libs/actions/Session/Reauthenticator.ts b/src/libs/actions/Session/Reauthenticator.ts index 5626976c1913..493831811da7 100644 --- a/src/libs/actions/Session/Reauthenticator.ts +++ b/src/libs/actions/Session/Reauthenticator.ts @@ -27,9 +27,6 @@ Onyx.connect({ } setTimeout(() => { isOffline = !!network.shouldForceOffline || !!network.isOffline; - if (isOffline) { - console.log(`@51888 reauthenticator is Offline`); - } }, 500); }, }); @@ -38,20 +35,9 @@ Onyx.connect({ Onyx.connect({ key: ONYXKEYS.SESSION, callback: (value) => { - if (value?.creationDate) { - console.log( - `@51888 reauthenticator new session received ${value?.authToken?.substring(0, 10)} creationDate ${new Date( - value?.creationDate, - ).toISOString()} , active ${active} isOffline? ${isOffline} currentActiveSession ${currentActiveSession?.authToken?.substring(0, 10)} creationDate ${ - currentActiveSession?.creationDate ? new Date(currentActiveSession?.creationDate).toISOString() : '' - }`, - ); - } if (!value || isSameSession(value) || !active) { - console.log(`@51888 reauthenticator new session received but not doing anything`); return; } - console.log(`@51888 reauthenticator new session received, deactivating`); deactivate(); }, }); @@ -61,7 +47,6 @@ function isSameSession(session: Session): boolean { } function deactivate() { - console.log(`@51888 reauthenticator deactivating`); active = false; currentActiveSession = {}; clearInterval(timer); @@ -75,10 +60,8 @@ function deactivate() { */ function activate(session: Session) { if (!session || isSameSession(session) || isOffline) { - console.log(`@51888 reauthenticator activation requested but already active or offline`); return; } - console.log(`@51888 reauthenticator activating`); currentActiveSession = session; active = true; // no need to Timers.register() @@ -89,7 +72,6 @@ function tryReauthenticate() { if (isOffline || !active) { return; } - console.log(`@51888 reauthenticator reauthenticating at ${new Date().toISOString()}`); reauthenticate(); } diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index aa5812c862b6..fad17136cdb6 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -777,7 +777,7 @@ function invalidateCredentials() { function invalidateAuthToken() { NetworkStore.setAuthToken('pizza'); - Onyx.merge(ONYXKEYS.SESSION, {authToken: 'pizza', encryptedAuthToken: 'pizza', creationDate: new Date().getTime()}); + Onyx.merge(ONYXKEYS.SESSION, {authToken: 'pizza', encryptedAuthToken: 'pizza'}); } /** From e5d504da5071bf70568a1391f582863c648b0b6c Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Fri, 20 Dec 2024 15:23:30 +0100 Subject: [PATCH 07/13] fix cors issues --- .project | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .project diff --git a/.project b/.project deleted file mode 100644 index 99a5444bf1b6..000000000000 --- a/.project +++ /dev/null @@ -1,11 +0,0 @@ - - - App - - - - - - - - From 170e7e81e16c0e9a52602002537a7b2cf741eaac Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Fri, 20 Dec 2024 15:49:17 +0100 Subject: [PATCH 08/13] fix cors issues --- src/components/ImageView/index.tsx | 2 +- src/components/Lightbox/index.tsx | 2 +- src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx | 2 +- src/libs/actions/Session/Reauthenticator.ts | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/ImageView/index.tsx b/src/components/ImageView/index.tsx index 791b4e902c40..319e81ac8c64 100644 --- a/src/components/ImageView/index.tsx +++ b/src/components/ImageView/index.tsx @@ -49,7 +49,7 @@ function ImageView({isAuthTokenRequired = false, url, fileName, onError}: ImageV const newZoomScale = Math.min(newContainerWidth / newImageWidth, newContainerHeight / newImageHeight); setZoomScale(newZoomScale); }; - + const onContainerLayoutChanged = (e: LayoutChangeEvent) => { const {width, height} = e.nativeEvent.layout; setScale(width, height, imgWidth, imgHeight); diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx index 21fbf7e373cb..ec0c6e5efcaa 100644 --- a/src/components/Lightbox/index.tsx +++ b/src/components/Lightbox/index.tsx @@ -163,7 +163,7 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan const isFallbackStillLoading = isFallbackVisible && !isFallbackImageLoaded; const isLightboxStillLoading = isLightboxVisible && !isLightboxImageLoaded; const isLoading = isActive && (isCanvasLoading || isFallbackStillLoading || isLightboxStillLoading); - + // Resets the lightbox when it becomes inactive useEffect(() => { if (isLightboxVisible) { diff --git a/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx b/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx index 7a5b2fafde98..832b5eef45f0 100644 --- a/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx +++ b/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx @@ -30,7 +30,7 @@ type VideoPlayerThumbnailProps = { function VideoPlayerThumbnail({thumbnailUrl, onPress, accessibilityLabel, isDeleted}: VideoPlayerThumbnailProps) { const styles = useThemeStyles(); - + return ( {!!thumbnailUrl && ( diff --git a/src/libs/actions/Session/Reauthenticator.ts b/src/libs/actions/Session/Reauthenticator.ts index 493831811da7..67698a676022 100644 --- a/src/libs/actions/Session/Reauthenticator.ts +++ b/src/libs/actions/Session/Reauthenticator.ts @@ -12,7 +12,6 @@ let timer: NodeJS.Timeout; // and we wish not to overlap and make a double reauthentication const TIMING_BEFORE_REAUTHENTICATION_MS = 3500; // 3.5s - // We subscribe to network's online/offline status Onyx.connect({ key: ONYXKEYS.NETWORK, @@ -61,7 +60,7 @@ function deactivate() { function activate(session: Session) { if (!session || isSameSession(session) || isOffline) { return; - } + } currentActiveSession = session; active = true; // no need to Timers.register() From 7ba9f8a36c6cfb5323ffe971686fec1baf86d842 Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Fri, 20 Dec 2024 15:53:08 +0100 Subject: [PATCH 09/13] fix cors issues --- src/components/EReceiptThumbnail.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/EReceiptThumbnail.tsx b/src/components/EReceiptThumbnail.tsx index c8cc60f2b90e..981c4d46e212 100644 --- a/src/components/EReceiptThumbnail.tsx +++ b/src/components/EReceiptThumbnail.tsx @@ -58,6 +58,7 @@ function EReceiptThumbnail({transaction, borderRadius, fileExtension, isReceiptT const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const colorCode = isReceiptThumbnail ? StyleUtils.getFileExtensionColorCode(fileExtension) : StyleUtils.getEReceiptColorCode(transaction); + const backgroundImage = useMemo(() => backgroundImages[colorCode], [colorCode]); const colorStyles = StyleUtils.getEReceiptColorStyles(colorCode); From d25f0b06a0256661e20588f5eb7e8ab13a9f50b0 Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Mon, 23 Dec 2024 12:29:52 +0100 Subject: [PATCH 10/13] fix cors errors on attachments --- src/libs/actions/Session/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index fad17136cdb6..f56f6cbdf264 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -776,8 +776,11 @@ function invalidateCredentials() { } function invalidateAuthToken() { - NetworkStore.setAuthToken('pizza'); - Onyx.merge(ONYXKEYS.SESSION, {authToken: 'pizza', encryptedAuthToken: 'pizza'}); + // expires the session after 50s + setTimeout(() => { + NetworkStore.setAuthToken('pizza'); + Onyx.merge(ONYXKEYS.SESSION, {authToken: 'pizza', encryptedAuthToken: 'pizza', creationDate: new Date().getTime() - CONST.SESSION_EXPIRATION_TIME_MS}); + }, 50000); } /** From 65fb38c425e20559b435a3fe3ff04d990e0a9405 Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Mon, 23 Dec 2024 12:59:03 +0100 Subject: [PATCH 11/13] fix cors errors on attachments --- src/components/Image/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index c2a7c8b75ae5..274b5c72782a 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -34,6 +34,7 @@ function Image({source: propsSource, isAuthTokenRequired = false, onLoad, object const handleLoad = useCallback( (event: ImageOnLoadEvent) => { const {width, height} = event.nativeEvent; + onLoad?.(event); updateAspectRatio(width, height); }, From 3040f196fee1af89f43bb51c81265b012993c229 Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Wed, 25 Dec 2024 11:28:37 +0100 Subject: [PATCH 12/13] fix cors errors on attachments --- src/libs/actions/Session/index.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index f56f6cbdf264..66448a8d29d6 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -776,11 +776,8 @@ function invalidateCredentials() { } function invalidateAuthToken() { - // expires the session after 50s - setTimeout(() => { - NetworkStore.setAuthToken('pizza'); - Onyx.merge(ONYXKEYS.SESSION, {authToken: 'pizza', encryptedAuthToken: 'pizza', creationDate: new Date().getTime() - CONST.SESSION_EXPIRATION_TIME_MS}); - }, 50000); + NetworkStore.setAuthToken('pizza'); + Onyx.merge(ONYXKEYS.SESSION, {authToken: 'pizza', encryptedAuthToken: 'pizza', creationDate: new Date().getTime() - CONST.SESSION_EXPIRATION_TIME_MS}); } /** From 5cbae416f83ab9d69133543be266f727e101a97d Mon Sep 17 00:00:00 2001 From: Kalydosos Date: Thu, 26 Dec 2024 18:53:12 +0100 Subject: [PATCH 13/13] fix cors errors on attachments --- src/components/TestToolMenu.tsx | 9 +++++++++ src/languages/en.ts | 2 ++ src/languages/es.ts | 2 ++ src/libs/actions/Session/index.ts | 14 +++++++++++++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/components/TestToolMenu.tsx b/src/components/TestToolMenu.tsx index 89f3fbc528ef..783b1f74cec2 100644 --- a/src/components/TestToolMenu.tsx +++ b/src/components/TestToolMenu.tsx @@ -111,6 +111,15 @@ function TestToolMenu() { /> + {/* Sends an expired session to the FE and invalidates the session by the same time in the BE. Action is delayed for 15s */} + +