fix(anon mode): user id could change for pending post, name field could bug out

This commit is contained in:
Tom (plebeius.eth)
2024-09-19 17:33:00 +02:00
parent b18c1bc491
commit bb976409ff
8 changed files with 40 additions and 110 deletions
+4 -16
View File
@@ -1,7 +1,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
import { Link, useLocation, useParams } from 'react-router-dom'; import { Link, useLocation, useParams } from 'react-router-dom';
import { Comment, useAccount, useAuthorAvatar, useComment, useEditedComment, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import { Comment, useAuthorAvatar, useComment, useEditedComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js'; import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import styles from '../../views/post/post.module.css'; import styles from '../../views/post/post.module.css';
import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils'; import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
@@ -9,14 +9,11 @@ import { hashStringToColor, getTextColorForBackground } from '../../lib/utils/po
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils'; import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isValidURL } from '../../lib/utils/url-utils'; import { isValidURL } from '../../lib/utils/url-utils';
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils'; import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
import useAnonModeStore from '../../stores/use-anon-mode-store';
import useAvatarVisibilityStore from '../../stores/use-avatar-visibility-store'; import useAvatarVisibilityStore from '../../stores/use-avatar-visibility-store';
import useAnonMode from '../../hooks/use-anon-mode';
import useAuthorAddressClick from '../../hooks/use-author-address-click'; import useAuthorAddressClick from '../../hooks/use-author-address-click';
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies'; import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame'; import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
import useHide from '../../hooks/use-hide'; import useHide from '../../hooks/use-hide';
import usePostCidForPendingPost from '../../hooks/use-post-cid';
import useReplies from '../../hooks/use-replies'; import useReplies from '../../hooks/use-replies';
import useStateString from '../../hooks/use-state-string'; import useStateString from '../../hooks/use-state-string';
import CommentMedia from '../comment-media'; import CommentMedia from '../comment-media';
@@ -68,22 +65,13 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
const isInPostPageView = isPostPageView(location.pathname, params); const isInPostPageView = isPostPageView(location.pathname, params);
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params); const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
// comment.author.shortAddress is undefined while the comment publishing state is pending, use account instead const userID = address && Plebbit.getShortAddress(address);
// in anon mode, use the newly generated signer.address instead, which will be comment.author.address const userIDBackgroundColor = hashStringToColor(userID);
const { anonMode } = useAnonMode(); const userIDTextColor = getTextColorForBackground(userIDBackgroundColor);
const { getThreadSigner, currentAnonSignerAddress } = useAnonModeStore();
const postCidForSigner = usePostCidForPendingPost(parentCid);
const anonSignerAddress = postCidForSigner ? getThreadSigner(postCidForSigner)?.address || currentAnonSignerAddress : null;
const account = useAccount();
const pendingShortAddress = anonMode ? anonSignerAddress && Plebbit.getShortAddress(anonSignerAddress) : account?.author?.shortAddress;
const handleUserAddressClick = useAuthorAddressClick(); const handleUserAddressClick = useAuthorAddressClick();
const numberOfPostsByAuthor = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length; const numberOfPostsByAuthor = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length;
const userID = shortAddress || pendingShortAddress;
const userIDBackgroundColor = hashStringToColor(userID);
const userIDTextColor = getTextColorForBackground(userIDBackgroundColor);
const { hidden } = useHide(post); const { hidden } = useHide(post);
return ( return (
+7 -4
View File
@@ -55,9 +55,9 @@ const useSubmitStore = create<SubmitState>((set) => ({
publishCommentOptions: {}, publishCommentOptions: {},
setSubmitStore: ({ author, displayName, signer, subplebbitAddress, title, content, link, spoiler }) => setSubmitStore: ({ author, displayName, signer, subplebbitAddress, title, content, link, spoiler }) =>
set((state) => { set((state) => {
const updatedAuthor = displayName ? { ...author, displayName } : author;
const nextState = { ...state }; const nextState = { ...state };
if (author !== undefined) nextState.author = updatedAuthor; if (author !== undefined) nextState.author = author;
if (displayName !== undefined) nextState.displayName = displayName;
if (signer !== undefined) nextState.signer = signer; if (signer !== undefined) nextState.signer = signer;
if (subplebbitAddress !== undefined) nextState.subplebbitAddress = subplebbitAddress; if (subplebbitAddress !== undefined) nextState.subplebbitAddress = subplebbitAddress;
if (title !== undefined) nextState.title = title || undefined; if (title !== undefined) nextState.title = title || undefined;
@@ -83,8 +83,11 @@ const useSubmitStore = create<SubmitState>((set) => ({
publishCommentOptions.signer = nextState.signer; publishCommentOptions.signer = nextState.signer;
} }
if (nextState.author) { if (nextState.author || nextState.displayName) {
publishCommentOptions.author = nextState.author; publishCommentOptions.author = {
...nextState.author,
displayName: nextState.displayName,
};
} }
nextState.publishCommentOptions = publishCommentOptions; nextState.publishCommentOptions = publishCommentOptions;
+5 -16
View File
@@ -1,20 +1,17 @@
import { useState } from 'react'; import { useState } from 'react';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
import { Link, useLocation, useParams } from 'react-router-dom'; import { Link, useLocation, useParams } from 'react-router-dom';
import { Comment, useAccount, useAuthorAvatar, useComment, useEditedComment, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import { Comment, useAuthorAvatar, useComment, useEditedComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js'; import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import styles from '../../views/post/post.module.css'; import styles from '../../views/post/post.module.css';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils'; import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
import { getTextColorForBackground, hashStringToColor } from '../../lib/utils/post-utils'; import { getTextColorForBackground, hashStringToColor } from '../../lib/utils/post-utils';
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils'; import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils'; import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
import useAnonModeStore from '../../stores/use-anon-mode-store';
import useAvatarVisibilityStore from '../../stores/use-avatar-visibility-store'; import useAvatarVisibilityStore from '../../stores/use-avatar-visibility-store';
import useAnonMode from '../../hooks/use-anon-mode';
import useAuthorAddressClick from '../../hooks/use-author-address-click'; import useAuthorAddressClick from '../../hooks/use-author-address-click';
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies'; import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
import useHide from '../../hooks/use-hide'; import useHide from '../../hooks/use-hide';
import usePostCidForPendingPost from '../../hooks/use-post-cid';
import useReplies from '../../hooks/use-replies'; import useReplies from '../../hooks/use-replies';
import useStateString from '../../hooks/use-state-string'; import useStateString from '../../hooks/use-state-string';
import CommentMedia from '../comment-media'; import CommentMedia from '../comment-media';
@@ -49,21 +46,13 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
const isReply = parentCid; const isReply = parentCid;
// comment.author.shortAddress is undefined while the comment publishing state is pending, use account instead
// in anon mode, use the newly generated signer.address instead, which will be comment.author.address
const { anonMode } = useAnonMode();
const { getThreadSigner, currentAnonSignerAddress } = useAnonModeStore();
const postCidForSigner = usePostCidForPendingPost(parentCid);
const anonSignerAddress = postCidForSigner ? getThreadSigner(postCidForSigner)?.address || currentAnonSignerAddress : null;
const account = useAccount();
const pendingShortAddress = anonMode ? anonSignerAddress && Plebbit.getShortAddress(anonSignerAddress) : account?.author?.shortAddress;
const stateString = useStateString(post); const stateString = useStateString(post);
const handleUserAddressClick = useAuthorAddressClick(); const handleUserAddressClick = useAuthorAddressClick();
const numberOfPostsByAuthor = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length; const numberOfPostsByAuthor = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length;
const userIDBackgroundColor = hashStringToColor(shortAddress || pendingShortAddress); const userID = shortAddress;
const userIDBackgroundColor = hashStringToColor(userID);
const userIDTextColor = getTextColorForBackground(userIDBackgroundColor); const userIDTextColor = getTextColorForBackground(userIDBackgroundColor);
const { hidden } = useHide(post); const { hidden } = useHide(post);
@@ -113,10 +102,10 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
<span <span
title={t('highlight_posts')} title={t('highlight_posts')}
className={styles.userAddress} className={styles.userAddress}
onClick={() => handleUserAddressClick(shortAddress || pendingShortAddress, postCid)} onClick={() => handleUserAddressClick(userID, postCid)}
style={{ backgroundColor: userIDBackgroundColor, color: userIDTextColor }} style={{ backgroundColor: userIDBackgroundColor, color: userIDTextColor }}
> >
{shortAddress || pendingShortAddress} {userID}
</span> </span>
} }
content={`${numberOfPostsByAuthor === 1 ? t('1_post_by_this_id') : t('x_posts_by_this_id', { number: numberOfPostsByAuthor })}`} content={`${numberOfPostsByAuthor === 1 ? t('1_post_by_this_id') : t('x_posts_by_this_id', { number: numberOfPostsByAuthor })}`}
+1 -5
View File
@@ -15,7 +15,6 @@ import styles from './reply-modal.module.css';
import { LinkTypePreviewer } from '../post-form'; import { LinkTypePreviewer } from '../post-form';
import _ from 'lodash'; import _ from 'lodash';
import useAnonMode from '../../hooks/use-anon-mode'; import useAnonMode from '../../hooks/use-anon-mode';
import useAnonModeStore from '../../stores/use-anon-mode-store';
interface ReplyModalProps { interface ReplyModalProps {
closeModal: () => void; closeModal: () => void;
@@ -41,7 +40,6 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
const address = comment?.author?.address; const address = comment?.author?.address;
const hasCalledAnonAddressRef = useRef(false); const hasCalledAnonAddressRef = useRef(false);
const { setCurrentAnonSignerAddress } = useAnonModeStore();
const getAnonAddressForReply = useCallback(async () => { const getAnonAddressForReply = useCallback(async () => {
if (anonMode && !hasCalledAnonAddressRef.current) { if (anonMode && !hasCalledAnonAddressRef.current) {
@@ -55,7 +53,6 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
displayName: displayName || undefined, displayName: displayName || undefined,
}, },
}); });
setCurrentAnonSignerAddress(existingSigner.address);
} else { } else {
const newSigner = await getNewSigner(); const newSigner = await getNewSigner();
if (newSigner) { if (newSigner) {
@@ -66,11 +63,10 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
displayName: displayName || undefined, displayName: displayName || undefined,
}, },
}); });
setCurrentAnonSignerAddress(newSigner.address);
} }
} }
} }
}, [address, getExistingSigner, getNewSigner, setPublishReplyOptions, anonMode, displayName, setCurrentAnonSignerAddress]); }, [address, getExistingSigner, getNewSigner, setPublishReplyOptions, anonMode, displayName]);
const onPublishReply = () => { const onPublishReply = () => {
const currentContent = textRef.current?.value.slice(contentPrefix.length).trim() || ''; const currentContent = textRef.current?.value.slice(contentPrefix.length).trim() || '';
+3 -9
View File
@@ -3,13 +3,12 @@ import useAnonModeStore from '../stores/use-anon-mode-store';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
const useAnonMode = (postCid?: string) => { const useAnonMode = (postCid?: string) => {
const { anonMode, threadSigners, setThreadSigner, setAddressSigner, getAddressSigner, setCurrentAnonSignerAddress } = useAnonModeStore((state) => ({ const { anonMode, threadSigners, setThreadSigner, setAddressSigner, getAddressSigner } = useAnonModeStore((state) => ({
anonMode: state.anonMode, anonMode: state.anonMode,
threadSigners: state.threadSigners, threadSigners: state.threadSigners,
setThreadSigner: state.setThreadSigner, setThreadSigner: state.setThreadSigner,
setAddressSigner: state.setAddressSigner, setAddressSigner: state.setAddressSigner,
getAddressSigner: state.getAddressSigner, getAddressSigner: state.getAddressSigner,
setCurrentAnonSignerAddress: state.setCurrentAnonSignerAddress,
})); }));
const threadSigner = useMemo(() => (postCid ? threadSigners[postCid] : undefined), [postCid, threadSigners]); const threadSigner = useMemo(() => (postCid ? threadSigners[postCid] : undefined), [postCid, threadSigners]);
@@ -27,7 +26,6 @@ const useAnonMode = (postCid?: string) => {
} else { } else {
setAddressSigner(signer); setAddressSigner(signer);
} }
setCurrentAnonSignerAddress(signer.address);
} }
return signer; return signer;
} catch (error) { } catch (error) {
@@ -36,7 +34,6 @@ const useAnonMode = (postCid?: string) => {
} else { } else {
try { try {
const signer = await account?.plebbit.createSigner({ type: 'ed25519', privateKey: threadSigner?.privateKey }); const signer = await account?.plebbit.createSigner({ type: 'ed25519', privateKey: threadSigner?.privateKey });
setCurrentAnonSignerAddress(signer.address);
return signer; return signer;
} catch (error) { } catch (error) {
console.error('Failed to retrieve anonymous signer:', error); console.error('Failed to retrieve anonymous signer:', error);
@@ -44,17 +41,14 @@ const useAnonMode = (postCid?: string) => {
} }
} }
return null; return null;
}, [anonMode, postCid, threadSigner, account, setThreadSigner, setAddressSigner, setCurrentAnonSignerAddress]); }, [anonMode, postCid, threadSigner, account, setThreadSigner, setAddressSigner]);
const getExistingSigner = useCallback( const getExistingSigner = useCallback(
(address: string) => { (address: string) => {
const signer = getAddressSigner(address); const signer = getAddressSigner(address);
if (signer) {
setCurrentAnonSignerAddress(signer.address);
}
return signer; return signer;
}, },
[getAddressSigner, setCurrentAnonSignerAddress], [getAddressSigner],
); );
return { anonMode, getNewSigner, getExistingSigner }; return { anonMode, getNewSigner, getExistingSigner };
-43
View File
@@ -1,43 +0,0 @@
import { useEffect, useReducer } from 'react';
import { useComment } from '@plebbit/plebbit-react-hooks';
type State = {
resolvedPostCid: string | null;
currentParentCid: string | undefined;
};
type Action = { type: 'SET_POST_CID'; payload: string } | { type: 'SET_PARENT_CID'; payload: string };
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'SET_POST_CID':
return { ...state, resolvedPostCid: action.payload };
case 'SET_PARENT_CID':
return { ...state, currentParentCid: action.payload };
default:
return state;
}
};
const usePostCidForPendingPost = (initialParentCid: string | undefined): string | null => {
const [state, dispatch] = useReducer(reducer, {
resolvedPostCid: null,
currentParentCid: initialParentCid,
});
const comment = useComment({ commentCid: state.currentParentCid });
useEffect(() => {
if (comment) {
if (comment.postCid) {
dispatch({ type: 'SET_POST_CID', payload: comment.postCid });
} else if (comment.parentCid) {
dispatch({ type: 'SET_PARENT_CID', payload: comment.parentCid });
}
}
}, [comment]);
return state.resolvedPostCid;
};
export default usePostCidForPendingPost;
+20 -8
View File
@@ -1,5 +1,5 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { ChallengeVerification, Comment, PublishCommentOptions, usePublishComment } from '@plebbit/plebbit-react-hooks'; import { ChallengeVerification, Comment, PublishCommentOptions, useAccount, usePublishComment } from '@plebbit/plebbit-react-hooks';
import { create } from 'zustand'; import { create } from 'zustand';
import { alertChallengeVerificationFailed } from '../lib/utils/challenge-utils'; import { alertChallengeVerificationFailed } from '../lib/utils/challenge-utils';
import useChallengesStore from '../stores/use-challenges-store'; import useChallengesStore from '../stores/use-challenges-store';
@@ -50,8 +50,6 @@ const useReplyStore = create<ReplyState>((set) => ({
content, content,
link, link,
spoiler, spoiler,
...(author && { author: updatedAuthor }),
...(signer && { signer }),
onChallenge: (...args: any) => addChallenge(args), onChallenge: (...args: any) => addChallenge(args),
onChallengeVerification: (challengeVerification: ChallengeVerification, comment: Comment) => { onChallengeVerification: (challengeVerification: ChallengeVerification, comment: Comment) => {
alertChallengeVerificationFailed(challengeVerification, comment); alertChallengeVerificationFailed(challengeVerification, comment);
@@ -62,6 +60,14 @@ const useReplyStore = create<ReplyState>((set) => ({
}, },
}; };
if (updatedAuthor) {
publishCommentOptions.author = updatedAuthor;
}
if (signer) {
publishCommentOptions.signer = signer;
}
return { return {
author: { ...state.author, [parentCid]: updatedAuthor }, author: { ...state.author, [parentCid]: updatedAuthor },
displayName: { ...state.displayName, [parentCid]: displayName }, displayName: { ...state.displayName, [parentCid]: displayName },
@@ -102,6 +108,8 @@ const useReply = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress:
const setReplyStore = useReplyStore((state) => state.setReplyStore); const setReplyStore = useReplyStore((state) => state.setReplyStore);
const resetReplyStore = useReplyStore((state) => state.resetReplyStore); const resetReplyStore = useReplyStore((state) => state.resetReplyStore);
const account = useAccount();
const setPublishReplyOptions = useCallback( const setPublishReplyOptions = useCallback(
(options: Partial<SetReplyStoreData>) => { (options: Partial<SetReplyStoreData>) => {
const newOptions: Partial<SetReplyStoreData> = { const newOptions: Partial<SetReplyStoreData> = {
@@ -110,7 +118,6 @@ const useReply = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress:
content: options.content ?? content, content: options.content ?? content,
link: options.link ?? link, link: options.link ?? link,
spoiler: options.spoiler ?? spoiler, spoiler: options.spoiler ?? spoiler,
signer: anonMode ? signer || options.signer : undefined,
}; };
if ('displayName' in options) { if ('displayName' in options) {
@@ -118,18 +125,23 @@ const useReply = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress:
} }
if (anonMode) { if (anonMode) {
const currentAuthor = signer?.author || author || {}; newOptions.signer = signer || options.signer;
newOptions.author = { newOptions.author = {
...currentAuthor, ...(author || {}),
address: newOptions.signer?.address,
...('displayName' in options && { displayName: options.displayName }), ...('displayName' in options && { displayName: options.displayName }),
}; };
} else { } else {
newOptions.author = undefined; newOptions.signer = undefined;
newOptions.author = {
address: account?.author?.address,
...('displayName' in options && { displayName: options.displayName }),
};
} }
setReplyStore(newOptions as SetReplyStoreData); setReplyStore(newOptions as SetReplyStoreData);
}, },
[subplebbitAddress, parentCid, author, signer, content, link, spoiler, setReplyStore, anonMode], [subplebbitAddress, parentCid, author, signer, content, link, spoiler, setReplyStore, anonMode, account],
); );
const resetPublishReplyOptions = useCallback(() => resetReplyStore(parentCid), [parentCid, resetReplyStore]); const resetPublishReplyOptions = useCallback(() => resetReplyStore(parentCid), [parentCid, resetReplyStore]);
-9
View File
@@ -10,8 +10,6 @@ interface AnonModeState {
getThreadSigner: (postCid: string) => any | undefined; getThreadSigner: (postCid: string) => any | undefined;
setAddressSigner: (signer: any) => void; setAddressSigner: (signer: any) => void;
getAddressSigner: (address: string) => any | undefined; getAddressSigner: (address: string) => any | undefined;
currentAnonSignerAddress: string | null;
setCurrentAnonSignerAddress: (address: string | null) => void;
} }
const anonModeStore = localForageLru.createInstance({ const anonModeStore = localForageLru.createInstance({
@@ -42,10 +40,6 @@ const useAnonModeStore = create<AnonModeState>((set, get) => ({
}, },
getAddressSigner: (address: string) => get().addressSigners[address], getAddressSigner: (address: string) => get().addressSigners[address],
currentAnonSignerAddress: null, currentAnonSignerAddress: null,
setCurrentAnonSignerAddress: (address: string | null) => {
set({ currentAnonSignerAddress: address });
anonModeStore.setItem('currentAnonSignerAddress', address);
},
})); }));
const initializeAnonModeStore = async () => { const initializeAnonModeStore = async () => {
@@ -64,13 +58,10 @@ const initializeAnonModeStore = async () => {
} }
}); });
const currentAnonSignerAddress = await anonModeStore.getItem('currentAnonSignerAddress');
useAnonModeStore.setState((state) => ({ useAnonModeStore.setState((state) => ({
anonMode, // Set the retrieved anonMode state anonMode, // Set the retrieved anonMode state
threadSigners: { ...threadSigners, ...state.threadSigners }, threadSigners: { ...threadSigners, ...state.threadSigners },
addressSigners: { ...addressSigners, ...state.addressSigners }, addressSigners: { ...addressSigners, ...state.addressSigners },
currentAnonSignerAddress: currentAnonSignerAddress || null,
})); }));
}; };