From c30566cf14ea7cfcdfc7b9c12c3f6c46c5b2242b Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Wed, 18 May 2022 10:33:08 +0300 Subject: [PATCH] feat: vote button reset cntr, replace old votes cntr in some components --- .../VoteButton/VoteButton.stories.tsx | 56 +++++++++++++++++ src/Components/VoteButton/VoteButton.tsx | 60 ++++++++++++++----- src/Components/VoteButton/styles.module.css | 8 +-- .../PostCard/BountyCard/BountyCard.tsx | 5 +- .../PostCard/QuestionCard/QuestionCard.tsx | 5 +- .../PostCard/StoryCard/StoryCard.tsx | 5 +- .../BountyPageContent/BountyPageContent.tsx | 5 +- .../Components/PostActions/PostActions.tsx | 13 ++-- .../pages/PostDetailsPage/PostDetailsPage.tsx | 1 - src/utils/hooks/usePressHolder.ts | 7 ++- 10 files changed, 127 insertions(+), 38 deletions(-) diff --git a/src/Components/VoteButton/VoteButton.stories.tsx b/src/Components/VoteButton/VoteButton.stories.tsx index 2ca2224..f0290cd 100644 --- a/src/Components/VoteButton/VoteButton.stories.tsx +++ b/src/Components/VoteButton/VoteButton.stories.tsx @@ -19,4 +19,60 @@ export const Default = Template.bind({}); Default.args = { initVotes: 540, onVote: () => { } +} + +export const Vertical = Template.bind({}); +Vertical.args = { + initVotes: 540, + onVote: () => { }, + direction: 'vertical' +} + +export const Dense = Template.bind({}); +Dense.args = { + initVotes: 540, + onVote: () => { }, + dense: true +} + +export const FillTypeUpdown = Template.bind({}); +FillTypeUpdown.args = { + initVotes: 540, + onVote: () => { }, + fillType: 'upDown' +} + +export const FillTypeBackground = Template.bind({}); +FillTypeBackground.args = { + initVotes: 540, + onVote: () => { }, + fillType: 'background' +} + +export const FillTypeRadial = Template.bind({}); +FillTypeRadial.args = { + initVotes: 540, + onVote: () => { }, + fillType: 'radial' +} + +export const NoCounter = Template.bind({}); +NoCounter.args = { + initVotes: 540, + onVote: () => { }, + disableCounter: true, +} + +export const CounterReset = Template.bind({}); +CounterReset.args = { + initVotes: 540, + onVote: () => { }, + resetCounterOnRelease: true +} + +export const NoShake = Template.bind({}); +NoShake.args = { + initVotes: 540, + onVote: () => { }, + disableShake: true, } \ No newline at end of file diff --git a/src/Components/VoteButton/VoteButton.tsx b/src/Components/VoteButton/VoteButton.tsx index dfc442a..006f85b 100644 --- a/src/Components/VoteButton/VoteButton.tsx +++ b/src/Components/VoteButton/VoteButton.tsx @@ -3,10 +3,11 @@ import Button from 'src/Components/Button/Button' import { useAppSelector, usePressHolder } from 'src/utils/hooks' import { ComponentProps, useRef, useState } from 'react' import styles from './styles.module.css' -import { random, randomItem } from 'src/utils/helperFunctions' +import { random, randomItem, numberFormatter } from 'src/utils/helperFunctions' import { useDebouncedCallback, useThrottledCallback } from '@react-hookz/web' + interface Particle { id: string, offsetX: number, @@ -19,18 +20,24 @@ interface Particle { type Props = { initVotes: number, - onVote: (Vote: number) => void, - fillType: 'leftRight' | 'upDown' | "background" | 'radial' + onVote?: (Vote: number) => void, + fillType?: 'leftRight' | 'upDown' | "background" | 'radial', + direction?: 'horizontal' | 'vertical' disableCounter?: boolean disableShake?: boolean + dense?: boolean + resetCounterOnRelease?: boolean } & Omit, 'children'> export default function VoteButton({ - disableCounter = false, - disableShake = false, - fillType = 'leftRight', initVotes, onVote = () => { }, + fillType = 'leftRight', + direction = 'horizontal', + disableCounter = false, + disableShake = false, + dense = false, + resetCounterOnRelease: resetCounterOnReleaseProp = false, ...props }: Props) { const [voteCnt, setVoteCnt] = useState(0) const voteCntRef = useRef(0); @@ -39,12 +46,15 @@ export default function VoteButton({ const [sparks, setSparks] = useState([]); const [wasActive, setWasActive] = useState(false); const [incrementsCount, setIncrementsCount] = useState(0); - const incrementsCountRef = useRef(0); + const totalIncrementsCountRef = useRef(0) + const currentIncrementsCountRef = useRef(0); const [increments, setIncrements] = useState>([]) const isMobileScreen = useAppSelector(s => s.ui.isMobileScreen); + const resetCounterOnRelease = resetCounterOnReleaseProp; + const doVote = useDebouncedCallback(() => { onVote(voteCntRef.current); voteCntRef.current = 0; @@ -59,9 +69,12 @@ export default function VoteButton({ setBtnShakeClass(s => s === styles.clicked_2 ? styles.clicked_1 : styles.clicked_2) - const _incStep = Math.ceil((incrementsCountRef.current + 1) / 5); - incrementsCountRef.current += 1; - setIncrementsCount(v => incrementsCountRef.current); + const _incStep = Math.ceil((currentIncrementsCountRef.current + 1) / 5); + + currentIncrementsCountRef.current += 1; + totalIncrementsCountRef.current += 1; + + setIncrementsCount(v => totalIncrementsCountRef.current); if (!disableCounter) setIncrements(v => { @@ -78,7 +91,7 @@ export default function VoteButton({ return newValue; }) - if (incrementsCountRef.current && incrementsCountRef.current % 5 === 0) { + if (totalIncrementsCountRef.current && totalIncrementsCountRef.current % 5 === 0) { const newSparks = Array(5).fill(0).map((_, idx) => ({ id: (Math.random() + 1).toString(), offsetX: random(-10, 99), @@ -104,7 +117,7 @@ export default function VoteButton({ const onHold = useThrottledCallback(clickIncrement, [], 150) - const { onPressDown, onPressUp } = usePressHolder(onHold, 100); + const { onPressDown, onPressUp, isHolding } = usePressHolder(onHold, 100); const handlePressDown = () => { setWasActive(true); @@ -119,6 +132,12 @@ export default function VoteButton({ onPressUp(); onHold(); + if (resetCounterOnRelease) + if (!isHolding) { + currentIncrementsCountRef.current = 0; + } else + setTimeout(() => + currentIncrementsCountRef.current = 0, 150) } return ( @@ -145,7 +164,11 @@ export default function VoteButton({ ref={btnContainerRef} className={` ${styles.btn_content} - relative p-10 rounded-lg text-gray-600 bg-white hover:bg-gray-50 + relative rounded-lg text-gray-600 ${!incrementsCount && 'bg-gray-50 hover:bg-gray-100'} + ${direction === 'vertical' ? + dense ? "py-4 px-12" : "py-8 px-20" + : + dense ? "py-4 px-8" : "p-8"} ${incrementsCount && "outline"} active:outline outline-1 outline-red-500 ${btnShakeClass} `} @@ -161,8 +184,15 @@ export default function VoteButton({ `} > -
- {initVotes + voteCnt} +
+ {numberFormatter(initVotes + voteCnt)}
{increments.map(increment =>
-
- +
+
{bounty.applicants_count} Applicants
diff --git a/src/features/Posts/Components/PostCard/QuestionCard/QuestionCard.tsx b/src/features/Posts/Components/PostCard/QuestionCard/QuestionCard.tsx index 5ecd680..b09a584 100644 --- a/src/features/Posts/Components/PostCard/QuestionCard/QuestionCard.tsx +++ b/src/features/Posts/Components/PostCard/QuestionCard/QuestionCard.tsx @@ -5,6 +5,7 @@ import { FiUsers } from "react-icons/fi" import Badge from "src/Components/Badge/Badge" import { Link } from "react-router-dom" import { trimText } from "src/utils/helperFunctions" +import VoteButton from "src/Components/VoteButton/VoteButton" export type QuestionCardType = Pick
-
- +
+
{question.answers_count} Answers
diff --git a/src/features/Posts/Components/PostCard/StoryCard/StoryCard.tsx b/src/features/Posts/Components/PostCard/StoryCard/StoryCard.tsx index 576669f..cbb9d5e 100644 --- a/src/features/Posts/Components/PostCard/StoryCard/StoryCard.tsx +++ b/src/features/Posts/Components/PostCard/StoryCard/StoryCard.tsx @@ -3,6 +3,7 @@ import { Story } from "src/features/Posts/types" import Header from "../Header/Header" import { BiComment } from 'react-icons/bi' import { Link } from "react-router-dom" +import VoteButton from "src/Components/VoteButton/VoteButton" export type StoryCardType = Pick{story.excerpt}


-
- +
+
{story.comments_count} Comments
diff --git a/src/features/Posts/pages/PostDetailsPage/Components/BountyPageContent/BountyPageContent.tsx b/src/features/Posts/pages/PostDetailsPage/Components/BountyPageContent/BountyPageContent.tsx index ef50742..87f3d80 100644 --- a/src/features/Posts/pages/PostDetailsPage/Components/BountyPageContent/BountyPageContent.tsx +++ b/src/features/Posts/pages/PostDetailsPage/Components/BountyPageContent/BountyPageContent.tsx @@ -8,6 +8,7 @@ import VotesCount from "src/Components/VotesCount/VotesCount"; import Button from "src/Components/Button/Button"; import { FiGithub, FiShare2 } from "react-icons/fi"; import BountyApplicants from "./BountyApplicants"; +import VoteButton from "src/Components/VoteButton/VoteButton"; interface Props { @@ -26,8 +27,8 @@ export default function BountyPageContent({ bounty }: Props) { Reward: {bounty.reward_amount} sats
-
- +
+
32 Comments
diff --git a/src/features/Posts/pages/PostDetailsPage/Components/PostActions/PostActions.tsx b/src/features/Posts/pages/PostDetailsPage/Components/PostActions/PostActions.tsx index 66e3aec..0ce906e 100644 --- a/src/features/Posts/pages/PostDetailsPage/Components/PostActions/PostActions.tsx +++ b/src/features/Posts/pages/PostDetailsPage/Components/PostActions/PostActions.tsx @@ -1,16 +1,12 @@ import { BsBookmark } from "react-icons/bs" import { MdIosShare, MdLocalFireDepartment } from "react-icons/md" +import VoteButton from "src/Components/VoteButton/VoteButton" export default function PostActions() { const actions = [ - { - icon: MdLocalFireDepartment, - value: 123, - classes: '' - }, { icon: BsBookmark, value: 27 @@ -23,10 +19,11 @@ export default function PostActions() { return (
    + {actions.map((action, idx) =>
  • - + className={`py-8 px-20 text-body5 flex flex-col justify-center items-center cursor-pointer rounded-8 + ${'text-gray-500 hover:bg-gray-50 active:bg-gray-100'}`}> + {action.value}
  • )}
diff --git a/src/features/Posts/pages/PostDetailsPage/PostDetailsPage.tsx b/src/features/Posts/pages/PostDetailsPage/PostDetailsPage.tsx index f6375dc..33e7436 100644 --- a/src/features/Posts/pages/PostDetailsPage/PostDetailsPage.tsx +++ b/src/features/Posts/pages/PostDetailsPage/PostDetailsPage.tsx @@ -43,7 +43,6 @@ export default function PostDetailsPage() { style={{ top: `${navHeight + 16}px`, maxHeight: `calc(100vh - ${navHeight}px - 16px)`, - overflowY: "scroll", }}>
diff --git a/src/utils/hooks/usePressHolder.ts b/src/utils/hooks/usePressHolder.ts index f017586..4623d82 100644 --- a/src/utils/hooks/usePressHolder.ts +++ b/src/utils/hooks/usePressHolder.ts @@ -1,4 +1,4 @@ -import { useRef } from "react"; +import { useRef, useState } from "react"; export const usePressHolder = (onHold: () => any, holdThreshold: number = 400) => { @@ -7,6 +7,7 @@ export const usePressHolder = (onHold: () => any, holdThreshold: number = 400) = timerID: 0, previousTimestamp: -1 }); + const [isHolding, setIsHolding] = useState(false) const onPressDown = () => { ref.current.timerID = requestAnimationFrame(timer) @@ -18,6 +19,7 @@ export const usePressHolder = (onHold: () => any, holdThreshold: number = 400) = cancelAnimationFrame(ref.current.timerID); ref.current.cntr = 0; ref.current.previousTimestamp = -1; + setIsHolding(false) } function timer(timestamp: number) { @@ -30,11 +32,12 @@ export const usePressHolder = (onHold: () => any, holdThreshold: number = 400) = ref.current.cntr += dt; } else { onHold(); + setIsHolding(true) } ref.current.timerID = requestAnimationFrame(timer); } - return { onPressUp, onPressDown } + return { onPressUp, onPressDown, isHolding } } \ No newline at end of file