Skip to content

Commit

Permalink
simplify and refactor comments page and voting mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
diegoalzate committed May 16, 2024
1 parent 346f226 commit 21124d5
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 82 deletions.
6 changes: 3 additions & 3 deletions packages/berlin/src/components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,14 @@ function Header() {
)
);
})}
<NavButton to={`/events/${events?.[0].id}/register`} $color="secondary">
My proposals
</NavButton>
<NavButton to={`/events/${events?.[0].id}/cycles`} $color="secondary">
Agenda
</NavButton>
</>
)}
<NavButton to={`/events/${events?.[0].id}/register`} $color="secondary">
My proposals
</NavButton>
<Button onClick={() => mutateLogout()}>Log out</Button>
<IconButton
onClick={() => navigate('/account')}
Expand Down
6 changes: 3 additions & 3 deletions packages/berlin/src/components/option-card/OptionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ type OptionCardProps = {
option: QuestionOption;
numOfVotes: number;
onVote: () => void;
onUnvote: () => void;
onUnVote: () => void;
};
function OptionCard({ option, numOfVotes, onVote, onUnvote }: OptionCardProps) {
function OptionCard({ option, numOfVotes, onVote, onUnVote }: OptionCardProps) {
const { eventId, cycleId } = useParams();
const theme = useAppStore((state) => state.theme);
const navigate = useNavigate();
Expand Down Expand Up @@ -67,7 +67,7 @@ function OptionCard({ option, numOfVotes, onVote, onUnvote }: OptionCardProps) {
$padding={0}
$color="secondary"
icon={{ src: `/icons/downvote-${theme}.svg`, alt: 'Downvote arrow' }}
onClick={onUnvote}
onClick={onUnVote}
$width={16}
$height={16}
disabled={numOfVotes === 0}
Expand Down
27 changes: 17 additions & 10 deletions packages/berlin/src/pages/Comments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ import {
fetchComments,
postComment,
fetchOptionUsers,
fetchCycle,
} from 'api';

// Hooks
import useUser from '../hooks/useUser';

// Utils
import { handleSaveVotes, handleUnvote, handleVote } from '../utils/voting';
import {
handleSaveVotes,
handleAvailableHearts,
handleLocalUnVote,
handleLocalVote,
} from '../utils/voting';

// Types
import { ResponseUserVotesType } from '../types/CycleType';
Expand Down Expand Up @@ -48,7 +52,8 @@ function Comments() {
const queryClient = useQueryClient();
const { cycleId, optionId } = useParams();
const { user } = useUser();
const { availableHearts, setAvailableHearts } = useAppStore((state) => state);
const availableHearts = useAppStore((state) => state.availableHearts);
const setAvailableHearts = useAppStore((state) => state.setAvailableHearts);
const [localUserVotes, setLocalUserVotes] = useState<LocalUserVotes>([]);
const [localOptionHearts, setLocalOptionHearts] = useState(0);
const [comment, setComment] = useState('');
Expand All @@ -68,13 +73,13 @@ function Comments() {
});

const { data: optionUsers } = useQuery({
queryKey: ['optionUsers', optionId],
queryKey: ['option', optionId, 'users'],
queryFn: () => fetchOptionUsers(optionId || ''),
enabled: !!optionId,
});

const { data: comments } = useQuery({
queryKey: ['comments', optionId],
queryKey: ['option', optionId, 'comments'],
queryFn: () => fetchComments({ optionId: optionId || '' }),
enabled: !!optionId,
// refetchInterval: 5000, // Poll every 5 seconds
Expand Down Expand Up @@ -102,19 +107,21 @@ function Comments() {
mutationFn: postComment,
onSuccess: (body) => {
if (body?.value) {
queryClient.invalidateQueries({ queryKey: ['comments', optionId] });
queryClient.invalidateQueries({ queryKey: ['option', optionId, 'comments'] });
}
},
});

const handleVoteWrapper = (optionId: string) => {
setLocalOptionHearts((prevLocalOptionHearts) => prevLocalOptionHearts + 1);
handleVote(optionId, availableHearts, setAvailableHearts, setLocalUserVotes);
setLocalUserVotes((prevLocalUserVotes) => handleLocalVote(optionId, prevLocalUserVotes));
setAvailableHearts(handleAvailableHearts(availableHearts, 'vote'));
};

const handleUnvoteWrapper = (optionId: string) => {
const handleUnVoteWrapper = (optionId: string) => {
setLocalOptionHearts((prevLocalOptionHearts) => Math.max(0, prevLocalOptionHearts - 1));
handleUnvote(optionId, availableHearts, setAvailableHearts, setLocalUserVotes);
setLocalUserVotes((prevLocalUserVotes) => handleLocalUnVote(optionId, prevLocalUserVotes));
setAvailableHearts(handleAvailableHearts(availableHearts, 'unVote'));
};

const { mutate: mutateVotes } = useMutation({
Expand Down Expand Up @@ -165,7 +172,7 @@ function Comments() {
$padding={0}
$color="secondary"
icon={{ src: `/icons/downvote-${theme}.svg`, alt: 'Downvote arrow' }}
onClick={() => handleUnvoteWrapper(option?.id ?? '')}
onClick={() => handleUnVoteWrapper(option?.id ?? '')}
$width={16}
$height={16}
disabled={localOptionHearts === 0}
Expand Down
17 changes: 12 additions & 5 deletions packages/berlin/src/pages/Cycle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import useCountdown from '../hooks/useCountdown';
import useUser from '../hooks/useUser';

// Utils
import { handleSaveVotes, handleUnvote, handleVote } from '../utils/voting';
import {
handleSaveVotes,
handleAvailableHearts,
handleLocalUnVote,
handleLocalVote,
} from '../utils/voting';

// Types
import { ResponseUserVotesType } from '../types/CycleType';
Expand Down Expand Up @@ -137,11 +142,13 @@ function Cycle() {
});

const handleVoteWrapper = (optionId: string) => {
handleVote(optionId, availableHearts, setAvailableHearts, setLocalUserVotes);
setLocalUserVotes((prevLocalUserVotes) => handleLocalVote(optionId, prevLocalUserVotes));
setAvailableHearts(handleAvailableHearts(availableHearts, 'vote'));
};

const handleUnvoteWrapper = (optionId: string) => {
handleUnvote(optionId, availableHearts, setAvailableHearts, setLocalUserVotes);
const handleUnVoteWrapper = (optionId: string) => {
setLocalUserVotes((prevLocalUserVotes) => handleLocalUnVote(optionId, prevLocalUserVotes));
setAvailableHearts(handleAvailableHearts(availableHearts, 'unVote'));
};

const handleSaveVotesWrapper = () => {
Expand Down Expand Up @@ -243,7 +250,7 @@ function Cycle() {
option={option}
numOfVotes={numOfVotes}
onVote={() => handleVoteWrapper(option.id)}
onUnvote={() => handleUnvoteWrapper(option.id)}
onUnVote={() => handleUnVoteWrapper(option.id)}
/>
);
})}
Expand Down
105 changes: 44 additions & 61 deletions packages/berlin/src/utils/voting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,51 @@ import { GetUserVotesResponse, PostVotesRequest } from 'api';
import { ResponseUserVotesType } from '../types/CycleType';
import toast from 'react-hot-toast';

export const handleVote = (
export const handleLocalVote = (
optionId: string,
availableHearts: number,
setAvailableHearts: (hearts: number) => void,
setLocalUserVotes: React.Dispatch<
React.SetStateAction<
| ResponseUserVotesType
| {
optionId: string;
numOfVotes: number;
}[]
>
>,
prevLocalUserVotes: ResponseUserVotesType | { optionId: string; numOfVotes: number }[],
) => {
if (availableHearts > 0) {
setLocalUserVotes((prevLocalUserVotes) => {
const temp = prevLocalUserVotes.find((x) => x.optionId === optionId);
if (!temp) {
return [...prevLocalUserVotes, { optionId, numOfVotes: 1 }];
}
const updatedLocalVotes = prevLocalUserVotes.map((prevLocalUserVote) => {
if (prevLocalUserVote.optionId === optionId) {
return { ...prevLocalUserVote, numOfVotes: prevLocalUserVote.numOfVotes + 1 };
}
return prevLocalUserVote;
});
return updatedLocalVotes;
});
setAvailableHearts(Math.max(0, availableHearts - 1));
// find if the user has already voted for this option
const prevVote = prevLocalUserVotes.find((x) => x.optionId === optionId);
if (!prevVote) {
// if the user has not voted for this option, add a new vote
return [...prevLocalUserVotes, { optionId, numOfVotes: 1 }];
}

// if the user has already voted for this option, update the number of votes
// this will just find the option and increase the number of votes by 1
const updatedLocalVotes = prevLocalUserVotes.map((prevLocalUserVote) => {
if (prevLocalUserVote.optionId === optionId) {
return { ...prevLocalUserVote, numOfVotes: prevLocalUserVote.numOfVotes + 1 };
}
return prevLocalUserVote;
});

return updatedLocalVotes;
};

export const handleUnvote = (
export const handleLocalUnVote = (
optionId: string,
availableHearts: number,
setAvailableHearts: (hearts: number) => void,
setLocalUserVotes: React.Dispatch<
React.SetStateAction<
| ResponseUserVotesType
| {
optionId: string;
numOfVotes: number;
}[]
>
>,
prevLocalUserVotes: ResponseUserVotesType | { optionId: string; numOfVotes: number }[],
) => {
setLocalUserVotes((prevLocalUserVotes) => {
const updatedLocalVotes = prevLocalUserVotes.map((prevLocalUserVote) => {
if (prevLocalUserVote.optionId === optionId) {
const newNumOfVotes = Math.max(0, prevLocalUserVote.numOfVotes - 1);
return { ...prevLocalUserVote, numOfVotes: newNumOfVotes };
}
return prevLocalUserVote;
});

return updatedLocalVotes;
// this will just find the option and decrease the number of votes by 1
const updatedLocalVotes = prevLocalUserVotes.map((prevLocalUserVote) => {
if (prevLocalUserVote.optionId === optionId) {
const newNumOfVotes = Math.max(0, prevLocalUserVote.numOfVotes - 1);
return { ...prevLocalUserVote, numOfVotes: newNumOfVotes };
}
return prevLocalUserVote;
});

setAvailableHearts(Math.min(20, availableHearts + 1));
return updatedLocalVotes;
};

export const handleAvailableHearts = (availableHearts: number, type: 'vote' | 'unVote') => {
if (type === 'vote') {
return Math.max(0, availableHearts - 1);
}

return Math.min(20, availableHearts + 1);
};

export const handleSaveVotes = (
Expand All @@ -82,18 +68,15 @@ export const handleSaveVotes = (

for (const localVote of localUserVotes) {
const matchingServerVote = serverVotesMap.get(localVote.optionId);

if (!matchingServerVote) {
mutateVotesReq.votes.push({
optionId: localVote.optionId,
numOfVotes: localVote.numOfVotes,
});
} else if (matchingServerVote.numOfVotes !== localVote.numOfVotes) {
mutateVotesReq.votes.push({
optionId: localVote.optionId,
numOfVotes: localVote.numOfVotes,
});
// if the vote is the same as the server vote, we don't need to send it
if (matchingServerVote && matchingServerVote.numOfVotes === localVote.numOfVotes) {
continue;
}

mutateVotesReq.votes.push({
optionId: localVote.optionId,
numOfVotes: localVote.numOfVotes,
});
}

mutateVotes(mutateVotesReq);
Expand Down

0 comments on commit 21124d5

Please sign in to comment.