mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-01-25 09:14:34 +01:00
update: make some improvements to the vote-btn, remove wrapper card for post-details page on mobile
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { MdLocalFireDepartment } from 'react-icons/md'
|
||||
import Button from 'src/Components/Button/Button'
|
||||
import { useAppSelector, usePressHolder, useResizeListener } from 'src/utils/hooks'
|
||||
import { ComponentProps, useRef, useState } from 'react'
|
||||
import { ComponentProps, SyntheticEvent, useRef, useState } from 'react'
|
||||
import styles from './styles.module.scss'
|
||||
import { random, randomItem, numberFormatter } from 'src/utils/helperFunctions'
|
||||
import { useDebouncedCallback, useMountEffect, useThrottledCallback } from '@react-hookz/web'
|
||||
@@ -58,9 +58,9 @@ export default function VoteButton({
|
||||
fillType = 'leftRight',
|
||||
direction = 'horizontal',
|
||||
disableCounter = false,
|
||||
disableShake = false,
|
||||
disableShake = true,
|
||||
dense = false,
|
||||
resetCounterOnRelease: resetCounterOnReleaseProp = false,
|
||||
resetCounterOnRelease = true,
|
||||
...props }: Props) {
|
||||
const [voteCnt, setVoteCnt] = useState(0)
|
||||
const voteCntRef = useRef(0);
|
||||
@@ -76,22 +76,52 @@ export default function VoteButton({
|
||||
const [btnState, setBtnState] = useState<BtnState>('ready');
|
||||
|
||||
const isMobileScreen = useAppSelector(s => s.ui.isMobileScreen);
|
||||
const resetCounterOnRelease = resetCounterOnReleaseProp;
|
||||
|
||||
const doVote = useDebouncedCallback(() => {
|
||||
setBtnState('loading');
|
||||
const amount = voteCntRef.current;
|
||||
onVote(amount, {
|
||||
onSuccess: () => setBtnState("success"),
|
||||
onSuccess: () => {
|
||||
setBtnState("success");
|
||||
spawnSparks(10);
|
||||
},
|
||||
onError: () => setBtnState('fail'),
|
||||
onSetteled: () => {
|
||||
setVoteCnt(v => v - amount);
|
||||
setTimeout(() => setBtnState("ready"), 2000);
|
||||
setTimeout(() => {
|
||||
setBtnState("ready")
|
||||
if (resetCounterOnRelease) {
|
||||
setIncrementsCount(0);
|
||||
totalIncrementsCountRef.current = 0;
|
||||
currentIncrementsCountRef.current = 0;
|
||||
}
|
||||
voteCntRef.current = 0;
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
voteCntRef.current = 0;
|
||||
|
||||
}, [], 1500)
|
||||
}, [], 1500);
|
||||
|
||||
const spawnSparks = (cnt = 5) => {
|
||||
const newSparks = Array(cnt).fill(0).map((_, idx) => ({
|
||||
id: (Math.random() + 1).toString(),
|
||||
offsetX: random(-10, 99),
|
||||
offsetY: random(10, 90),
|
||||
animation: randomItem(styles.fly_spark_1, styles.fly_spark_1),
|
||||
animationSpeed: randomItem(1, 1.5, 2),
|
||||
color: `hsl(0deg 86% ${random(50, 63)}%)`,
|
||||
scale: random(1, 1.5)
|
||||
}))
|
||||
|
||||
// if on mobile screen, reduce number of sparks particles to 60%
|
||||
setSparks(oldSparks => [...oldSparks, ...newSparks])
|
||||
setTimeout(() => {
|
||||
setSparks(s => {
|
||||
return s.filter(spark => !newSparks.some(newSpark => newSpark.id === spark.id))
|
||||
})
|
||||
|
||||
}, 2 * 1000)
|
||||
}
|
||||
|
||||
const clickIncrement = () => {
|
||||
if (!disableShake)
|
||||
@@ -120,33 +150,17 @@ export default function VoteButton({
|
||||
return newValue;
|
||||
})
|
||||
|
||||
if (totalIncrementsCountRef.current && totalIncrementsCountRef.current % 5 === 0) {
|
||||
const newSparks = Array(5).fill(0).map((_, idx) => ({
|
||||
id: (Math.random() + 1).toString(),
|
||||
offsetX: random(-10, 99),
|
||||
offsetY: random(40, 90),
|
||||
animation: randomItem(styles.fly_spark_1, styles.fly_spark_1),
|
||||
animationSpeed: randomItem(1, 1.5, 2),
|
||||
color: `hsl(0deg 86% ${random(50, 63)}%)`,
|
||||
scale: random(1, 1.5)
|
||||
}))
|
||||
// Each time the button make 5 increments, spawn some flames
|
||||
if (totalIncrementsCountRef.current && totalIncrementsCountRef.current % 5 === 0)
|
||||
spawnSparks(5);
|
||||
|
||||
// if on mobile screen, reduce number of sparks particles to 60%
|
||||
setSparks(oldSparks => [...oldSparks, ...newSparks])
|
||||
setTimeout(() => {
|
||||
setSparks(s => {
|
||||
return s.filter(spark => !newSparks.some(newSpark => newSpark.id === spark.id))
|
||||
})
|
||||
|
||||
}, 2 * 1000)
|
||||
}
|
||||
|
||||
doVote();
|
||||
}
|
||||
|
||||
const onHold = useThrottledCallback(clickIncrement, [], 150)
|
||||
|
||||
const { onPressDown, onPressUp, isHolding } = usePressHolder(onHold, 100);
|
||||
const { onPressDown, onPressUp } = usePressHolder(onHold, 200);
|
||||
|
||||
const handlePressDown = () => {
|
||||
if (btnState !== 'ready' && btnState !== 'voting') return;
|
||||
@@ -155,19 +169,13 @@ export default function VoteButton({
|
||||
onPressDown();
|
||||
}
|
||||
|
||||
const handlePressUp = (event?: any) => {
|
||||
const handlePressUp = (event?: SyntheticEvent) => {
|
||||
if (btnState !== 'voting') return;
|
||||
|
||||
if (event?.preventDefault) event.preventDefault();
|
||||
|
||||
onPressUp();
|
||||
onHold();
|
||||
if (resetCounterOnRelease)
|
||||
if (!isHolding) {
|
||||
currentIncrementsCountRef.current = 0;
|
||||
} else
|
||||
setTimeout(() =>
|
||||
currentIncrementsCountRef.current = 0, 150)
|
||||
}
|
||||
|
||||
useMountEffect(() => {
|
||||
@@ -197,7 +205,7 @@ export default function VoteButton({
|
||||
<button
|
||||
onMouseDown={handlePressDown}
|
||||
onMouseUp={handlePressUp}
|
||||
onMouseLeave={handlePressUp}
|
||||
onMouseLeave={() => onPressUp()}
|
||||
onTouchStart={handlePressDown}
|
||||
onTouchEnd={handlePressUp}
|
||||
|
||||
@@ -264,7 +272,7 @@ export default function VoteButton({
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className={styles.success}>
|
||||
Thanks!!
|
||||
+{numberFormatter(voteCntRef.current)}
|
||||
</motion.div>
|
||||
}
|
||||
</AnimatePresence>
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
--bg-color: hsl(0deg 86% max(calc((93 - var(--increments) / 3) * 1%), 68%));
|
||||
/* transition: background-color 1s; */
|
||||
/* background-color: hsl(25, 100%, max(calc((95 - var(--scale) / 4) * 1%), 63%)); */
|
||||
|
||||
transition: transform 0.1s ease-out;
|
||||
&:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.btn_content.clicked_1 {
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function StoryPageContent({ story }: Props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id="content" className="bg-white p-32 border-2 border-gray-200 rounded-16 relative">
|
||||
<div id="content" className="bg-white md:p-32 md:border-2 border-gray-200 rounded-16 relative">
|
||||
|
||||
|
||||
{story.cover_image &&
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
$screen-xs-min: 320px;
|
||||
|
||||
@import "./tw.scss", "./shared.scss", "./vendors.scss", "./scrollbar.scss";
|
||||
@import "/src/styles/mixins/index.scss";
|
||||
|
||||
html {
|
||||
scrollbar-gutter: stable;
|
||||
@@ -14,7 +15,12 @@ body {
|
||||
}
|
||||
|
||||
.page-container {
|
||||
width: calc(min(100% - 32px, 1440px));
|
||||
--padding: 16px;
|
||||
|
||||
@include gt-sm {
|
||||
--padding: 24px;
|
||||
}
|
||||
width: calc(min(100%, 1440px) - 2 * var(--padding));
|
||||
margin: 0 auto;
|
||||
padding: 32px 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user