Files
pwap/mobile/src/contexts/AuthContext.tsx
T
pezkuwichain 1a47363938 Fix all ESLint errors in mobile app (157 errors -> 0)
Major fixes:
- Replace `any` types with proper TypeScript types across all files
- Convert require() imports to ES module imports
- Add __DEV__ guards to console statements
- Escape special characters in JSX (' and ")
- Fix unused variables (prefix with _ or remove)
- Fix React hooks violations (useCallback, useMemo patterns)
- Convert wasm-crypto-shim.js to TypeScript
- Add eslint-disable comments for valid setState patterns

Files affected: 50+ screens, components, contexts, and services

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 02:55:03 +03:00

304 lines
8.0 KiB
TypeScript

import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
import { supabase } from '../lib/supabase';
import AsyncStorage from '@react-native-async-storage/async-storage';
import type { User } from '@supabase/supabase-js';
// Session timeout configuration
const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
const ACTIVITY_CHECK_INTERVAL_MS = 60 * 1000; // Check every 1 minute
const LAST_ACTIVITY_KEY = '@pezkuwi_last_activity';
interface AuthContextType {
user: User | null;
loading: boolean;
isAdmin: boolean;
signIn: (email: string, password: string) => Promise<{ error: Error | null }>;
signUp: (email: string, password: string, username: string, referralCode?: string) => Promise<{ error: Error | null }>;
signOut: () => Promise<void>;
changePassword: (newPassword: string, currentPassword: string) => Promise<{ error: Error | null }>;
resetPassword: (email: string) => Promise<{ error: Error | null }>;
checkAdminStatus: () => Promise<boolean>;
updateActivity: () => void;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
};
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [isAdmin, setIsAdmin] = useState(false);
// Update last activity timestamp
const updateActivity = useCallback(async () => {
try {
await AsyncStorage.setItem(LAST_ACTIVITY_KEY, Date.now().toString());
} catch (error) {
if (__DEV__) console.error('Failed to update activity:', error);
}
}, []);
const signOut = useCallback(async () => {
setIsAdmin(false);
setUser(null);
await AsyncStorage.removeItem(LAST_ACTIVITY_KEY);
await supabase.auth.signOut();
}, []);
// Check if session has timed out
const checkSessionTimeout = useCallback(async () => {
if (!user) return;
try {
const lastActivity = await AsyncStorage.getItem(LAST_ACTIVITY_KEY);
if (!lastActivity) {
await updateActivity();
return;
}
const lastActivityTime = parseInt(lastActivity, 10);
const now = Date.now();
const inactiveTime = now - lastActivityTime;
if (inactiveTime >= SESSION_TIMEOUT_MS) {
if (__DEV__) console.warn('⏱️ Session timeout - logging out due to inactivity');
await signOut();
}
} catch (error) {
if (__DEV__) console.error('Error checking session timeout:', error);
}
}, [user, updateActivity, signOut]);
// Setup activity monitoring
useEffect(() => {
if (!user) return;
// Initial activity timestamp
updateActivity();
// Check for timeout periodically
const timeoutChecker = setInterval(checkSessionTimeout, ACTIVITY_CHECK_INTERVAL_MS);
return () => {
clearInterval(timeoutChecker);
};
}, [user, updateActivity, checkSessionTimeout]);
// Check admin status
const checkAdminStatus = useCallback(async (): Promise<boolean> => {
if (!user) return false;
try {
// Skip admin check in development if column doesn't exist
if (process.env.EXPO_PUBLIC_ENV === 'development') {
setIsAdmin(false);
return false;
}
const { data, error } = await supabase
.from('profiles')
.select('is_admin')
.eq('id', user.id)
.single();
if (error) {
// Silently fail in dev mode - column might not exist yet
setIsAdmin(false);
return false;
}
const adminStatus = data?.is_admin || false;
setIsAdmin(adminStatus);
return adminStatus;
} catch {
setIsAdmin(false);
return false;
}
}, [user]);
// Sign in function
const signIn = async (email: string, password: string): Promise<{ error: Error | null }> => {
try {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
return { error };
}
if (data.user) {
setUser(data.user);
await updateActivity();
await checkAdminStatus();
}
return { error: null };
} catch (error) {
return { error: error as Error };
}
};
// Sign up function
const signUp = async (
email: string,
password: string,
username: string,
referralCode?: string
): Promise<{ error: Error | null }> => {
try {
// Create auth user
const { data: authData, error: authError } = await supabase.auth.signUp({
email,
password,
options: {
data: {
username,
referral_code: referralCode,
},
},
});
if (authError) {
return { error: authError };
}
// Create profile
if (authData.user) {
const { error: profileError } = await supabase
.from('profiles')
.insert({
id: authData.user.id,
username,
email,
referral_code: referralCode,
});
if (profileError) {
if (__DEV__) console.error('Profile creation error:', profileError);
// Don't fail signup if profile creation fails
}
setUser(authData.user);
await updateActivity();
}
return { error: null };
} catch (error) {
return { error: error as Error };
}
};
// Change password function
const changePassword = async (newPassword: string, currentPassword: string): Promise<{ error: Error | null }> => {
try {
if (!user || !user.email) {
return { error: new Error('User not authenticated') };
}
// First verify current password by attempting to sign in
const { error: verifyError } = await supabase.auth.signInWithPassword({
email: user.email,
password: currentPassword,
});
if (verifyError) {
return { error: new Error('Current password is incorrect') };
}
// If current password is correct, update to new password
const { error: updateError } = await supabase.auth.updateUser({
password: newPassword,
});
if (updateError) {
return { error: updateError };
}
return { error: null };
} catch (error) {
return { error: error as Error };
}
};
// Reset password function (forgot password)
const resetPassword = async (email: string): Promise<{ error: Error | null }> => {
try {
const { error } = await supabase.auth.resetPasswordForEmail(email, {
redirectTo: 'pezkuwichain://reset-password',
});
if (error) {
return { error };
}
return { error: null };
} catch (error) {
return { error: error as Error };
}
};
// Initialize auth state
useEffect(() => {
const initAuth = async () => {
try {
// Get initial session
const { data: { session } } = await supabase.auth.getSession();
if (session?.user) {
setUser(session.user);
await checkAdminStatus();
await updateActivity();
}
} catch (error) {
if (__DEV__) console.error('Error initializing auth:', error);
} finally {
setLoading(false);
}
};
initAuth();
// Listen for auth changes
const { data: { subscription } } = supabase.auth.onAuthStateChange(
async (_event, session) => {
setUser(session?.user ?? null);
if (session?.user) {
await checkAdminStatus();
await updateActivity();
} else {
setIsAdmin(false);
}
}
);
return () => {
subscription.unsubscribe();
};
}, [checkAdminStatus, updateActivity]);
const value = {
user,
loading,
isAdmin,
signIn,
signUp,
signOut,
changePassword,
resetPassword,
checkAdminStatus,
updateActivity,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};