import { DetailedHTMLProps, HTMLAttributes, MouseEvent, ReactNode, UIEvent } from 'react' import VanillaTilt, { TiltOptions } from 'vanilla-tilt' import protocolCheck from 'custom-protocol-check' import Icon from '@ant-design/icons' import '@/assets/css/pages/tools/store.scss' import { COLOR_BACKGROUND, COLOR_MAIN, COLOR_PRODUCTION, DATABASE_SELECT_SUCCESS } from '@/constants/common.constants' import { checkDesktop } from '@/util/common' import { navigateToSource, navigateToStore, navigateToView } from '@/util/navigation' import { r_tool_add_favorite, r_tool_remove_favorite, r_tool_store_get } from '@/services/tool' import Card from '@/components/common/Card' import FlexBox from '@/components/common/FlexBox' import FitFullscreen from '@/components/common/FitFullscreen' import HideScrollbar from '@/components/common/HideScrollbar' interface CommonCardProps extends DetailedHTMLProps, HTMLDivElement> { icon: ReactNode toolName: string toolId: string toolDesc: string options?: TiltOptions authorName: string authorAvatar: string authorUsername: string ver: string platform: Platform supportPlatform: Platform[] favorite: boolean } const CommonCard = ({ style, // eslint-disable-next-line @typescript-eslint/no-unused-vars ref, icon, toolName, toolId, toolDesc, options = { reverse: true, max: 8, glare: true, ['max-glare']: 0.3, scale: 1.03 }, authorName, authorAvatar, authorUsername, ver, platform, supportPlatform, favorite, ...props }: CommonCardProps) => { const navigate = useNavigate() const [modal, contextHolder] = AntdModal.useModal() const cardRef = useRef(null) const [favorite_, setFavorite_] = useState(favorite) useEffect(() => { cardRef.current && VanillaTilt.init(cardRef.current, options) }, [options]) const handleCardOnClick = () => { if (!checkDesktop() && platform === 'DESKTOP') { void message.warning('此应用需要桌面端环境,请在桌面端打开') return } if (platform === 'ANDROID') { void modal.info({ icon: , title: 'Android 端', centered: true, maskClosable: true, content: ( 请使用手机端扫描上方二维码 ) }) return } navigateToView(navigate, authorUsername, toolId, platform) } const handleOnClickAuthor = (e: MouseEvent) => { e.stopPropagation() navigateToStore(navigate, authorUsername) } const handleOnSourceBtnClick = (e: MouseEvent) => { e.stopPropagation() navigateToSource(navigate, authorUsername, toolId, platform) } const handleOnStarBtnClick = (e: MouseEvent) => { e.stopPropagation() if (favorite_) { void r_tool_remove_favorite({ username: authorUsername, toolId: toolId, platform: platform }).then((res) => { const response = res.data if (response.success) { setFavorite_(false) } else { void message.error('取消收藏失败,请稍后重试') } }) } else { void r_tool_add_favorite({ username: authorUsername, toolId: toolId, platform: platform }).then((res) => { const response = res.data if (response.success) { setFavorite_(true) } else { void message.error('收藏失败,请稍后重试') } }) } } const handleOnAndroidBtnClick = (e: MouseEvent) => { e.stopPropagation() void modal.info({ icon: , title: 'Android 端', centered: true, maskClosable: true, content: ( 请使用手机端扫描上方二维码 ) }) } const handleOnDesktopBtnClick = (e: MouseEvent) => { e.stopPropagation() if (!checkDesktop()) { void message.loading({ content: '启动桌面端中……', key: 'LOADING', duration: 0 }) protocolCheck( `oxygen://openurl/view/${authorUsername}/${toolId}`, () => { void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试') void message.destroy('LOADING') }, () => { void message.destroy('LOADING') }, 2000, () => { void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试') void message.destroy('LOADING') } ) return } navigateToView(navigate, authorUsername, toolId, platform) } const handleOnWebBtnClick = (e: MouseEvent) => { e.stopPropagation() navigateToView(navigate, authorUsername, toolId, platform) } return ( <> {icon} {platform.slice(0, 1)}-{ver} {toolName} {`ID: ${toolId}`} {toolDesc && {`简介:${toolDesc}`}} } style={{ background: COLOR_BACKGROUND }} /> {authorName} {platform !== 'ANDROID' && supportPlatform.includes('ANDROID') && ( )} {platform === 'DESKTOP' && supportPlatform.includes('WEB') && ( )} {platform === 'WEB' && supportPlatform.includes('DESKTOP') && ( )} {contextHolder} > ) } interface LoadMoreCardProps { onClick: () => void } const LoadMoreCard = ({ onClick }: LoadMoreCardProps) => { const cardRef = useRef(null) useEffect(() => { cardRef.current && VanillaTilt.init(cardRef.current, { reverse: true, max: 8, glare: true, ['max-glare']: 0.3, scale: 1.03 }) }, []) return ( {' '} 加载更多 ) } const Store = () => { const scrollTopRef = useRef(0) const [isLoading, setIsLoading] = useState(false) const [currentPage, setCurrentPage] = useState(0) const [hasNextPage, setHasNextPage] = useState(false) const [toolData, setToolData] = useState([]) const [hideSearch, setHideSearch] = useState(false) const [searchValue, setSearchValue] = useState('') const handleOnSearch = (value: string) => { setSearchValue(value) getTool(1, value) } const handleOnScroll = (event: UIEvent) => { if (event.currentTarget.scrollTop < scrollTopRef.current) { setHideSearch(false) } else { setHideSearch(true) } scrollTopRef.current = event.currentTarget.scrollTop } const handleOnLoadMore = () => { if (isLoading) { return } getTool(currentPage + 1, searchValue) } const getTool = (page: number, searchValue: string) => { if (isLoading) { return } setIsLoading(true) void message.loading({ content: '加载工具列表中', key: 'LOADING', duration: 0 }) void r_tool_store_get({ currentPage: page, searchValue }) .then((res) => { const response = res.data switch (response.code) { case DATABASE_SELECT_SUCCESS: setCurrentPage(response.data!.current) if ( response.data!.current === response.data!.pages || response.data!.total === 0 ) { setHasNextPage(false) } else { setHasNextPage(true) } if (response.data!.current === 1) { setToolData(response.data!.records) } else { setToolData([...toolData, ...response.data!.records]) } break default: void message.error('加载失败,请稍后重试') } }) .finally(() => { setIsLoading(false) message.destroy('LOADING') }) } useEffect(() => { getTool(1, '') }, []) return ( <> {!toolData.length && 未找到任何工具} {toolData ?.reduce((previousValue: ToolVo[], currentValue) => { if ( !previousValue.some( (value) => value.author.id === currentValue.author.id && value.toolId === currentValue.toolId ) ) { previousValue.push(currentValue) } return previousValue }, []) .map((item) => { const tools = toolData.filter( (value) => value.author.id === item.author.id && value.toolId === item.toolId ) const webTool = tools.find((value) => value.platform === 'WEB') const desktopTool = tools.find( (value) => value.platform === 'DESKTOP' ) const androidTool = tools.find( (value) => value.platform === 'ANDROID' ) const firstTool = (checkDesktop() ? desktopTool || webTool : webTool || desktopTool) || androidTool return ( } toolName={firstTool!.name} toolId={firstTool!.toolId} toolDesc={firstTool!.description} authorName={firstTool!.author.userInfo.nickname} authorAvatar={firstTool!.author.userInfo.avatar} authorUsername={firstTool!.author.username} ver={firstTool!.ver} platform={firstTool!.platform} supportPlatform={tools.map((value) => value.platform)} favorite={firstTool!.favorite} /> ) })} {hasNextPage && } > ) } export default Store