Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(WIP): follow / unfollow and comments handling #330

Merged
merged 31 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
89eb240
feat: add type safety to LoaderContainer with BoxProps
cswni Dec 17, 2024
9669913
feat(redux-followers): integrate followers management state
cswni Dec 17, 2024
e5b3005
feat(comments): support pending comments in state and UI
cswni Dec 17, 2024
2e12a99
fix(user-profile-view): handle undefined profile id
cswni Dec 17, 2024
d561dc9
refactor: posting comments effects
cswni Dec 17, 2024
0e76fff
refactor: optimize UI styles and improve comment handling
cswni Dec 17, 2024
d633328
feat(publication-comment-item): adjust like icon color for pending co…
cswni Dec 17, 2024
4e05263
refactor(publication): remove unused updateCommentStatus import
cswni Dec 17, 2024
93b190c
refactor(layout): remove unused LoaderDuringCreationProfile
cswni Dec 18, 2024
8afbb1b
feat(auth): add currentStep state and relevant actions
cswni Dec 18, 2024
0b52e76
feat: add customizable padding prop to NeonPaperContainer
cswni Dec 18, 2024
12f0a99
feat: improve profile creation UI with dynamic button logic
cswni Dec 18, 2024
bed2c7d
refactor(wip): change sessionData flow
cswni Dec 18, 2024
327ac9b
fix: session data redux
Jadapema Dec 18, 2024
c00e73d
refactor: replace useSession with useSelector for session handling
cswni Dec 18, 2024
0f98755
fix(modal): adjust BackdropProps and remove unused import
cswni Dec 18, 2024
28bf2c4
feat: close login modal after profile actions complete
cswni Dec 18, 2024
30957b6
feat: added background task middleware and worker
Jadapema Dec 18, 2024
24db1e5
refactor(view): update profile filtering logic
cswni Dec 18, 2024
2e8b34f
feat(profile-header): add hide/unhide profile functionality
cswni Dec 18, 2024
fec9a68
Merge remote-tracking branch 'origin/app/refactor/follow-comments' in…
cswni Dec 18, 2024
e5a6e84
feat: add dynamic borderRadius and rainbow effect with NeonPaper
cswni Dec 18, 2024
e1259db
feat: added update metadata background task
Jadapema Dec 18, 2024
879ee33
feat: added update metadata background task
Jadapema Dec 18, 2024
9386037
refactor: profile update in bg
cswni Dec 18, 2024
454e3c6
Merge remote-tracking branch 'origin/app/refactor/follow-comments' in…
cswni Dec 18, 2024
5d0db30
fix: empty image
Jadapema Dec 18, 2024
b023b3f
fix: empty image
Jadapema Dec 18, 2024
0c5fa25
fix: empty image
Jadapema Dec 18, 2024
a274b75
fix: timing on create profile
Jadapema Dec 18, 2024
851cbc2
fix: some small fixes
Jadapema Dec 18, 2024
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
7 changes: 7 additions & 0 deletions src/components/follow-unfollow-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useLazyProfile } from '@lens-protocol/react';
// REDUX IMPORTS
import { openLoginModal } from '@redux/auth';
import { useDispatch } from 'react-redux';
import { removeFollowing, addFollowing } from '@redux/followers';

// NOTIFICATIONS IMPORTS
import { useSnackbar } from 'notistack';
Expand Down Expand Up @@ -90,6 +91,12 @@ const FollowUnfollowButton = ({ profileId, size = 'medium', followButtonMinWidth
await result.value.waitForCompletion();
handleUpdateProfile();

// Update the following list
if(action === unfollow) {
dispatch(removeFollowing(profileId));
}else{
dispatch(addFollowing(profile));
}

// Send notification to the profile being followed
const notificationPayload = generatePayload('FOLLOW', {
Expand Down
8 changes: 6 additions & 2 deletions src/components/login-modal/LoaderDuringCreationProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box } from "@mui/system";
import { Box, BoxProps } from "@mui/system";
import { WatchitLoader } from "@src/components/watchit-loader";
import { useSelector } from 'react-redux';
import { COLORS } from "@src/layouts/config-layout.ts";
Expand All @@ -7,6 +7,10 @@ import TextMaxLine from "@src/components/text-max-line";
import { IconCheck, IconLoader, IconSquare } from "@tabler/icons-react";
import { styled, keyframes } from '@mui/material/styles';

interface LoaderContainerProps extends BoxProps {
show: boolean;
}

const spin = keyframes`
0% {
transform: rotate(0deg);
Expand All @@ -24,7 +28,7 @@ const IconSquaredStyledFullyTransparent = styled(IconSquare)`
opacity: 0;
`;

const LoaderContainer = styled(Box)`
const LoaderContainer = styled(Box)<LoaderContainerProps>`
display: ${({ show }) => (show ? 'flex' : 'none')};
position: fixed;
width: 100%;
Expand Down
28 changes: 28 additions & 0 deletions src/redux/comments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ export type CommentsReducerState = {
};
hiddenComments: AnyPublication[];
counterLikes: { [publicationId: string]: number };
comments: { [publicationId: string]: AnyPublication[] };
pendingComments: { [publicationId: string]: AnyPublication[] };
};

const initialState: CommentsReducerState = {
refetchTriggerByPublication: {},
hiddenComments: [],
counterLikes: {},
comments: {},
pendingComments: {},
};

const commentsSlice = createSlice({
Expand Down Expand Up @@ -45,6 +49,27 @@ const commentsSlice = createSlice({
state.counterLikes[publicationId] -= 1;
}
},
addComment: (state, action: PayloadAction<{ publicationId: string; comment: AnyPublication }>) => {
const { publicationId, comment } = action.payload;
if (!state.comments[publicationId]) {
state.comments[publicationId] = [];
}
state.comments[publicationId].push(comment);
},
addPendingComment: (state, action: PayloadAction<{ publicationId: string; comment: AnyPublication }>) => {
const { publicationId, comment } = action.payload;
if (!state.pendingComments[publicationId]) {
state.pendingComments[publicationId] = [];
}
// Prepend new comment to the beginning of the list
state.pendingComments[publicationId] = [comment, ...state.pendingComments[publicationId]];
},
updateCommentStatus: (state, action: PayloadAction<{ publicationId: string; commentId: string; status: string }>) => {
const { publicationId, commentId, status } = action.payload;
const comment = state.comments[publicationId]?.find(comment => comment.id === commentId);
state.comments[publicationId] = state.comments[publicationId].filter(comment => comment.id !== commentId);
Copy link
Preview

Copilot AI Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updateCommentStatus reducer removes the comment from the state.comments array but does not update its status. Ensure the comment's status is updated correctly.

Suggested change
state.comments[publicationId] = state.comments[publicationId].filter(comment => comment.id !== commentId);
if (comment) comment.status = status;

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options

},
},
});

Expand All @@ -54,6 +79,9 @@ export const {
setCounterLikes,
incrementCounterLikes,
decrementCounterLikes,
addComment,
addPendingComment,
updateCommentStatus,
} = commentsSlice.actions;

export default commentsSlice.reducer;
37 changes: 37 additions & 0 deletions src/redux/followers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { Profile } from '@lens-protocol/api-bindings';

interface FollowersState {
followers: Profile[];
followings: Profile[];
}

const initialState: FollowersState = {
followers: [],
followings: [],
};

const followersSlice = createSlice({
name: 'followers',
initialState,
reducers: {
setFollowers: (state, action: PayloadAction<Profile[]>) => {
state.followers = action.payload;
},
setFollowings: (state, action: PayloadAction<Profile[]>) => {
state.followings = action.payload;
},

addFollowing: (state, action: PayloadAction<Profile>) => {
state.followings.push(action.payload);
},

removeFollowing: (state, action: PayloadAction<string>) => {
state.followings = state.followings.filter(following => following.id !== action.payload);
},
},
});

export const { setFollowers, setFollowings, removeFollowing, addFollowing } = followersSlice.actions;

export default followersSlice.reducer;
4 changes: 3 additions & 1 deletion src/redux/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import authReducer from '@redux/auth/index';
import notificationsReducer from '@redux/notifications/index';
import bookmarkReducer from '@redux/bookmark/index';
import commentsReducer from '@redux/comments/index';
import followersReducer from '@redux/followers/index';

const appReducer = combineReducers({
minibar: minibarReducer,
drawer: drawerReducer,
auth: authReducer,
bookmark: bookmarkReducer,
comments: commentsReducer,
notifications: notificationsReducer
notifications: notificationsReducer,
followers: followersReducer,
});

const rootReducer = (state: any, action: any) => {
Expand Down
9 changes: 8 additions & 1 deletion src/sections/publication/publication-comments-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Props = {
};

export default function PostCommentList({ publicationId: id, showReplies }: Props) {
const pendingComments = useSelector((state: any) => state.comments.pendingComments);
const { data: comments, error, loading, execute } = useLazyPublications();
const { hiddenComments, refetchTriggerByPublication } = useSelector((state: any) => state.comments);
const refetchTrigger = refetchTriggerByPublication[id] || 0;
Expand All @@ -37,7 +38,13 @@ export default function PostCommentList({ publicationId: id, showReplies }: Prop

if (error) return <p>Error loading comments: {error.message}</p>;

const commentsFiltered = (comments ?? [])
// Join the comments with the pending comments but append the pending comments at the beginning of the list
const commentsWithPending = pendingComments[id] ? [...pendingComments[id], ...(comments ?? [])] : comments;
Copy link
Preview

Copilot AI Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commentsWithPending variable should default to an empty array if comments are null or undefined to avoid potential runtime errors.

Suggested change
const commentsWithPending = pendingComments[id] ? [...pendingComments[id], ...(comments ?? [])] : comments;
const commentsWithPending = pendingComments[id] ? [...pendingComments[id], ...(comments ?? [])] : (comments ?? []);

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options

console.log('comments', comments);
console.log('comments pending', pendingComments[id]);

const commentsFiltered = (commentsWithPending ?? [])
.filter((comment) => !hiddenComments.some((hiddenComment: any) => hiddenComment.id === comment.id))
.filter((comment) => !comment.isHidden)

Expand Down
56 changes: 53 additions & 3 deletions src/sections/publication/publication-details-comment-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import { ReadResult } from '@lens-protocol/react/dist/declarations/src/helpers/r
import { uploadMetadataToIPFS, verifyIpfsData } from '@src/utils/ipfs';
import uuidv4 from '@src/utils/uuidv4.ts';
import { useDispatch } from 'react-redux';
import { refetchCommentsByPublication } from '@redux/comments';
import {refetchCommentsByPublication, addPendingComment, updateCommentStatus} from '@redux/comments';
import {useNotifications} from "@src/hooks/use-notifications.ts";
import { useNotificationPayload } from '@src/hooks/use-notification-payload.ts';
import {AnyPublication} from "@lens-protocol/api-bindings";

// Define the props types
type MovieCommentFormProps = {
Expand Down Expand Up @@ -106,9 +107,11 @@ const MovieCommentForm = ({ commentOn, owner, root }: MovieCommentFormProps) =>
*/
const onSubmit = handleSubmit(async (data) => {
try {
const uuid = uuidv4();

const metadata = textOnly({
appId: 'watchit',
id: uuidv4(),
id: uuid,
attributes: [
{ type: MetadataAttributeType.STRING, key: 'publication', value: commentOn },
{ type: MetadataAttributeType.STRING, key: 'creator', value: sessionData?.profile?.handle?.localName },
Expand All @@ -124,10 +127,55 @@ const MovieCommentForm = ({ commentOn, owner, root }: MovieCommentFormProps) =>
{ display_type: MarketplaceMetadataAttributeDisplayType.STRING, value: 'watchit' },
],
description: data.comment,
external_url: `https://watchit.movie/comment/${uuidv4()}`,
external_url: `https://watchit.movie/comment/${uuid}`,
}
});

// Create a pending comment object
const pendingComment: AnyPublication = {
// @ts-ignore
id: uuid as string,
isHidden: false,
isHiddenByAuthor: false,
// @ts-ignore
// Add metadata to the comment but replace the content with the original comment
metadata: {
...metadata,
content: data.comment,
},
// @ts-ignore
operations: {
hasUpvoted: false
},
status: 'pending',
by: sessionData?.profile,
createdAt: new Date().toISOString(),
__typename: 'Comment',
hashtagsMentioned: [],
profilesMentioned: [],
quoteOn: {
// @ts-ignore
__typename: 'CommentFields',
// @ts-ignore
id: commentOn,
createdAt: new Date().toISOString(),
// @ts-ignore
metadata,
by: sessionData?.profile,
stats: {
// @ts-ignore
totalAmountOfMirrors: 0,
totalAmountOfCollects: 0,
totalAmountOfComments: 0,
totalAmountOfUpvotes: 0,
},
},
};

reset(); // Reset the form
// Dispatch the addPendingComment action
dispatch(addPendingComment({ publicationId: commentOn, comment: pendingComment }));

// Validate metadata against the schema
const validation = PublicationMetadataSchema.safeParse(metadata);
if (!validation.success) {
Expand All @@ -146,6 +194,8 @@ const MovieCommentForm = ({ commentOn, owner, root }: MovieCommentFormProps) =>
commentOn: commentOn as any,
metadata: uri,
}).then(() => {
// Update the comment status to confirmed
dispatch(updateCommentStatus({ publicationId: commentOn, commentId: pendingComment.id, status: 'confirmed' }));
// Send notifications to the author of the publication
const notificationPayload = generatePayload('COMMENT', {
id: owner?.id,
Expand Down
11 changes: 4 additions & 7 deletions src/sections/user/profile-followers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,21 @@ import Typography from '@mui/material/Typography';

// LENS IMPORTS
import { Profile } from '@lens-protocol/api-bindings';
import { useProfileFollowers } from '@lens-protocol/react';

// LOCAL IMPORTS
import { UserItem } from '@src/components/user-item';
import { useSelector } from 'react-redux';

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

interface Props {
profile: Profile;
onActionFinished?: () => void;
}

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

const ProfileFollowers = ({ profile, onActionFinished }: Props) => {
const { data: followers } = useProfileFollowers({
of: profile.id,
});
const ProfileFollowers = ({ onActionFinished }: Props) => {
const followers: Profile[] = useSelector((state: any) => state.followers.followers);

return (
<Box
Expand All @@ -42,7 +39,7 @@ const ProfileFollowers = ({ profile, onActionFinished }: Props) => {
}}
>
{followers?.length ? (
followers.map((follower, index) => (
followers.map((follower: any, index: any) => (
<UserItem
key={`follower-${index}`}
profile={follower}
Expand Down
13 changes: 3 additions & 10 deletions src/sections/user/profile-following.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,12 @@ import { useProfileFollowing } from '@lens-protocol/react';

// LOCAL IMPORTS
import { UserItem } from '@src/components/user-item';
import {useSelector} from "react-redux";

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

interface Props {
profile: Profile;
}

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

const ProfileFollowing = ({ profile }: Props) => {
const { data: following } = useProfileFollowing({
for: profile.id,
});
const ProfileFollowing = () => {
const following: Profile[] = useSelector((state: any) => state.followers.followings);

return (
<Box
Expand Down
Loading
Loading