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

[HOLD for payment 2023-05-22] [$4000] Not showing any data on search page when press Cmd+K shortcut while loading after login #14490

Closed
1 task done
kavimuru opened this issue Jan 23, 2023 · 70 comments
Assignees
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 Engineering External Added to denote the issue can be worked on by a contributor

Comments

@kavimuru
Copy link

kavimuru commented Jan 23, 2023

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


Action Performed:

  1. Update network connection to Slow 3G (to reproduce easily)
  2. Login with any account
  3. While full screen loading, press Cmd+K shortcut
  4. Search page opens

This also applies to other pages like Profile, New chat, Send money, Workspaces, not only search

Expected Result:

  • When the user launches the app and reports data is still loading, they can open the search page with CMD+K as usual
  • The search page opens, and displays a skeleton UI (just as the LHN would when it doesn’t yet have reports data)
  • While the search page is loading, the options in the header are not disabled. So the user can:
    • Enter a search query (still, just a skeleton UI is shown)
    • Press < to close the RHP
    • Tap outside or swipe to close the RHP
  • If the reports load while the search page is displaying a skeleton UI, then any reports matching the search query are displayed. If there are no results matching the search query, then the skeleton UI is replaced by the standard No results found UI

Actual Result:

shows nothing on Search page

Workaround:

Close and re-open Search page

Platforms:

Which of our officially supported platforms is this issue occurring on?

  • MacOS / Chrome / Safari

Version Number: 1.2.58-2
Reproducible in staging?: y
Reproducible in production?: y
If this was caught during regression testing, add the test name, ID and link from TestRail:
Email or phone of affected tester (no customers):
Logs: https://stackoverflow.com/c/expensify/questions/4856
Notes/Photos/Videos:

bug.mov
Recording.76.mp4

Expensify/Expensify Issue URL:
Issue reported by: @aimane-chnaif
Slack conversation: https://expensify.slack.com/archives/C049HHMV9SM/p1674469304602449

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~013d319d96dd608a1f
  • Upwork Job ID: 1620163656058773504
  • Last Price Increase: 2023-03-31
@kavimuru kavimuru added Daily KSv2 Bug Something is broken. Auto assigns a BugZero manager. labels Jan 23, 2023
@melvin-bot
Copy link

melvin-bot bot commented Jan 23, 2023

Triggered auto assignment to @mallenexpensify (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details.

@melvin-bot melvin-bot bot locked and limited conversation to collaborators Jan 23, 2023
@mallenexpensify
Copy link
Contributor

For the workaround, you can also start typing then delete text and search will populate.
I'm unsure this is a bug because there's will always(?) be a time between clicking Sign in in and being able to take any action. ie. there'll always be a loading spinner, even if it's only a fraction of a second - are all app functions that don't work in that that fraction of a second bugs?

Coincidentally... we're working on high-level testing guides for QA, I wonder if we should include "After clicking "`Sign in", app should load within x seconds (cuz... in both examples above it took multiple seconds to load)

I'm going to keep this assigned for now and unlock it for feedback

@Expensify Expensify unlocked this conversation Jan 27, 2023
@aimane-chnaif
Copy link
Contributor

aimane-chnaif commented Jan 27, 2023

@mallenexpensify this should be issue after #14456 merged.
Now we unlock all the buttons (user interactions) as well as shortcuts while showing skeleton (before loading data)
i.e. Search, Profile icons on header, New chat, New group, New workspace, etc on FAB menu.
Discussion is here.

I am happy to review this GH either Internal or External

@melvin-bot
Copy link

melvin-bot bot commented Jan 27, 2023

Triggered auto assignment to @amyevans (Engineering), see https://stackoverflow.com/c/expensify/questions/4319 for more details.

@mallenexpensify
Copy link
Contributor

Thanks for pointing me to the Slack thread @aimane-chnaif , I forgot to check it :ohnothing:
@amyevans , I'm unsure if this can be External.

@melvin-bot melvin-bot bot added the Overdue label Jan 30, 2023
@amyevans
Copy link
Contributor

I've updated the expected results in the OP to capture what @roryabraham shared in Slack here.

Since @aimane-chnaif has full context, I'll assign you as C+. And we can open up to external contributors.

@melvin-bot melvin-bot bot removed the Overdue label Jan 30, 2023
@amyevans amyevans added the External Added to denote the issue can be worked on by a contributor label Jan 30, 2023
@melvin-bot melvin-bot bot changed the title Not showing any data on search page when press Cmd+K shortcut while loading after login [$1000] Not showing any data on search page when press Cmd+K shortcut while loading after login Jan 30, 2023
@melvin-bot
Copy link

melvin-bot bot commented Jan 30, 2023

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

@melvin-bot
Copy link

melvin-bot bot commented Jan 30, 2023

Current assignee @mallenexpensify is eligible for the External assigner, not assigning anyone new.

@melvin-bot
Copy link

melvin-bot bot commented Jan 30, 2023

Current assignee @aimane-chnaif is eligible for the External assigner, not assigning anyone new.

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label Jan 30, 2023
@melvin-bot
Copy link

melvin-bot bot commented Jan 30, 2023

Current assignee @amyevans is eligible for the External assigner, not assigning anyone new.

@s77rt
Copy link
Contributor

s77rt commented Jan 31, 2023

Proposal

Part 1

diff --git a/src/CONST.js b/src/CONST.js
index 0a84a4c108..34d7a5cd02 100755
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -523,6 +523,7 @@ const CONST = {
             3: 100,
         },
     },
+    OPTION_LIST_SKELETON_VIEW_ROW_HEIGHT: 60,
     EXPENSIFY_PARTNER_NAME: 'expensify.com',
     EMAIL: {
         CONCIERGE: 'concierge@expensify.com',
diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js
index b7b6ebcd5a..aa8a60612d 100644
--- a/src/components/OptionsList/BaseOptionsList.js
+++ b/src/components/OptionsList/BaseOptionsList.js
@@ -7,6 +7,7 @@ import variables from '../../styles/variables';
 import OptionRow from '../OptionRow';
 import SectionList from '../SectionList';
 import Text from '../Text';
+import OptionsListSkeletonView from './OptionsListSkeletonView';
 import {propTypes as optionsListPropTypes, defaultProps as optionsListDefaultProps} from './optionsListPropTypes';
 
 const propTypes = {
@@ -42,13 +43,18 @@ class BaseOptionsList extends Component {
         this.didLayout = false;
 
         this.flattenedData = this.buildFlatSectionArray();
+
+        this.state = {
+            skeletonViewContainerHeight: 0,
+        };
     }
 
-    shouldComponentUpdate(nextProps) {
+    shouldComponentUpdate(nextProps, nextState) {
         return nextProps.focusedIndex !== this.props.focusedIndex
             || nextProps.selectedOptions.length !== this.props.selectedOptions.length
             || nextProps.headerMessage !== this.props.headerMessage
-            || !_.isEqual(nextProps.sections, this.props.sections)
+            || nextProps.isLoadingReportData !== this.props.isLoadingReportData
+            || nextState.skeletonViewContainerHeight !== this.state.skeletonViewContainerHeight
             || !_.isEqual(nextProps.sections, this.props.sections);
     }
 
@@ -202,36 +208,54 @@ class BaseOptionsList extends Component {
 
     render() {
         return (
-            <View style={this.props.listContainerStyles}>
-                {this.props.headerMessage ? (
-                    <View style={[styles.ph5, styles.pb5]}>
-                        <Text style={[styles.textLabel, styles.colorMuted]}>
-                            {this.props.headerMessage}
-                        </Text>
-                    </View>
-                ) : null}
-                <SectionList
-                    ref={this.props.innerRef}
-                    indicatorStyle="white"
-                    keyboardShouldPersistTaps="always"
-                    keyboardDismissMode={this.props.keyboardDismissMode}
-                    onScrollBeginDrag={this.props.onScrollBeginDrag}
-                    onScroll={this.props.onScroll}
-                    contentContainerStyle={this.props.contentContainerStyles}
-                    showsVerticalScrollIndicator={false}
-                    sections={this.props.sections}
-                    keyExtractor={this.extractKey}
-                    stickySectionHeadersEnabled={false}
-                    renderItem={this.renderItem}
-                    getItemLayout={this.getItemLayout}
-                    renderSectionHeader={this.renderSectionHeader}
-                    extraData={this.props.focusedIndex}
-                    initialNumToRender={12}
-                    maxToRenderPerBatch={5}
-                    windowSize={5}
-                    viewabilityConfig={{viewAreaCoveragePercentThreshold: 95}}
-                    onViewableItemsChanged={this.onViewableItemsChanged}
-                />
+            <View
+                style={this.props.listContainerStyles}
+                onLayout={(event) => {
+                    const skeletonViewContainerHeight = event.nativeEvent.layout.height;
+
+                    // The height can be 0 if the component unmounts - we are not interested in this value and want to know how much space it
+                    // takes up so we can set the skeleton view container height.
+                    if (skeletonViewContainerHeight === 0) {
+                        return;
+                    }
+                    this.setState({skeletonViewContainerHeight});
+                }}
+            >
+                {this.props.isLoadingReportData ? (
+                    <OptionsListSkeletonView containerHeight={this.state.skeletonViewContainerHeight} />
+                ) : (
+                    <>
+                        {this.props.headerMessage ? (
+                            <View style={[styles.ph5, styles.pb5]}>
+                                <Text style={[styles.textLabel, styles.colorMuted]}>
+                                    {this.props.headerMessage}
+                                </Text>
+                            </View>
+                        ) : null}
+                        <SectionList
+                            ref={this.props.innerRef}
+                            indicatorStyle="white"
+                            keyboardShouldPersistTaps="always"
+                            keyboardDismissMode={this.props.keyboardDismissMode}
+                            onScrollBeginDrag={this.props.onScrollBeginDrag}
+                            onScroll={this.props.onScroll}
+                            contentContainerStyle={this.props.contentContainerStyles}
+                            showsVerticalScrollIndicator={false}
+                            sections={this.props.sections}
+                            keyExtractor={this.extractKey}
+                            stickySectionHeadersEnabled={false}
+                            renderItem={this.renderItem}
+                            getItemLayout={this.getItemLayout}
+                            renderSectionHeader={this.renderSectionHeader}
+                            extraData={this.props.focusedIndex}
+                            initialNumToRender={12}
+                            maxToRenderPerBatch={5}
+                            windowSize={5}
+                            viewabilityConfig={{viewAreaCoveragePercentThreshold: 95}}
+                            onViewableItemsChanged={this.onViewableItemsChanged}
+                        />
+                    </>
+                )}
             </View>
         );
     }
diff --git a/src/components/OptionsList/OptionsListSkeletonView.js b/src/components/OptionsList/OptionsListSkeletonView.js
new file mode 100644
index 0000000000..d316cfc1c6
--- /dev/null
+++ b/src/components/OptionsList/OptionsListSkeletonView.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {Rect, Circle} from 'react-native-svg';
+import SkeletonViewContentLoader from 'react-content-loader/native';
+import themeColors from '../../styles/themes/default';
+import styles from '../../styles/styles';
+import CONST from '../../CONST';
+
+const propTypes = {
+    /** Height of the container component */
+    containerHeight: PropTypes.number.isRequired,
+
+    /** Whether to animate the skeleton view */
+    animate: PropTypes.bool,
+};
+
+const defaultProps = {
+    animate: true,
+};
+
+const OptionsListSkeletonView = (props) => {
+    // Determines the number of content items based on container height
+    const possibleVisibleContentItems = Math.ceil(props.containerHeight / CONST.OPTION_LIST_SKELETON_VIEW_ROW_HEIGHT);
+
+    const skeletonOptionsList = [];
+    for (let index = 0; index < possibleVisibleContentItems; index++) {
+        skeletonOptionsList.push(
+            <SkeletonViewContentLoader
+                animate={props.animate}
+                height={CONST.OPTION_LIST_SKELETON_VIEW_ROW_HEIGHT}
+                backgroundColor={themeColors.highlightBG}
+                foregroundColor={themeColors.border}
+                style={styles.mr5}
+                key={`skeletonOptionsList${index}`}
+            >
+                <Circle cx="40" cy="26" r="20" />
+                <Rect x="67" y="11" width="20%" height="8" />
+                <Rect x="67" y="31" width="100%" height="8" />
+            </SkeletonViewContentLoader>,
+        );
+    }
+
+    return <>{skeletonOptionsList}</>;
+};
+
+OptionsListSkeletonView.displayName = 'OptionsListSkeletonView';
+OptionsListSkeletonView.propTypes = propTypes;
+OptionsListSkeletonView.defaultProps = defaultProps;
+export default OptionsListSkeletonView;
diff --git a/src/components/OptionsList/index.js b/src/components/OptionsList/index.js
index 31128766f6..5e6d4c2fbf 100644
--- a/src/components/OptionsList/index.js
+++ b/src/components/OptionsList/index.js
@@ -1,8 +1,11 @@
 import React, {Component, forwardRef} from 'react';
 import {Keyboard} from 'react-native';
+import {withOnyx} from 'react-native-onyx';
 import _ from 'underscore';
 import BaseOptionsList from './BaseOptionsList';
 import withWindowDimensions from '../withWindowDimensions';
+import compose from '../../libs/compose';
+import ONYXKEYS from '../../ONYXKEYS';
 import {propTypes, defaultProps} from './optionsListPropTypes';
 import * as DeviceCapabilities from '../../libs/DeviceCapabilities';
 
@@ -65,7 +68,14 @@ OptionsList.propTypes = {
 };
 OptionsList.defaultProps = defaultProps;
 
-export default withWindowDimensions(forwardRef((props, ref) => (
+export default compose(
+    withWindowDimensions,
+    withOnyx({
+        isLoadingReportData: {
+            key: ONYXKEYS.IS_LOADING_REPORT_DATA,
+        },
+    }),
+)(forwardRef((props, ref) => (
     // eslint-disable-next-line react/jsx-props-no-spreading
     <OptionsList forwardedRef={ref} {...props} />
 )));
diff --git a/src/components/OptionsList/index.native.js b/src/components/OptionsList/index.native.js
index 7701096fde..b5326854e2 100644
--- a/src/components/OptionsList/index.native.js
+++ b/src/components/OptionsList/index.native.js
@@ -1,6 +1,8 @@
 import React, {forwardRef} from 'react';
 import {Keyboard} from 'react-native';
+import {withOnyx} from 'react-native-onyx';
 import BaseOptionsList from './BaseOptionsList';
+import ONYXKEYS from '../../ONYXKEYS';
 import {propTypes, defaultProps} from './optionsListPropTypes';
 
 const OptionsList = forwardRef((props, ref) => (
@@ -16,4 +18,8 @@ OptionsList.propTypes = propTypes;
 OptionsList.defaultProps = defaultProps;
 OptionsList.displayName = 'OptionsList';
 
-export default OptionsList;
+export default withOnyx({
+    isLoadingReportData: {
+        key: ONYXKEYS.IS_LOADING_REPORT_DATA,
+    },
+})(OptionsList);
diff --git a/src/components/OptionsList/optionsListPropTypes.js b/src/components/OptionsList/optionsListPropTypes.js
index 0ca8b7a709..a27bfe80f7 100644
--- a/src/components/OptionsList/optionsListPropTypes.js
+++ b/src/components/OptionsList/optionsListPropTypes.js
@@ -70,6 +70,8 @@ const propTypes = {
 
     /** Whether to show a line separating options in list */
     shouldHaveOptionSeparator: PropTypes.bool,
+
+    isLoadingReportData: PropTypes.bool,
 };
 
 const defaultProps = {
@@ -90,6 +92,7 @@ const defaultProps = {
     isDisabled: false,
     onLayout: undefined,
     shouldHaveOptionSeparator: false,
+    isLoadingReportData: false,
 };
 
 export {propTypes, defaultProps};

In this part we will handle the core feature request where I decided to implement the skeleton view globally on <OptionsList /> to cover similar issues on other places.

First I created a new skeleton component <OptionsListSkeletonView />
This skeleton view will only be shown if reports are still being loaded and for that we are going to use onyx (key: IS_LOADING_REPORT_DATA)

Basically that's all what part 1 is about. The skeleton height calculation logic is the same as on <ReportScreen /> where we use <View />'s onLayout method to get the container height and draw X rows where X = container height / height of one row

Part 2

diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js
index 3cefc6a98c..3c0965c4a1 100755
--- a/src/pages/SearchPage.js
+++ b/src/pages/SearchPage.js
@@ -30,6 +30,8 @@ const propTypes = {
     /** All of the personal details for everyone */
     personalDetails: personalDetailsPropType.isRequired,
 
+    isLoadingReportData: PropTypes.bool,
+
     /** All reports shared with the user */
     reports: PropTypes.objectOf(reportPropTypes).isRequired,
 
@@ -44,6 +46,10 @@ const propTypes = {
     ...withLocalizePropTypes,
 };
 
+const defaultProps = {
+    isLoadingReportData: false,
+};
+
 class SearchPage extends Component {
     constructor(props) {
         super(props);
@@ -75,6 +81,13 @@ class SearchPage extends Component {
         };
     }
 
+    componentDidUpdate(prevProps) {
+        if (prevProps.isLoadingReportData === this.props.isLoadingReportData) {
+            return;
+        }
+        this.debouncedUpdateOptions();
+    }
+
     onChangeText(searchValue = '') {
         this.setState({searchValue}, this.debouncedUpdateOptions);
     }
@@ -198,11 +211,15 @@ class SearchPage extends Component {
 }
 
 SearchPage.propTypes = propTypes;
+SearchPage.defaultProps = defaultProps;
 
 export default compose(
     withLocalize,
     withWindowDimensions,
     withOnyx({
+        isLoadingReportData: {
+            key: ONYXKEYS.IS_LOADING_REPORT_DATA,
+        },
         reports: {
             key: ONYXKEYS.COLLECTION.REPORT,
         },

This part is specifically for <SearchPage /> as this page suffers from stale data. Meaning if reports gets loaded/updated after the search page is already rendered, it won't re-render to match the new reports and it will be out of sync.

As @mallenexpensify explained we have a workaround here where if we type in the search input you will get the expected results that's because changing the input will call debouncedUpdateOptions which rebuilds the list using fresh data. Fixing the stale data issue can be done in many ways as all we have to do is to call debouncedUpdateOptions.

Since we are only looking to rebuild the list if reports load after render, we can use the same onyx key we used before (IS_LOADING_REPORT_DATA).

I think the code in this part is self-explanatory.

Results

Kooha-2023-01-31-14-05-11.mp4

@amyevans
Copy link
Contributor

amyevans commented Feb 6, 2023

Understanding that implementation here will ultimately be on hold for the merge of #14456, but @aimane-chnaif can you review the above proposal in the meantime?

@melvin-bot melvin-bot bot removed the Overdue label Feb 6, 2023
@aimane-chnaif
Copy link
Contributor

After merging #14456, there will be many pages to be opened before loading personal or reports data.
So I think this issue should be not only tied to Search page but also all possible pages including Profile, New chat, Send money, Workspaces, etc.
@amyevans what do you think?

@melvin-bot melvin-bot bot changed the title [$1000] Not showing any data on search page when press Cmd+K shortcut while loading after login [$2000] Not showing any data on search page when press Cmd+K shortcut while loading after login Feb 6, 2023
@kavimuru
Copy link
Author

kavimuru commented May 8, 2023

Keyboard short cut bugs are not in scope. Closing the bug as per this comment

@kavimuru kavimuru closed this as completed May 8, 2023
@aimane-chnaif
Copy link
Contributor

@kavimuru this is not keyboard shortcut issue. It's not the main reproducible step though issue title says. Please re-open.

@amyevans amyevans reopened this May 9, 2023
@melvin-bot
Copy link

melvin-bot bot commented May 11, 2023

⚠️ Looks like this issue was linked to a Deploy Blocker here

If you are the assigned CME please investigate whether the linked PR caused a regression and leave a comment with the results.

If a regression has occurred and you are the assigned CM follow the instructions here.

If this regression could have been avoided please consider also proposing a recommendation to the PR checklist so that we can avoid it in the future.

@mountiny
Copy link
Contributor

The regression here was impossible to catch so the payments should not be degraded because of it

@melvin-bot melvin-bot bot added Weekly KSv2 Awaiting Payment Auto-added when associated PR is deployed to production and removed Daily KSv2 labels May 15, 2023
@melvin-bot melvin-bot bot changed the title [$4000] Not showing any data on search page when press Cmd+K shortcut while loading after login [HOLD for payment 2023-05-22] [$4000] Not showing any data on search page when press Cmd+K shortcut while loading after login May 15, 2023
@melvin-bot melvin-bot bot removed the Reviewing Has a PR in review label May 15, 2023
@melvin-bot
Copy link

melvin-bot bot commented May 15, 2023

Reviewing label has been removed, please complete the "BugZero Checklist".

@melvin-bot
Copy link

melvin-bot bot commented May 15, 2023

The solution for this issue has been 🚀 deployed to production 🚀 in version 1.3.13-5 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

If no regressions arise, payment will be issued on 2023-05-22. 🎊

After the hold period is over and BZ checklist items are completed, please complete any of the applicable payments for this issue, and check them off once done.

  • External issue reporter
  • Contributor that fixed the issue
  • Contributor+ that helped on the issue and/or PR

As a reminder, here are the bonuses/penalties that should be applied for any External issue:

  • Merged PR within 3 business days of assignment - 50% bonus
  • Merged PR more than 9 business days after assignment - 50% penalty

@melvin-bot
Copy link

melvin-bot bot commented May 15, 2023

BugZero Checklist: The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed:

  • [@aimane-chnaif] The PR that introduced the bug has been identified. Link to the PR:
  • [@aimane-chnaif] The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake. Link to comment:
  • [@aimane-chnaif] A discussion in #expensify-bugs has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner. Link to discussion:
  • [@aimane-chnaif] Determine if we should create a regression test for this bug.
  • [@aimane-chnaif] If we decide to create a regression test for the bug, please propose the regression test steps to ensure the same bug will not reach production again.
  • [@mallenexpensify] Link the GH issue for creating/updating the regression test once above steps have been agreed upon: https://github.com/Expensify/Expensify/issues/286289

@amyevans
Copy link
Contributor

Bumped @mallenexpensify in New Dot for payment

@melvin-bot melvin-bot bot removed the Overdue label May 24, 2023
@mallenexpensify
Copy link
Contributor

Thanks @amyevans , @aimane-chnaif, can you please accept the job and reply here once you have?
https://www.upwork.com/jobs/~0193e6015e52379df0

@aimane-chnaif can you address the items in the BZ checklist above too please?
#14490 (comment)

@aimane-chnaif
Copy link
Contributor

This bug existed from the beginning when we implement options list pages.
This was edge case first time but after implementing skeleton UI in #12698, became normal case.

Regression Test Proposal

  1. Open the app and logout if already logged in
  2. Set network throttling to Slow 3G or slower
  3. Login any account (HT account is better to reproduce)
  4. Open one of the pages as soon as the app loads:
  • Search Page (Cmd + K)
  • New Chat Page / New Group Page (Cmd + Shift + K)
  • Send Money / Request Money / Split Bill -> enter amount -> tap Next
  1. Verify that options list displays a skeleton loading state
  2. Verify that data should replace the loading state once available
  3. If search term is entered while data is loading, verify that it's applied once data is displayed

@mallenexpensify
Copy link
Contributor

Thanks @aimane-chnaif , TC GH is here

Let me know when you've accepted in Upwork and I'll pay

@melvin-bot melvin-bot bot added the Overdue label May 29, 2023
@melvin-bot
Copy link

melvin-bot bot commented May 30, 2023

@amyevans, @mallenexpensify, @jczekalski, @aimane-chnaif Huh... This is 4 days overdue. Who can take care of this?

@mallenexpensify
Copy link
Contributor

mallenexpensify commented May 31, 2023

@aimane-chnaif paid $4000. I wasn't 100% sure if this is accurate but, it seemed fair upon a cursory review as the associated PR fixed a few related issues. My thinking was that any negative timeliness bonuses would be negated due to the PR fixing multiple issues. If anyone disagrees, please comment.

@aimane-chnaif
Copy link
Contributor

Thanks @mallenexpensify
As this was agency PR, no timeline rule applied.
And this is minor but I might also be eligible for reporting bonus.

@mallenexpensify
Copy link
Contributor

And this is minor but I might also be eligible for reporting bonus.

@aimane-chnaif , you def were/are. I missed it, apologies. Just paid ya $250.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 Engineering External Added to denote the issue can be worked on by a contributor
Projects
None yet
Development

No branches or pull requests