-
Notifications
You must be signed in to change notification settings - Fork 5
RR-T40 Logout fix, close call fix, units state fix, contacts fix, pro… #89
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |||||||||||||||||||||||||
| import { Env } from '@env'; | ||||||||||||||||||||||||||
| import { useFocusEffect } from '@react-navigation/native'; | ||||||||||||||||||||||||||
| import { useColorScheme } from 'nativewind'; | ||||||||||||||||||||||||||
| import React, { useCallback, useEffect } from 'react'; | ||||||||||||||||||||||||||
| import React, { useCallback, useEffect, useState } from 'react'; | ||||||||||||||||||||||||||
| import { useTranslation } from 'react-i18next'; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import { BackgroundGeolocationItem } from '@/components/settings/background-geolocation-item'; | ||||||||||||||||||||||||||
|
|
@@ -16,14 +16,18 @@ import { ServerUrlBottomSheet } from '@/components/settings/server-url-bottom-sh | |||||||||||||||||||||||||
| import { ThemeItem } from '@/components/settings/theme-item'; | ||||||||||||||||||||||||||
| import { ToggleItem } from '@/components/settings/toggle-item'; | ||||||||||||||||||||||||||
| import { FocusAwareStatusBar, ScrollView } from '@/components/ui'; | ||||||||||||||||||||||||||
| import { AlertDialog, AlertDialogBackdrop, AlertDialogBody, AlertDialogContent, AlertDialogFooter, AlertDialogHeader } from '@/components/ui/alert-dialog'; | ||||||||||||||||||||||||||
| import { Box } from '@/components/ui/box'; | ||||||||||||||||||||||||||
| import { Button, ButtonText } from '@/components/ui/button'; | ||||||||||||||||||||||||||
| import { Card } from '@/components/ui/card'; | ||||||||||||||||||||||||||
| import { Heading } from '@/components/ui/heading'; | ||||||||||||||||||||||||||
| import { Text } from '@/components/ui/text'; | ||||||||||||||||||||||||||
| import { VStack } from '@/components/ui/vstack'; | ||||||||||||||||||||||||||
| import { useAnalytics } from '@/hooks/use-analytics'; | ||||||||||||||||||||||||||
| import { useAuth, useAuthStore } from '@/lib'; | ||||||||||||||||||||||||||
| import { logger } from '@/lib/logging'; | ||||||||||||||||||||||||||
| import { getBaseApiUrl } from '@/lib/storage/app'; | ||||||||||||||||||||||||||
| import { clearAllAppData } from '@/lib/storage/clear-all-data'; | ||||||||||||||||||||||||||
| import { openLinkInBrowser } from '@/lib/utils'; | ||||||||||||||||||||||||||
| import { useUnitsStore } from '@/stores/units/store'; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
@@ -36,6 +40,8 @@ export default function Settings() { | |||||||||||||||||||||||||
| const { login, status, isAuthenticated } = useAuth(); | ||||||||||||||||||||||||||
| const [showServerUrl, setShowServerUrl] = React.useState(false); | ||||||||||||||||||||||||||
| const [showUnitSelection, setShowUnitSelection] = React.useState(false); | ||||||||||||||||||||||||||
| const [showLogoutConfirm, setShowLogoutConfirm] = useState(false); | ||||||||||||||||||||||||||
| const [isLoggingOut, setIsLoggingOut] = useState(false); | ||||||||||||||||||||||||||
| const { units } = useUnitsStore(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Track analytics when view becomes visible | ||||||||||||||||||||||||||
|
|
@@ -83,9 +89,54 @@ export default function Settings() { | |||||||||||||||||||||||||
| trackEvent('settings_logout_pressed', { | ||||||||||||||||||||||||||
| timestamp: new Date().toISOString(), | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| signOut(); | ||||||||||||||||||||||||||
| setShowLogoutConfirm(true); | ||||||||||||||||||||||||||
| }, [trackEvent]); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const handleLogoutConfirm = useCallback(async () => { | ||||||||||||||||||||||||||
| setIsLoggingOut(true); | ||||||||||||||||||||||||||
| trackEvent('settings_logout_confirmed', { | ||||||||||||||||||||||||||
| timestamp: new Date().toISOString(), | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| logger.info({ | ||||||||||||||||||||||||||
| message: 'User confirmed logout, clearing all app data', | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||
| // Clear all app data (stores, storage, cached values) | ||||||||||||||||||||||||||
| await clearAllAppData({ | ||||||||||||||||||||||||||
| resetStores: true, | ||||||||||||||||||||||||||
| clearStorage: true, | ||||||||||||||||||||||||||
| clearFilters: true, | ||||||||||||||||||||||||||
| clearSecure: false, // Keep secure keys for re-login | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Sign out the user | ||||||||||||||||||||||||||
| await signOut(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| logger.info({ | ||||||||||||||||||||||||||
| message: 'Logout completed successfully, all data cleared', | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||
| logger.error({ | ||||||||||||||||||||||||||
| message: 'Error during logout data cleanup', | ||||||||||||||||||||||||||
| context: { error }, | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| // Still sign out even if cleanup fails | ||||||||||||||||||||||||||
| await signOut(); | ||||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||||
| setIsLoggingOut(false); | ||||||||||||||||||||||||||
| setShowLogoutConfirm(false); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }, [trackEvent, signOut]); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const handleLogoutCancel = useCallback(() => { | ||||||||||||||||||||||||||
| trackEvent('settings_logout_cancelled', { | ||||||||||||||||||||||||||
| timestamp: new Date().toISOString(), | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| setShowLogoutConfirm(false); | ||||||||||||||||||||||||||
| }, [trackEvent]); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const handleSupportLinkPress = useCallback( | ||||||||||||||||||||||||||
| (linkType: string, url: string) => { | ||||||||||||||||||||||||||
| trackEvent('settings_support_link_pressed', { | ||||||||||||||||||||||||||
|
|
@@ -160,6 +211,27 @@ export default function Settings() { | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| <LoginInfoBottomSheet isOpen={showLoginInfo} onClose={() => setShowLoginInfo(false)} onSubmit={handleLoginInfoSubmit} /> | ||||||||||||||||||||||||||
| <ServerUrlBottomSheet isOpen={showServerUrl} onClose={() => setShowServerUrl(false)} /> | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| {/* Logout Confirmation Dialog */} | ||||||||||||||||||||||||||
| <AlertDialog isOpen={showLogoutConfirm} onClose={handleLogoutCancel}> | ||||||||||||||||||||||||||
| <AlertDialogBackdrop /> | ||||||||||||||||||||||||||
| <AlertDialogContent> | ||||||||||||||||||||||||||
| <AlertDialogHeader> | ||||||||||||||||||||||||||
| <Heading size="lg">{t('settings.logout_confirm_title')}</Heading> | ||||||||||||||||||||||||||
| </AlertDialogHeader> | ||||||||||||||||||||||||||
| <AlertDialogBody className="mb-4 mt-3"> | ||||||||||||||||||||||||||
| <Text>{t('settings.logout_confirm_message')}</Text> | ||||||||||||||||||||||||||
| </AlertDialogBody> | ||||||||||||||||||||||||||
| <AlertDialogFooter> | ||||||||||||||||||||||||||
| <Button variant="outline" action="secondary" onPress={handleLogoutCancel} disabled={isLoggingOut} className="mr-3"> | ||||||||||||||||||||||||||
| <ButtonText>{t('settings.logout_confirm_cancel')}</ButtonText> | ||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||
| <Button action="negative" onPress={handleLogoutConfirm} disabled={isLoggingOut}> | ||||||||||||||||||||||||||
| <ButtonText>{isLoggingOut ? '...' : t('settings.logout_confirm_yes')}</ButtonText> | ||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||
|
Comment on lines
+226
to
+231
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Check if the file exists and read the relevant section
head -250 "src/app/(app)/settings.tsx" | tail -30Repository: Resgrid/Responder Length of output: 1368 🏁 Script executed: # Check for any translation files and existing logout-related keys
fd -t f -e json -e ts -e tsx | grep -i "translation\|locale\|i18n" | head -20Repository: Resgrid/Responder Length of output: 261 🏁 Script executed: # Search for existing logout confirmation translation keys
rg "logout_confirm" --type ts --type tsxRepository: Resgrid/Responder Length of output: 88 🏁 Script executed: # Check imports and i18n setup in the settings file
head -50 "src/app/(app)/settings.tsx"Repository: Resgrid/Responder Length of output: 2750 🏁 Script executed: # Check the English translation file for logout confirmation keys
cat "src/translations/en.json" | grep -A 20 "logout_confirm"Repository: Resgrid/Responder Length of output: 1409 🏁 Script executed: # Get the full logout_confirm section from translation file
rg "logout_confirm" "src/translations/en.json" -A 3 -B 1Repository: Resgrid/Responder Length of output: 463 🏁 Script executed: # Check the full structure of settings keys in en.json
python3 << 'EOF'
import json
with open('src/translations/en.json', 'r') as f:
data = json.load(f)
if 'settings' in data:
settings = data['settings']
logout_keys = {k: v for k, v in settings.items() if 'logout' in k}
print("Logout-related keys found:")
for key, value in sorted(logout_keys.items()):
print(f" {key}: {value}")
else:
print("No 'settings' key found in translation file")
EOFRepository: Resgrid/Responder Length of output: 147 🏁 Script executed: # Search for any existing loading or status indicator keys in settings
rg "loading\|loading_state\|please_wait" "src/translations/en.json" | head -20Repository: Resgrid/Responder Length of output: 43 🏁 Script executed: # Double-check the exact code in the file around line 226-231
sed -n '220,235p' "src/app/(app)/settings.tsx"Repository: Resgrid/Responder Length of output: 874 Localize the logout loading indicator. Suggested change- <ButtonText>{isLoggingOut ? '...' : t('settings.logout_confirm_yes')}</ButtonText>
+ <ButtonText>{isLoggingOut ? t('settings.logout_confirm_loading') : t('settings.logout_confirm_yes')}</ButtonText>Then add the key 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| </AlertDialogFooter> | ||||||||||||||||||||||||||
| </AlertDialogContent> | ||||||||||||||||||||||||||
| </AlertDialog> | ||||||||||||||||||||||||||
| </Box> | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 102
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 106
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 1508
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 1881
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 97
🏁 Script executed:
# Read the actual Button component implementation cat -n src/components/ui/button.tsxRepository: Resgrid/Responder
Length of output: 121
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 88
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 88
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 88
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 192
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 850
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 13940
🏁 Script executed:
# Check if there are other button-related files ls -la src/components/ui/button/Repository: Resgrid/Responder
Length of output: 237
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 998
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 403
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 3355
🏁 Script executed:
Repository: Resgrid/Responder
Length of output: 179
Button mock's ButtonText doesn't accept or forward props.
The mock ButtonText only accepts
{children}while production ButtonText spreads{...props}. Tests press ButtonText viagetByText, and without forwardingonPress, handlers may not trigger reliably. Spread props in ButtonText to match production behavior and ensure test reliability.🤖 Prompt for AI Agents