From c3f66c78ea57b249f873f484d190ef7daeb2944f Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Tue, 27 Jan 2026 15:34:05 +0100 Subject: [PATCH] Refactor filter overlay in items list to work the same for iOS and Android --- .../app/(tabs)/items/folder/[id].tsx | 188 ++++++++++-------- apps/mobile-app/app/(tabs)/items/index.tsx | 154 +------------- 2 files changed, 116 insertions(+), 226 deletions(-) diff --git a/apps/mobile-app/app/(tabs)/items/folder/[id].tsx b/apps/mobile-app/app/(tabs)/items/folder/[id].tsx index 2bbb14609..c00954ee0 100644 --- a/apps/mobile-app/app/(tabs)/items/folder/[id].tsx +++ b/apps/mobile-app/app/(tabs)/items/folder/[id].tsx @@ -460,13 +460,30 @@ export default function FolderViewScreen(): React.ReactNode { lineHeight: 22, }, // Filter menu styles - filterMenu: { + filterMenuOverlay: { backgroundColor: colors.accentBackground, borderColor: colors.accentBorder, borderRadius: 8, borderWidth: 1, - marginBottom: 8, + elevation: 8, + left: 14, overflow: 'hidden', + position: 'absolute', + right: 14, + shadowColor: colors.black, + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.25, + shadowRadius: 4, + top: Platform.OS === 'ios' ? paddingTop + 104 : paddingTop +44, + zIndex: 1001, + }, + filterMenuBackdrop: { + bottom: 0, + left: 0, + position: 'absolute', + right: 0, + top: 0, + zIndex: 1000, }, filterMenuItem: { paddingHorizontal: 16, @@ -540,105 +557,114 @@ export default function FolderViewScreen(): React.ReactNode { }); /** - * Render the filter menu. + * Render the filter menu as an absolute overlay. */ - const renderFilterMenu = (): React.ReactNode => { + const renderFilterOverlay = (): React.ReactNode => { if (!showFilterMenu) { return null; } return ( - - {/* All items filter */} + <> + {/* Backdrop to close menu when tapping outside */} { - setFilterType('all'); - setShowFilterMenu(false); - }} - > - - {t('items.filters.all')} - - - - - - {/* Item type filters */} - {ITEM_TYPE_OPTIONS.map((option) => ( + style={styles.filterMenuBackdrop} + activeOpacity={1} + onPress={() => setShowFilterMenu(false)} + /> + {/* Menu content */} + + {/* All items filter */} { - setFilterType(option.type); + setFilterType('all'); setShowFilterMenu(false); }} > - - {t(option.titleKey)} + {t('items.filters.all')} - ))} - + - {/* Passkeys filter */} - { - setFilterType('passkeys'); - setShowFilterMenu(false); - }} - > - - {t('items.filters.passkeys')} - - + {/* Item type filters */} + {ITEM_TYPE_OPTIONS.map((option) => ( + { + setFilterType(option.type); + setShowFilterMenu(false); + }} + > + + + {t(option.titleKey)} + + + ))} - {/* Attachments filter */} - { - setFilterType('attachments'); - setShowFilterMenu(false); - }} - > - - {t('common.attachments')} - - - + + + {/* Passkeys filter */} + { + setFilterType('passkeys'); + setShowFilterMenu(false); + }} + > + + {t('items.filters.passkeys')} + + + + {/* Attachments filter */} + { + setFilterType('attachments'); + setShowFilterMenu(false); + }} + > + + {t('common.attachments')} + + + + ); }; @@ -666,9 +692,6 @@ export default function FolderViewScreen(): React.ReactNode { /> - {/* Filter menu */} - {renderFilterMenu()} - {/* Search input */} + {/* Filter menu overlay */} + {renderFilterOverlay()} + {/* Folder modals */} { - if (!showFilterMenu) { - return null; - } - - return ( - - {/* All items filter */} - { - setFilterType('all'); - setShowFilterMenu(false); - }} - > - - {t('items.filters.all')} - - - - - - {/* Item type filters */} - {ITEM_TYPE_OPTIONS.map((option) => ( - { - setFilterType(option.type); - setShowFilterMenu(false); - }} - > - - - {t(option.titleKey)} - - - ))} - - - - {/* Passkeys filter */} - { - setFilterType('passkeys'); - setShowFilterMenu(false); - }} - > - - {t('items.filters.passkeys')} - - - - {/* Attachments filter */} - { - setFilterType('attachments'); - setShowFilterMenu(false); - }} - > - - {t('common.attachments')} - - - - - - {/* Recently deleted link */} - { - setShowFilterMenu(false); - router.push('/(tabs)/items/deleted'); - }} - > - - - {t('items.recentlyDeleted.title')} - - {recentlyDeletedCount > 0 && ( - - {recentlyDeletedCount} - - )} - - - - ); - }; - - /** - * Render the Android filter menu as an absolute overlay. - */ - const renderAndroidFilterOverlay = (): React.ReactNode => { - if (Platform.OS !== 'android' || !showFilterMenu || hasItemsInFoldersOnly) { + const renderFilterOverlay = (): React.ReactNode => { + if (!showFilterMenu || hasItemsInFoldersOnly) { return null; } @@ -978,9 +845,6 @@ export default function ItemsScreen(): React.ReactNode { ) )} - {/* Filter menu (iOS only - Android uses absolute overlay, only when not all items in folders) */} - {Platform.OS === 'ios' && !hasItemsInFoldersOnly && renderFilterMenu()} - {/* Search input */} - {/* Android filter menu overlay */} - {renderAndroidFilterOverlay()} + {/* Filter menu overlay */} + {renderFilterOverlay()} {/* Create folder modal */}