Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show static skeleton UI if no reportAction exists for chat report in ONYX when the client is offline #13233

Conversation

techievivek
Copy link
Contributor

@techievivek techievivek commented Dec 1, 2022

Details

Opening a chat that was not opened recently while being offline seems to take forever to load and just displays the animating skeleton UI. I have updated the code to now show a non-animating skeleton UI along with the compose box so optimistic action can be taken while the user is offline. I have also added some general fixes to not load the report if we are offline which was needed as part of this code change.

Fixed Issues

$ #13088
PROPOSAL: GH_LINK_ISSUE(COMMENT)

Tests

  1. Login to newDot with an account that contains several chats with other users.
  2. Now go to settings -> preferences and toggle the offline mode using Force offline switch.
  3. Once you are offline, navigate to a chat report that was not open recently.
  4. Make sure you see a non-animating skeleton UI and compose box to add new messages to the chat report.
  5. Send a few messages and go back to online mode.
  6. Once you are online it will start showing the skeleton UI in an animating state and chat history will get loaded.
  • Verify that no errors appear in the JS console

Offline tests

  1. If you are offline and you opened a chat that was not opened recently you are expected to see a non-animating skeleton UI along with compose box input that will allow you to add new messages even though the chat history doesn't exist in the ONYX.
  2. Once you come online you should an animating skeleton UI and the chat history along with the newly added messages will get loaded.

QA Steps

Same as the testing step above.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by adding the Waiting for Copy label for a copy review on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • I have checked off every checkbox in the PR author checklist, including those that don't apply to this PR.

Screenshots/Videos

Web
Screen.Recording.2022-12-20.at.5.31.20.PM.mov
Mobile Web - Chrome
Screen.Recording.2022-12-20.at.6.22.47.PM.mov
Mobile Web - Safari
Screen.Recording.2022-12-20.at.5.50.55.PM.mov
Desktop
Screen.Recording.2022-12-20.at.5.33.39.PM.mov
iOS
Screen.Recording.2022-12-20.at.5.48.45.PM.mov
Android
Screen.Recording.2022-12-20.at.6.19.37.PM.mov

@techievivek techievivek self-assigned this Dec 1, 2022
@techievivek
Copy link
Contributor Author

techievivek commented Dec 1, 2022

This is a DRAFT PR for now because I wanted to discuss something here.
So there are two ways to tackle this PR.

  1. Since report actions are empty and we are offline I can falsely include a report action with the action CREATED which will get passed here which calls ReportActionsList here and it will automatically render the ReportActionItemCreated component. You can look through the flow to understand it better.
  2. Second way is what I have done in this PR, which is to return early in ReportActionsView with ReportActionItemCreated component. This does seem to work but it gets messed up (styling) in the Native Android and iOS apps which I can look into but the button doesn't seem to response so I am thinking I am missing something here.

Screenshot 2022-12-01 at 7 02 12 PM

Looking for some suggestions as I am touching the App code after many months.

@techievivek techievivek requested a review from a team December 1, 2022 13:33
@melvin-bot melvin-bot bot requested review from eVoloshchak and marcaaron and removed request for a team December 1, 2022 13:33
@melvin-bot
Copy link

melvin-bot bot commented Dec 1, 2022

@eVoloshchak @marcaaron One of you needs to copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@techievivek techievivek force-pushed the techievivek_show_newly_created_chat_action_if_no_reportAction_exists_while_Offline branch from c8278a7 to 1297bee Compare December 1, 2022 13:38
Copy link
Contributor

@marcaaron marcaaron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can falsely include a report action with the action CREATED

I think by "falsely include" you mean that we will add a fake "created" action into the reportActions? I am not sure if it's a good idea to add it to the data in Onyx - I think not as it will soon be migrated to an object keyed by reportActionID and would be very confusing to have to remove it somehow later when we are "online".

Though we could possibly develop a solution around modifying the final array that gets passed here:

data={this.props.sortedReportActions}

I think this would be a better way to do it as then we would not need to explicitly return early with the created action. Though - this could have other unintended side effects...

return early in ReportActionsView with ReportActionItemCreated component. This does seem to work but it gets messed up (styling) in the Native Android and iOS apps which I can look into but the button doesn't seem to response so I am thinking I am missing something here.

We have to look closer at the ReportActionsList styles to understand why this doesn't look good and isn't working.

This is a good start - but I think we should bring this one to Slack to get more ideas.

@@ -215,7 +215,10 @@ class ReportScreen extends React.Component {
const screenWrapperStyle = [styles.appContent, styles.flex1, {marginTop: this.state.viewportOffsetTop}];

// There are no reportActions at all to display and we are still in the process of loading the next set of actions.
const isLoadingInitialReportActions = _.isEmpty(this.props.reportActions) && this.props.report.isLoadingReportActions;
const isLoadingInitialReportActions = (_.isEmpty(this.props.reportActions) && !this.props.network.isOffline) && this.props.report.isLoadingReportActions;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the thing that confuses me here is why isLoadingReportActions would be true if we are offline. I would maybe expect it to be false or ask why we are triggering a loader state when we know we can't fetch these actions because we are offline.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my first question as well. I will look into this part and probably clean it.

const isLoadingInitialReportActions = (_.isEmpty(this.props.reportActions) && !this.props.network.isOffline) && this.props.report.isLoadingReportActions;

// Check if for the current chat report there are no report actions available while we are offline
const isChatReportEmptyWhileOffline = _.isEmpty(this.props.reportActions) && this.props.network.isOffline;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think since only the ReportActionsView cares about this we could move the check into that component instead of here.

@@ -293,6 +296,7 @@ class ReportScreen extends React.Component {
isComposerFullSize={this.props.isComposerFullSize}
isDrawerOpen={this.props.isDrawerOpen}
parentViewHeight={this.state.skeletonViewContainerHeight}
isChatReportEmptyWhileOffline={isChatReportEmptyWhileOffline}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continuing the previous thought.. we are creating this new variable and prop - but nothing else uses isChatReportEmptyWhileOffline so it seems like the responsibility of the ReportActionsView to:

  1. Check the reportActions
  2. See if we're offline
  3. If there are no reportActions and we are offline then incorporate the fallback action

// If the `isChatReportEmptyWhileOffline` prop is true then let's render ReportActionItemCreated component
if (this.props.isChatReportEmptyWhileOffline) {
return <ReportActionItemCreated reportID={this.props.report.reportID} />;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works, but I think is going to be a bit weird UX. As soon as someone adds an optimistic action while offline this will disappear since reportActions.length > 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But do we have any other way out? Because it is bound to happen, right? Can we show some additional message to let user know we don't have data cached?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, very tricky. So to summarize:

  • We can tell a report has not loaded yet if it has 0 reportActions.length
  • If we are offline then we can show this new special "created" action
  • As soon as they add a comment it will look like the report has loaded

So the question becomes...

How can we tell that we need to keep showing the "created" action if we are depending on reportActions.length and adding optimistic actions will increase this length?

First solution that comes to mind is something like...

const hasAtLeastOneNonPendingAction = reportActions.some(action => !action.pendingAction);

Which would be the same as "empty or empty with only pending actions". What do you think?

Copy link
Contributor Author

@techievivek techievivek Dec 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if I am thinking in the right direction or not but the problem with some will be that even if a chat report is loaded and we added an optimistic action to the report the above will evaluate to true right? So it all boils down to the fact the all report actions must be in a pending state or the list should be empty in order for us to show the created action, no?

I also went through the other issue which is very similar to this one and I really liked the idea of showing [broken cloud] Let's sync up when you get back online rather than a created action because both seem to be aligned with our optimistic approach but the latter one defines the situation better here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the problem with some will be that even if a chat report is loaded and we added an optimistic action to the report the above will evaluate to true right

My assumption is that if the report has truly loaded it will have a "created" action that is not pending.

all report actions must be in a pending state or the list should be empty in order for us to show the created action, no

Yep, I think that makes sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering should be showing the "created" action at all in this case. Because what if you have tons of chat history in the chat but you're simply offline so you haven't loaded it yet? In this issue we discussed showing the Skeleton UI without the animation, along with the offline indicator under the chat input. I wonder if we think this is a viable alternative?

I will bring this discussion into slack though so we can get on the same page a bit quicker.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I agree we can show the skeleton UI without animation rather than a created action.

@techievivek techievivek force-pushed the techievivek_show_newly_created_chat_action_if_no_reportAction_exists_while_Offline branch from 83c5d00 to 8547182 Compare December 8, 2022 18:35
@techievivek

This comment was marked as off-topic.

@techievivek
Copy link
Contributor Author

Keeping this on HOLD until the online behaviour gets fixed here.

@techievivek techievivek force-pushed the techievivek_show_newly_created_chat_action_if_no_reportAction_exists_while_Offline branch from 6867fce to 7b03e39 Compare December 20, 2022 11:44
@techievivek techievivek marked this pull request as ready for review December 20, 2022 12:59
@techievivek techievivek requested a review from a team as a code owner December 20, 2022 12:59
@melvin-bot melvin-bot bot removed the request for review from a team December 20, 2022 12:59
@melvin-bot
Copy link

melvin-bot bot commented Dec 20, 2022

@Santhosh-Sellavel @robertjchen One of you needs to copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@techievivek
Copy link
Contributor Author

This is ready for review. @marcaaron @yuwenmemon
I am seeing a bug where messages sent offline change positions once the user comes online, but this is already being discussed here and is not a priority for now.

@@ -145,6 +145,10 @@ class ReportActionsList extends React.Component {
}

render() {
// Non-animating skeleton UI represents that chat history exists but is not loaded locally in Onyx.
// While offline if there is at least one non-pending action or if this is a new chat then we don't need to show the non-animating skeleton UI.
const shouldShowNonAnimatingSkeleton = !(_.some(this.props.sortedReportActions, action => !action.pendingAction || action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parens are unneccessary

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this different from this.props.report.isLoadingReportActions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldShowNonAnimatingSkeleton is not a great variable name either. It should ideally reflect what it represents - so in this case ... whether or not we still have report actions to load? (Hence why I have my question above)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.props.isLoadingReportActions should be false when we are offline(more here), so I have updated some parts of code, so we don't call openReport when we are offline.

So we need another way to know whether we want to show the non-animating skeleton UI. (i.e., when we don't have any chat history in onyx and all we are left with are optimistic actions that were added by the user while being offline). So we will show the non-animating skeleton UI only if every action is optimistic and it is not a new chat report.

Copy link
Contributor Author

@techievivek techievivek Dec 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the condition to be more clear.

// Non-animating skeleton UI represents that chat history exists but is not loaded locally in Onyx.
// While offline if there is at least one non-pending action or if this is a new chat then we don't need to show the non-animating skeleton UI.
const shouldShowNonAnimatingSkeleton = !this.props.report.isOptimisticReport && _.every(this.props.sortedReportActions, action => action.pendingAction);

@@ -163,7 +167,7 @@ class ReportActionsList extends React.Component {
keyExtractor={this.keyExtractor}
initialRowHeight={32}
initialNumToRender={this.calculateInitialNumToRender()}
onEndReached={this.props.loadMoreChats}
onEndReached={() => !this.props.network.isOffline && this.props.loadMoreChats()}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we not move the isOffline check into the loadMoreChats function itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, that would be cleaner.

@techievivek techievivek force-pushed the techievivek_show_newly_created_chat_action_if_no_reportAction_exists_while_Offline branch from d4d629d to e4219bc Compare December 21, 2022 07:33
@techievivek
Copy link
Contributor Author

Gentle bump for reminding everyone to review this PR. Thanks

Copy link
Contributor

@marcaaron marcaaron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having some trouble wrapping my head around these changes, but don't want to be a blocker on this PR. I have a couple of ideas on how to proceed:

  1. Discuss more widely what it is we are trying to solve and the best way to do it. It feels like we are experimenting in the PR a bit (which is OK, but might be less effective than leading a conversation to get everyone on the same page before making changes).

  2. Bring someone else in on the review who is also familiar with the ReportScreen flows to weigh in on some of the comments I've left.

@@ -165,7 +165,8 @@ class ReportScreen extends React.Component {
// It possible that we may not have the report object yet in Onyx yet e.g. we navigated to a URL for an accessible report that
// is not stored locally yet. If props.report.reportID exists, then the report has been stored locally and nothing more needs to be done.
// If it doesn't exist, then we fetch the report from the API.
if (this.props.report.reportID) {
// If the client is offline, we cannot fetch the report, so let's just return.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment explains what we are doing but not "why".

Looking at this - and I don't understand why being offline should prevent us from making this request. Here's my understanding of how our networking layer works:

  1. OpenReport is a "write" request because we update the "last read" while fetching the report
  2. If we are offline we will queue any "write" requests

App/src/libs/API.js

Lines 47 to 48 in 0ed2636

// Write commands can be saved and retried, so push it to the SequentialQueue
SequentialQueue.push(request);

// Add request to Persisted Requests so that it can be retried if it fails
PersistedRequests.save([request]);
// If we are offline we don't need to trigger the queue to empty as it will happen when we come back online
if (NetworkStore.isOffline()) {
return;
}

  1. When we come back from offline -> online those queued "write" requests will run

// Flush the queue when the connection resumes
NetworkStore.onReconnection(flush);

In this case, we are offline - so shouldn't it be fine to make the request? And when we come back online the report will be fetched?

Is there some problem with the way this is working now that needs to be addressed? Because I want to say it's an anti-pattern to prevent a "write" from happening if we are offline (as that's what the queue itself was designed to do) but maybe I am missing something.

Copy link
Contributor Author

@techievivek techievivek Dec 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I added this change was otherwise in the openReport method call

optimisticData: [{
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {
isLoadingReportActions: true,
isLoadingMoreReportActions: false,

it would set isLoadingReportActions: true and because of that in the ReportScreen.js
{(this.isReportReadyForDisplay() && !isLoadingInitialReportActions) && (
<>
<ReportActionsView
reportActions={this.props.reportActions}
report={this.props.report}

we will keep showing the animating full-screen skeletonUI and the reportActionsView will never get loaded. I don't think that is expected if we want users to take optimistic actions here. What do you think about it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change and one below in ReportActionsView page are mainly to prevent setting value for a report prop. Do you have any recommendations on how to handle this better?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's stop here and consult the team on the best way to handle this.

The main question in my mind is why we should ever set any loading state before the API request actually begins? In the current design, it seems like we have a hybrid where loading states are set to true even though we are offline and not actually "loading" anything.

Taking a look at our usages elsewhere - and this isLoading is mostly only being used in a few "blocking read requests" (stuff related to payments and bank accounts) that I don't think will ever happen unless we are online. So it feels like there's an inconsistency or exception being made for the report page - and I'm not sure it's valid.

@@ -145,6 +141,10 @@ class ReportActionsList extends React.Component {
}

render() {
// Non-animating skeleton UI represents that chat history exists but is not loaded locally in Onyx.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-animating skeleton UI represents that

I'd rephrase this in this format

When [x] happens a non-animating skeleton UI is shown because...

But honestly, I'm a bit confused about how to finish that sentence.

I'm having trouble understanding the idea that "chat history exists but is not loaded locally in Onyx" so I think we need to improve this comment more generally.

Does it mean that the data exists in storage, but not yet read into memory?

Or does it mean that we know some data exists on the server, but we haven't tried to load it yet?

Let's please try to be very specific here so that people don't have to work too hard to understand why we are doing something.

Copy link
Contributor Author

@techievivek techievivek Dec 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or does it mean that we know some data exists on the server, but we haven't tried to load it yet?

This is the case here, I will update it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome thanks! And to make sure I understand - we know this is the case because we have some report data already stored, the report is "not optimistic", and no report actions?

Copy link
Contributor Author

@techievivek techievivek Jan 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that's my understanding so far 😄 (This is my first App PR in like 4-5 months).

@@ -145,6 +141,10 @@ class ReportActionsList extends React.Component {
}

render() {
// Non-animating skeleton UI represents that chat history exists but is not loaded locally in Onyx.
// While offline if there is at least one non-pending action or if this is a new chat then we don't need to show the non-animating skeleton UI.
const shouldShowNonAnimatingSkeleton = !this.props.report.isOptimisticReport && _.every(this.props.sortedReportActions, action => action.pendingAction);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const shouldShowNonAnimatingSkeleton = !this.props.report.isOptimisticReport && _.every(this.props.sortedReportActions, action => action.pendingAction);
const shouldShowStaticSkeleton = !this.props.report.isOptimisticReport && _.every(this.props.sortedReportActions, action => action.pendingAction);

We generally prefer variables that describe what something is vs. what it is not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, change this to shouldAnimateSkeleton and then we don't have to invert later !shouldShowNonAnimatingSkeleton. I think I like that suggestion better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems unrelated to the changes here. But I'm kind of confused about why and how isOptimisticReport got introduced. I'd think that an optimistic report would have a pendingAction and not a separate flag to denote it as "optimistic".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have decided to show the static skeleton UI for the chats that we know exist on the server but aren't loaded locally in ONYX.

Now if the chat is not loaded locally and we are offline then we can still take optimistic action but we will need to keep showing the static skeleton UI. But in the case of a new chat report, we don't have to show the static skeleton UI because we know this chat report doesn't exist on our server. But if I just check by pendingAction then static skeleton UI would show up for new chats as well so to filter them out I have added the condition to see if it is an optimistic report(which I think denotes a new chat created while offline).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah hmm, that's good context, but your response here isn't directly addressing either of my two concerns above (though perhaps you meant to add this context to a different thread).

Would you mind asking a follow up question or restating what you think I'm asking about here? I am happy to clarify things or say them in a different way to help us get on the same page.

Copy link
Contributor Author

@techievivek techievivek Jan 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah, my bad. I see your question now. But to be honest, I don't have any explanation for this one(will have to check the blame). I used it because it was right in the report object, but this could have been easily computed as well.

@@ -177,12 +177,13 @@ class ReportActionsList extends React.Component {
// Make sure the oldest report action loaded is not the first. This is so we do not show the
// skeleton view above the created action in a newly generated optimistic chat or one with not
// that many comments.
// We will show non-animating skeleton UI if we are offline and chat history is not loaded in ONYX.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment does not explain "why" only the "what". Also let's try to be more consistent about capitalization. In other comments we have "Onyx" and this one "ONYX".

Suggested change
// We will show non-animating skeleton UI if we are offline and chat history is not loaded in ONYX.
// We will show a static skeleton UI if we are offline and chat history is not loaded in Onyx because...

@@ -145,6 +141,10 @@ class ReportActionsList extends React.Component {
}

render() {
// Non-animating skeleton UI represents that chat history exists but is not loaded locally in Onyx.
// While offline if there is at least one non-pending action or if this is a new chat then we don't need to show the non-animating skeleton UI.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does being offline come into play here? This component does not seem aware of the network status anymore since we are removing withNetwork.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah, I was just trying to explain in comments and used that word. I will get rid of it.

@@ -270,7 +270,7 @@ class ReportActionsView extends React.Component {

// If the report is optimistic (AKA not yet created) we don't need to call openReport again
openReportIfNecessary() {
if (this.props.report.isOptimisticReport) {
if (this.props.report.isOptimisticReport || this.props.network.isOffline) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first comment applies here too.

@@ -283,7 +283,7 @@ class ReportActionsView extends React.Component {
*/
loadMoreChats() {
// Only fetch more if we are not already fetching so that we don't initiate duplicate requests.
if (this.props.report.isLoadingMoreReportActions) {
if (this.props.report.isLoadingMoreReportActions || this.props.network.isOffline) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we preventing this request from happening?

Copy link
Contributor Author

@techievivek techievivek Dec 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have prevented calling this otherwise it would render an animating skeleton UI in the ReportActionList.js page.

if (this.props.report.isLoadingMoreReportActions) {
return (
<ReportActionsSkeletonView
containerHeight={CONST.CHAT_SKELETON_VIEW.AVERAGE_ROW_HEIGHT * 3}
/>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I think this is also related to the weird issue where we are setting "loading" states when no "loading" is actually happening and we should pause until there is agreement about how to proceed with that.

@@ -352,7 +352,7 @@ class ReportActionsView extends React.Component {

render() {
// Comments have not loaded at all yet do nothing
if (!_.size(this.props.reportActions)) {
if (!_.size(this.props.reportActions) && !this.props.network.isOffline) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs a comment. It's not obvious why we should choose to return null when there are no report actions and we are online.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also feels like maybe we should not be rendering the ReportActionsView at all if we are offline and have no actions.

Copy link
Contributor Author

@techievivek techievivek Dec 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this whole flow is a bit messed up. I am using ListFooterComponent to show the static skeleton UI so I have this additional condition that will prevent ReportActionsView from returning null.

Another thing I can explore is to show the static skeleton view right from the ReportScreen page.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this whole flow is a bit messed up.

I agree and think it's a possible sign that we are gonna make it even more messed up than it already is.

I would propose we stop exploring code changes for a moment and instead try to pull things back into the realm of problem/solution.

Next steps:

  1. Figure out what problem we need to solve (I think we have mostly done this on the issue)
  2. Look at how things work today. Does the existing code easily support it or not? Does an obvious solution present itself or not? What solution should we ultimately go with?
  3. Summarize our best idea and take it to a few people to see what they think (either #engineering-chat or #expensify-open-source seems good)
  4. Agree on a plan forward + make sure everyone understands the general thought process behind the changes
  5. Make the changes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this discussion happening? If yes, it's the channel I don't have access to?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aimane-chnaif No, @techievivek has been OOO - so you're not missing out on anything I believe 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catching up, I will open a thread to discuss this issue in the open-source channel by tomorrow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have opened a discussion here feel free to jump to add your thoughts.

@techievivek techievivek changed the title Show chat as new with offline message if no report action exists for chat report while offline Show static skeleton UI if no reportAction exists for chat report in ONYX when the client is offline Dec 30, 2022
@techievivek techievivek requested review from aimane-chnaif and removed request for eVoloshchak December 30, 2022 11:57
@aimane-chnaif
Copy link
Contributor

aimane-chnaif commented Jan 4, 2023

@techievivek please pull from latest main when ready for review. Now report footer (composer) is always shown while loading skeleton.
Also FYI in #12698, we'll show skeleton on Report screen even before personalDetails are loaded.

@marcaaron
Copy link
Contributor

I think we are still discussing these changes. @techievivek do you think it might be helpful to just close out this PR for now until there's more of a consensus on what an agreed Problem and Solution? I'm trying to help move this one forward, and if feels like there's been so much conversation that it would be best to just start over at this point with a fresh Problem/Solution statement.

@yuwenmemon
Copy link
Contributor

Based off of #13088 (comment) I do think we should close this out. Feel free to reopen if you disagree!

@yuwenmemon yuwenmemon closed this Jan 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants