This commit is contained in:
Danish Arora 2025-09-23 20:17:01 +05:30
parent 619c914e1b
commit 8d0f86fb2e
No known key found for this signature in database
GPG Key ID: 1C6EF37CDAE1426E
23 changed files with 91 additions and 23 deletions

View File

@ -1,10 +1,20 @@
export * from './provider/OpChanProvider';
export { ClientProvider, useClient } from './contexts/ClientContext';
export { AuthProvider, useAuth } from './contexts/AuthContext';
export { ForumProvider, useForum as useForumContext } from './contexts/ForumContext';
export { ModerationProvider, useModeration } from './contexts/ModerationContext';
export { useIdentity } from './hooks/useIdentity';
// New v1 exports are namespaced to avoid breaking the app while we migrate.
// Old API remains available under ./old/index exports.
export * from './old/index';
export {
OpChanProvider as OpChanProviderV1,
useClient as useClientV1,
} from './v1/context/ClientContext';
export { OpChanProvider as NewOpChanProvider } from './v1/provider/OpChanProvider';
export { useAuth as useAuthV1 } from './v1/hooks/useAuth';
export { useContent as useContentV1 } from './v1/hooks/useContent';
export { usePermissions as usePermissionsV1 } from './v1/hooks/usePermissions';
export { useNetwork as useNetworkV1 } from './v1/hooks/useNetwork';
export { useUserDisplay as useUserDisplayV1 } from './v1/hooks/useUserDisplay';
export { useForum as useForumV1 } from './v1/hooks/useForum';
export * from './hooks';
export { useForumApi as useForum } from './hooks/useForum';

View File

@ -5,8 +5,15 @@ import { DelegationDuration } from '@opchan/core';
import { useAppKitAccount } from '@reown/appkit/react';
import { useClient } from './ClientContext';
// Extend the base User with convenient, display-focused fields
export type CurrentUser = User & {
displayName: string;
ensName?: string;
ordinalDetailsText?: string;
};
export interface AuthContextValue {
currentUser: User | null;
currentUser: CurrentUser | null;
isAuthenticated: boolean;
isAuthenticating: boolean;
verificationStatus: EVerificationStatus;
@ -31,7 +38,7 @@ const AuthContext = createContext<AuthContextValue | null>(null);
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const client = useClient();
const [currentUser, setCurrentUser] = useState<User | null>(null);
const [currentUser, setCurrentUser] = useState<CurrentUser | null>(null);
const [isAuthenticating, setIsAuthenticating] = useState(false);
// Get wallet connection status from AppKit
@ -42,6 +49,30 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
const connectedAddress = bitcoinAccount.address || ethereumAccount.address;
const walletType = bitcoinAccount.isConnected ? 'bitcoin' : 'ethereum';
// Helper: enrich a base User with identity-derived display fields
const enrichUserWithIdentity = useCallback(async (baseUser: User): Promise<CurrentUser> => {
const address = baseUser.address;
// Resolve identity (debounced) and read display name from service
const identity = await client.userIdentityService.getUserIdentity(address);
const displayName = client.userIdentityService.getDisplayName(address);
const ensName = identity?.ensName ?? baseUser.ensDetails?.ensName;
const ordinalDetailsText = identity?.ordinalDetails?.ordinalDetails ?? baseUser.ordinalDetails?.ordinalDetails;
const callSign = identity?.callSign ?? baseUser.callSign;
const displayPreference = identity?.displayPreference ?? baseUser.displayPreference;
const verificationStatus = identity?.verificationStatus ?? baseUser.verificationStatus;
return {
...baseUser,
callSign,
displayPreference,
verificationStatus,
displayName,
ensName,
ordinalDetailsText,
} as CurrentUser;
}, [client]);
// ✅ Removed console.log to prevent infinite loop spam
// Define verifyOwnership function early so it can be used in useEffect dependencies
@ -56,14 +87,15 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
const newVerificationStatus = identity?.verificationStatus ?? EVerificationStatus.WALLET_CONNECTED;
const updatedUser = {
const updatedUser: User = {
...currentUser,
verificationStatus: newVerificationStatus,
ensDetails: identity?.ensName ? { ensName: identity.ensName } : undefined,
ordinalDetails: identity?.ordinalDetails,
} as User;
setCurrentUser(updatedUser);
const enriched = await enrichUserWithIdentity(updatedUser);
setCurrentUser(enriched);
await localDatabase.storeUser(updatedUser);
await localDatabase.upsertUserIdentity(currentUser.address, {
@ -77,11 +109,12 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
} catch (error) {
console.error('❌ Verification failed:', error);
const updatedUser = { ...currentUser, verificationStatus: EVerificationStatus.WALLET_CONNECTED } as User;
setCurrentUser(updatedUser);
const enriched = await enrichUserWithIdentity(updatedUser);
setCurrentUser(enriched);
await localDatabase.storeUser(updatedUser);
return false;
}
}, [client, currentUser]);
}, [client, currentUser, enrichUserWithIdentity]);
// Hydrate user from LocalDatabase on mount
useEffect(() => {
@ -90,7 +123,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
try {
const user = await localDatabase.loadUser();
if (mounted && user) {
setCurrentUser(user);
const enriched = await enrichUserWithIdentity(user);
setCurrentUser(enriched);
// 🔄 Sync verification status with UserIdentityService
await localDatabase.upsertUserIdentity(user.address, {
@ -118,7 +152,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
return () => {
mounted = false;
};
}, []); // Remove verifyOwnership dependency to prevent infinite loops
}, [enrichUserWithIdentity]); // Remove verifyOwnership dependency to prevent infinite loops
// Auto-connect when wallet is detected
useEffect(() => {
@ -143,7 +177,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
lastChecked: Date.now(),
};
setCurrentUser(user);
const enriched = await enrichUserWithIdentity(user);
setCurrentUser(enriched);
await localDatabase.storeUser(user);
// Also store identity info so UserIdentityService can access it
@ -174,7 +209,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
};
autoConnect();
}, [isWalletConnected, connectedAddress, walletType]); // Remove currentUser and verifyOwnership dependencies
}, [isWalletConnected, connectedAddress, walletType, enrichUserWithIdentity]); // Remove currentUser and verifyOwnership dependencies
// Ensure verificationStatus reflects a connected wallet even if a user was preloaded
useEffect(() => {
@ -203,7 +238,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
lastChecked: Date.now(),
} as User;
setCurrentUser(updatedUser);
const enriched = await enrichUserWithIdentity(updatedUser);
setCurrentUser(enriched);
await localDatabase.storeUser(updatedUser);
await localDatabase.upsertUserIdentity(connectedAddress, {
ensName: updatedUser.ensDetails?.ensName,
@ -216,7 +252,20 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
syncConnectedStatus();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isWalletConnected, connectedAddress, walletType, currentUser]);
}, [isWalletConnected, connectedAddress, walletType, currentUser, enrichUserWithIdentity]);
// Keep currentUser in sync with identity updates (e.g., profile changes)
useEffect(() => {
if (!currentUser) return;
const off = client.userIdentityService.addRefreshListener(async (addr) => {
if (addr !== currentUser.address) return;
const enriched = await enrichUserWithIdentity(currentUser as User);
setCurrentUser(enriched);
});
return () => {
try { off && off(); } catch {}
};
}, [client, currentUser, enrichUserWithIdentity]);
const connectWallet = useCallback(async (): Promise<boolean> => {
if (!isWalletConnected || !connectedAddress) return false;
@ -231,7 +280,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
verificationStatus: EVerificationStatus.WALLET_CONNECTED,
lastChecked: Date.now(),
};
setCurrentUser(user);
const enriched = await enrichUserWithIdentity(user);
setCurrentUser(enriched);
await localDatabase.storeUser(user);
return true;
} catch (e) {
@ -240,7 +290,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
} finally {
setIsAuthenticating(false);
}
}, [currentUser?.displayPreference, isWalletConnected, connectedAddress, walletType]);
}, [currentUser?.displayPreference, isWalletConnected, connectedAddress, walletType, enrichUserWithIdentity]);
const disconnectWallet = useCallback(() => {
setCurrentUser(null);

View File

@ -6,7 +6,6 @@ export { useForumApi } from './useForum';
export { useForumData } from './core/useForumData';
export { usePermissions } from './core/usePermissions';
export { useUserDisplay } from './core/useUserDisplay';
export { useIdentity } from './useIdentity';
// Derived hooks (data slicing utilities)
export { useCell } from './derived/useCell';

View File

@ -0,0 +1,9 @@
export * from './provider/OpChanProvider';
export { ClientProvider, useClient } from './contexts/ClientContext';
export { AuthProvider, useAuth } from './contexts/AuthContext';
export { ForumProvider, useForum as useForumContext } from './contexts/ForumContext';
export { ModerationProvider, useModeration } from './contexts/ModerationContext';
export * from './hooks';
export { useForumApi as useForum } from './hooks/useForum';