Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
daebabb
feat: seed default My expenses saved search for dual-role users and r…
aswin-s Jun 14, 2026
0dc1db1
style: apply prettier formatting
aswin-s Jun 14, 2026
dc4eca7
fix: correct dual-role eligibility check and remove stale collapse-tr…
aswin-s Jun 17, 2026
bfa4f17
fix: extract isDualRoleUser, fix retry-loop dep, add unit tests
aswin-s Jun 17, 2026
db9bef9
fix: resolve CI failures from RENAME_SAVED_SEARCH removal
aswin-s Jun 17, 2026
dc6637b
fix: resolve ESLint failures
aswin-s Jun 17, 2026
0eca823
fix: use optional chaining on entry.at(0) to satisfy TypeScript
aswin-s Jun 17, 2026
c976f0d
feat: add translations for mySavedSearch key
aswin-s Jun 17, 2026
008446c
fix: move seeding useEffect to SearchTypeMenuWide so re-seeding works…
aswin-s Jun 17, 2026
9d7b8ba
fix: move seeding logic to SearchPage so it runs on narrow (mobile) l…
aswin-s Jun 17, 2026
26f83c5
fix: resolve merge conflict in SearchPage, drop unused CONST import
aswin-s Jun 17, 2026
29ba6de
fix: override pendingAction to null in PolicyTest to prevent random D…
aswin-s Jun 17, 2026
0f4f319
Merge remote-tracking branch 'origin/main' into fix/issue-92780
aswin-s Jun 17, 2026
183fbca
fix: add synchronous ref guard to prevent duplicate seedMyExpensesSea…
aswin-s Jun 17, 2026
68dc40d
fix: skip seeding My Expenses search if query hash already exists
aswin-s Jun 17, 2026
3dd32fd
refactor: extract seeding logic into useSeedMyExpensesSearch hook
aswin-s Jun 17, 2026
891c334
test: add useSeedMyExpensesSearch hook tests
aswin-s Jun 17, 2026
7e64b4a
fix: resolve ESLint errors in seedMyExpensesSearch hook and tests
aswin-s Jun 18, 2026
3197642
Merge remote-tracking branch 'origin/main' into fix/issue-92780
aswin-s Jun 18, 2026
c0448c0
refactor: address review feedback on My expenses seed hook
aswin-s Jun 22, 2026
ad0bddb
Merge remote-tracking branch 'upstream/main' into fix/issue-92780
aswin-s Jun 22, 2026
b288569
fix: use named API import in seedMyExpensesSearch after main migration
aswin-s Jun 22, 2026
6b1e7bc
refactor: extract My expenses seed selector to a module-level factory
aswin-s Jun 22, 2026
0b425c6
Merge remote-tracking branch 'upstream/main' into fix/issue-92780
aswin-s Jul 9, 2026
26c0f72
style: apply oxfmt formatting to seed My expenses hook and tests
aswin-s Jul 9, 2026
6adada5
Merge remote-tracking branch 'origin/main' into fix/issue-92780
aswin-s Jul 12, 2026
e81b073
fix: remove deprecated useOnyx dependencies argument
aswin-s Jul 13, 2026
1fea5c0
Merge remote-tracking branch 'origin/main' into fix/issue-92780
aswin-s Jul 18, 2026
819eabd
Add nvp_hasSeededMyExpensesSearch to safe Onyx export keys
aswin-s Jul 18, 2026
1d3a95e
Merge remote-tracking branch 'origin/main' into fix/issue-92780
aswin-s Jul 24, 2026
592f51e
Broaden dual-role eligibility to mirror suggested-search visibility
aswin-s Jul 24, 2026
77534e4
Wait for saved searches to load before seeding My expenses search
aswin-s Jul 24, 2026
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
1 change: 0 additions & 1 deletion src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8121,7 +8121,6 @@ const CONST = {
// TODO: CONCIERGE_LHN_GBR tooltip will be replaced by a tooltip in the #admins room
// https://github.com/Expensify/App/issues/57045#issuecomment-2701455668
CONCIERGE_LHN_GBR: 'conciergeLHNGBR',
RENAME_SAVED_SEARCH: 'renameSavedSearch',
OUTSTANDING_FILTER: 'outstandingFilter',
ACCOUNT_SWITCHER: 'accountSwitcher',
SCAN_TEST_DRIVE_CONFIRMATION: 'scanTestDriveConfirmation',
Expand Down
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,9 @@ const ONYXKEYS = {
/** Stores the information about the saved searches */
SAVED_SEARCHES: 'nvp_savedSearches',

/** Tracks whether the "My expenses" saved search has been seeded for this account */
NVP_HAS_SEEDED_MY_EXPENSES_SEARCH: 'nvp_hasSeededMyExpensesSearch',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need this to be added on backend?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, if user explicitly deletes the seeded search, then it won't be reflected on another device or after a fresh login on same device. The 'My Expense' search item will get recreated though user had explicitly deleted it.


/** Stores the information about the recent searches */
RECENT_SEARCHES: 'nvp_recentSearches',

Expand Down Expand Up @@ -1498,6 +1501,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.NVP_TRY_NEW_DOT]: OnyxTypes.TryNewDot;
[ONYXKEYS.RECENT_SEARCHES]: Record<string, OnyxTypes.RecentSearchItem>;
[ONYXKEYS.SAVED_SEARCHES]: OnyxTypes.SaveSearch;
[ONYXKEYS.NVP_HAS_SEEDED_MY_EXPENSES_SEARCH]: boolean;
[ONYXKEYS.SEARCH_CONTEXT]: OnyxTypes.SearchContext;
[ONYXKEYS.SEARCH_QUERY_BY_HASH]: Record<string, string>;
[ONYXKEYS.RECENTLY_USED_CURRENCIES]: string[];
Expand Down
9 changes: 1 addition & 8 deletions src/components/ProductTrainingContext/TOOLTIPS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {TranslationPaths} from '@src/languages/types';

import type {ValueOf} from 'type-fest';

const {CONCIERGE_LHN_GBR, RENAME_SAVED_SEARCH, OUTSTANDING_FILTER, ACCOUNT_SWITCHER, SCAN_TEST_DRIVE_CONFIRMATION, GPS_TOOLTIP, HAS_FILTER_NEGATION, MILEAGE_RATE_AUTO_UPDATED} =
const {CONCIERGE_LHN_GBR, OUTSTANDING_FILTER, ACCOUNT_SWITCHER, SCAN_TEST_DRIVE_CONFIRMATION, GPS_TOOLTIP, HAS_FILTER_NEGATION, MILEAGE_RATE_AUTO_UPDATED} =
CONST.PRODUCT_TRAINING_TOOLTIP_NAMES;

type ProductTrainingTooltipName = Exclude<ValueOf<typeof CONST.PRODUCT_TRAINING_TOOLTIP_NAMES>, typeof CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.MULTI_SCAN_EDUCATIONAL_MODAL>;
Expand Down Expand Up @@ -36,13 +36,6 @@ const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
// https://github.com/Expensify/App/issues/57045#issuecomment-2701455668
shouldShow: () => false,
},
[RENAME_SAVED_SEARCH]: {
content: 'productTrainingTooltip.saveSearchTooltip',
onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(RENAME_SAVED_SEARCH, isDismissedUsingCloseButton),
name: RENAME_SAVED_SEARCH,
priority: 1250,
shouldShow: ({shouldUseNarrowLayout}) => !shouldUseNarrowLayout,
},
[ACCOUNT_SWITCHER]: {
content: 'productTrainingTooltip.accountSwitcher',
onHideTooltip: () => dismissProductTraining(ACCOUNT_SWITCHER),
Expand Down
48 changes: 48 additions & 0 deletions src/hooks/useSeedMyExpensesSearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {seedMyExpensesSearch} from '@libs/actions/Search';
import {isSubmitterAndApprover} from '@libs/PolicyUtils';

import ONYXKEYS from '@src/ONYXKEYS';
import {accountIDSelector, emailSelector} from '@src/selectors/Session';
import type {Policy} from '@src/types/onyx';

import type {OnyxCollection} from 'react-native-onyx';

import {useEffect, useMemo, useRef} from 'react';

import useLocalize from './useLocalize';
import useOnyx from './useOnyx';

const createIsSubmitterAndApproverSelector = (currentUserEmail: string | undefined) => (policies: OnyxCollection<Policy>) => isSubmitterAndApprover(policies, currentUserEmail);

function useSeedMyExpensesSearch() {
const {translate} = useLocalize();
const [hasSeededMyExpensesSearch] = useOnyx(ONYXKEYS.NVP_HAS_SEEDED_MY_EXPENSES_SEARCH);
const [savedSearches, savedSearchesMetadata] = useOnyx(ONYXKEYS.SAVED_SEARCHES);
const [currentUserAccountID] = useOnyx(ONYXKEYS.SESSION, {selector: accountIDSelector});
const [currentUserEmail] = useOnyx(ONYXKEYS.SESSION, {selector: emailSelector});
const isSubmitterAndApproverSelector = useMemo(() => createIsSubmitterAndApproverSelector(currentUserEmail), [currentUserEmail]);
const [isSubmitterAndApproverUser] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: isSubmitterAndApproverSelector});
const hasSeededRef = useRef(false);

useEffect(() => {
// Wait for the saved searches to finish loading before seeding. Their value is `undefined` both while loading and when empty,
// so gating on the load status (instead of the value) prevents seeding over a pre-existing search that has not synced yet.
if (
hasSeededRef.current ||
hasSeededMyExpensesSearch ||
!currentUserAccountID ||
!currentUserEmail ||
isSubmitterAndApproverUser === undefined ||
savedSearchesMetadata.status !== 'loaded'
) {
return;
}

if (isSubmitterAndApproverUser) {
hasSeededRef.current = true;
seedMyExpensesSearch(currentUserAccountID, translate('search.mySavedSearch'), savedSearches);
}
}, [hasSeededMyExpensesSearch, currentUserAccountID, currentUserEmail, isSubmitterAndApproverUser, translate, savedSearches, savedSearchesMetadata.status]);
}

export default useSeedMyExpensesSearch;
2 changes: 1 addition & 1 deletion src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8854,6 +8854,7 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
deleteSavedSearchConfirm: 'Möchtest du diese Suche wirklich löschen?',
searchName: 'Namen suchen',
savedSearchesMenuItemTitle: 'Gespeichert',
mySavedSearch: 'Meine Ausgaben',
urlCopied: 'URL kopiert',
groupedExpenses: 'gruppierte Ausgaben',
bulkActions: {
Expand Down Expand Up @@ -10197,7 +10198,6 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
},
productTrainingTooltip: {
conciergeLHNGBR: '<tooltip>Beginne <strong>hier!</strong></tooltip>',
saveSearchTooltip: '<tooltip><strong>Benenne deine gespeicherten Suchen um</strong> – hier!</tooltip>',
accountSwitcher: '<tooltip>Greifen Sie hier auf Ihre <strong>Copilot-Konten</strong> zu</tooltip>',
outstandingFilter: '<tooltip>Nach Ausgaben filtern,\ndie <strong>genehmigt werden müssen</strong></tooltip>',
scanTestDriveTooltip: '<tooltip>Sende diese Quittung, um\n<strong>die Probefahrt abzuschließen!</strong></tooltip>',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8974,6 +8974,7 @@ const translations = {
deleteSavedSearchConfirm: 'Are you sure you want to delete this search?',
searchName: 'Search name',
savedSearchesMenuItemTitle: 'Saved',
mySavedSearch: 'My expenses',
urlCopied: 'URL copied',
spendOverTime: 'Spend over time',
groupedExpenses: 'grouped expenses',
Expand Down Expand Up @@ -10329,7 +10330,6 @@ const translations = {
// TODO: CONCIERGE_LHN_GBR tooltip will be replaced by a tooltip in the #admins room
// https://github.com/Expensify/App/issues/57045#issuecomment-2701455668
conciergeLHNGBR: '<tooltip>Get started <strong>here!</strong></tooltip>',
saveSearchTooltip: '<tooltip><strong>Rename your saved searches</strong> here!</tooltip>',
accountSwitcher: '<tooltip>Access your <strong>Copilot accounts</strong> here</tooltip>',
outstandingFilter: '<tooltip>Filter for expenses\nthat <strong>need approval</strong></tooltip>',
scanTestDriveTooltip: '<tooltip>Send this receipt to\n<strong>complete the test drive!</strong></tooltip>',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8671,6 +8671,7 @@ El plan Controlar empieza en 9 $ por miembro activo al mes.`,
view: {label: 'Ver', table: 'Tabla', bar: 'Barra', line: 'Línea', pie: 'Circular'},
saveView: 'Guardar vista',
savedSearchesMenuItemTitle: 'Guardadas',
mySavedSearch: 'Mis gastos',
urlCopied: 'URL copiada',
spendOverTime: 'Evolución de gastos',
searchName: 'Nombre de la búsqueda',
Expand Down Expand Up @@ -10356,7 +10357,6 @@ El plan Controlar empieza en 9 $ por miembro activo al mes.`,
},
productTrainingTooltip: {
conciergeLHNGBR: '<tooltip>¡Comienza <strong>aquí</strong>!</tooltip>',
saveSearchTooltip: '<tooltip><strong>Renombra tus búsquedas guardadas</strong> aquí</tooltip>',
accountSwitcher: '<tooltip>Accede a tus <strong>cuentas copiloto</strong> aquí</tooltip>',
outstandingFilter: '<tooltip>Filtra los gastos\nque <strong>necesitan aprobación</strong></tooltip>',
scanTestDriveTooltip: '<tooltip>¡Envía este recibo para\n<strong>completar la prueba</strong>!</tooltip>',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8890,6 +8890,7 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e
deleteSavedSearchConfirm: 'Voulez-vous vraiment supprimer cette recherche ?',
searchName: 'Rechercher un nom',
savedSearchesMenuItemTitle: 'Enregistré',
mySavedSearch: 'Mes dépenses',
urlCopied: 'URL copiée',
groupedExpenses: 'dépenses groupées',
bulkActions: {
Expand Down Expand Up @@ -10231,7 +10232,6 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e
},
productTrainingTooltip: {
conciergeLHNGBR: '<tooltip>Commencez <strong>ici&nbsp;!</strong></tooltip>',
saveSearchTooltip: '<tooltip><strong>Renommez vos recherches enregistrées</strong> ici !</tooltip>',
accountSwitcher: '<tooltip>Accédez à vos <strong>comptes Copilot</strong> ici</tooltip>',
outstandingFilter: '<tooltip>Filtrer les dépenses\nqui <strong>doivent être approuvées</strong></tooltip>',
scanTestDriveTooltip: '<tooltip>Envoyez ce reçu pour\n<strong>terminer l’essai !</strong></tooltip>',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8835,6 +8835,7 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
deleteSavedSearchConfirm: 'Sei sicuro di voler eliminare questa ricerca?',
searchName: 'Cerca nome',
savedSearchesMenuItemTitle: 'Salvato',
mySavedSearch: 'Le mie spese',
urlCopied: 'URL copiato',
groupedExpenses: 'spese raggruppate',
bulkActions: {
Expand Down Expand Up @@ -10177,7 +10178,6 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
},
productTrainingTooltip: {
conciergeLHNGBR: '<tooltip>Inizia <strong>qui!</strong></tooltip>',
saveSearchTooltip: '<tooltip><strong>Rinomina qui le ricerche salvate</strong>!</tooltip>',
accountSwitcher: '<tooltip>Accedi ai tuoi <strong>account Copilot</strong> qui</tooltip>',
outstandingFilter: '<tooltip>Filtra per le spese\nche <strong>necessitano di approvazione</strong></tooltip>',
scanTestDriveTooltip: '<tooltip>Invia questa ricevuta per\n<strong>completare il test drive!</strong></tooltip>',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8725,6 +8725,7 @@ ${reportName}`,
deleteSavedSearchConfirm: 'この検索を削除してもよろしいですか?',
searchName: '名前を検索',
savedSearchesMenuItemTitle: '保存済み',
mySavedSearch: '自分の経費',
urlCopied: 'URLをコピーしました',
groupedExpenses: 'グループ化された経費',
bulkActions: {
Expand Down Expand Up @@ -10043,7 +10044,6 @@ ${reportName}`,
},
productTrainingTooltip: {
conciergeLHNGBR: '<tooltip>まずは<strong>こちらから!</strong></tooltip>',
saveSearchTooltip: '<tooltip>保存済み検索の<strong>名前を変更</strong>しましょう!</tooltip>',
accountSwitcher: '<tooltip>ここから<strong>Copilot アカウント</strong>にアクセスできます</tooltip>',
outstandingFilter: '<tooltip><strong>承認が必要な</strong>経費を絞り込む</tooltip>',
scanTestDriveTooltip: '<tooltip>このレシートを送信して\n<strong>試用を完了しましょう!</strong></tooltip>',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8807,6 +8807,7 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
deleteSavedSearchConfirm: 'Weet je zeker dat je deze zoekopdracht wilt verwijderen?',
searchName: 'Zoeknaam',
savedSearchesMenuItemTitle: 'Opgeslagen',
mySavedSearch: 'Mijn uitgaven',
urlCopied: 'URL gekopieerd',
groupedExpenses: 'gegroepeerde uitgaven',
bulkActions: {
Expand Down Expand Up @@ -10148,7 +10149,6 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
},
productTrainingTooltip: {
conciergeLHNGBR: '<tooltip>Begin <strong>hier!</strong></tooltip>',
saveSearchTooltip: '<tooltip><strong>Hernoem hier je opgeslagen zoekopdrachten</strong>!</tooltip>',
accountSwitcher: '<tooltip>Krijg hier toegang tot je <strong>Copilot-accounts</strong></tooltip>',
outstandingFilter: '<tooltip>Filter voor uitgaven\ndie <strong>goedkeuring nodig hebben</strong></tooltip>',
scanTestDriveTooltip: '<tooltip>Stuur deze bon om\n<strong>de proefrit te voltooien!</strong></tooltip>',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8786,6 +8786,7 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
deleteSavedSearchConfirm: 'Na pewno chcesz usunąć to wyszukiwanie?',
searchName: 'Wyszukaj nazwę',
savedSearchesMenuItemTitle: 'Zapisano',
mySavedSearch: 'Moje wydatki',
urlCopied: 'URL skopiowany',
groupedExpenses: 'zgrupowane wydatki',
bulkActions: {
Expand Down Expand Up @@ -10119,7 +10120,6 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
},
productTrainingTooltip: {
conciergeLHNGBR: '<tooltip>Rozpocznij <strong>tutaj!</strong></tooltip>',
saveSearchTooltip: '<tooltip><strong>Zmień nazwę zapisanych wyszukiwań</strong> tutaj!</tooltip>',
accountSwitcher: '<tooltip>Uzyskaj tutaj dostęp do <strong>kont Copilot</strong></tooltip>',
outstandingFilter: '<tooltip>Filtruj wydatki,\nktóre <strong>wymagają zatwierdzenia</strong></tooltip>',
scanTestDriveTooltip: '<tooltip>Wyślij ten paragon, aby\n<strong>zakończyć jazdę próbną!</strong></tooltip>',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8794,6 +8794,7 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
deleteSavedSearchConfirm: 'Tem certeza de que deseja excluir esta pesquisa?',
searchName: 'Pesquisar nome',
savedSearchesMenuItemTitle: 'Salvo',
mySavedSearch: 'Minhas despesas',
urlCopied: 'URL copiado',
groupedExpenses: 'despesas agrupadas',
bulkActions: {
Expand Down Expand Up @@ -10133,7 +10134,6 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
},
productTrainingTooltip: {
conciergeLHNGBR: '<tooltip>Comece <strong>aqui!</strong></tooltip>',
saveSearchTooltip: '<tooltip><strong>Renomeie suas buscas salvas</strong> aqui!</tooltip>',
accountSwitcher: '<tooltip>Acesse suas <strong>contas Copilot</strong> aqui</tooltip>',
outstandingFilter: '<tooltip>Filtrar despesas\nque <strong>precisam de aprovação</strong></tooltip>',
scanTestDriveTooltip: '<tooltip>Envie este recibo para\n<strong>concluir o test drive!</strong></tooltip>',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8509,6 +8509,7 @@ ${reportName}`,
deleteSavedSearchConfirm: '确定要删除此搜索吗?',
searchName: '搜索名称',
savedSearchesMenuItemTitle: '已保存',
mySavedSearch: '我的报销费用',
urlCopied: 'URL 已复制',
groupedExpenses: '已分组的报销费用',
bulkActions: {
Expand Down Expand Up @@ -9814,7 +9815,6 @@ ${reportName}`,
},
productTrainingTooltip: {
conciergeLHNGBR: '<tooltip>从<strong>这里</strong>开始!</tooltip>',
saveSearchTooltip: '<tooltip>在这里<strong>重命名已保存的搜索</strong>!</tooltip>',
accountSwitcher: '<tooltip>在此访问你的<strong>Copilot 账户</strong></tooltip>',
outstandingFilter: '<tooltip>筛选<strong>待审批</strong>的报销</tooltip>',
scanTestDriveTooltip: '<tooltip>发送此收据以\n<strong>完成试用体验!</strong></tooltip>',
Expand Down
1 change: 1 addition & 0 deletions src/libs/ExportOnyxState/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ const safeOnyxKeys = new Set<string>([
ONYXKEYS.NVP_DISMISSED_REFERRAL_BANNERS,
ONYXKEYS.NVP_DISMISSED_REJECT_USE_EXPLANATION,
ONYXKEYS.NVP_EMPTY_REPORTS_CONFIRMATION_DISMISSED,
ONYXKEYS.NVP_HAS_SEEDED_MY_EXPENSES_SEARCH,
ONYXKEYS.NVP_HAS_SEEN_FOR_YOU_TODO,
ONYXKEYS.NVP_HAS_SEEN_TRACK_TRAINING,
ONYXKEYS.NVP_LAST_ANDROID_LOGIN,
Expand Down
28 changes: 28 additions & 0 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1476,6 +1476,33 @@ function arePaymentsEnabled(policy: OnyxEntry<Policy>): boolean {
return policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO;
}

/**
* Returns true when the user is both a submitter and an approver, mirroring the Submit/Approve suggested-search eligibility in
* `getSuggestedSearchesVisibility` (SearchUIUtils): a submitter is a member of any group workspace, and an approver is a member of a
* group workspace with a non-optional approval flow whom `isPolicyApprover` recognizes (named approver or someone reports submit/forward to).
*/
function isSubmitterAndApprover(policies: OnyxCollection<Policy> | null | undefined, currentUserEmail: string | undefined): boolean {
if (!policies || !currentUserEmail) {
return false;
}
let isSubmitter = false;
let isApprover = false;
for (const policy of Object.values(policies)) {
if (!policy) {
continue;
}
isSubmitter = isSubmitter || isGroupPolicy(policy);
if (!isApprover) {
const hasApprovalFlow = isGroupPolicy(policy) && !!policy.approvalMode && policy.approvalMode !== CONST.POLICY.APPROVAL_MODE.OPTIONAL;
isApprover = hasApprovalFlow && isPolicyApprover(policy, currentUserEmail);
}
if (isSubmitter && isApprover) {
return true;
}
}
return false;
}

function isControlOnAdvancedApprovalMode(policy: OnyxInputOrEntry<Policy>): boolean {
return policy?.type === CONST.POLICY.TYPE.CORPORATE && getApprovalWorkflow(policy) === CONST.POLICY.APPROVAL_MODE.ADVANCED;
}
Expand Down Expand Up @@ -2964,6 +2991,7 @@ export {
isPolicyMember,
isPolicyPayer,
arePaymentsEnabled,
isSubmitterAndApprover,
isSubmitAndClose,
isTaxTrackingEnabled,
shouldShowPolicy,
Expand Down
Loading
Loading