Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 11 additions & 16 deletions src/components/login-modal/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export interface LoginModalProps {
}

export const LoginModal: React.FC<LoginModalProps> = ({ open, onClose }) => {
const { session, isAuthLoading: accountLoading } = useAuth();
const { login, logout, syncAddress } = useAccountSession();
const { session } = useAuth();
const { login, logout, syncAddress, loading: sessionLoading } = useAccountSession();
const [showForm, setShowForm] = useState(false);
const loginTriggered = useRef(false);

Expand Down Expand Up @@ -47,30 +47,25 @@ export const LoginModal: React.FC<LoginModalProps> = ({ open, onClose }) => {
}, [open, session]);

useEffect(() => {
if (sessionLoading) return;

if (session.user) {
setShowForm(false);
onClose();
} else if (session.address && !session.user) {
} else if (session.address) {
setShowForm(true);
}
}, [session]);

const handleSuccess = () => {
syncAddress();
}
}, [sessionLoading, session]);

const handleCancel = () => {
logout();
onClose();
}
const handleSuccess = () => syncAddress();
const handleCancel = () => { logout(); onClose(); };

return (
<Modal
sx={{ overflow: 'scroll', display: open ? 'flex' : 'none' }}
open={open}
onClose={onClose}
closeAfterTransition
disableScrollLock={true}
disableScrollLock
BackdropComponent={Backdrop}
BackdropProps={{ timeout: 500, onClick: handleCancel }}
>
Expand All @@ -90,13 +85,13 @@ export const LoginModal: React.FC<LoginModalProps> = ({ open, onClose }) => {
overflow: 'auto',
}}
>
{accountLoading && (
{sessionLoading && (
<Box display="flex" justifyContent="center" alignItems="center" height={200}>
<WatchitLoader />
</Box>
)}

{!accountLoading && showForm && session.address && (
{!sessionLoading && showForm && session.address && (
<ProfileFormView
onSuccess={handleSuccess}
onCancel={handleCancel}
Expand Down
23 changes: 15 additions & 8 deletions src/components/login-modal/profile-form-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,22 @@ export const ProfileFormView: React.FC<ProfileFormProps> = ({

const validationSchema = Yup.object({
username: Yup.string()
.min(5, 'Username must be at least 5 characters')
.min(3, 'Username must be at least 3 characters')
.max(15, 'Username must be at most 15 characters')
.required('Username is required'),
displayName: Yup.string().min(3, 'Name must be at least 3 characters').required('Name is required'),
bio: Yup.string().min(10, 'Bio must be at least 10 characters').required('Bio is required'),
socialLinks: Yup.object({
twitter: Yup.string().url('Enter a valid URL'),
instagram: Yup.string().url('Enter a valid URL'),
orb: Yup.string().url('Enter a valid URL'),
farcaster: Yup.string().url('Enter a valid URL'),
displayName: Yup.string()
.min(3, 'Display name must be at least 3 character')
.max(30, 'Display name must be at most 30 characters')
.required('Display name is required'),
bio: Yup.string()
.min(10, 'Bio must be at least 10 characters')
.max(200, 'Bio must be at most 200 characters')
.required('Bio is required'),
socialLinks: Yup.object().shape({
twitter: Yup.string().url('Enter a valid URL').notRequired(),
instagram: Yup.string().url('Enter a valid URL').notRequired(),
orb: Yup.string().url('Enter a valid URL').notRequired(),
farcaster: Yup.string().url('Enter a valid URL').notRequired(),
}),
});

Expand Down
22 changes: 17 additions & 5 deletions src/components/publication-detail-main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@
import { useToggleBookmark } from '@src/hooks/use-toggle-bookmark';
import {
useHidePostMutation,
useGetIsPostLikedQuery,
useGetIsPostLikedLazyQuery,
useTogglePostLikeMutation,
} from '@src/graphql/generated/hooks.tsx';
import { resolveSrc } from '@src/utils/image.ts';
import { useBookmarks } from '@src/hooks/use-bookmark.ts';
import { RootState } from '@redux/store.ts';
import { decrementCounterLikes, incrementCounterLikes, setCounterLikes } from '@redux/comments';

// ----------------------------------------------------------------------

Expand All @@ -72,7 +73,8 @@
const [openConfirmModal, setOpenConfirmModal] = useState(false);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [hasLiked, setHasLiked] = useState(false);
const [likesCount, setLikesCount] = useState(post.likeCount);
const likesCount = useSelector((s: RootState) => s.comments.counterLikes[post.id] ?? post.likeCount);
const counters = useSelector((s: RootState) => s.comments.counterLikes);

Check failure on line 77 in src/components/publication-detail-main/index.tsx

View workflow job for this annotation

GitHub Actions / Static Analysis and Test | Node 20

'counters' is assigned a value but never used

const router = useRouter();
const theme = useTheme();
Expand All @@ -81,7 +83,7 @@
const [ hidePost ] = useHidePostMutation();
const { sendNotification } = useNotifications();
const { generatePayload } = useNotificationPayload(sessionData);
const { data: postLikedData, loading: postLikedLoading } = useGetIsPostLikedQuery({ variables: { postId: post.id } })
const [getIsPostLiked, { data: postLikedData, loading: postLikedLoading }] = useGetIsPostLikedLazyQuery()
const [ togglePostLike, { loading: togglePostLikeLoading } ] = useTogglePostLikeMutation()
const { has, loading: loadingList } = useBookmarks();
const { toggle, loading: loadingToggle } = useToggleBookmark();
Expand Down Expand Up @@ -119,8 +121,11 @@
}
});

setHasLiked(res?.data?.togglePostLike ?? false); // Toggle the UI based on the reaction state
setLikesCount(res?.data?.togglePostLike ? likesCount + 1 : likesCount - 1); // Update the likes count
const isLiked = res?.data?.togglePostLike ?? false;

setHasLiked(isLiked);
dispatch(isLiked ? incrementCounterLikes(post.id) : decrementCounterLikes(post.id));

// Send notification to the author when not already liked
if (res?.data?.togglePostLike) {
sendNotification(post.author.address, sessionData?.user?.address ?? '', payloadForNotification);
Expand All @@ -134,6 +139,13 @@
setHasLiked(postLikedData?.getIsPostLiked ?? false);
}, [postLikedData]);

useEffect(() => {
getIsPostLiked({ variables: { postId: post.id }, fetchPolicy: 'network-only' });
if (post.likeCount !== undefined) {
dispatch(setCounterLikes({ publicationId: post.id, likes: post.likeCount }));
}
}, [post.likeCount, post.id]);

const handleHide = async () => {
await hidePost({ variables: { postId: post.id } });
router.reload();
Expand Down
134 changes: 69 additions & 65 deletions src/hooks/use-account-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,55 @@ import { ADAPTER_EVENTS } from '@web3auth/base';

// LOCAL IMPORTS
import { useAuth } from '@src/hooks/use-auth';
import { ensureAAReady } from '@src/utils/wallet.ts';
import { ensureAAReady } from '@src/utils/wallet';
import { useWeb3Auth } from '@src/hooks/use-web3-auth';
import { useGetUserLazyQuery } from '@src/graphql/generated/hooks';

interface UseAccountSessionHook {
login: () => Promise<void>;
logout: (silent?: boolean) => Promise<void>;
syncAddress: () => Promise<void>;
refreshUser: () => Promise<void>;
loading: boolean;
login: () => Promise<void>;
logout: () => Promise<void>;
syncAddress: () => Promise<void>;
refreshUser: () => Promise<void>;
loading: boolean;
userChecked: boolean;
}

let listenerAttached = false;
let restoreDone = false;
let loginPerformed = false;

export const useAccountSession = (): UseAccountSessionHook => {
const [bootstrapping, setBootstrapping] = useState(true);
const [loginInProgress, setLoginInProgress] = useState(false);
const { session, isAuthLoading: reduxLoading } = useAuth();
const { web3Auth } = useWeb3Auth();
const dispatch = useDispatch();
const { web3Auth, bundlerClient, smartAccount } = useWeb3Auth();
const { isAuthLoading: reduxLoading, session } = useAuth();
const [loadUser, { data: userData, loading: apiLoading }] = useGetUserLazyQuery({ fetchPolicy: 'cache-and-network' });
const lastFetchedAddressRef = useRef<Address | undefined>(undefined);
const userAddressRef = useRef<Address | undefined>(undefined);
const sessionRef = useRef(session);
const [loadUser] = useGetUserLazyQuery({ fetchPolicy: 'cache-and-network' });

const [loginInProgress, setLoginInProgress] = useState(false);
const [verifyingUser, setVerifyingUser] = useState(false);
const [userChecked, setUserChecked] = useState(false);

const sessionRef = useRef(session);
const lastVerifiedRef = useRef<Address | null>(null);
const loading = loginInProgress || reduxLoading || verifyingUser;

const mergeSession = (patch: Partial<ReduxSession>) => {
const prev = sessionRef.current;
const derivedAddress = patch.user?.address as Address | undefined;

const next: ReduxSession = {
...prev,
...patch,
address: patch.address ?? prev.address ?? derivedAddress,
authenticated: Boolean(
(patch.address ?? prev.address ?? derivedAddress) &&
(patch.user ?? prev.user)
),
};

if (JSON.stringify(prev) !== JSON.stringify(next)) {
dispatch(setSession({ session: next }));
}
};

const getPrimaryAddress = useCallback(async (): Promise<Address | undefined> => {
const accs = (await web3Auth.provider?.request({ method: 'eth_accounts' })) as string[] | undefined;
Expand All @@ -56,46 +79,55 @@ export const useAccountSession = (): UseAccountSessionHook => {
const clearRedux = () => {
dispatch(setBalance({ balance: 0 }));
dispatch(setSession({ session: defaultSession }));
userAddressRef.current = undefined;
lastFetchedAddressRef.current = undefined;
setUserChecked(false);
loginPerformed = false;
};

const mergeSession = (patch: Partial<ReduxSession>) => {
const prev = sessionRef.current;
const next = { ...prev, ...patch };
next.authenticated = Boolean(next.address && next.user);

if (JSON.stringify(next) !== JSON.stringify(prev)) {
dispatch(setSession({ session: next }));
}
};

const syncAddress = async () => {
const address = await getPrimaryAddress();
let { info } = sessionRef.current;
if (!address) throw new Error('No address found');
if (address === lastVerifiedRef.current && userChecked && !verifyingUser) return;

lastVerifiedRef.current = address;

let { info } = sessionRef.current;
if (!info) info = await web3Auth.getUserInfo?.();

mergeSession({ address, info });
loadUser({ variables: { input: { address, idSession: info?.idToken } } });
setVerifyingUser(true);
setUserChecked(false);

const { data } = await loadUser({
variables: { input: { address, idSession: info?.idToken } },
});
if (data?.getUser) {
dispatch(setUser({ user: data.getUser }));
mergeSession({
user: data.getUser,
address: data.getUser.address as Address,
});
}
setVerifyingUser(false);
setUserChecked(true);
};

const refreshUser = async () => {
const address = await getPrimaryAddress();
if (!address) throw new Error('No address found');
const result = await loadUser({ variables: { input: { address } } });

dispatch(setUser({ user: result.data.getUser }));
const { data } = await loadUser({ variables: { input: { address } } });
if (data?.getUser) {
dispatch(setUser({ user: data.getUser }));
mergeSession({ user: data.getUser });
}
};

const logout = useCallback(async () => {
if (web3Auth.connected) await web3Auth.logout();
await web3Auth.logout?.();
clearRedux();
restoreDone = false;
}, [web3Auth]);

const login = useCallback(async () => {
if (loginInProgress) return;
if (loginInProgress || loginPerformed) return;

setLoginInProgress(true);
Expand All @@ -113,15 +145,9 @@ export const useAccountSession = (): UseAccountSessionHook => {
setLoginInProgress(false);
dispatch(setAuthLoading({ isAuthLoading: false }));
}
}, [web3Auth, bundlerClient, smartAccount, loginInProgress]);

useEffect(() => {
sessionRef.current = session;
}, [session]);
}, [web3Auth, loginInProgress]);

useEffect(() => {
userAddressRef.current = session.user ? session.address : undefined;
}, [session.user, session.address]);
useEffect(() => { sessionRef.current = session }, [session]);

useEffect(() => {
if (!web3Auth || restoreDone) return;
Expand All @@ -134,8 +160,6 @@ export const useAccountSession = (): UseAccountSessionHook => {
}
} catch {
await logout();
} finally {
setBootstrapping(false);
}
})();
}, [logout]);
Expand All @@ -152,27 +176,7 @@ export const useAccountSession = (): UseAccountSessionHook => {
web3Auth.off(ADAPTER_EVENTS.CONNECTED, syncAddress);
listenerAttached = false;
};
}, [logout]);

useEffect(() => {
if (userData?.getUser) {
const address = sessionRef.current.address as Address;
}, [web3Auth, logout]);

if (!address) { return; }

if (userAddressRef.current !== address) {
userAddressRef.current = address;
dispatch(setUser({ user: userData.getUser }));
mergeSession({ user: userData.getUser });
}
}
}, [userData]);

return {
login,
logout,
syncAddress,
refreshUser,
loading: bootstrapping || reduxLoading || apiLoading || loginInProgress,
};
return { login, logout, syncAddress, refreshUser, loading, userChecked };
};
Loading