From 62ab5f2ad14107a8e69a1bf768a8b6a5ce73c370 Mon Sep 17 00:00:00 2001 From: keeandev <38674879+keeandev@users.noreply.github.com> Date: Wed, 19 Nov 2025 14:03:58 -0600 Subject: [PATCH 01/10] Add react-native-keyboard-controller for improved keyboard handling - Integrated `react-native-keyboard-controller` to enhance keyboard management across various views. - Updated multiple components (e.g., AuthModal, ReportModal, and SignInView) to utilize `KeyboardAwareScrollView` for better user experience when interacting with input fields. - Refactored existing keyboard handling logic to streamline focus and submission behavior. --- app/_layout.tsx | 21 +- components/AuthModal.tsx | 37 +- components/NewReportModal.tsx | 71 +-- components/ProjectMembershipModal.tsx | 363 ++++++----- components/ReportModal.tsx | 173 +++--- components/ui/drawer/index.tsx | 123 +--- components/ui/input.tsx | 12 + package-lock.json | 15 + package.json | 1 + views/ForgotPasswordView.tsx | 144 ++--- views/ProfileView.tsx | 525 ++++++++-------- views/RegisterView.tsx | 88 ++- views/SignInView.tsx | 37 +- views/new/NextGenAssetsView.tsx | 1 + views/new/NextGenProjectsView.tsx | 1 + views/new/NextGenTranslationModalAlt.tsx | 570 +++++++++--------- views/new/OnboardingFlow.tsx | 43 +- views/new/ProjectDirectoryView.tsx | 1 + views/new/QuestListView.tsx | 2 +- views/new/SimpleOnboardingFlow.tsx | 29 +- .../recording/components/RenameAssetModal.tsx | 173 +++--- 21 files changed, 1179 insertions(+), 1251 deletions(-) diff --git a/app/_layout.tsx b/app/_layout.tsx index 7a8227980..e70c38d81 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -34,6 +34,7 @@ import { DarkTheme, DefaultTheme } from '@react-navigation/native'; import { StatusBar } from 'expo-status-bar'; import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; +import { KeyboardProvider } from 'react-native-keyboard-controller'; import { configureReanimatedLogger, ReanimatedLogLevel @@ -136,15 +137,17 @@ export default function RootLayout() { - - {/* OTA Update Banner - shown before login and after */} - - - - - - - + + + {/* OTA Update Banner - shown before login and after */} + + + + + + + + diff --git a/components/AuthModal.tsx b/components/AuthModal.tsx index afe444db9..941ce7be1 100644 --- a/components/AuthModal.tsx +++ b/components/AuthModal.tsx @@ -3,13 +3,7 @@ import { AuthNavigator } from '@/navigators/AuthNavigator'; import { useThemeColor } from '@/utils/styleUtils'; import { XIcon } from 'lucide-react-native'; import React from 'react'; -import { - KeyboardAvoidingView, - Modal, - Platform, - Pressable, - View -} from 'react-native'; +import { Modal, Pressable, View } from 'react-native'; import { Icon } from './ui/icon'; interface AuthModalProps { @@ -32,24 +26,19 @@ export function AuthModal({ presentationStyle="pageSheet" onRequestClose={onClose} > - - - {/* Close button */} - - - - - - + + {/* Close button */} + + + + - + + ); } diff --git a/components/NewReportModal.tsx b/components/NewReportModal.tsx index e213e6b11..9733a9dc3 100644 --- a/components/NewReportModal.tsx +++ b/components/NewReportModal.tsx @@ -14,13 +14,15 @@ import { XIcon } from 'lucide-react-native'; import React, { useMemo, useState } from 'react'; import { Alert, - KeyboardAvoidingView, Modal, Pressable, - ScrollView, TouchableWithoutFeedback, View } from 'react-native'; +import { + KeyboardAwareScrollView, + KeyboardToolbar +} from 'react-native-keyboard-controller'; interface ReportModalProps { isVisible: boolean; @@ -171,21 +173,22 @@ export const ReportModal: React.FC = ({ > - - e.stopPropagation()}> - - - {modalTitle} - - - - + e.stopPropagation()}> + + + {modalTitle} + + + + - + @@ -261,27 +264,27 @@ export const ReportModal: React.FC = ({ )} - + - - - - + + + + ); }; diff --git a/components/ProjectMembershipModal.tsx b/components/ProjectMembershipModal.tsx index bdff81999..398f08e8f 100644 --- a/components/ProjectMembershipModal.tsx +++ b/components/ProjectMembershipModal.tsx @@ -5,7 +5,7 @@ import { Checkbox } from '@/components/ui/checkbox'; import { Icon } from '@/components/ui/icon'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; -import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Text } from '@/components/ui/text'; import { useAuth } from '@/contexts/AuthContext'; import type { profile, request } from '@/db/drizzleSchema'; @@ -34,15 +34,8 @@ import { XIcon } from 'lucide-react-native'; import React, { useState } from 'react'; -import { - Alert, - KeyboardAvoidingView, - Modal, - Platform, - Pressable, - ScrollView, - View -} from 'react-native'; +import { Alert, Modal, Pressable, ScrollView, View } from 'react-native'; +import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'; const MAX_INVITE_ATTEMPTS = 3; @@ -1009,132 +1002,110 @@ export const ProjectMembershipModal: React.FC = ({ const isInviteButtonEnabled = inviteEmail.trim() && isValidEmail(inviteEmail); return ( - - - - - - {t('projectMembers')} - - - - + + + {t('projectMembers')} + + + + - - {projectLoading ? ( - - {t('loadingProjectDetails')} - - ) : ( - + {projectLoading ? ( + + {t('loadingProjectDetails')} + + ) : ( + + {/* Tabs */} + + setActiveTab(value as 'members' | 'invited' | 'requests') + } + className="flex-1" > - {/* Tabs Header */} - - setActiveTab(value as 'members' | 'invited' | 'requests') - } - > - - + + + + {t('members')} ({sortedMembers.length}) + + + + + {t('invited')} ({visibleInvitations.length}) + + + {sendInvitePermissions.hasAccess && ( + - {t('members')} ({sortedMembers.length}) + {t('requests')} ({requestsData.length}) - - - {t('invited')} ({visibleInvitations.length}) + )} + + + + + {sortedMembers.length > 0 ? ( + sortedMembers.map(renderMember) + ) : ( + + {t('noMembers')} - - {sendInvitePermissions.hasAccess && ( - - - {t('requests')} ({requestsData.length}) - - )} - - - - {/* Tab Content - Manual switching for better control */} - - {activeTab === 'members' && ( - - {sortedMembers.length > 0 ? ( - sortedMembers.map(renderMember) - ) : ( - - {t('noMembers')} - - )} - - )} - - {activeTab === 'invited' && ( - - {visibleInvitations.length > 0 ? ( - visibleInvitations.map(renderInvitation) - ) : ( - - {t('noInvitations')} - - )} - - )} + + + + {visibleInvitations.length > 0 ? ( + visibleInvitations.map(renderInvitation) + ) : ( + + {t('noInvitations')} + + )} + - {activeTab === 'requests' && ( - + {sendInvitePermissions.hasAccess && ( + {requestsData.length > 0 ? ( requestsData.map(renderRequest) ) : ( @@ -1142,81 +1113,87 @@ export const ProjectMembershipModal: React.FC = ({ {t('noPendingRequests')} )} - + )} - - - {/* Invite Section */} - - {sendInvitePermissions.hasAccess ? ( - <> - - {t('inviteMembers')} - - - - setInviteAsOwner(!inviteAsOwner)} - > - - - - setShowTooltip(!showTooltip)} - > - - - - {showTooltip && ( - - {t('ownerTooltip')} - - )} - - - ) : ( - - - - {t('onlyOwnersCanInvite')} - + + + + setShowTooltip(!showTooltip)} + > + + - )} - - - )} - + {showTooltip && ( + + {t('ownerTooltip')} + + )} + + + ) : ( + + + + {t('onlyOwnersCanInvite')} + + + )} + + + )} - - + + ); }; diff --git a/components/ReportModal.tsx b/components/ReportModal.tsx index 80b274770..6c6e2750b 100644 --- a/components/ReportModal.tsx +++ b/components/ReportModal.tsx @@ -1,6 +1,3 @@ -import { useAuth } from '@/contexts/AuthContext'; -import { blockService } from '@/database_services/blockService'; -import { reportService } from '@/database_services/reportService'; import { Button } from '@/components/ui/button'; import { Icon } from '@/components/ui/icon'; import { Label } from '@/components/ui/label'; @@ -8,6 +5,9 @@ import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Switch } from '@/components/ui/switch'; import { Text } from '@/components/ui/text'; import { Textarea } from '@/components/ui/textarea'; +import { useAuth } from '@/contexts/AuthContext'; +import { blockService } from '@/database_services/blockService'; +import { reportService } from '@/database_services/reportService'; import { reasonOptions } from '@/db/constants'; import { useLocalization } from '@/hooks/useLocalization'; import { useMutation, useQueryClient } from '@tanstack/react-query'; @@ -15,13 +15,15 @@ import { XIcon } from 'lucide-react-native'; import React, { useState } from 'react'; import { Alert, - KeyboardAvoidingView, Modal, Pressable, - ScrollView, TouchableWithoutFeedback, View } from 'react-native'; +import { + KeyboardAwareScrollView, + KeyboardToolbar +} from 'react-native-keyboard-controller'; // Uncomment these imports when implementing duplicate report checking // import { useHybridData } from '@/views/new/useHybridData'; // import { toCompilableQuery } from '@powersync/drizzle-driver'; @@ -208,100 +210,99 @@ export const ReportModal: React.FC = ({ > - - e.stopPropagation()}> - - - {t('reportTranslation')} - - - - + e.stopPropagation()}> + + + {t('reportTranslation')} + + + + - - - - - {t('selectReasonLabel')} - - - handleReasonSelect( - value as (typeof reasonOptions)[number] - ) - } - > - {reasonOptions.map((option) => ( - - ))} - - + + + + + {t('selectReasonLabel')} + + + handleReasonSelect( + value as (typeof reasonOptions)[number] + ) + } + > + {reasonOptions.map((option) => ( + + ))} + + + + + + {t('additionalDetails')} + +