| import React, { useCallback, useEffect, useState } from 'react';  | 
| import { Upload, Tooltip, Popover, Modal, Progress, Spin, Result } from 'antd';  | 
| import * as AntdIcons from '@ant-design/icons';  | 
| import { useIntl } from '@umijs/max';  | 
| import './style.less';  | 
|   | 
| const allIcons: { [key: string]: any } = AntdIcons;  | 
|   | 
| const { Dragger } = Upload;  | 
| interface AntdIconClassifier {  | 
|   load: () => void;  | 
|   predict: (imgEl: HTMLImageElement) => void;  | 
| }  | 
| declare global {  | 
|   interface Window {  | 
|     antdIconClassifier: AntdIconClassifier;  | 
|   }  | 
| }  | 
|   | 
| interface PicSearcherState {  | 
|   loading: boolean;  | 
|   modalOpen: boolean;  | 
|   popoverVisible: boolean;  | 
|   icons: iconObject[];  | 
|   fileList: any[];  | 
|   error: boolean;  | 
|   modelLoaded: boolean;  | 
| }  | 
|   | 
| interface iconObject {  | 
|   type: string;  | 
|   score: number;  | 
| }  | 
|   | 
| const PicSearcher: React.FC = () => {  | 
|   const intl = useIntl();  | 
|   const {formatMessage} = intl;  | 
|   const [state, setState] = useState<PicSearcherState>({  | 
|     loading: false,  | 
|     modalOpen: false,  | 
|     popoverVisible: false,  | 
|     icons: [],  | 
|     fileList: [],  | 
|     error: false,  | 
|     modelLoaded: false,  | 
|   });  | 
|   const predict = (imgEl: HTMLImageElement) => {  | 
|     try {  | 
|       let icons: any[] = window.antdIconClassifier.predict(imgEl);  | 
|       if (gtag && icons.length) {  | 
|         gtag('event', 'icon', {  | 
|           event_category: 'search-by-image',  | 
|           event_label: icons[0].className,  | 
|         });  | 
|       }  | 
|       icons = icons.map(i => ({ score: i.score, type: i.className.replace(/\s/g, '-') }));  | 
|       setState(prev => ({ ...prev, loading: false, error: false, icons }));  | 
|     } catch {  | 
|       setState(prev => ({ ...prev, loading: false, error: true }));  | 
|     }  | 
|   };  | 
|   // eslint-disable-next-line class-methods-use-this  | 
|   const toImage = (url: string) =>  | 
|     new Promise(resolve => {  | 
|       const img = new Image();  | 
|       img.setAttribute('crossOrigin', 'anonymous');  | 
|       img.src = url;  | 
|       img.onload = () => {  | 
|         resolve(img);  | 
|       };  | 
|     });  | 
|   | 
|   const uploadFile = useCallback((file: File) => {  | 
|     setState(prev => ({ ...prev, loading: true }));  | 
|     const reader = new FileReader();  | 
|     reader.onload = () => {  | 
|       toImage(reader.result as string).then(predict);  | 
|       setState(prev => ({  | 
|         ...prev,  | 
|         fileList: [{ uid: 1, name: file.name, status: 'done', url: reader.result }],  | 
|       }));  | 
|     };  | 
|     reader.readAsDataURL(file);  | 
|   }, []);  | 
|   | 
|   const onPaste = useCallback((event: ClipboardEvent) => {  | 
|     const items = event.clipboardData && event.clipboardData.items;  | 
|     let file = null;  | 
|     if (items && items.length) {  | 
|       for (let i = 0; i < items.length; i++) {  | 
|         if (items[i].type.includes('image')) {  | 
|           file = items[i].getAsFile();  | 
|           break;  | 
|         }  | 
|       }  | 
|     }  | 
|     if (file) {  | 
|       uploadFile(file);  | 
|     }  | 
|   }, []);  | 
|   const toggleModal = useCallback(() => {  | 
|     setState(prev => ({  | 
|       ...prev,  | 
|       modalOpen: !prev.modalOpen,  | 
|       popoverVisible: false,  | 
|       fileList: [],  | 
|       icons: [],  | 
|     }));  | 
|     if (!localStorage.getItem('disableIconTip')) {  | 
|       localStorage.setItem('disableIconTip', 'true');  | 
|     }  | 
|   }, []);  | 
|   | 
|   useEffect(() => {  | 
|     const script = document.createElement('script');  | 
|     script.onload = async () => {  | 
|       await window.antdIconClassifier.load();  | 
|       setState(prev => ({ ...prev, modelLoaded: true }));  | 
|       document.addEventListener('paste', onPaste);  | 
|     };  | 
|     script.src = 'https://cdn.jsdelivr.net/gh/lewis617/antd-icon-classifier@0.0/dist/main.js';  | 
|     document.head.appendChild(script);  | 
|     setState(prev => ({ ...prev, popoverVisible: !localStorage.getItem('disableIconTip') }));  | 
|     return () => {  | 
|       document.removeEventListener('paste', onPaste);  | 
|     };  | 
|   }, []);  | 
|   | 
|   return (  | 
|     <div className="iconPicSearcher">  | 
|       <Popover  | 
|         content={formatMessage({id: 'app.docs.components.icon.pic-searcher.intro'})}  | 
|         open={state.popoverVisible}  | 
|       >  | 
|         <AntdIcons.CameraOutlined className="icon-pic-btn" onClick={toggleModal} />  | 
|       </Popover>  | 
|       <Modal  | 
|         title={intl.formatMessage({  | 
|           id: 'app.docs.components.icon.pic-searcher.title',  | 
|           defaultMessage: '信息',  | 
|         })}  | 
|         open={state.modalOpen}  | 
|         onCancel={toggleModal}  | 
|         footer={null}  | 
|       >  | 
|         {state.modelLoaded || (  | 
|           <Spin  | 
|             spinning={!state.modelLoaded}  | 
|             tip={formatMessage({  | 
|               id: 'app.docs.components.icon.pic-searcher.modelloading',  | 
|   | 
|             })}  | 
|           >  | 
|             <div style={{ height: 100 }} />  | 
|           </Spin>  | 
|         )}  | 
|         {state.modelLoaded && (  | 
|           <Dragger  | 
|             accept="image/jpeg, image/png"  | 
|             listType="picture"  | 
|             customRequest={o => uploadFile(o.file as File)}  | 
|             fileList={state.fileList}  | 
|             showUploadList={{ showPreviewIcon: false, showRemoveIcon: false }}  | 
|           >  | 
|             <p className="ant-upload-drag-icon">  | 
|               <AntdIcons.InboxOutlined />  | 
|             </p>  | 
|             <p className="ant-upload-text">  | 
|               {formatMessage({id: 'app.docs.components.icon.pic-searcher.upload-text'})}  | 
|             </p>  | 
|             <p className="ant-upload-hint">  | 
|               {formatMessage({id: 'app.docs.components.icon.pic-searcher.upload-hint'})}  | 
|             </p>  | 
|           </Dragger>  | 
|         )}  | 
|         <Spin  | 
|           spinning={state.loading}  | 
|           tip={formatMessage({id: 'app.docs.components.icon.pic-searcher.matching'})}  | 
|         >  | 
|           <div className="icon-pic-search-result">  | 
|             {state.icons.length > 0 && (  | 
|               <div className="result-tip">  | 
|                 {formatMessage({id: 'app.docs.components.icon.pic-searcher.result-tip'})}  | 
|               </div>  | 
|             )}  | 
|             <table>  | 
|               {state.icons.length > 0 && (  | 
|                 <thead>  | 
|                   <tr>  | 
|                     <th className="col-icon">  | 
|                       {formatMessage({id: 'app.docs.components.icon.pic-searcher.th-icon'})}  | 
|                     </th>  | 
|                     <th>{formatMessage({id: 'app.docs.components.icon.pic-searcher.th-score'})}</th>  | 
|                   </tr>  | 
|                 </thead>  | 
|               )}  | 
|               <tbody>  | 
|                 {state.icons.map(icon => {  | 
|                   const { type } = icon;  | 
|                   const iconName = `${type  | 
|                     .split('-')  | 
|                     .map(str => `${str[0].toUpperCase()}${str.slice(1)}`)  | 
|                     .join('')}Outlined`;  | 
|                   return (  | 
|                     <tr key={iconName}>  | 
|                       <td className="col-icon">  | 
|                           <Tooltip title={icon.type} placement="right">  | 
|                             {React.createElement(allIcons[iconName])}  | 
|                           </Tooltip>  | 
|                       </td>  | 
|                       <td>  | 
|                         <Progress percent={Math.ceil(icon.score * 100)} />  | 
|                       </td>  | 
|                     </tr>  | 
|                   );  | 
|                 })}  | 
|               </tbody>  | 
|             </table>  | 
|             {state.error && (  | 
|               <Result  | 
|                 status="500"  | 
|                 title="503"  | 
|                 subTitle={formatMessage({id: 'app.docs.components.icon.pic-searcher.server-error'})}  | 
|               />  | 
|             )}  | 
|           </div>  | 
|         </Spin>  | 
|       </Modal>  | 
|     </div>  | 
|   );  | 
| };  | 
|   | 
| export default PicSearcher;  |