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

Minimal server search / enable improved focus mode #27819

Merged
merged 38 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4023821
Only search when online
marcaaron Sep 20, 2023
dae32f2
Remove Text
marcaaron Sep 20, 2023
c0e5658
Add missing file
marcaaron Sep 20, 2023
61e4199
Run prettier
marcaaron Sep 20, 2023
0d9a313
Slow down server search
marcaaron Sep 21, 2023
470e323
Merge branch 'main' into marcaaron-minimalSearch
marcaaron Sep 22, 2023
630a6fd
Make NewChatPage also search
marcaaron Sep 25, 2023
b881942
Merge branch 'main' into marcaaron-minimalSearch
marcaaron Sep 28, 2023
a66dbd9
Update translations
marcaaron Sep 28, 2023
8e94c93
Merge branch 'main' into marcaaron-minimalSearch
marcaaron Sep 29, 2023
1bbde1a
Use debounce
marcaaron Sep 29, 2023
ad952ae
Make some requested changes
marcaaron Sep 29, 2023
98724e9
Make requested changes
marcaaron Oct 2, 2023
9a8387a
Fix conflicts
marcaaron Oct 2, 2023
3ab1772
add missing useCallback
marcaaron Oct 3, 2023
3bdf7bb
Merge branch 'main' into marcaaron-minimalSearch
marcaaron Oct 3, 2023
97e5bb1
Change UX so we are showing loading row below options instead of above
marcaaron Oct 3, 2023
af3fec7
Remove unneeded changes. Fix propTypes
marcaaron Oct 3, 2023
ea4a0f1
Undo bad whitespace change
marcaaron Oct 3, 2023
06531cb
undo another whitespace change
marcaaron Oct 3, 2023
95d436b
Fix UI race issue with No results found
marcaaron Oct 3, 2023
16474cd
Fix conflicts
marcaaron Oct 3, 2023
3d10710
Add loadingRow to NewChat page
marcaaron Oct 3, 2023
4c801f7
Clean up
marcaaron Oct 3, 2023
92c0ed4
remove new line
marcaaron Oct 3, 2023
b101e17
Style
marcaaron Oct 4, 2023
007d2d3
Show loading spinner instead of skeleton
marcaaron Oct 4, 2023
4e6826a
Undo skeleton row changes
marcaaron Oct 4, 2023
96400ed
run prettier
marcaaron Oct 4, 2023
15f9492
Call OpenApp when the priorityMode changes
marcaaron Oct 5, 2023
94411ae
Fix loading when offline
marcaaron Oct 6, 2023
3816569
Add constant for debounce time
marcaaron Oct 6, 2023
b09bad3
Fix conflicts and propTypes
marcaaron Oct 10, 2023
5719736
make requested comment change
marcaaron Oct 10, 2023
1ea9f28
Merge branch 'main' into marcaaron-minimalSearch
marcaaron Oct 11, 2023
992ae36
Update src/libs/actions/App.js
marcaaron Oct 12, 2023
352d376
Use withNetwork. propType comment
marcaaron Oct 12, 2023
6df2efa
Merge branch 'marcaaron-minimalSearch' of https://github.com/Expensif…
marcaaron Oct 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const ONYXKEYS = {
/** Boolean flag set whenever the sidebar has loaded */
IS_SIDEBAR_LOADED: 'isSidebarLoaded',

/** Boolean flag set whenever we are searching for reports in the server */
IS_SEARCHING_FOR_REPORTS: 'isSearchingForReports',

/** Note: These are Persisted Requests - not all requests in the main queue as the key name might lead one to believe */
PERSISTED_REQUESTS: 'networkRequestQueue',

Expand Down
48 changes: 48 additions & 0 deletions src/components/OptionsListSkeletonRow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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 CONST from '../CONST';
import styles from '../styles/styles';

const propTypes = {
/** Whether to animate the skeleton view */
shouldAnimate: PropTypes.bool.isRequired,

/** Line width string */
lineWidth: PropTypes.string.isRequired,
};

function OptionsListSkeletonRow({lineWidth, shouldAnimate}) {
return (
<SkeletonViewContentLoader
animate={shouldAnimate}
height={CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT}
backgroundColor={themeColors.skeletonLHNIn}
foregroundColor={themeColors.skeletonLHNOut}
style={styles.mr5}
>
<Circle
cx="40"
cy="32"
r="20"
/>
<Rect
x="72"
y="18"
width="20%"
height="8"
/>
<Rect
x="72"
y="38"
width={lineWidth}
height="8"
/>
</SkeletonViewContentLoader>
);
}

OptionsListSkeletonRow.propTypes = propTypes;
export default OptionsListSkeletonRow;
33 changes: 5 additions & 28 deletions src/components/OptionsListSkeletonView.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import React from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import {Rect, Circle} from 'react-native-svg';
import SkeletonViewContentLoader from 'react-content-loader/native';
import CONST from '../CONST';
import themeColors from '../styles/themes/default';
import styles from '../styles/styles';
import OptionsListSkeletonRow from './OptionsListSkeletonRow';

const propTypes = {
/** Whether to animate the skeleton view */
Expand Down Expand Up @@ -56,32 +54,11 @@ class OptionsListSkeletonView extends React.Component {
lineWidth = '25%';
}
skeletonViewItems.push(
<SkeletonViewContentLoader
<OptionsListSkeletonRow
key={`skeletonViewItems${i}`}
animate={this.props.shouldAnimate}
height={CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT}
backgroundColor={themeColors.skeletonLHNIn}
foregroundColor={themeColors.skeletonLHNOut}
style={styles.mr5}
>
<Circle
cx="40"
cy="32"
r="20"
/>
<Rect
x="72"
y="18"
width="20%"
height="8"
/>
<Rect
x="72"
y="38"
width={lineWidth}
height="8"
/>
</SkeletonViewContentLoader>,
shouldAnimate={this.props.shouldAnimate}
lineWidth={lineWidth}
/>,
);
}

Expand Down
129 changes: 74 additions & 55 deletions src/components/OptionsSelector/BaseOptionsSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {propTypes as optionsSelectorPropTypes, defaultProps as optionsSelectorDe
import setSelection from '../../libs/setSelection';
import compose from '../../libs/compose';
import getPlatform from '../../libs/getPlatform';
import OptionsListSkeletonRow from '../OptionsListSkeletonRow';
import FormHelpMessage from '../FormHelpMessage';

const propTypes = {
/** padding bottom style of safe area */
Expand Down Expand Up @@ -344,63 +346,80 @@ class BaseOptionsSelector extends Component {
const shouldShowDefaultConfirmButton = !this.props.footerContent && defaultConfirmButtonText;
const safeAreaPaddingBottomStyle = shouldShowFooter ? undefined : this.props.safeAreaPaddingBottomStyle;
const textInput = (
<TextInput
ref={(el) => (this.textInput = el)}
value={this.props.value}
label={this.props.textInputLabel}
accessibilityLabel={this.props.textInputLabel}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT}
onChangeText={this.props.onChangeText}
onSubmitEditing={this.selectFocusedOption}
placeholder={this.props.placeholderText}
maxLength={this.props.maxLength}
keyboardType={this.props.keyboardType}
onBlur={(e) => {
if (!this.props.shouldFocusOnSelectRow) {
return;
}
this.relatedTarget = e.relatedTarget;
}}
selectTextOnFocus
blurOnSubmit={Boolean(this.state.allOptions.length)}
spellCheck={false}
/>
<>
<TextInput
ref={(el) => (this.textInput = el)}
value={this.props.value}
label={this.props.textInputLabel}
accessibilityLabel={this.props.textInputLabel}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT}
onChangeText={this.props.onChangeText}
onSubmitEditing={this.selectFocusedOption}
placeholder={this.props.placeholderText}
maxLength={this.props.maxLength}
keyboardType={this.props.keyboardType}
onBlur={(e) => {
if (!this.props.shouldFocusOnSelectRow) {
return;
}
this.relatedTarget = e.relatedTarget;
}}
selectTextOnFocus
blurOnSubmit={Boolean(this.state.allOptions.length)}
spellCheck={false}
/>
{this.props.textInputAlert && (
marcaaron marked this conversation as resolved.
Show resolved Hide resolved
<FormHelpMessage
message={this.props.textInputAlert}
style={[styles.mb3]}
isError={false}
/>
)}
</>
);
const optionsList = (
<OptionsList
ref={(el) => (this.list = el)}
optionHoveredStyle={this.props.optionHoveredStyle}
onSelectRow={this.props.onSelectRow ? this.selectRow : undefined}
sections={this.props.sections}
focusedIndex={this.state.focusedIndex}
selectedOptions={this.props.selectedOptions}
canSelectMultipleOptions={this.props.canSelectMultipleOptions}
shouldShowMultipleOptionSelectorAsButton={this.props.shouldShowMultipleOptionSelectorAsButton}
multipleOptionSelectorButtonText={this.props.multipleOptionSelectorButtonText}
onAddToSelection={this.props.onAddToSelection}
hideSectionHeaders={this.props.hideSectionHeaders}
headerMessage={this.props.headerMessage}
boldStyle={this.props.boldStyle}
showTitleTooltip={this.props.showTitleTooltip}
isDisabled={this.props.isDisabled}
shouldHaveOptionSeparator={this.props.shouldHaveOptionSeparator}
highlightSelectedOptions={this.props.highlightSelectedOptions}
onLayout={() => {
if (this.props.selectedOptions.length === 0) {
this.scrollToIndex(this.state.focusedIndex, false);
}

if (this.props.onLayout) {
this.props.onLayout();
}
}}
contentContainerStyles={[safeAreaPaddingBottomStyle, ...this.props.contentContainerStyles]}
listContainerStyles={this.props.listContainerStyles}
listStyles={this.props.listStyles}
isLoading={!this.props.shouldShowOptions}
showScrollIndicator={this.props.showScrollIndicator}
isRowMultilineSupported={this.props.isRowMultilineSupported}
/>
<>
{this.props.shouldShowLoader && (
<OptionsListSkeletonRow
lineWidth="100%"
shouldAnimate
/>
)}
<OptionsList
ref={(el) => (this.list = el)}
optionHoveredStyle={this.props.optionHoveredStyle}
onSelectRow={this.props.onSelectRow ? this.selectRow : undefined}
sections={this.props.sections}
focusedIndex={this.state.focusedIndex}
selectedOptions={this.props.selectedOptions}
canSelectMultipleOptions={this.props.canSelectMultipleOptions}
shouldShowMultipleOptionSelectorAsButton={this.props.shouldShowMultipleOptionSelectorAsButton}
multipleOptionSelectorButtonText={this.props.multipleOptionSelectorButtonText}
onAddToSelection={this.props.onAddToSelection}
hideSectionHeaders={this.props.hideSectionHeaders}
headerMessage={this.props.headerMessage}
boldStyle={this.props.boldStyle}
showTitleTooltip={this.props.showTitleTooltip}
isDisabled={this.props.isDisabled}
shouldHaveOptionSeparator={this.props.shouldHaveOptionSeparator}
highlightSelectedOptions={this.props.highlightSelectedOptions}
onLayout={() => {
if (this.props.selectedOptions.length === 0) {
this.scrollToIndex(this.state.focusedIndex, false);
}

if (this.props.onLayout) {
this.props.onLayout();
}
}}
contentContainerStyles={[safeAreaPaddingBottomStyle, ...this.props.contentContainerStyles]}
listContainerStyles={this.props.listContainerStyles}
listStyles={this.props.listStyles}
isLoading={!this.props.shouldShowOptions}
showScrollIndicator={this.props.showScrollIndicator}
isRowMultilineSupported={this.props.isRowMultilineSupported}
/>
</>
);
return (
<ArrowKeyFocusManager
Expand Down
3 changes: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1561,6 +1561,9 @@ export default {
screenShare: 'Screen share',
screenShareRequest: 'Expensify is inviting you to a screen share',
},
search: {
resultsAreLimited: 'Search results are limited.',
},
genericErrorPage: {
title: 'Uh-oh, something went wrong!',
body: {
Expand Down
3 changes: 3 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,9 @@ export default {
screenShare: 'Compartir pantalla',
screenShareRequest: 'Expensify te está invitando a compartir la pantalla',
},
search: {
resultsAreLimited: 'Los resultados de búsqueda están limitados.',
},
genericErrorPage: {
title: '¡Uh-oh, algo salió mal!',
body: {
Expand Down
3 changes: 2 additions & 1 deletion src/libs/actions/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false) {
*/
function openApp() {
getPolicyParamsForOpenOrReconnect().then((policyParams) => {
API.read('OpenApp', policyParams, getOnyxDataForOpenOrReconnect(true));
const params = {enablePriorityModeFilter: true, ...policyParams};
API.read('OpenApp', params, getOnyxDataForOpenOrReconnect(true));
});
}

Expand Down
46 changes: 46 additions & 0 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -2135,7 +2135,53 @@ function clearPrivateNotesError(reportID, accountID) {
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {privateNotes: {[accountID]: {errors: null}}});
}

/**
* @private
* @param {string} searchInput
*/
function searchInServer(searchInput) {
// We do not try to make this request while offline because it sets a loading indicator optimistically
if (isNetworkOffline) {
return;
}

API.read(
'SearchForReports',
{searchInput},
{
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS,
value: true,
},
],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS,
value: false,
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS,
value: false,
},
],
},
);
}

/**
* @private
* @param {string} searchInput
*/
const throttledSearchInServer = _.throttle(searchInServer, 1000, {leading: false});
marcaaron marked this conversation as resolved.
Show resolved Hide resolved

export {
throttledSearchInServer,
addComment,
addAttachment,
reconnect,
Expand Down
14 changes: 13 additions & 1 deletion src/pages/NewChatPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import compose from '../libs/compose';
import personalDetailsPropType from './personalDetailsPropType';
import reportPropTypes from './reportPropTypes';
import variables from '../styles/variables';
import useNetwork from '../hooks/useNetwork';

const propTypes = {
/** Beta features list */
Expand All @@ -44,12 +45,13 @@ const defaultProps = {

const excludedGroupEmails = _.without(CONST.EXPENSIFY_EMAILS, CONST.EMAIL.CONCIERGE);

function NewChatPage({betas, isGroupChat, personalDetails, reports, translate}) {
function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, isSearchingForReports}) {
Beamanator marked this conversation as resolved.
Show resolved Hide resolved
const [searchTerm, setSearchTerm] = useState('');
const [filteredRecentReports, setFilteredRecentReports] = useState([]);
const [filteredPersonalDetails, setFilteredPersonalDetails] = useState([]);
const [filteredUserToInvite, setFilteredUserToInvite] = useState();
const [selectedOptions, setSelectedOptions] = useState([]);
const {isOffline} = useNetwork();

const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS;
const headerMessage = OptionsListUtils.getHeaderMessage(
Expand Down Expand Up @@ -167,6 +169,10 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [reports, personalDetails, searchTerm]);

// When search term updates we will fetch any reports
useEffect(() => {
Report.throttledSearchInServer(searchTerm);
}, [searchTerm]);
marcaaron marked this conversation as resolved.
Show resolved Hide resolved
return (
<ScreenWrapper
shouldEnableKeyboardAvoidingView={false}
Expand Down Expand Up @@ -202,9 +208,11 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate})
shouldShowOptions={isOptionsDataReady}
shouldShowConfirmButton
confirmButtonText={selectedOptions.length > 1 ? translate('newChatPage.createGroup') : translate('newChatPage.createChat')}
textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''}
onConfirmSelection={createGroup}
textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
shouldShowLoader={isSearchingForReports}
/>
</View>
</KeyboardAvoidingView>
Expand All @@ -230,5 +238,9 @@ export default compose(
betas: {
key: ONYXKEYS.BETAS,
},
isSearchingForReports: {
key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS,
initWithStoredValues: false,
},
}),
)(NewChatPage);
Loading
Loading