Skip to content

Commit

Permalink
add useUserProfile
Browse files Browse the repository at this point in the history
  • Loading branch information
pablof7z committed Nov 29, 2024
1 parent 6b3ea8b commit c5390d4
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 18 deletions.
20 changes: 20 additions & 0 deletions docs/tutorial/mobile/profile-hook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Using `useUserProfile` in `ndk-mobile`

Efficiently managing user profiles is crucial for mobile applications. The `useUserProfile` hook helps streamline this process by using the SQLite cache adapter, reducing unnecessary re-renderings when the profile can be loaded synchronously.

Here's a simple example of how to use the `useUserProfile` hook in a component:

```tsx
import React from "react";
import { useUserProfile } from "ndk-mobile";

function UserProfile({ pubkey }) {
const { userProfile, loading } = useUserProfile(pubkey);

if (loading) return <Text>Loading...</Text>;

return <Text>{userProfile.displayName}</Text>;
}

export default UserProfile;
```
55 changes: 37 additions & 18 deletions ndk-mobile/src/hooks/user-profile.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,66 @@
import { useEffect, useState, useMemo } from 'react'
import { useEffect, useState, useMemo, useCallback, useRef } from 'react'
import { useNDK } from './ndk'

export function useUserProfile(pubkey: string | undefined) {
export function useUserProfile(pubkey: string) {
const { ndk } = useNDK()

const user = useMemo(() => ndk?.getUser({ pubkey }), [ndk, pubkey]);

const fetchFromCache = useCallback(() => {
if (!ndk) return null;

const cachedProfile = ndk.cacheAdapter.fetchProfileSync?.(pubkey)
if (cachedProfile) cachedProfile.pubkey = pubkey;

return cachedProfile;
}, [ndk, pubkey]);

// Try to load synchronously first
const initialState = useMemo(() => {
if (!ndk || !pubkey) {
return { userProfile: null, user: null, loading: false }
return { userProfile: null, user: null, loading: false, cache: false, pubkey }
}

const user = ndk.getUser({ pubkey })
const cachedProfile = ndk.cacheAdapter.fetchProfileSync?.(pubkey)

if (cachedProfile) {
return { userProfile: cachedProfile, user, loading: false }
}

return { userProfile: null, user, loading: true }
const cachedProfile = fetchFromCache();
return { userProfile: cachedProfile, user, loading: !cachedProfile, cache: !!cachedProfile, pubkey }
}, [ndk, pubkey])

const [state, setState] = useState(initialState)

useEffect(() => {
if (!ndk || !pubkey || state.userProfile) return

let mounted = true

if (!mounted) return;

if (!ndk || !pubkey || (state.userProfile && state.userProfile.pubkey === pubkey)) return

// if the props changed, we need to refetch
if (state.userProfile && state.userProfile.pubkey !== pubkey) {
const newProfile = fetchFromCache();

setState(prev => ({ ...prev, userProfile: newProfile, loading: !newProfile, cache: !!newProfile }));

// if we got a new profile from the cache, we don't need to update the state again
if (newProfile) return;
}

state.user?.fetchProfile()
.then(profile => {
if (mounted) {
setState({ userProfile: profile, user: state.user, loading: false })
}
// make sure we are mounted and that the pubkey prop didn't change
if (!mounted || state.pubkey !== pubkey) return;

if (profile) profile.pubkey = pubkey;
setState(prev => ({ ...prev, pubkey, userProfile: profile, cache: false, loading: false }))
})
.catch(error => {
console.error('Error fetching user profile:', error)
if (mounted) {
setState(prev => ({ ...prev, loading: false }))
setState(prev => ({ ...prev, pubkey, loading: false, fromCache: false }))
}
})

return () => { mounted = false }
}, [ndk, pubkey, state.user, state.userProfile])
}, [ndk, pubkey])

return state
}

0 comments on commit c5390d4

Please sign in to comment.