🏷️ Search for custom tailnet name offers with keywords.
at master 200 lines 5.9 kB view raw
1import { type FC, useEffect, useState } from 'react'; 2 3import AlertModal from '$components/AlertModal'; 4import EligibilityModal from '$components/EligibilityModal'; 5import Footer from '$components/Footer'; 6import LoadingOverlay from '$components/LoadingOverlay'; 7 8import { useEligibility } from '$hooks/useEligibility'; 9import { useInputValidation } from '$hooks/useInputValidation'; 10import { useModal } from '$hooks/useModal'; 11import { useStatus } from '$hooks/useStatus'; 12import { useTailnetNames } from '$hooks/useTailnetNames'; 13import { useTimer } from '$hooks/useTimer'; 14import { useTokens } from '$hooks/useTokens'; 15import { useWords } from '$hooks/useWords'; 16 17import MainScreen from '$screens/MainScreen'; 18import TailnetWordsScreen from '$screens/TailnetWords'; 19import TokenListScreen from '$screens/TokenList'; 20import WordListScreen from '$screens/WordList'; 21 22const App: FC = () => { 23 const [inputValue, setInputValue] = useState(''); 24 const [error, _setError] = useState<string | null>(null); 25 const [showAlert, setShowAlert] = useState(false); 26 const [alertMessage, setAlertMessage] = useState(''); 27 const [claimedToken, setClaimedToken] = useState<string | null>(null); 28 const [screen, setScreen] = useState< 29 'main' | 'words' | 'tokens' | 'wordlist' 30 >('main'); 31 const [isTransitioning, setIsTransitioning] = useState(false); 32 33 const handleShowWordsScreen = () => { 34 setIsTransitioning(true); 35 setScreen('words'); 36 }; 37 const handleShowTokensScreen = () => { 38 setIsTransitioning(true); 39 setScreen('tokens'); 40 }; 41 const handleBackToWords = () => { 42 setIsTransitioning(true); 43 setScreen('words'); 44 }; 45 const handleBackToMain = () => { 46 setIsTransitioning(true); 47 setScreen('main'); 48 }; 49 const handleShowWordListScreen = () => { 50 setIsTransitioning(true); 51 setScreen('wordlist'); 52 }; 53 54 const { status, setStatus, timer, loading, setLoading } = useTimer(); 55 const { handleStart, handleStop } = useStatus(setStatus); 56 const { tails, scales } = useWords(); 57 const { 58 tailnetNames, 59 setTailnetNames, 60 handleAddTailnet, 61 handleRemoveTailnet, 62 } = useTailnetNames(tails, scales, setStatus, inputValue, setInputValue); 63 const [ 64 tokens, 65 setTokens, 66 { handleClaimToken, handleRemoveToken, error: claimTokenError }, 67 ] = useTokens({ 68 setLoading, 69 setAlertMessage, 70 setShowAlert, 71 setClaimedToken, 72 }); 73 74 const { eligibility, eligibilityLoading } = useEligibility(); 75 const { alertModalRef, alertCloseBtnRef, handleAlertCloseModal } = useModal( 76 showAlert, 77 setShowAlert, 78 setAlertMessage, 79 claimedToken, 80 setClaimedToken, 81 setTokens, 82 setTailnetNames, 83 ); 84 const isInputValid = useInputValidation( 85 inputValue, 86 tails, 87 scales, 88 tailnetNames, 89 ); 90 91 useEffect(() => { 92 if (isTransitioning) { 93 const timer = setTimeout(() => setIsTransitioning(false), 300); 94 return () => clearTimeout(timer); 95 } 96 }, [isTransitioning]); 97 98 return ( 99 <div className="flex flex-col min-w-[600px] max-w-lg min-h-[600px] bg-background overflow-hidden mx-auto h-full"> 100 <div className="flex-1 relative flex flex-col h-full"> 101 <div className="flex-1 relative overflow-x-hidden overflow-y-auto [&::-webkit-scrollbar]:hidden"> 102 <div 103 className={`absolute inset-0 transition-all duration-500 bg-background ${ 104 screen === 'main' 105 ? 'translate-x-0 opacity-100 pointer-events-auto' 106 : 'translate-x-[-100%] opacity-0 pointer-events-none' 107 }`} 108 > 109 <MainScreen 110 onShowWords={handleShowWordsScreen} 111 onShowTokens={handleShowTokensScreen} 112 status={status} 113 timer={timer} 114 handleStart={handleStart} 115 handleStop={handleStop} 116 tailnetNames={tailnetNames} 117 tokens={tokens} 118 /> 119 </div> 120 121 <div 122 className={`absolute inset-0 transition-all duration-500 bg-background ${ 123 screen === 'words' 124 ? 'translate-x-0 opacity-100 pointer-events-auto' 125 : screen === 'main' 126 ? 'translate-x-[100%] opacity-0 pointer-events-none' 127 : 'translate-x-[-100%] opacity-0 pointer-events-none' 128 }`} 129 > 130 <TailnetWordsScreen 131 tailnetNames={tailnetNames} 132 handleAddTailnet={handleAddTailnet} 133 handleRemoveTailnet={handleRemoveTailnet} 134 inputValue={inputValue} 135 setInputValue={setInputValue} 136 isInputValid={!!isInputValid} 137 loading={loading} 138 onBack={handleBackToMain} 139 error={error} 140 tails={tails} 141 scales={scales} 142 onShowWordList={handleShowWordListScreen} 143 /> 144 </div> 145 146 <div 147 className={`absolute inset-0 transition-all duration-500 bg-background ${ 148 screen === 'tokens' 149 ? 'translate-x-0 opacity-100 pointer-events-auto' 150 : 'translate-x-[100%] opacity-0 pointer-events-none' 151 }`} 152 > 153 <TokenListScreen 154 tokens={tokens} 155 handleClaimToken={handleClaimToken} 156 handleRemoveToken={handleRemoveToken} 157 loading={loading} 158 error={claimTokenError} 159 onBack={handleBackToMain} 160 /> 161 </div> 162 163 <div 164 className={`absolute inset-0 transition-all duration-500 bg-background ${ 165 screen === 'wordlist' 166 ? 'translate-x-0 opacity-100 pointer-events-auto' 167 : 'translate-x-[100%] opacity-0 pointer-events-none' 168 }`} 169 > 170 <WordListScreen 171 tails={tails} 172 scales={scales} 173 onBack={handleBackToWords} 174 /> 175 </div> 176 177 <LoadingOverlay 178 eligibilityLoading={eligibilityLoading} 179 loading={loading} 180 /> 181 <EligibilityModal 182 eligibility={eligibility} 183 eligibilityLoading={eligibilityLoading} 184 /> 185 <AlertModal 186 alertCloseBtnRef={alertCloseBtnRef} 187 alertMessage={alertMessage} 188 alertModalRef={alertModalRef} 189 handleAlertCloseModal={handleAlertCloseModal} 190 showAlert={showAlert} 191 /> 192 </div> 193 194 <Footer /> 195 </div> 196 </div> 197 ); 198}; 199 200export default App;