Skip to content

Commit f515e06

Browse files
authored
Merge pull request #729 from WatchItDev/app/fix/redux
fix: issues
2 parents 49d3c2c + d5d35dc commit f515e06

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+724
-701
lines changed

src/components/LoadingFade.tsx

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import {
2+
ReactNode,
3+
useLayoutEffect,
4+
useRef,
5+
useState,
6+
} from 'react';
7+
import { AnimatePresence, m } from 'framer-motion';
8+
9+
export interface LoadingFadeProps {
10+
loading: boolean;
11+
skeleton: ReactNode;
12+
children: ReactNode;
13+
durationMs?: number; // fade (default 300 ms)
14+
delayMs?: number; // delay before fade starts (default 0 ms)
15+
}
16+
17+
export const LoadingFade = ({
18+
loading,
19+
skeleton,
20+
children,
21+
durationMs = 300,
22+
delayMs = 0,
23+
}: LoadingFadeProps) => {
24+
const durationSec = durationMs / 1000;
25+
const delaySec = delayMs / 1000;
26+
27+
const skelRef = useRef<HTMLDivElement>(null);
28+
const [minHeight, setMinHeight] = useState<number>();
29+
const [overlaySkeleton, setOverlaySkeleton] = useState(false);
30+
31+
useLayoutEffect(() => {
32+
if (loading && skelRef.current && minHeight === undefined) {
33+
const h = skelRef.current.getBoundingClientRect().height;
34+
if (h) {
35+
setMinHeight(h);
36+
setOverlaySkeleton(true);
37+
}
38+
}
39+
}, [loading, minHeight]);
40+
41+
const handleSkeletonExit = () => {
42+
setMinHeight(undefined);
43+
setOverlaySkeleton(false);
44+
};
45+
46+
return (
47+
<div style={{ position: 'relative', width: '100%', minHeight }}>
48+
<AnimatePresence onExitComplete={handleSkeletonExit}>
49+
{loading && (
50+
<m.div
51+
key="skeleton"
52+
ref={skelRef}
53+
initial={{ opacity: 1 }}
54+
animate={{ opacity: 1 }}
55+
exit={{ opacity: 0 }}
56+
transition={{ duration: durationSec, delay: delaySec }}
57+
style={{
58+
position: overlaySkeleton ? 'absolute' : 'static',
59+
inset: overlaySkeleton ? 0 : 'unset',
60+
width: '100%',
61+
pointerEvents: 'none',
62+
}}
63+
>
64+
{skeleton}
65+
</m.div>
66+
)}
67+
</AnimatePresence>
68+
69+
<m.div
70+
initial={{ opacity: 0 }}
71+
animate={{ opacity: loading ? 0 : 1 }}
72+
transition={{ duration: durationSec, delay: delaySec }}
73+
style={{ width: '100%' }}
74+
>
75+
{children}
76+
</m.div>
77+
</div>
78+
);
79+
};

src/components/carousel/variants/carousel-top-titles.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,13 @@ export default function CarouselTopTitles({ posts, category }: Readonly<Carousel
2525
alignItems: 'stretch',
2626
},
2727
'.slick-slide': {
28-
height: 'auto',
28+
height: 'fit-content',
2929
},
3030
'.slick-list': {
3131
height: 'auto',
3232
},
3333
'.slick-slide > div': {
34-
height: '100%',
35-
minHeight: '100%',
36-
maxHeight: '100%',
34+
height: 'auto',
3735
},
3836
}}
3937
>
@@ -47,7 +45,7 @@ export default function CarouselTopTitles({ posts, category }: Readonly<Carousel
4745
>
4846
<Carousel ref={carousel.carouselRef} {...carousel.carouselSettings}>
4947
{posts.map((post: Post) => (
50-
<Box key={`${category}-${post.id}`} sx={{ px: 0.75 }}>
48+
<Box key={`${category}-${post.id}`} sx={{ px: 0.75, height: '100%' }}>
5149
<PosterTopTitles post={post} />
5250
</Box>
5351
))}

src/components/follow-unfollow-button.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import LoadingButton from '@mui/lab/LoadingButton';
77
// REDUX IMPORTS
88
import { openLoginModal } from '@redux/auth';
99
import { useDispatch } from 'react-redux';
10-
import { addFollower, removeFollower } from '@redux/followers';
1110

1211
// LOCAL IMPORTS
1312
import Box from '@mui/material/Box';
@@ -31,6 +30,7 @@ import { User } from '@src/graphql/generated/graphql.ts';
3130

3231
interface FollowUnfollowButtonProps {
3332
profileId: string;
33+
onActionFinish?: () => void;
3434
followButtonMinWidth?: number;
3535
size?: 'small' | 'medium' | 'large';
3636
}
@@ -41,6 +41,7 @@ const FollowUnfollowButton = ({
4141
profileId,
4242
size = 'medium',
4343
followButtonMinWidth = 120,
44+
onActionFinish = () => {},
4445
}: PropsWithChildren<FollowUnfollowButtonProps>) => {
4546
const dispatch = useDispatch();
4647
const [loadProfile, { data: profileData, loading: profileLoading }] = useGetUserLazyQuery();
@@ -86,12 +87,7 @@ const FollowUnfollowButton = ({
8687

8788
setIsFollowed(result?.data?.toggleFollow);
8889
handleUpdateProfile();
89-
90-
if (result?.data?.toggleFollow) {
91-
dispatch(addFollower(profile));
92-
} else {
93-
dispatch(removeFollower(profileId));
94-
}
90+
onActionFinish();
9591

9692
// Send notification to the profile being followed
9793
const notificationPayload = generatePayload(

src/components/poster/variants/poster-top-titles.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const PosterTopTitles = ({ post }: { post: Post }) => {
3333

3434
return (
3535
<Stack
36-
sx={{ position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center' }}
36+
sx={{ position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', overflow: 'hidden' }}
3737
alignItems={'stretch'}
3838
spacing={{ xs: 1, sm: 2, md: 4 }}
3939
>
@@ -47,7 +47,7 @@ const PosterTopTitles = ({ post }: { post: Post }) => {
4747
top: 0,
4848
left: 0,
4949
width: '100%',
50-
height: '100%',
50+
height: '99%',
5151
opacity: 0.2,
5252
filter: "blur(5px) !important",
5353
backgroundSize: 'cover',

src/components/publication-detail-main/index.tsx

Lines changed: 38 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import { SubscribeToUnlockCard } from '@src/components/subscribe-to-unlock-card/
4343
import Popover from '@mui/material/Popover';
4444
import { useNotifications } from '@src/hooks/use-notifications.ts';
4545
import { openLoginModal } from '@redux/auth';
46-
import { useDispatch, useSelector } from 'react-redux';
46+
import { useDispatch } from 'react-redux';
4747
import { useNotificationPayload } from '@src/hooks/use-notification-payload.ts';
4848
import AvatarProfile from "@src/components/avatar/avatar.tsx";
4949
import { PublicationDetailProps } from '@src/components/publication-detail-main/types.ts';
@@ -56,8 +56,6 @@ import {
5656
} from '@src/graphql/generated/hooks.tsx';
5757
import { resolveSrc } from '@src/utils/image.ts';
5858
import { useBookmarks } from '@src/hooks/use-bookmark.ts';
59-
import { RootState } from '@redux/store.ts';
60-
import { decrementCounterLikes, incrementCounterLikes, setCounterLikes } from '@redux/comments';
6159

6260
// ----------------------------------------------------------------------
6361

@@ -73,8 +71,8 @@ export default function PublicationDetailMain({
7371
const [openConfirmModal, setOpenConfirmModal] = useState(false);
7472
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
7573
const [hasLiked, setHasLiked] = useState(false);
76-
const likesCount = useSelector((s: RootState) => s.comments.counterLikes[post.id] ?? post.likeCount);
77-
const counters = useSelector((s: RootState) => s.comments.counterLikes);
74+
const [likesCount, setLikesCount] = useState(post.likeCount);
75+
const [commentCount, setCommentCount] = useState(post.commentCount);
7876

7977
const router = useRouter();
8078
const theme = useTheme();
@@ -83,68 +81,61 @@ export default function PublicationDetailMain({
8381
const [ hidePost ] = useHidePostMutation();
8482
const { sendNotification } = useNotifications();
8583
const { generatePayload } = useNotificationPayload(sessionData);
86-
const [getIsPostLiked, { data: postLikedData, loading: postLikedLoading }] = useGetIsPostLikedLazyQuery()
84+
const [getIsPostLiked, { loading: postLikedLoading }] = useGetIsPostLikedLazyQuery()
8785
const [ togglePostLike, { loading: togglePostLikeLoading } ] = useTogglePostLikeMutation()
8886
const { has, loading: loadingList } = useBookmarks();
8987
const { toggle, loading: loadingToggle } = useToggleBookmark();
90-
const commentCount = useSelector((s: RootState) => s.comments.postCommentCount[post.id] ?? post.commentCount);
9188

9289
const isBookmarked = has(post.id);
9390
const isLoading = togglePostLikeLoading || postLikedLoading
9491
const variants = theme.direction === 'rtl' ? varFade().inLeft : varFade().inRight;
9592
const openMenu = Boolean(anchorEl);
9693

97-
const toggleReaction = async () => {
94+
const handleToggleLike = async () => {
9895
if (!sessionData?.authenticated) return dispatch(openLoginModal());
9996

100-
// Send a notification to the profile owner using the sendNotification function from useNotifications hook
101-
const payloadForNotification = generatePayload(
102-
'LIKE',
103-
{
104-
id: post.author.address,
105-
displayName: post.author.displayName ?? 'Watchit',
106-
avatar: resolveSrc(post.author.profilePicture || post.author.address, 'profile'),
107-
},
108-
{
109-
rawDescription: `${sessionData?.user?.displayName} liked ${post.title}`,
110-
root_id: post.id,
111-
post_title: post.title,
112-
}
113-
);
114-
11597
try {
116-
const res = await togglePostLike({
117-
variables: {
118-
input: {
119-
postId: post.id
120-
}
121-
}
122-
});
98+
const res = await togglePostLike({ variables: { input: { postId: post.id } } });
99+
const isNowLiked = res.data?.togglePostLike ?? false;
123100

124-
const isLiked = res?.data?.togglePostLike ?? false;
101+
setHasLiked(isNowLiked);
102+
setLikesCount((prev) => prev + (isNowLiked ? 1 : -1));
125103

126-
setHasLiked(isLiked);
127-
dispatch(isLiked ? incrementCounterLikes(post.id) : decrementCounterLikes(post.id));
104+
if (isNowLiked) {
105+
// Send a notification to the profile owner using the sendNotification function from useNotifications hook
106+
const payloadForNotification = generatePayload(
107+
'LIKE',
108+
{
109+
id: post.author.address,
110+
displayName: post.author.displayName ?? 'Watchit',
111+
avatar: resolveSrc(post.author.profilePicture || post.author.address, 'profile'),
112+
},
113+
{
114+
rawDescription: `${sessionData?.user?.displayName} liked ${post.title}`,
115+
root_id: post.id,
116+
post_title: post.title,
117+
}
118+
);
128119

129-
// Send notification to the author when not already liked
130-
if (res?.data?.togglePostLike) {
131120
sendNotification(post.author.address, sessionData?.user?.address ?? '', payloadForNotification);
132121
}
133122
} catch (err) {
134-
console.error('Error toggling reaction:', err);
123+
console.error(err);
135124
}
136125
};
137126

138-
useEffect(() => {
139-
setHasLiked(postLikedData?.getIsPostLiked ?? false);
140-
}, [postLikedData]);
127+
const handleCommentSuccess = (wasReply = false) => {
128+
if (!wasReply) {
129+
// Es un comentario raíz → solo incrementamos el counter del post
130+
setCommentCount((c) => (c ?? 0) + 1);
131+
}
132+
};
141133

142134
useEffect(() => {
143-
getIsPostLiked({ variables: { postId: post.id } });
144-
if (post.likeCount !== undefined) {
145-
dispatch(setCounterLikes({ publicationId: post.id, likes: post.likeCount }));
146-
}
147-
}, [post.likeCount, post.id]);
135+
getIsPostLiked({ variables: { postId: post.id } }).then((res) =>
136+
setHasLiked(res.data?.getIsPostLiked ?? false),
137+
);
138+
}, [post.id]);
148139

149140
const handleHide = async () => {
150141
await hidePost({ variables: { postId: post.id } });
@@ -350,7 +341,7 @@ export default function PublicationDetailMain({
350341
height: '40px',
351342
minWidth: '40px',
352343
}}
353-
onClick={toggleReaction}
344+
onClick={handleToggleLike}
354345
disabled={isLoading}
355346
>
356347
{isLoading ? (
@@ -444,6 +435,7 @@ export default function PublicationDetailMain({
444435
displayName: post.author.displayName ?? 'Watchit',
445436
avatar: resolveSrc(post.author.profilePicture || post.author.address, 'profile'),
446437
}}
438+
onSuccess={() => handleCommentSuccess(false)}
447439
/>
448440
) : (
449441
<Typography
@@ -462,7 +454,7 @@ export default function PublicationDetailMain({
462454
)}
463455
</Box>
464456
<Box sx={{ display: 'flex', flexDirection: 'column', mt: 2, pr: 1 }}>
465-
<PostCommentList publicationId={post.id} showReplies />
457+
<PostCommentList publicationId={post.id} showReplies onReplyCreated={() => handleCommentSuccess(true)} />
466458
</Box>
467459
</Box>
468460
)}

src/components/subscribe-to-unlock-card/subscribe-to-unlock-card.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export const SubscribeToUnlockCard = ({
9191
size="lg"
9292
/>
9393
)}
94-
{isAuthorized && (
94+
{isJoinButtonVisible && (
9595
<Box sx={{ mt: 3, borderRadius: 1 }}>
9696
<Typography variant="body2" color="textSecondary">
9797
Join now for just <strong>{totalCostMMC} MMC/month</strong> and access to{' '}

src/components/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ReactNode } from 'react';
2+
13
export interface HandleActionErrorProps extends Error{
24
requestedAmount?: {
35
asset?: { symbol: string };
@@ -9,3 +11,10 @@ export interface ActivateSubscriptionProfileModalProps {
911
isOpen: boolean;
1012
onClose: () => void;
1113
}
14+
15+
export interface LoadingFadeProps {
16+
loading: boolean;
17+
skeleton: ReactNode;
18+
children: ReactNode;
19+
duration?: number;
20+
}

src/config-global.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export interface GlobalConstants {
3838
ATTESTATION_BASE_URL: string;
3939
FROM_BLOCK: bigint | string | number;
4040
GQL_ENDPOINT: string;
41+
OPEN_COLLECTIVE: string;
42+
TERMS_AND_CONDITIONS: string;
4143
}
4244

4345
export const GLOBAL_CONSTANTS: GlobalConstants = {
@@ -101,6 +103,8 @@ export const GLOBAL_CONSTANTS: GlobalConstants = {
101103
process.env.VITE_URL_ATTESTATION_BASE || import.meta.env.VITE_URL_ATTESTATION_BASE || "",
102104
FROM_BLOCK: process.env.VITE_BLOCK_FROM || import.meta.env.VITE_BLOCK_FROM || 0n,
103105
GQL_ENDPOINT: process.env.VITE_GQL_ENDPOINT || import.meta.env.VITE_GQL_ENDPOINT || 'http://localhost:4000/graphql',
106+
OPEN_COLLECTIVE: process.env.VITE_URL_OPEN_COLLECTIVE || import.meta.env.VITE_URL_OPEN_COLLECTIVE || '',
107+
TERMS_AND_CONDITIONS: process.env.VITE_URL_TERMS_AND_CONDITIONS || import.meta.env.VITE_URL_TERMS_AND_CONDITIONS || '',
104108
};
105109

106110
export const PATH_AFTER_LOGIN = paths.dashboard.root;

0 commit comments

Comments
 (0)