Expensify / App

Welcome to New Expensify: a complete re-imagination of financial collaboration, centered around chat. Help us build the next generation of Expensify by sharing feedback and contributing to the code.
https://new.expensify.com
MIT License
3.42k stars 2.8k forks source link

[$250] Android - Avatar - Profile avatar can be seen only if tap on blank screen #49553

Open IuliiaHerets opened 1 week ago

IuliiaHerets commented 1 week ago

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Version Number: 9.0.39-0 Reproducible in staging?: Y Reproducible in production?: Y Issue reported by: Applause Internal Team

Action Performed:

  1. Go to any chat
  2. Tap on user's avatar> Open the Avatar
  3. Verify there is a blank screen and Avatar is not displayed
  4. Tap on black screen

Expected Result:

Profile avatar should be displayed when open it

Actual Result:

Profile avatar can be seen only if tap on blank screen Same issue occurs when Take photo

Workaround:

Unknown

Platforms:

Screenshots/Videos

https://github.com/user-attachments/assets/05367f4d-e64f-48cd-bf85-9ab211629dcb

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021837227522028493087
  • Upwork Job ID: 1837227522028493087
  • Last Price Increase: 2024-09-27
Issue OwnerCurrent Issue Owner: @dukenv0307
melvin-bot[bot] commented 1 week ago

Triggered auto assignment to @puneetlath (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

IuliiaHerets commented 1 week ago

@puneetlath FYI I haven't added the External label as I wasn't 100% sure about this issue. Please take a look and add the label if you agree it's a bug and can be handled by external contributors

melvin-bot[bot] commented 1 week ago

Job added to Upwork: https://www.upwork.com/jobs/~021837227522028493087

melvin-bot[bot] commented 1 week ago

Triggered auto assignment to Contributor-plus team member for initial proposal review - @dukenv0307 (External)

huult commented 1 week ago

Edited by proposal-police: This proposal was edited at 2024-09-21 10:37:45 UTC.```

Proposal

Please re-state the problem that we are trying to solve in this issue.

Android - Avatar - Profile avatar can be seen only if tap on blank screen

What is the root cause of that problem?

The layout is not changing, so onLayout is not being called, which prevents updateCanvasSize from being triggered. https://github.com/Expensify/App/blob/9160fa585c26d3312fd6d6fbd69d334e66cb373d/src/components/Lightbox/index.tsx#L204-L207

So, the canvasSize will be undefined, and isCanvasLoading will be set to true. https://github.com/Expensify/App/blob/9160fa585c26d3312fd6d6fbd69d334e66cb373d/src/components/Lightbox/index.tsx#L94-L95

isCanvasLoading is true, the image component will not render, resulting in a blank screen, and this issue occurs https://github.com/Expensify/App/blob/9160fa585c26d3312fd6d6fbd69d334e66cb373d/src/components/Lightbox/index.tsx#L208

The reason onLayout is not being triggered is that shouldLoadAttachment is enabled after the parent component has rendered.

The reason onLayout is not being triggered is that the Send Button is rendered before the AttachmentView component. Its presence at the bottom might be affecting how the is rendered or when its onLayout event is triggered. Since React Native handles layouts asynchronously, the positioning of components in the view hierarchy can influence when their dimensions are calculated and when onLayout is fired.

What changes do you think we should make in order to solve the problem?

To solve this problem, we just need to render the Send Button at the same time as the AttachmentView. The code will change to something like this:

// .src/components/AttachmentModal.tsx#L580
- {!!onConfirm && !isConfirmButtonDisabled && (
+ {!!onConfirm && !isConfirmButtonDisabled && shouldLoadAttachment && (
                 <Button
                            ref={viewRef(submitRef)}
                            success
                            large
                            style={[styles.buttonConfirm, shouldUseNarrowLayout ? {} : styles.attachmentButtonBigScreen]}
                            textStyles={[styles.buttonConfirmText]}
                            text={translate('common.send')}
                            onPress={submitAndClose}
                            isDisabled={isConfirmButtonDisabled}
                            pressOnEnter
                        />
                    )}

[!Note] Note: As I tested, this issue only occurs on Android. We can either update it for Android only or for all platforms, and it will still work under this condition

What alternative solutions did you explore? (Optional)

Alternative Solutions 1

We should set shouldLoadAttachment to true when isModalOpen is set. This way, the AttachmentView will render with the modal, allowing the onLayout for the attachment view to be triggered. We should set shouldLoadAttachment to true when isModalOpen is set. This way, the AttachmentView will render same time with Send Button

// .src/components/AttachmentModal.tsx#L368
} else if (fileObject.uri) {
                    const inputModalType = getModalType(fileObject.uri, fileObject);
+                    setShouldLoadAttachment(true);
                    setIsModalOpen(true);
                    setSourceState(fileObject.uri);
                    setFile(fileObject);
                    setModalType(inputModalType);
                }

Alternative Solutions 2

We should use a ref so that the measurement occurs after the component has mounted, something like this:

// .src/components/Lightbox/index.tsx#L167
+    const viewRef = useRef<View>(null);

// .src/components/Lightbox/index.tsx#L172
+    useEffect(() => {
+        const measureView = () => {
+            viewRef.current?.measure((_, __, width, height) => {
+                setCanvasSize({
+                    width: PixelRatio.roundToNearestPixel(width),
+                    height: PixelRatio.roundToNearestPixel(height),
+                });
+            });
+        };

+        measureView();
+    }, []);

// .src/components/Lightbox/index.tsx#L204
        <View
            style={[StyleSheet.absoluteFill, style]}
            onLayout={updateCanvasSize}
+            ref={viewRef}
        >
POC https://github.com/user-attachments/assets/765c8d29-8bcb-44da-9b88-64ca2639efb8
struc commented 1 week ago

Proposal

Please re-state the problem that we are trying to solve in this issue.

Avatar image does not show up or load.

What is the root cause of that problem?

On mobile, we use Lightbox to render the avatar image, however onLayout is never called because the view does not have a defined size yet.

Which also means the image view is never mounted and ImageView.onload is also never called. https://github.com/Expensify/App/blob/9160fa585c26d3312fd6d6fbd69d334e66cb373d/src/components/Lightbox/index.tsx#L204-L207

What changes do you think we should make in order to solve the problem?

Initialize canvasSize with any value (i.e 0x0) will force a layout and mount the image view and thus load the image.

so we replace

https://github.com/Expensify/App/blob/9160fa585c26d3312fd6d6fbd69d334e66cb373d/src/components/Lightbox/index.tsx#L94

with

const [canvasSize, setCanvasSize] = useState<CanvasSize>({width: 0, height: 0});

What alternative solutions did you explore? (Optional)

N/A

https://github.com/user-attachments/assets/7928de0e-f53d-498c-99a1-84303a16ed58

dukenv0307 commented 1 week ago

@huult @struc Thanks for your proposals. onLayout should be called on mount (works well in iOS). Can you dig deeper to find the RCA?

FYI, I just tested onLayout on snack.expo.dev, it worked well on Android

import React from 'react';
import {View, Text} from 'react-native';

const ViewBoxesWithColorAndText = () => {
  const [a,setA] = React.useState(0)

  return (
    <View>
      <Text>{a}</Text>
      <View
      onLayout={(e)=>{
        setA(111111)
      }}>
    </View>
    </View>
  );
};

export default ViewBoxesWithColorAndText;
huult commented 1 week ago

Updated Proposal

dukenv0307 commented 1 week ago

The reason onLayout is not being triggered is that shouldLoadAttachment is enabled after the parent component has rendered.

@huult Can you elaborate that?

huult commented 1 week ago

Updated Proposal

Apologies, @dukenv0307 for the many changes in the proposal. Could you please review it again? I've updated the problem with more accuracy, and I appreciate your help. Thank you!

dukenv0307 commented 1 week ago

@huult It doesn't really convince me, why does this happen on Android only?

melvin-bot[bot] commented 6 days ago

📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸

huult commented 6 days ago

@dukenv0307 , I believe this issue is related to the rendering process in Android React Native. According to my RCA, the AttachmentView is rendered last, after all other components. I'm unsure why the onLayout is being prevented in AttachmentView.

With the solution I provided, rendering the Send Button at the same time as the AttachmentView seems reasonable and fixes the issue. I also tested it on the web and iOS, and it is still working well.

// .src/components/AttachmentModal.tsx#L580
- {!!onConfirm && !isConfirmButtonDisabled && (
+ {!!onConfirm && !isConfirmButtonDisabled && shouldLoadAttachment && (
melvin-bot[bot] commented 3 days ago

@puneetlath, @dukenv0307 Eep! 4 days overdue now. Issues have feelings too...

melvin-bot[bot] commented 1 day ago

@puneetlath, @dukenv0307 Still overdue 6 days?! Let's take care of this!

dukenv0307 commented 2 hours ago

I believe this issue is related to the rendering process in Android React Native

I tested this issue on React Native expo, I didn't face this issue. Can you dig deeper to find the exact RCA?

We also open to receive more proposals