mirror of
https://github.com/DocNR/POWR.git
synced 2025-04-19 10:51:19 +00:00
fix(profile): standardize login experience across profile screens
- Create reusable NostrProfileLogin component for consistent UI across all screens - Fix profile feed error when not authenticated by conditionally calling useSocialFeed - Resolve hook ordering inconsistencies to prevent React errors - Improve feed loading state management during authentication transitions - Update all profile screens (overview, activity, progress, settings) to use shared component - Add proper error handling for profile data loading when not authenticated This change resolves the console error that appeared when accessing the profile tab while not logged in, while also providing a better user experience through consistent styling and messaging across all profile screens.
This commit is contained in:
parent
fdd17ef261
commit
755e86e30b
11
CHANGELOG.md
11
CHANGELOG.md
@ -15,8 +15,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Added "Sign with Amber" option to login screen
|
||||
- Added comprehensive documentation in docs/technical/nostr/external-signers.md
|
||||
- Added technical documentation in docs/technical/nostr/amber-integration-fixes.md
|
||||
- Standardized login experience across profile screens
|
||||
- Created reusable NostrProfileLogin component for consistent UI
|
||||
- Added customizable messaging for context-specific instructions
|
||||
- Standardized button styling and text formatting
|
||||
- Improved visual hierarchy with consistent spacing
|
||||
|
||||
### Fixed
|
||||
- Profile tab login experience
|
||||
- Fixed error when accessing profile feed without authentication
|
||||
- Created standardized login component across all profile screens
|
||||
- Added conditional hook calling to prevent "rendered fewer hooks than expected" error
|
||||
- Improved state management during authentication transitions
|
||||
- Enhanced profile data loading with better error handling
|
||||
- Android: Fixed Amber external signer integration issues
|
||||
- Added extensive logging to better diagnose communication issues
|
||||
- Improved error handling in `AmberSignerModule.kt`
|
||||
|
@ -8,6 +8,7 @@ import { useNDKCurrentUser } from '@/lib/hooks/useNDK';
|
||||
import { ActivityIndicator } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import NostrLoginSheet from '@/components/sheets/NostrLoginSheet';
|
||||
import NostrProfileLogin from '@/components/social/NostrProfileLogin';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { useAnalytics } from '@/lib/hooks/useAnalytics';
|
||||
import { PersonalRecord } from '@/lib/services/AnalyticsService';
|
||||
@ -54,25 +55,7 @@ export default function ActivityScreen() {
|
||||
|
||||
// Show different UI when not authenticated
|
||||
if (!isAuthenticated) {
|
||||
return (
|
||||
<View className="flex-1 items-center justify-center p-6">
|
||||
<Text className="text-center text-muted-foreground mb-8">
|
||||
Login with your Nostr private key to view your activity and stats.
|
||||
</Text>
|
||||
<Button
|
||||
onPress={() => setIsLoginSheetOpen(true)}
|
||||
className="px-6"
|
||||
>
|
||||
<Text className="text-white">Login with Nostr</Text>
|
||||
</Button>
|
||||
|
||||
{/* NostrLoginSheet */}
|
||||
<NostrLoginSheet
|
||||
open={isLoginSheetOpen}
|
||||
onClose={() => setIsLoginSheetOpen(false)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
return <NostrProfileLogin message="Login with your Nostr private key to view your activity and stats." />;
|
||||
}
|
||||
|
||||
if (loading || workoutsLoading || templatesLoading) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// app/(tabs)/profile/overview.tsx
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import { View, FlatList, RefreshControl, Pressable, TouchableOpacity, ImageBackground, Clipboard } from 'react-native';
|
||||
import { Text } from '@/components/ui/text';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@ -7,6 +7,7 @@ import { useNDKCurrentUser } from '@/lib/hooks/useNDK';
|
||||
import { ActivityIndicator } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import NostrLoginSheet from '@/components/sheets/NostrLoginSheet';
|
||||
import NostrProfileLogin from '@/components/social/NostrProfileLogin';
|
||||
import EnhancedSocialPost from '@/components/social/EnhancedSocialPost';
|
||||
import EmptyFeed from '@/components/social/EmptyFeed';
|
||||
import { useSocialFeed } from '@/lib/hooks/useSocialFeed';
|
||||
@ -44,20 +45,31 @@ export default function OverviewScreen() {
|
||||
const theme = useTheme() as CustomTheme;
|
||||
const { currentUser, isAuthenticated } = useNDKCurrentUser();
|
||||
const [isLoginSheetOpen, setIsLoginSheetOpen] = useState(false);
|
||||
// Always use useSocialFeed regardless of authentication state to avoid hook inconsistency
|
||||
// This prevents the "Rendered fewer hooks than expected" error when auth state changes
|
||||
const {
|
||||
feedItems,
|
||||
loading,
|
||||
refresh,
|
||||
isOffline
|
||||
} = useSocialFeed({
|
||||
// Initialize feed related state
|
||||
const [feedItems, setFeedItems] = useState<any[]>([]);
|
||||
const [feedLoading, setFeedLoading] = useState(false);
|
||||
const [isOffline, setIsOffline] = useState(false);
|
||||
|
||||
// Only call useSocialFeed when authenticated to prevent the error
|
||||
const socialFeed = isAuthenticated ? useSocialFeed({
|
||||
feedType: 'profile',
|
||||
// Always provide an array for authors, empty if not authenticated
|
||||
// This way the hook is always called with the same pattern
|
||||
authors: currentUser?.pubkey ? [currentUser.pubkey] : [],
|
||||
limit: 30
|
||||
});
|
||||
}) : null;
|
||||
|
||||
// Extract values from socialFeed when authenticated
|
||||
const loading = isAuthenticated ? socialFeed?.loading || false : feedLoading;
|
||||
const refresh = isAuthenticated
|
||||
? (socialFeed?.refresh ? socialFeed.refresh : () => Promise.resolve())
|
||||
: () => Promise.resolve();
|
||||
|
||||
// Update feedItems when socialFeed.feedItems changes
|
||||
useEffect(() => {
|
||||
if (isAuthenticated && socialFeed) {
|
||||
setFeedItems(socialFeed.feedItems);
|
||||
setIsOffline(socialFeed.isOffline);
|
||||
}
|
||||
}, [isAuthenticated, socialFeed?.feedItems, socialFeed?.isOffline]);
|
||||
|
||||
// Convert to the format expected by the component
|
||||
const entries = React.useMemo(() => {
|
||||
@ -359,26 +371,9 @@ export default function OverviewScreen() {
|
||||
// Render functions for different app states
|
||||
const renderLoginScreen = useCallback(() => {
|
||||
return (
|
||||
<View className="flex-1 items-center justify-center p-6">
|
||||
<Text className="text-center text-muted-foreground mb-8">
|
||||
Login with your Nostr private key to view your profile and posts.
|
||||
</Text>
|
||||
<Button
|
||||
onPress={() => setIsLoginSheetOpen(true)}
|
||||
className="px-6 py-3"
|
||||
style={{ backgroundColor: 'hsl(261 90% 66%)' }}
|
||||
>
|
||||
<Text className="text-white font-medium">Login with Nostr</Text>
|
||||
</Button>
|
||||
|
||||
{/* NostrLoginSheet */}
|
||||
<NostrLoginSheet
|
||||
open={isLoginSheetOpen}
|
||||
onClose={() => setIsLoginSheetOpen(false)}
|
||||
/>
|
||||
</View>
|
||||
<NostrProfileLogin message="Login with your Nostr private key to view your profile and posts." />
|
||||
);
|
||||
}, [isLoginSheetOpen]);
|
||||
}, []);
|
||||
|
||||
const renderLoadingScreen = useCallback(() => {
|
||||
return (
|
||||
|
@ -1,5 +1,6 @@
|
||||
// app/(tabs)/profile/progress.tsx
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import NostrProfileLogin from '@/components/social/NostrProfileLogin';
|
||||
import { View, ScrollView, Switch, TouchableOpacity } from 'react-native';
|
||||
import { Text } from '@/components/ui/text';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@ -167,13 +168,7 @@ export default function ProgressScreen() {
|
||||
};
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return (
|
||||
<View className="flex-1 items-center justify-center p-6">
|
||||
<Text className="text-center text-muted-foreground">
|
||||
Log in to view your progress
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
return <NostrProfileLogin message="Login with your Nostr private key to view your progress." />;
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
|
@ -8,6 +8,7 @@ import { useNDKCurrentUser, useNDKAuth } from '@/lib/hooks/useNDK';
|
||||
import { ActivityIndicator } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import NostrLoginSheet from '@/components/sheets/NostrLoginSheet';
|
||||
import NostrProfileLogin from '@/components/social/NostrProfileLogin';
|
||||
import TermsOfServiceModal from '@/components/TermsOfServiceModal';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import type { CustomTheme } from '@/lib/theme';
|
||||
@ -25,26 +26,7 @@ export default function SettingsScreen() {
|
||||
|
||||
// Show different UI when not authenticated
|
||||
if (!isAuthenticated) {
|
||||
return (
|
||||
<View className="flex-1 items-center justify-center p-6">
|
||||
<Text className="text-center text-muted-foreground mb-8">
|
||||
Login with your Nostr private key to access settings.
|
||||
</Text>
|
||||
<Button
|
||||
variant="purple"
|
||||
onPress={() => setIsLoginSheetOpen(true)}
|
||||
className="px-6"
|
||||
>
|
||||
<Text className="text-white">Login with Nostr</Text>
|
||||
</Button>
|
||||
|
||||
{/* NostrLoginSheet */}
|
||||
<NostrLoginSheet
|
||||
open={isLoginSheetOpen}
|
||||
onClose={() => setIsLoginSheetOpen(false)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
return <NostrProfileLogin message="Login with your Nostr private key to access settings." />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
36
components/social/NostrProfileLogin.tsx
Normal file
36
components/social/NostrProfileLogin.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { Text } from '@/components/ui/text';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import NostrLoginSheet from '@/components/sheets/NostrLoginSheet';
|
||||
|
||||
interface NostrProfileLoginProps {
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export default function NostrProfileLogin({
|
||||
message = "Login with your Nostr private key to access your profile data."
|
||||
}: NostrProfileLoginProps) {
|
||||
const [isLoginSheetOpen, setIsLoginSheetOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<View className="flex-1 items-center justify-center p-6">
|
||||
<Text className="text-center text-muted-foreground mb-8">
|
||||
{message}
|
||||
</Text>
|
||||
<Button
|
||||
variant="purple"
|
||||
onPress={() => setIsLoginSheetOpen(true)}
|
||||
className="px-6 py-3"
|
||||
>
|
||||
<Text className="text-white font-medium">Login with Nostr</Text>
|
||||
</Button>
|
||||
|
||||
{/* NostrLoginSheet */}
|
||||
<NostrLoginSheet
|
||||
open={isLoginSheetOpen}
|
||||
onClose={() => setIsLoginSheetOpen(false)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user