Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 75 additions & 25 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
import { GitHubUser, Repository, Issue, AppRoute } from './types';
import { GitHubUser, Repository, Issue, AppRoute, Account } from './types';
import { TokenGate } from './views/TokenGate';
import { Dashboard } from './views/Dashboard';
import { RepoDetail } from './views/RepoDetail';
Expand All @@ -9,25 +9,44 @@ import { validateToken } from './services/githubService';
import { ThemeProvider } from './contexts/ThemeContext';

const App: React.FC = () => {
const [token, setToken] = useState<string | null>(localStorage.getItem('gh_token'));
const [user, setUser] = useState<GitHubUser | null>(
localStorage.getItem('gh_user') ? JSON.parse(localStorage.getItem('gh_user')!) : null
);
const [accounts, setAccounts] = useState<Account[]>(() => {
const savedAccounts = localStorage.getItem('gh_accounts');
return savedAccounts ? JSON.parse(savedAccounts) : [];
});

const [activeAccount, setActiveAccount] = useState<Account | null>(() => {
const savedAccounts = localStorage.getItem('gh_accounts');
if (!savedAccounts) return null;
const accountsList: Account[] = JSON.parse(savedAccounts);
const activeLogin = localStorage.getItem('gh_active_account_login');
if (activeLogin) {
return accountsList.find(acc => acc.user.login === activeLogin) || accountsList[0] || null;
}
return accountsList[0] || null;
});

const [checkingRedirect, setCheckingRedirect] = useState(true);

const [currentRoute, setCurrentRoute] = useState<AppRoute>(
token && user ? AppRoute.REPO_LIST : AppRoute.TOKEN_INPUT
activeAccount ? AppRoute.REPO_LIST : AppRoute.TOKEN_INPUT
);
const [selectedRepo, setSelectedRepo] = useState<Repository | null>(null);
const [selectedIssue, setSelectedIssue] = useState<Issue | null>(null);

// Handle redirect result from Firebase OAuth (for popup-blocked fallback)
useEffect(() => {
localStorage.setItem('gh_accounts', JSON.stringify(accounts));
if (activeAccount) {
localStorage.setItem('gh_active_account_login', activeAccount.user.login);
} else {
localStorage.removeItem('gh_active_account_login');
}
}, [accounts, activeAccount]);

useEffect(() => {
const checkRedirectResult = async () => {
try {
const result = await handleRedirectResult();
if (result) {
// Validate token and get user data from GitHub API
const ghUser = await validateToken(result.accessToken);
handleLogin(result.accessToken, ghUser);
}
Expand All @@ -42,29 +61,59 @@ const App: React.FC = () => {
}, []);

const handleLogin = (newToken: string, newUser: GitHubUser) => {
setToken(newToken);
setUser(newUser);
localStorage.setItem('gh_token', newToken);
localStorage.setItem('gh_user', JSON.stringify(newUser));
const newAccount: Account = { user: newUser, token: newToken };

setAccounts(prevAccounts => {
const existingAccountIndex = prevAccounts.findIndex(acc => acc.user.login === newUser.login);
if (existingAccountIndex > -1) {
const updatedAccounts = [...prevAccounts];
updatedAccounts[existingAccountIndex] = newAccount;
return updatedAccounts;
} else {
return [...prevAccounts, newAccount];
}
});

setActiveAccount(newAccount);
setCurrentRoute(AppRoute.REPO_LIST);
};

const handleLogout = async () => {
// Sign out from Firebase
if (!activeAccount) return;

try {
await signOutFromFirebase();
} catch (err) {
console.error('Firebase sign out error:', err);
}

setToken(null);
setUser(null);
localStorage.removeItem('gh_token');
localStorage.removeItem('gh_user');
setCurrentRoute(AppRoute.TOKEN_INPUT);
const newAccounts = accounts.filter(acc => acc.user.login !== activeAccount.user.login);
setAccounts(newAccounts);

if (newAccounts.length > 0) {
setActiveAccount(newAccounts[0]);
} else {
setActiveAccount(null);
setCurrentRoute(AppRoute.TOKEN_INPUT);
}

setSelectedRepo(null);
};

const switchAccount = (login: string) => {
const accountToSwitch = accounts.find(acc => acc.user.login === login);
if (accountToSwitch) {
setActiveAccount(accountToSwitch);
setSelectedRepo(null);
setSelectedIssue(null);
setCurrentRoute(AppRoute.REPO_LIST);
}
};

const addNewAccount = () => {
setCurrentRoute(AppRoute.TOKEN_INPUT);
};

const navigateToRepo = (repo: Repository) => {
setSelectedRepo(repo);
setCurrentRoute(AppRoute.REPO_DETAIL);
Expand All @@ -86,7 +135,6 @@ const App: React.FC = () => {
setCurrentRoute(AppRoute.REPO_DETAIL);
};

// Render Logic
if (checkingRedirect) {
return (
<div className="min-h-screen flex items-center justify-center bg-slate-50 dark:bg-slate-900">
Expand All @@ -95,14 +143,14 @@ const App: React.FC = () => {
);
}

if (currentRoute === AppRoute.TOKEN_INPUT || !token || !user) {
return <TokenGate onSuccess={handleLogin} />;
if (currentRoute === AppRoute.TOKEN_INPUT || !activeAccount) {
return <TokenGate onSuccess={handleLogin} hasAccounts={accounts.length > 0} onBack={() => setCurrentRoute(AppRoute.REPO_LIST)} />;
}

if (currentRoute === AppRoute.ISSUE_DETAIL && selectedRepo && selectedIssue) {
return (
<IssueDetail
token={token}
token={activeAccount.token}
repo={selectedRepo}
issue={selectedIssue}
onBack={navigateBackToRepo}
Expand All @@ -113,7 +161,7 @@ const App: React.FC = () => {
if (currentRoute === AppRoute.REPO_DETAIL && selectedRepo) {
return (
<RepoDetail
token={token}
token={activeAccount.token}
repo={selectedRepo}
onBack={navigateBack}
onIssueSelect={navigateToIssue}
Expand All @@ -123,10 +171,12 @@ const App: React.FC = () => {

return (
<Dashboard
token={token}
user={user}
activeAccount={activeAccount}
accounts={accounts}
onRepoSelect={navigateToRepo}
onLogout={handleLogout}
onSwitchAccount={switchAccount}
onAddNewAccount={addNewAccount}
/>
);
};
Expand Down
5 changes: 5 additions & 0 deletions types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ export interface GitHubUser {
name: string;
}

export interface Account {
user: GitHubUser;
token: string;
}

export interface Repository {
id: number;
name: string;
Expand Down
Loading