Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
91 changes: 81 additions & 10 deletions lib/hooks/useLuckyDraw.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import {
useWorkerParser,
usePlayerState,
usePlayback,
Canvas,
} from '@react-gifs/tools';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Codacy found a critical Error Prone issue: Unable to resolve path to module '@react-gifs/tools'.

The issue reported by the ESLint linter, "Unable to resolve path to module '@react-gifs/tools'", indicates that the module '@react-gifs/tools' cannot be found in the project's dependencies. This could be due to several reasons, such as the package not being installed, a typo in the module name, or the module being located in a different path.

To fix this issue, you should ensure that the module is correctly installed in your project. If it is indeed a valid package, you can install it using npm or yarn. However, if you are certain that the package is correctly named and installed, you may want to check your project's configuration or paths.

Assuming the package is not installed yet, the following code suggestion will help you install it:

Suggested change
} from '@react-gifs/tools';
npm install @react-gifs/tools

If the package is already installed and the error persists, you might need to check your tsconfig.json or module resolution settings.


This comment was generated by an experimental AI tool.

import { User } from '../types';
import { getRandomInteger, isBrowser, globalThis } from '../utils';

Expand Down Expand Up @@ -30,9 +36,18 @@ const StyledMaskDivInner = styled(maskDiv)`
}
`;

const StyledAnimationContainer = styled(maskDiv)`
.target-canvas {
position: relative;
width: 100%;
height: 100%;
background-color: #1b1818;
}
`;

type Props = (
allCandidates: User[],
willAutoDrawRemainCount?: Boolean,
willAutoDrawRemainCount?: boolean,
) => {
candidates: User[];
winners: User[];
Expand All @@ -41,19 +56,70 @@ type Props = (
clearWinners: () => void;
reset: () => void;
currentRound: number;
hasDraw: Boolean;
hasDraw: boolean;
MaskDiv: React.FC;
AnimationMask: any;
isAnimationPlaying: boolean;
/** must send it to update props of Animation */
setIsAnimationPlaying: React.Dispatch<React.SetStateAction<boolean>>;
};

function MaskDiv() {
return (
<>
<StyledMaskDivOuter />
<StyledMaskDivInner />
</>
);
}
type GifPlayerProps = {
src: string;
onStart?: () => void;
onEnded?: () => void;
};

type GifMaskProps = { src: string; update: (state: boolean) => void };

const MaskDiv = () => (
<>
<StyledMaskDivOuter />
<StyledMaskDivInner />
</>
);

const GifPlayer = ({
src,
onStart = () => {},
onEnded = () => {},
}: GifPlayerProps) => {
const FAILED_TO_FETCH = 'Failed to fetch';
const [state, update] = usePlayerState();
useWorkerParser(src, info => {
if ('error' in info && info.error.message === FAILED_TO_FETCH) {
console.warn(FAILED_TO_FETCH);
onEnded();
update(() => ({ autoPlay: false, playing: false }));
return;
}
onStart();
update(() => ({ ...info }));
});
usePlayback(state, () => {
if (state.length !== 0 && state.index === state.length - 1) {
onEnded();
return update(() => ({ playing: false }));
}
return update(({ index }) => ({ index: index + 1 }));
});

return <Canvas className="target-canvas" {...state} />;
};

const GifMask = ({ src, update }: GifMaskProps) => (
<StyledAnimationContainer>
<GifPlayer
src={src}
onStart={() => {
update(true);
}}
onEnded={() => {
update(false);
}}
/>
</StyledAnimationContainer>
);
/**
* useLuckyDraw - pass all candidates, use the draw function with number of round winners to get each round winners, remain candidates and allWinners.
* - Record by localstorage for custom feature. ex: key: 'http://localhost:9000/?page=2'(location href), value: allWinners<User[][]> (This feature can only use in client side and will not clear.)
Expand All @@ -67,6 +133,7 @@ export const useLuckyDraw: Props = (
const [allWinners, setAllWinners] = useState<User[][]>([]);
const [currentRound, setCurrentRound] = useState<number>(0);
const [hasDraw, setHasDraw] = useState<boolean>(false);
const [isAnimationPlaying, setIsAnimationPlaying] = useState<boolean>(false);

const draw = (drawCount: number) => {
if (!drawCount) {
Expand Down Expand Up @@ -156,6 +223,7 @@ export const useLuckyDraw: Props = (
return prevCandidates;
});
}, [allCandidates, currentRound]);

return {
candidates,
hasDraw,
Expand All @@ -166,6 +234,9 @@ export const useLuckyDraw: Props = (
reset,
currentRound,
MaskDiv,
AnimationMask: GifMask,
isAnimationPlaying,
setIsAnimationPlaying,
};
};
export default useLuckyDraw;
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vmo-lib",
"version": "2.1.0",
"version": "2.1.1",
"main": "lib/index.ts",
"module": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -12,6 +12,7 @@
"@babel/preset-env": "^7.14.2",
"@babel/preset-react": "^7.13.13",
"@babel/preset-typescript": "^7.13.0",
"@types/gh-pages": "^5.0.1",
"@types/jest": "^29.5.2",
"@types/react-window": "^1.8.5",
"@typescript-eslint/eslint-plugin": "^4.23.0",
Expand All @@ -20,7 +21,6 @@
"css-loader": "^5.2.4",
"eslint": "^7.26.0",
"eslint-config-airbnb": "^18.2.1",
"@types/gh-pages": "^5.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
Expand All @@ -35,19 +35,20 @@
"jest-environment-jsdom": "^29.5.0",
"lint-staged": "^13.2.2",
"prettier": "^2.3.0",
"sh-exec": "^2.1.0",
"style-loader": "^2.0.0",
"ts-jest": "^26.5.6",
"sh-exec": "^2.1.0",
"ts-node": "^10.9.1",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Codacy found a medium Security issue: Package dependencies with variant versions may lead to dependency hijack and confusion attacks.

The issue identified by the Semgrep linter relates to the use of a version range (indicated by the caret ^) for the ts-node package. Using variant versions allows for the possibility that a malicious actor could publish a compromised version of the package that could be installed automatically, leading to potential security vulnerabilities. To mitigate this risk, it's recommended to specify an exact version of the package.

To resolve this issue, you can change the version specification for ts-node to a fixed version. Here's the code suggestion:

Suggested change
"ts-node": "^10.9.1",
"ts-node": "10.9.1",

This comment was generated by an experimental AI tool.

"tsc-files": "^1.1.3",
"typescript": "5.0.4",
"webpack": "^5.37.0",
"webpack-cli": "^4.7.0",
"webpack-dev-server": "^3.11.2",
"ts-node": "^10.9.1"
"webpack-dev-server": "^3.11.2"
},
"dependencies": {
"@17media/dad": "github:17media/dad#latest",
"@babel/runtime": "^7.15.4",
"@react-gifs/tools": "^0.1.2",
"@sentry/react": "^7.21.1",
"@sentry/tracing": "^7.21.1",
"@testing-library/react": "^14.0.0",
Expand Down Expand Up @@ -92,9 +93,9 @@
]
},
"peerDependencies": {
"axios": ">=1.3.0",
"react": ">=18.2.0",
"react-dom": ">=18.2.0",
"axios": ">=1.3.0",
"styled-components": ">=5.3.9"
}
}
99 changes: 80 additions & 19 deletions playground/LuckyDraw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,25 @@ const Input = styled.input`
padding: 5px 10px;
`;

const RowContainer = styled.div`
position: relative;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
width: 100%;
& > * {
margin-bottom: 10px;
}
`;

const LuckyDraw = React.memo(() => {
const allCandidates: User[] = mockUsers.slice(0, 50);
const [drawCount, setDrawCount] = useState<number>(20);
const [willAutoDrawRemainCount, setwillAutoDrawRemainCount] =
useState<boolean>(true);
const [useAnimationMask, setUseAnimationMask] = useState<boolean>(false);
const [animationImageSrc, setAnimationImageSrc] = useState<string>('');
const {
MaskDiv,
hasDraw,
Expand All @@ -51,6 +65,9 @@ const LuckyDraw = React.memo(() => {
draw,
clearWinners,
reset,
AnimationMask,
isAnimationPlaying,
setIsAnimationPlaying,
} = useLuckyDraw(allCandidates, willAutoDrawRemainCount);
const href = window.localStorage.getItem(window.location.href);
const recordAllWinners = href ? JSON.parse(href) : [];
Expand All @@ -68,34 +85,78 @@ const LuckyDraw = React.memo(() => {
e: React.ChangeEvent<HTMLInputElement>,
) => setwillAutoDrawRemainCount(e.target.checked);

const handleUseAnimationMask = (e: React.ChangeEvent<HTMLInputElement>) =>
setUseAnimationMask(e.target.checked);

const handleAnimationMaskImageSrc = (
e: React.ChangeEvent<HTMLInputElement>,
) => {
setAnimationImageSrc(e.target.value);
};

return (
<Wrapper>
<h1>抽獎 sample</h1>
<div>
<p>設定每輪開獎人數並開獎(依照 rank 排序)</p>
<Input
type="number"
value={drawCount}
placeholder="請輸入開獎人數"
onChange={handleWinnersCount}
/>
<label htmlFor="autoDrawRemainCount">
自動抽出剩餘人數
<input
id="autoDrawRemainCount"
type="checkbox"
checked={willAutoDrawRemainCount}
onChange={handleAutoDrawWithRemainCount}
<RowContainer>
<div>
<p>設定每輪開獎人數並開獎(依照 rank 排序)</p>
<Input
type="number"
value={drawCount}
placeholder="請輸入開獎人數"
onChange={handleWinnersCount}
/>
</label>
</div>
<p>(當參加者不足抽獎人數時, 是否抽出所有剩餘人數)</p>
<label htmlFor="autoDrawRemainCount">
自動抽出剩餘人數
<input
id="autoDrawRemainCount"
type="checkbox"
checked={willAutoDrawRemainCount}
onChange={handleAutoDrawWithRemainCount}
/>
</label>
<p>(當參加者不足抽獎人數時, 是否抽出所有剩餘人數)</p>
</div>
<div>
<label htmlFor="animationMask">
使用動畫過場(目前僅支援 .gif)
{/*
*/}
<input
id="animationMask"
type="checkbox"
checked={useAnimationMask}
onChange={handleUseAnimationMask}
/>
</label>
</div>
{useAnimationMask && (
<div>
<span>圖片連結:</span>
<Input
type="text"
value={animationImageSrc}
placeholder="請輸入圖片連結"
onChange={handleAnimationMaskImageSrc}
/>
</div>
)}
</RowContainer>
<div>
<Button onClick={handleDraw}>開獎</Button>
<Button onClick={handleClearWinners}>清空得獎者</Button>
<Button onClick={handleReset}>重新開始</Button>
</div>
{hasDraw && <MaskDiv />}
{(() => {
if (!useAnimationMask && hasDraw) return <MaskDiv />;
if (useAnimationMask && (hasDraw || isAnimationPlaying))
return (
<AnimationMask
src={animationImageSrc}
update={setIsAnimationPlaying}
/>
);
})()}
<LuckyDrawSection>
<Left>
<h2>參加者名單</h2>
Expand Down
19 changes: 19 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,13 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@react-gifs/tools@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@react-gifs/tools/-/tools-0.1.2.tgz#b2e2b03be1ba4d38d6d624c9d7f5b317f790fce3"
integrity sha512-KC4IXwoT/teTMlkLy8Qp6i/NlYx3kEJsPgRKlXwfTjjmhCLo2tCobnET2am0R7bB0KegDDTO7h10+NbBPRbs5A==
dependencies:
gifuct-js "^2.0.0"

"@sentry-internal/feedback@7.90.0":
version "7.90.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.90.0.tgz#053e36b4d01dd94c948e5eb6902f264c1d4436e6"
Expand Down Expand Up @@ -4514,6 +4521,13 @@ gh-pages@^5.0.0:
fs-extra "^8.1.0"
globby "^6.1.0"

gifuct-js@^2.0.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/gifuct-js/-/gifuct-js-2.1.2.tgz#06152437ba30ec914db8398bd838bd0fbc8a6ecd"
integrity sha512-rI2asw77u0mGgwhV3qA+OEgYqaDn5UNqgs+Bx0FGwSpuqfYn+Ir6RQY5ENNQ8SbIiG/m5gVa7CD5RriO4f4Lsg==
dependencies:
js-binary-schema-parser "^2.0.3"

glob-parent@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
Expand Down Expand Up @@ -5789,6 +5803,11 @@ jest@^29.5.0:
import-local "^3.0.2"
jest-cli "^29.7.0"

js-binary-schema-parser@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz#3d7848748e8586e63b34e8911b643f59cfb6396e"
integrity sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==

"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
Expand Down