added download button, downlaod handler and necessary utility functions (#85)
Co-authored-by: Patanjali Kumar <patanjali@oddup.com>
This commit is contained in:
		
							parent
							
								
									d30471f5a9
								
							
						
					
					
						commit
						a1a8ac42a6
					
				|  | @ -1,41 +1,71 @@ | ||||||
| import { FC, useState } from "react"; | import {FC, useState} from "react"; | ||||||
| import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; | import {Prism as SyntaxHighlighter} from "react-syntax-highlighter"; | ||||||
| import { oneDark, oneLight } from "react-syntax-highlighter/dist/cjs/styles/prism"; | import {oneDark, oneLight} from "react-syntax-highlighter/dist/cjs/styles/prism"; | ||||||
|  | import {IconDownload} from '@tabler/icons-react'; | ||||||
|  | import {generateRandomString, programmingLanguages} from '@/utils/app/data'; | ||||||
| 
 | 
 | ||||||
| interface Props { | interface Props { | ||||||
|   language: string; |     language: string; | ||||||
|   value: string; |     value: string; | ||||||
|   lightMode: "light" | "dark"; |     lightMode: "light" | "dark"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const CodeBlock: FC<Props> = ({ language, value, lightMode }) => { | export const CodeBlock: FC<Props> = ({language, value, lightMode}) => { | ||||||
|   const [buttonText, setButtonText] = useState("Copy code"); |     const [buttonText, setButtonText] = useState("Copy code"); | ||||||
| 
 | 
 | ||||||
|   const copyToClipboard = () => { |     const copyToClipboard = () => { | ||||||
|     navigator.clipboard.writeText(value).then(() => { |         navigator.clipboard.writeText(value).then(() => { | ||||||
|       setButtonText("Copied!"); |             setButtonText("Copied!"); | ||||||
| 
 | 
 | ||||||
|       setTimeout(() => { |             setTimeout(() => { | ||||||
|         setButtonText("Copy code"); |                 setButtonText("Copy code"); | ||||||
|       }, 2000); |             }, 2000); | ||||||
|     }); |         }); | ||||||
|   }; |     }; | ||||||
|  |     const downloadAsFile = () => { | ||||||
|  |         const fileExtension = programmingLanguages[language] || '.file'; | ||||||
|  |         const suggestedFileName = `file-${generateRandomString(3, true)}${fileExtension}`; | ||||||
|  |         const fileName = window.prompt('Enter file name', suggestedFileName); | ||||||
| 
 | 
 | ||||||
|   return ( |         if(!fileName){ | ||||||
|     <div className="relative text-[16px] pt-2"> |             // user pressed cancel on prompt
 | ||||||
|       <SyntaxHighlighter |             return; | ||||||
|         language={language} |         } | ||||||
|         style={lightMode === "light" ? oneLight : oneDark} |  | ||||||
|       > |  | ||||||
|         {value} |  | ||||||
|       </SyntaxHighlighter> |  | ||||||
| 
 | 
 | ||||||
|       <button |         const blob = new Blob([value], { type: "text/plain" }); | ||||||
|         className="absolute top-[-8px] right-[0px] text-white bg-none py-0.5 px-2 rounded focus:outline-none hover:bg-blue-700 text-xs" |         const url = URL.createObjectURL(blob); | ||||||
|         onClick={copyToClipboard} |         const link = document.createElement("a"); | ||||||
|       > |         link.download = fileName; | ||||||
|         {buttonText} |         link.href = url; | ||||||
|       </button> |         link.style.display = "none"; | ||||||
|     </div> |         document.body.appendChild(link); | ||||||
|   ); |         link.click(); | ||||||
|  |         document.body.removeChild(link); | ||||||
|  |         URL.revokeObjectURL(url); | ||||||
|  |     }; | ||||||
|  |     return ( | ||||||
|  |         <div className="relative text-[16px]"> | ||||||
|  |             <div className="flex items-center justify-end"> | ||||||
|  |                 <button | ||||||
|  |                     className="text-white bg-none py-0.5 px-2 rounded focus:outline-none hover:bg-blue-700 text-xs" | ||||||
|  |                     onClick={copyToClipboard} | ||||||
|  |                 > | ||||||
|  |                     {buttonText} | ||||||
|  |                 </button> | ||||||
|  |                 <button | ||||||
|  |                     className="text-white bg-none py-0.5 px-2 rounded focus:outline-none hover:bg-blue-700 text-xs" | ||||||
|  |                     onClick={downloadAsFile} | ||||||
|  |                 > | ||||||
|  |                     <IconDownload size={16}/> | ||||||
|  |                 </button> | ||||||
|  |             </div> | ||||||
|  | 
 | ||||||
|  |             <SyntaxHighlighter | ||||||
|  |                 language={language} | ||||||
|  |                 style={lightMode === "light" ? oneLight : oneDark} | ||||||
|  |             > | ||||||
|  |                 {value} | ||||||
|  |             </SyntaxHighlighter> | ||||||
|  |         </div> | ||||||
|  |     ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -21,3 +21,44 @@ export const importConversations = (conversations: Conversation[]) => { | ||||||
|   localStorage.setItem("conversationHistory", JSON.stringify(conversations)); |   localStorage.setItem("conversationHistory", JSON.stringify(conversations)); | ||||||
|   localStorage.setItem("selectedConversation", JSON.stringify(conversations[conversations.length - 1])); |   localStorage.setItem("selectedConversation", JSON.stringify(conversations[conversations.length - 1])); | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | interface languageMap { | ||||||
|  |   [key: string]: string | undefined | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const programmingLanguages: languageMap = { | ||||||
|  |   'javascript': '.js', | ||||||
|  |   'python': '.py', | ||||||
|  |   'java': '.java', | ||||||
|  |   'c': '.c', | ||||||
|  |   'cpp': '.cpp', | ||||||
|  |   'c++': '.cpp', | ||||||
|  |   'c#': '.cs', | ||||||
|  |   'ruby': '.rb', | ||||||
|  |   'php': '.php', | ||||||
|  |   'swift': '.swift', | ||||||
|  |   'objective-c': '.m', | ||||||
|  |   'kotlin': '.kt', | ||||||
|  |   'typescript': '.ts', | ||||||
|  |   'go': '.go', | ||||||
|  |   'perl': '.pl', | ||||||
|  |   'rust': '.rs', | ||||||
|  |   'scala': '.scala', | ||||||
|  |   'haskell': '.hs', | ||||||
|  |   'lua': '.lua', | ||||||
|  |   'shell': '.sh', | ||||||
|  |   'sql': '.sql', | ||||||
|  |   'html': '.html', | ||||||
|  |   'css': '.css' | ||||||
|  |   // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export const generateRandomString = (length: Number, lowercase=false) => { | ||||||
|  |   const chars = "ABCDEFGHJKLMNPQRSTUVWXY3456789"; // excluding similar looking characters like Z, 2, I, 1, O, 0
 | ||||||
|  |   let result = ""; | ||||||
|  |   for (let i = 0; i < length; i++) { | ||||||
|  |     result += chars.charAt(Math.floor(Math.random() * chars.length)); | ||||||
|  |   } | ||||||
|  |   return lowercase ? result.toLowerCase() : result; | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue