useProfile hook, hooks reorg

This commit is contained in:
Martti Malmi 2023-08-21 12:21:27 +03:00
parent e698638fed
commit eb3df4098f
13 changed files with 94 additions and 144 deletions

View File

@ -4,7 +4,6 @@ import { useEffect, useState } from 'preact/hooks';
import Events from '../../../nostr/Events';
import Key from '../../../nostr/Key';
import SocialNetwork from '../../../nostr/SocialNetwork';
const Like = ({ event }) => {
const [state, setState] = useState({
@ -14,16 +13,7 @@ const Like = ({ event }) => {
});
useEffect(() => {
const unsubProfile = SocialNetwork.getProfile(event.pubkey, (profile) => {
if (!profile) return;
});
const unsubLikes = Events.getLikes(event.id, handleLikes);
return () => {
unsubProfile();
unsubLikes();
};
return Events.getLikes(event.id, handleLikes);
}, [event]);
const handleLikes = (likedBy) => {

View File

@ -3,7 +3,6 @@ import { useEffect, useState } from 'preact/hooks';
import Events from '../../../nostr/Events';
import Key from '../../../nostr/Key';
import SocialNetwork from '../../../nostr/SocialNetwork';
const Repost = ({ event }) => {
const [state, setState] = useState({
@ -13,16 +12,7 @@ const Repost = ({ event }) => {
});
useEffect(() => {
const unsubProfile = SocialNetwork.getProfile(event.pubkey, (profile) => {
if (!profile) return;
});
const unsubReposts = Events.getReposts(event.id, handleReposts);
return () => {
unsubProfile();
unsubReposts();
};
return Events.getReposts(event.id, handleReposts);
}, [event]);
const handleReposts = (repostedBy) => {

View File

@ -4,13 +4,13 @@ import { useEffect, useState } from 'preact/hooks';
import Show from '@/components/helpers/Show';
import EventDB from '@/nostr/EventDB';
import { useProfile } from '@/nostr/hooks/useProfile.ts';
import Key from '@/nostr/Key';
import { getZappingUser } from '@/nostr/utils';
import useLocalState from '@/state/useLocalState.ts';
import Icons from '@/utils/Icons';
import Events from '../../../nostr/Events';
import SocialNetwork from '../../../nostr/SocialNetwork';
import { decodeInvoice, formatAmount } from '../../../utils/Lightning';
import ZapModal from '../../modal/Zap';
@ -20,12 +20,13 @@ const Zap = ({ event }) => {
formattedZapAmount: '',
zapped: false,
showZapModal: false,
lightning: undefined,
defaultZapAmount: 0,
});
const [defaultZapAmount] = useLocalState('defaultZapAmount', 0);
const [longPress, setLongPress] = useState(false); // state to determine if it's a long press
const { lud16, lud06 } = useProfile(event.pubkey);
const lightning = lud16 || lud06;
let pressTimer: any = null;
@ -55,21 +56,7 @@ const Zap = ({ event }) => {
};
useEffect(() => {
const unsubProfile = SocialNetwork.getProfile(event.pubkey, (profile) => {
if (!profile) return;
const lightning = profile.lud16 || profile.lud06;
setState((prevState) => ({
...prevState,
lightning,
}));
});
const unsubZaps = Events.getZaps(event.id, handleZaps);
return () => {
unsubProfile();
unsubZaps();
};
return Events.getZaps(event.id, handleZaps);
}, [event]);
const handleZaps = debounce(
@ -102,7 +89,7 @@ const Zap = ({ event }) => {
{ leading: true },
);
return state.lightning ? (
return lightning ? (
<>
<a
onMouseDown={onMouseDown}
@ -120,7 +107,7 @@ const Zap = ({ event }) => {
<ZapModal
quickZap={!!defaultZapAmount && !longPress}
show={true}
lnurl={state.lightning}
lnurl={lightning}
note={event.id}
recipient={event.pubkey}
onClose={() => setState((prevState) => ({ ...prevState, showZapModal: false }))}

View File

@ -8,7 +8,7 @@ import ShowNewEvents from '@/components/feed/ShowNewEvents';
import { FeedProps } from '@/components/feed/types';
import InfiniteScroll from '@/components/helpers/InfiniteScroll';
import Show from '@/components/helpers/Show';
import useSubscribe from '@/hooks/useSubscribe';
import useSubscribe from '@/nostr/hooks/useSubscribe';
import Key from '@/nostr/Key';
import useHistoryState from '@/state/useHistoryState.ts';
import useLocalState from '@/state/useLocalState.ts';

View File

@ -2,6 +2,8 @@ import React, { useEffect, useState } from 'react';
import { sha256 } from '@noble/hashes/sha256';
import Identicon from 'identicon.js';
import { useProfile } from '@/nostr/hooks/useProfile.ts';
import Key from '../../nostr/Key';
import SocialNetwork from '../../nostr/SocialNetwork';
import Show from '../helpers/Show';
@ -17,41 +19,29 @@ type Props = {
};
const MyAvatar: React.FC<Props> = (props) => {
const [picture, setPicture] = useState<string | null>(null);
const [name, setName] = useState<string | null>(null);
const [activity] = useState<string | null>(null); // TODO
const [avatar, setAvatar] = useState<string | null>(null);
const [hasError, setHasError] = useState<boolean>(false);
const hex = React.useMemo(() => Key.toNostrHexAddress(props.str as string), [props.str]);
const { picture, name } = useProfile(hex || '');
useEffect(() => {
const updateAvatar = () => {
const hash = sha256(hex || (props.str as string));
const hexVal = Array.from(new Uint8Array(hash))
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
const identicon = new Identicon(hexVal, {
width: props.width,
format: 'svg',
});
setAvatar(`data:image/svg+xml;base64,${identicon.toString()}`);
};
if (hex) {
updateAvatar();
const unsub = SocialNetwork.getProfile(hex, (profile) => {
if (profile) {
setPicture(profile.picture);
setName(profile.name);
}
});
return () => unsub?.();
if (!hex) {
return;
}
const hash = sha256(hex || (props.str as string));
const hexVal = Array.from(new Uint8Array(hash))
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
const identicon = new Identicon(hexVal, {
width: props.width,
format: 'svg',
});
setAvatar(`data:image/svg+xml;base64,${identicon.toString()}`);
}, [hex, props.str]);
const width = props.width;

View File

@ -1,25 +0,0 @@
import { useEffect, useState } from 'react';
import SocialNetwork from '../nostr/SocialNetwork';
import { ID } from '../utils/UniqueIds';
export const useProfile = (pub: string) => {
const [nostrAddr] = useState(pub);
const [profile, setProfile] = useState(SocialNetwork.profiles.get(ID(nostrAddr)));
useEffect(() => {
if (!nostrAddr) return;
const unsub = SocialNetwork.getProfile(nostrAddr, (p) => {
if (p) {
setProfile(p);
}
});
return () => {
unsub();
};
}, [nostrAddr]);
return profile;
};

View File

@ -0,0 +1,18 @@
import { useEffect, useState } from 'react';
import { ID } from '../../utils/UniqueIds.ts';
import SocialNetwork from '../SocialNetwork.ts';
export const useProfile = (pub: string) => {
const [profile, setProfile] = useState(SocialNetwork.profiles.get(ID(pub)) || {});
useEffect(() => {
if (!pub) return;
return SocialNetwork.getProfile(pub, (p) => {
p && setProfile(p);
});
}, [pub]);
return profile;
};

View File

@ -2,9 +2,9 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import throttle from 'lodash/throttle';
import { Event } from 'nostr-tools';
import Filter from '@/nostr/Filter';
import PubSub from '@/nostr/PubSub';
import SortedMap from '@/utils/SortedMap';
import Filter from '@/nostr/Filter.ts';
import PubSub from '@/nostr/PubSub.ts';
import SortedMap from '@/utils/SortedMap.tsx';
interface SubscribeOptions {
filter: Filter;

View File

@ -6,8 +6,6 @@ function useHistoryState(initialValue, key) {
const myInitialValue = currentHistoryState === undefined ? initialValue : currentHistoryState;
const [state, setState] = useState(myInitialValue);
console.log('loaded history state for key', key, 'with value', currentHistoryState, initialValue);
const latestValue = useRef(state);
const throttledSetHistoryState = useRef(

View File

@ -2,15 +2,16 @@ import { memo, useMemo } from 'react';
import { nip19 } from 'nostr-tools';
import { Link, route } from 'preact-router';
import { useProfile } from '@/nostr/hooks/useProfile';
import FollowButton from '../components/buttons/Follow';
import SmallFeed from '../components/feed/SmallFeed';
import SearchBox from '../components/SearchBox';
import Avatar from '../components/user/Avatar';
import Name from '../components/user/Name';
import useCachedFetch from '../hooks/useCachedFetch';
import { useProfile } from '../hooks/useProfile';
import Events from '../nostr/Events';
import Key from '../nostr/Key';
import useCachedFetch from '../utils/useCachedFetch.ts';
const SuggestionProfile = memo(({ pubkey }: { pubkey: string }) => {
const profile = useProfile(pubkey);

View File

@ -2,6 +2,7 @@ import debounce from 'lodash/debounce';
import { useEffect, useState } from 'preact/hooks';
import { route } from 'preact-router';
import { ID } from '@/utils/UniqueIds.ts';
import { RouteProps } from '@/views/types.ts';
import Upload from '../../components/buttons/Upload.tsx';
@ -19,7 +20,7 @@ const explainers = {
};
const EditProfile: React.FC<RouteProps> = () => {
const [profile, setProfile] = useState({});
const [profile, setProfile] = useState(SocialNetwork.profiles.get(ID(Key.getPubKey())) || {});
const [newFieldName, setNewFieldName] = useState('');
const [newFieldValue, setNewFieldValue] = useState('');
const [edited, setEdited] = useState(false);

View File

@ -1,8 +1,8 @@
import { useMemo } from 'react';
import { useEffect, useState } from 'preact/hooks';
import { useEffect, useMemo, useState } from 'preact/hooks';
import { route } from 'preact-router';
import SimpleImageModal from '@/components/modal/Image.tsx';
import { useProfile } from '@/nostr/hooks/useProfile.ts';
import { getEventReplyingTo, isRepost } from '@/nostr/utils.ts';
import useLocalState from '@/state/useLocalState.ts';
import ProfileHelmet from '@/views/profile/Helmet.tsx';
@ -20,11 +20,45 @@ function Profile(props) {
const [blocked, setBlocked] = useState(false);
const [hexPub, setHexPub] = useState('');
const [npub, setNpub] = useState('');
const [profile, setProfile] = useState({} as any);
const [banner, setBanner] = useState('');
const [bannerModalOpen, setBannerModalOpen] = useState(false);
const setIsMyProfile = useLocalState('isMyProfile', false)[1];
const profile = useProfile(hexPub);
useEffect(() => {
if (!hexPub) {
return;
}
const isMyProfile = hexPub === Key.getPubKey();
setIsMyProfile(isMyProfile);
SocialNetwork.getBlockedUsers((blockedUsers) => {
setBlocked(blockedUsers.has(hexPub));
});
}, [hexPub]);
useEffect(() => {
if (!profile) {
return;
}
let bannerURL;
try {
bannerURL = profile.banner && new URL(profile.banner).toString();
if (!bannerURL) {
return;
}
bannerURL = isSafeOrigin(bannerURL)
? bannerURL
: `https://imgproxy.iris.to/insecure/rs:fit:948:948/plain/${bannerURL}`;
setBanner(bannerURL);
} catch (e) {
console.log('Invalid banner URL', profile.banner);
}
}, [profile]);
useEffect(() => {
const pub = props.id;
const npubComputed = Key.toNostrBech32Address(pub, 'npub');
@ -35,7 +69,11 @@ function Profile(props) {
}
const hexPubComputed = Key.toNostrHexAddress(pub) || '';
if (!hexPubComputed) {
if (hexPubComputed) {
setHexPub(hexPubComputed);
setNpub(Key.toNostrBech32Address(hexPubComputed, 'npub') || '');
} else {
let nostrAddress = pub;
if (!nostrAddress.match(/.+@.+\..+/)) {
if (nostrAddress.match(/.+\..+/)) {
@ -51,7 +89,6 @@ function Profile(props) {
if (npubComputed && npubComputed !== pubKey) {
setNpub(npubComputed);
setHexPub(pubKey);
loadProfile(pubKey);
}
} else {
setNpub(''); // To indicate not found
@ -59,9 +96,6 @@ function Profile(props) {
});
}
setHexPub(hexPubComputed);
setNpub(Key.toNostrBech32Address(hexPubComputed, 'npub') || '');
loadProfile(hexPubComputed);
setTimeout(() => {
window.prerenderReady = true;
}, 1000);
@ -90,40 +124,6 @@ function Profile(props) {
];
}, [hexPub]);
const loadProfile = (hexPub) => {
if (!hexPub) {
return;
}
const isMyProfile = hexPub === Key.getPubKey();
setIsMyProfile(isMyProfile);
SocialNetwork.getBlockedUsers((blockedUsers) => {
setBlocked(blockedUsers.has(hexPub));
});
SocialNetwork.getProfile(hexPub, (profileData) => {
if (!profileData) {
return;
}
setProfile(profileData);
let bannerURL;
try {
bannerURL = profileData.banner && new URL(profileData.banner).toString();
if (!bannerURL) {
return;
}
bannerURL = isSafeOrigin(bannerURL)
? bannerURL
: `https://imgproxy.iris.to/insecure/rs:fit:948:948/plain/${bannerURL}`;
setBanner(bannerURL);
} catch (e) {
console.log('Invalid banner URL', profileData.banner);
}
});
};
if (!hexPub) {
return <div></div>;
}