Skip to content
59 changes: 27 additions & 32 deletions lib/components/ScratchOffCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect, useRef, useState, memo } from 'react';
import styled from 'styled-components';
import brushImg from './brush.jpg';
import {
getAngleBetween,
getDistanceBetween,
Expand All @@ -9,6 +8,8 @@
} from './utils';

const DEFAULT_REVEAL_PERCENTAGE = 50;
const BRUSH_IMG =
'data:image/jpeg;base64,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';

const StyledScratchOffCard = styled.div<{ width: number; height: number }>`
position: relative;
Expand Down Expand Up @@ -39,7 +40,7 @@
visibility: hidden;
`;

interface Props {
export interface ScratchOffCardProps {
revealPercentage?: number;
width: number;
height: number;
Expand All @@ -48,7 +49,7 @@
handleReveal: () => void;
}

const ScratchOffCard: React.FC<Props> = ({
export const ScratchOffCard: React.FC<ScratchOffCardProps> = ({

Check notice on line 52 in lib/components/ScratchOffCard/index.tsx

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

lib/components/ScratchOffCard/index.tsx#L52

Function component is not a function declaration

Choose a reason for hiding this comment

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

ℹ️ Codacy found a minor Code Style issue: Function component is not a function declaration

The ESLint issue indicates that the linter prefers function declarations over function expressions for defining React components. This is a stylistic preference that can help with readability and consistency in codebases.

To resolve this issue, you can change the function component from a function expression to a function declaration. Here's the suggested code change:

Suggested change
export const ScratchOffCard: React.FC<ScratchOffCardProps> = ({
export function ScratchOffCard({ revealPercentage = DEFAULT_REVEAL_PERCENTAGE, width, height, coverImgSrc, children, handleReveal }: ScratchOffCardProps) {

This comment was generated by an experimental AI tool.

revealPercentage = DEFAULT_REVEAL_PERCENTAGE,
width,
height,
Expand All @@ -60,27 +61,30 @@
const canvasRef = useRef<HTMLCanvasElement>(null);
const [isCoverImageReady, setIsCoverImageReady] = useState<boolean>(false);

const handleCoverImgOnLoad = () => {
canvasRef.current
?.getContext('2d')
.drawImage(coverImgRef.current, 0, 0, width, height);
setIsCoverImageReady(true);
};
useEffect(() => {
coverImgRef.current!.src = coverImgSrc;
coverImgRef.current!.onload = () => {
setIsCoverImageReady(true);
};
}, [coverImgSrc]);

useEffect(() => {
if (!isCoverImageReady) {
return;
}

let isDrawing: boolean;
let lastPoint: { x: number; y: number };
const canvas = canvasRef.current;
const ctx = canvasRef.current?.getContext('2d') as CanvasRenderingContext2D;
const image = coverImgRef.current as HTMLImageElement;
const ctx = canvas?.getContext('2d');
const brush = new Image();
brush.src = brushImg;

ctx.drawImage(image, 0, 0, width, height);
brush.src = BRUSH_IMG;
ctx!.drawImage(coverImgRef.current!, 0, 0, width, height);

const handleMouseDown = (e: MouseEvent | TouchEvent) => {
isDrawing = true;
lastPoint = getMouse(e, canvasRef.current);
lastPoint = getMouse(e, canvas);
};

const handleMouseMove = (e: MouseEvent | TouchEvent) => {
Expand All @@ -90,7 +94,7 @@

e.preventDefault();

const currentPoint = getMouse(e, canvasRef.current);
const currentPoint = getMouse(e, canvas);
const dist = getDistanceBetween(lastPoint, currentPoint);
const angle = getAngleBetween(lastPoint, currentPoint);
let x;
Expand All @@ -99,19 +103,16 @@
for (let i = 0; i < dist; i += 1) {
x = lastPoint.x + Math.sin(angle) * i - 25;
y = lastPoint.y + Math.cos(angle) * i - 25;
ctx.globalCompositeOperation = 'destination-out';
ctx.drawImage(brush, x, y);
ctx!.globalCompositeOperation = 'destination-out';
ctx!.drawImage(brush, x, y);
}

lastPoint = currentPoint;
const currentPercentage = getFilledInPixels(32, ctx, width, height);
const currentPercentage = getFilledInPixels(32, ctx!, width, height);

if (
currentPercentage > revealPercentage &&
canvasRef.current?.parentNode
) {
if (currentPercentage > revealPercentage && canvas?.parentNode) {
handleReveal();
canvasRef.current?.parentNode.removeChild(canvasRef.current);
canvas?.parentNode.removeChild(canvas);
}
};

Expand All @@ -134,23 +135,17 @@
canvas?.removeEventListener('mouseup', handleMouseUp, false);
canvas?.removeEventListener('touchend', handleMouseUp, false);
};
}, [handleReveal, revealPercentage, height, width]);
}, [handleReveal, revealPercentage, height, width, isCoverImageReady]);

return (
<StyledScratchOffCard width={width} height={height}>
<StyledCanvas ref={canvasRef} width={width} height={height} />
<StyledResultContainer isCoverImageReady={isCoverImageReady}>
{children}
</StyledResultContainer>
<StyledCoverImg
alt=""
ref={coverImgRef}
src={coverImgSrc}
onLoad={handleCoverImgOnLoad}
crossOrigin="anonymous"
/>
<StyledCoverImg alt="" ref={coverImgRef} crossOrigin="anonymous" />
</StyledScratchOffCard>
);
};

export default memo(ScratchOffCard);
export default ScratchOffCard;
12 changes: 6 additions & 6 deletions lib/components/TransitionLeaderboardWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React from 'react';
import React, { memo } from 'react';
import styled from 'styled-components';
import useItemTransition, { ItemStyle } from '../hooks/useItemTransition';
import { User } from '../types';

export interface Props {
export interface TransitionLeaderboardWrapperProps {
user: User[];
rowCount: number;
itemStyle: ItemStyle;
children: string | React.ReactNode;
}

const Wrapper = styled.div`
Expand All @@ -19,7 +20,7 @@
transition: 'all 0.5s ease 0.3s',
};

export const TransitionLeaderboardWrapper: React.FC<Props> = React.memo(
export const TransitionLeaderboardWrapper: React.FC<TransitionLeaderboardWrapperProps> =

Check notice on line 23 in lib/components/TransitionLeaderboardWrapper.tsx

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

lib/components/TransitionLeaderboardWrapper.tsx#L23

Replace `TransitionLeaderboardWrapperProps>·=⏎·` with `⏎··TransitionLeaderboardWrapperProps⏎>·=`

Choose a reason for hiding this comment

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

ℹ️ Codacy found a minor Code Style issue: Replace TransitionLeaderboardWrapperProps>·=⏎· with ⏎··TransitionLeaderboardWrapperProps⏎>·=

The issue reported by the ESLint linter is related to the formatting of the TypeScript code, specifically regarding the placement of the type annotation for the TransitionLeaderboardWrapper component. The linter suggests that the type should be placed on a new line to improve readability and adhere to the code style guidelines.

To fix the issue, you should move the type annotation TransitionLeaderboardWrapperProps to a new line before the assignment operator =.

Here’s the suggested change:

Suggested change
export const TransitionLeaderboardWrapper: React.FC<TransitionLeaderboardWrapperProps> =
export const TransitionLeaderboardWrapper: React.FC<TransitionLeaderboardWrapperProps> =

This should be changed to:

export const TransitionLeaderboardWrapper: React.FC<TransitionLeaderboardWrapperProps>
  =

This comment was generated by an experimental AI tool.

({ user, itemStyle, rowCount, children }) => {
const { itemTransitionStyle } = useItemTransition(
itemStyle,
Expand All @@ -45,7 +46,6 @@
}

return <Wrapper>{renderChild()}</Wrapper>;
},
);
};

Check notice on line 49 in lib/components/TransitionLeaderboardWrapper.tsx

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

lib/components/TransitionLeaderboardWrapper.tsx#L49

Delete `··`

Choose a reason for hiding this comment

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

Codacy has a fix for the issue: Delete ··

Suggested change
};
};


export default TransitionLeaderboardWrapper;
export default memo(TransitionLeaderboardWrapper);
6 changes: 3 additions & 3 deletions lib/components/VirtualizedList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable react/no-children-prop */
import React, { useEffect, useRef, useCallback, ReactNode } from 'react';
import React, { useEffect, useRef, useCallback, ReactNode, memo } from 'react';
import { VariableSizeList } from 'react-window';
import styled from 'styled-components';

Expand Down Expand Up @@ -57,7 +57,7 @@ export interface IVirtualizedListProps {
* 在 url 上加上 streamerUserID= 直播主的id,就會將畫面移動到直播主的位置
* @param IVirtualizedListProps 使用方法參考 IVirtualizedListProps 說明
*/
export const VirtualizedList: React.FC<IVirtualizedListProps> = ({
const VirtualizedList: React.FC<IVirtualizedListProps> = ({
dataset,
children,
itemHeight = 80,
Expand Down Expand Up @@ -142,4 +142,4 @@ export const VirtualizedList: React.FC<IVirtualizedListProps> = ({
);
};

export default VirtualizedList;
export default memo(VirtualizedList);
12 changes: 9 additions & 3 deletions lib/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { TransitionLeaderboardWrapper } from './TransitionLeaderboardWrapper';
import { VirtualizedList } from './VirtualizedList';
import TransitionLeaderboardWrapper from './TransitionLeaderboardWrapper';
import VirtualizedList from './VirtualizedList';
import ScratchOffCard from './ScratchOffCard';
Copy link
Contributor

Choose a reason for hiding this comment

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

這邊這樣寫的話,vmo-frontend應該沒辦用
import { ScratchOffCard } from 'vmo-lib';
這種方式引入,變得要宣告絕對路徑吧?

Copy link
Contributor

Choose a reason for hiding this comment

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


export * from './TransitionLeaderboardWrapper';
export * from './VirtualizedList';
export * from './ScratchOffCard';

export default { TransitionLeaderboardWrapper, VirtualizedList };
export default {
TransitionLeaderboardWrapper,
VirtualizedList,
ScratchOffCard,
};
2 changes: 1 addition & 1 deletion lib/template/offlineNormal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import styled from 'styled-components';
import usePageData from '../hooks/usePageData';
import { TransitionLeaderboardWrapper } from '../components/TransitionLeaderboardWrapper';
import TransitionLeaderboardWrapper from '../components/TransitionLeaderboardWrapper';
import { ItemStyle } from '../hooks/useItemTransition';
import { qs } from '../utils';

Expand Down
2 changes: 1 addition & 1 deletion playground/ScrollToStreamer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import styled from 'styled-components';
import { VirtualizedList } from '../lib/components/VirtualizedList';
import VirtualizedList from '../lib/components/VirtualizedList';
import fakeLeaderBoardData from './fakeLeaderBoardData.json';

const StyledSpace = styled.div`
Expand Down