diff --git a/src/components/TelegramConnectionModal.tsx b/src/components/TelegramConnectionModal.tsx index 973a13e8f..ce590cd61 100644 --- a/src/components/TelegramConnectionModal.tsx +++ b/src/components/TelegramConnectionModal.tsx @@ -125,7 +125,7 @@ const TelegramConnectionModal = ({ isOpen, onClose, onComplete }: TelegramConnec } resolve(password.trim()); }; - + // Set a timeout to prevent hanging forever setTimeout(() => { if (passwordResolverRef.current) { @@ -139,7 +139,7 @@ const TelegramConnectionModal = ({ isOpen, onClose, onComplete }: TelegramConnec async (err) => { // Handle errors const errorMessage = err.message || 'Authentication error'; - + // Check if it's a 2FA password needed error if (errorMessage.includes('SESSION_PASSWORD_NEEDED') || errorMessage.includes('PASSWORD')) { // This should trigger the password callback, but if it doesn't, handle it here @@ -178,7 +178,7 @@ const TelegramConnectionModal = ({ isOpen, onClose, onComplete }: TelegramConnec } catch (err) { setIsAuthenticating(false); const errorMessage = err instanceof Error ? err.message : 'Authentication failed'; - + // If it's a password needed error, we should already be on the 2FA step // Don't show it as an error, just log it if (errorMessage.includes('SESSION_PASSWORD_NEEDED')) { @@ -190,7 +190,7 @@ const TelegramConnectionModal = ({ isOpen, onClose, onComplete }: TelegramConnec } return; // Don't show error, password callback should handle it } - + setError(errorMessage); setCurrentStep('error'); dispatch(setAuthError(errorMessage)); @@ -207,7 +207,7 @@ const TelegramConnectionModal = ({ isOpen, onClose, onComplete }: TelegramConnec const updateTimer = () => { const remaining = Math.max(0, Math.floor((qrCodeExpires * 1000 - Date.now()) / 1000)); setTimeRemaining(remaining); - + // If timer reaches 0 and we're on QR step, reload the QR code if (remaining === 0 && currentStep === 'qr' && !isAuthenticating) { startQrCodeFlow(); @@ -235,7 +235,7 @@ const TelegramConnectionModal = ({ isOpen, onClose, onComplete }: TelegramConnec if (!client) return; const isAuthorized = await client.checkAuthorization(); - + if (isAuthorized) { // User is authenticated - check if we need password try { @@ -277,12 +277,12 @@ const TelegramConnectionModal = ({ isOpen, onClose, onComplete }: TelegramConnec const handle2FASubmit = async () => { const passwordValue = password.trim(); - + if (!passwordValue) { setError('Please enter your password'); return; } - + if (!passwordResolverRef.current) { console.error('Password resolver is null - password callback may not be active'); setError('Authentication session expired. Please try again.'); @@ -298,10 +298,10 @@ const TelegramConnectionModal = ({ isOpen, onClose, onComplete }: TelegramConnec // This will pass the password to the Telegram library const resolver = passwordResolverRef.current; passwordResolverRef.current = null; // Clear before resolving to prevent double calls - + console.log('Resolving password promise - sending password to Telegram'); resolver(passwordValue); - + // The authentication will continue in the background via signInWithQrCode // The success/error will be handled by the signInWithQrCode promise // We keep isAuthenticating true until the promise resolves or rejects @@ -340,9 +340,9 @@ const TelegramConnectionModal = ({ isOpen, onClose, onComplete }: TelegramConnec if (!isOpen) return null; const modalContent = ( -
-
- {/* Close button */} - + {/* Close button */} + - {currentStep === 'loading' ? ( -
-
-

Initializing Telegram connection...

+ {currentStep === 'loading' ? ( +
+
+

Initializing Telegram connection...

+
+ ) : currentStep === 'error' ? ( + <> + {/* Error Screen */} +
+
+ + + +
+

Connection Error

+

{error || 'An error occurred'}

+
+ + +
- ) : currentStep === 'error' ? ( - <> - {/* Error Screen */} -
-
- - - -
-

Connection Error

-

{error || 'An error occurred'}

-
- - + + ) : currentStep === 'qr' ? ( + <> + {/* QR Code Screen */} +
+ {/* QR Code Container */} +
+
+ {qrCodeUrl ? ( +
+ +
+ ) : ( +
+ {isAuthenticating ? ( +
+
+

Generating QR code...

+
+ ) : ( +

Loading QR code...

+ )} +
+ )}
- - ) : currentStep === 'qr' ? ( - <> - {/* QR Code Screen */} -
- {/* QR Code Container */} -
-
- {qrCodeUrl ? ( -
- -
- ) : ( -
- {isAuthenticating ? ( -
-
-

Generating QR code...

-
- ) : ( -

Loading QR code...

- )} -
- )} -
-
- {qrCodeExpires && timeRemaining > 0 && ( -

- This code expires in {timeRemaining} seconds -

- )} - - {error && ( -
-

{error}

-
- )} - - {/* Instructions */} -
-
-
- 1 -
-

Open Telegram on your phone

-
- -
-
- 2 -
-

Go to Settings > Devices > Link Desktop Device

-
- -
-
- 3 -
-

Point your phone at this screen to confirm login

-
-
-
- - ) : ( - <> - {/* 2FA Screen */} -
-

Enter Your Password

-

- {passwordHint - ? `Your account is protected with two-step verification. Hint: ${passwordHint}` - : 'Your account is protected with two-step verification. Please enter your password to continue.'} + {qrCodeExpires && timeRemaining > 0 && ( +

+ This code expires in {timeRemaining} seconds

+ )} - {error && ( -
-

{error}

+ {error && ( +
+

{error}

+
+ )} + + {/* Instructions */} +
+
+
+ 1
- )} - - {/* Password input */} -
- setPassword(e.target.value)} - onKeyDown={(e) => { - if (e.key === 'Enter' && password.trim() && !isAuthenticating) { - handle2FASubmit(); - } - }} - placeholder="Enter your password" - className="w-full px-4 py-3 bg-black/50 border border-stone-700 rounded-xl text-white placeholder-opacity-50 focus:outline-none focus:border-primary-500 transition-colors" - autoComplete="off" - data-form-type="other" - data-lpignore="true" - data-1p-ignore="true" - data-bwignore="true" - data-dashlane-ignore="true" - data-bitwarden-watching="false" - autoFocus - disabled={isAuthenticating} - /> +

Open Telegram on your phone

- {/* Action buttons */} -
- - +
+
+ 2 +
+

Go to Settings > Devices > Link Desktop Device

+
+ +
+
+ 3 +
+

Point your phone at this screen to confirm login

- - )} -
+
+ + ) : ( + <> + {/* 2FA Screen */} +
+

Enter Your Password

+

+ {passwordHint + ? `Your account is protected with two-step verification. Hint: ${passwordHint}` + : 'Your account is protected with two-step verification. Please enter your password to continue.'} +

+ + {error && ( +
+

{error}

+
+ )} + + {/* Password input */} +
+ setPassword(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter' && password.trim() && !isAuthenticating) { + handle2FASubmit(); + } + }} + placeholder="Enter your password" + className="w-full px-4 py-3 bg-black/50 border border-stone-700 rounded-xl text-white placeholder-opacity-50 focus:outline-none focus:border-primary-500 transition-colors" + autoComplete="off" + data-form-type="other" + data-lpignore="true" + data-1p-ignore="true" + data-bwignore="true" + data-dashlane-ignore="true" + data-bitwarden-watching="false" + autoFocus + disabled={isAuthenticating} + /> +
+ + {/* Action buttons */} +
+ + +
+
+ + )} +
); diff --git a/src/pages/onboarding/steps/ConnectStep.tsx b/src/pages/onboarding/steps/ConnectStep.tsx index 9f6a78472..1a4e06c9e 100644 --- a/src/pages/onboarding/steps/ConnectStep.tsx +++ b/src/pages/onboarding/steps/ConnectStep.tsx @@ -2,10 +2,10 @@ import { useState } from 'react'; import GoogleIcon from '../../../assets/icons/GoogleIcon'; import TelegramConnectionModal from '../../../components/TelegramConnectionModal'; -import BinanceIcon from './icons/binance.svg'; -import NotionIcon from './icons/notion.svg'; -import TelegramIcon from './icons/telegram.svg'; -import MetamaskIcon from './icons/metamask.svg'; +import BinanceIcon from '../../../assets/icons/binance.svg'; +import NotionIcon from '../../../assets/icons/notion.svg'; +import TelegramIcon from '../../../assets/icons/telegram.svg'; +import MetamaskIcon from '../../../assets/icons/metamask.svg'; interface ConnectStepProps { diff --git a/src/store/telegram/reducers.ts b/src/store/telegram/reducers.ts index e4b3e6842..93e6db5a9 100644 --- a/src/store/telegram/reducers.ts +++ b/src/store/telegram/reducers.ts @@ -1,4 +1,4 @@ -import { PayloadAction } from '@reduxjs/toolkit'; +import { PayloadAction } from "@reduxjs/toolkit"; import type { TelegramState, TelegramConnectionStatus, @@ -7,50 +7,74 @@ import type { TelegramChat, TelegramMessage, TelegramThread, -} from './types'; +} from "./types"; export const reducers = { // Connection actions - setConnectionStatus: (state: TelegramState, action: PayloadAction) => { + setConnectionStatus: ( + state: TelegramState, + action: PayloadAction, + ) => { state.connectionStatus = action.payload; - if (action.payload !== 'error') { + if (action.payload !== "error") { state.connectionError = null; } }, - setConnectionError: (state: TelegramState, action: PayloadAction) => { + setConnectionError: ( + state: TelegramState, + action: PayloadAction, + ) => { state.connectionError = action.payload; if (action.payload) { - state.connectionStatus = 'error'; + state.connectionStatus = "error"; } }, // Authentication actions - setAuthStatus: (state: TelegramState, action: PayloadAction) => { + setAuthStatus: ( + state: TelegramState, + action: PayloadAction, + ) => { state.authStatus = action.payload; - if (action.payload !== 'error') { + if (action.payload !== "error") { state.authError = null; } }, - setAuthError: (state: TelegramState, action: PayloadAction) => { + setAuthError: ( + state: TelegramState, + action: PayloadAction, + ) => { state.authError = action.payload; if (action.payload) { - state.authStatus = 'error'; + state.authStatus = "error"; } }, - setPhoneNumber: (state: TelegramState, action: PayloadAction) => { + setPhoneNumber: ( + state: TelegramState, + action: PayloadAction, + ) => { state.phoneNumber = action.payload; }, - setSessionString: (state: TelegramState, action: PayloadAction) => { + setSessionString: ( + state: TelegramState, + action: PayloadAction, + ) => { state.sessionString = action.payload; }, // User actions - setCurrentUser: (state: TelegramState, action: PayloadAction) => { + setCurrentUser: ( + state: TelegramState, + action: PayloadAction, + ) => { state.currentUser = action.payload; }, // Chat actions - setChats: (state: TelegramState, action: PayloadAction>) => { + setChats: ( + state: TelegramState, + action: PayloadAction>, + ) => { state.chats = action.payload; }, addChat: (state: TelegramState, action: PayloadAction) => { @@ -60,7 +84,10 @@ export const reducers = { state.chatsOrder.unshift(chat.id); } }, - updateChat: (state: TelegramState, action: PayloadAction & { id: string }>) => { + updateChat: ( + state: TelegramState, + action: PayloadAction & { id: string }>, + ) => { const { id, ...updates } = action.payload; if (state.chats[id]) { state.chats[id] = { ...state.chats[id], ...updates }; @@ -74,7 +101,10 @@ export const reducers = { state.selectedChatId = null; } }, - setSelectedChat: (state: TelegramState, action: PayloadAction) => { + setSelectedChat: ( + state: TelegramState, + action: PayloadAction, + ) => { state.selectedChatId = action.payload; // Clear selected thread when changing chat if (action.payload !== state.selectedChatId) { @@ -86,7 +116,10 @@ export const reducers = { }, // Message actions - addMessage: (state: TelegramState, action: PayloadAction) => { + addMessage: ( + state: TelegramState, + action: PayloadAction, + ) => { const message = action.payload; const { chatId, id } = message; @@ -100,7 +133,10 @@ export const reducers = { state.messagesOrder[chatId].push(id); } }, - addMessages: (state: TelegramState, action: PayloadAction<{ chatId: string; messages: TelegramMessage[] }>) => { + addMessages: ( + state: TelegramState, + action: PayloadAction<{ chatId: string; messages: TelegramMessage[] }>, + ) => { const { chatId, messages } = action.payload; if (!state.messages[chatId]) { @@ -117,7 +153,11 @@ export const reducers = { }, updateMessage: ( state: TelegramState, - action: PayloadAction<{ chatId: string; messageId: string; updates: Partial }> + action: PayloadAction<{ + chatId: string; + messageId: string; + updates: Partial; + }>, ) => { const { chatId, messageId, updates } = action.payload; if (state.messages[chatId]?.[messageId]) { @@ -127,12 +167,15 @@ export const reducers = { }; } }, - removeMessage: (state: TelegramState, action: PayloadAction<{ chatId: string; messageId: string }>) => { + removeMessage: ( + state: TelegramState, + action: PayloadAction<{ chatId: string; messageId: string }>, + ) => { const { chatId, messageId } = action.payload; if (state.messages[chatId]?.[messageId]) { delete state.messages[chatId][messageId]; state.messagesOrder[chatId] = state.messagesOrder[chatId].filter( - (id) => id !== messageId + (id) => id !== messageId, ); } }, @@ -159,7 +202,11 @@ export const reducers = { }, updateThread: ( state: TelegramState, - action: PayloadAction<{ chatId: string; threadId: string; updates: Partial }> + action: PayloadAction<{ + chatId: string; + threadId: string; + updates: Partial; + }>, ) => { const { chatId, threadId, updates } = action.payload; if (state.threads[chatId]?.[threadId]) { @@ -169,15 +216,24 @@ export const reducers = { }; } }, - setSelectedThread: (state: TelegramState, action: PayloadAction) => { + setSelectedThread: ( + state: TelegramState, + action: PayloadAction, + ) => { state.selectedThreadId = action.payload; }, // Search actions - setSearchQuery: (state: TelegramState, action: PayloadAction) => { + setSearchQuery: ( + state: TelegramState, + action: PayloadAction, + ) => { state.searchQuery = action.payload; }, - setFilteredChatIds: (state: TelegramState, action: PayloadAction) => { + setFilteredChatIds: ( + state: TelegramState, + action: PayloadAction, + ) => { state.filteredChatIds = action.payload; },