29 KiB
Workspace Improvements Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Polish the workspace by removing logo/login button clutter, adding a history toggle, enforcing mandatory login after 3 guest uploads, and adding email verification code to the registration flow.
Architecture: Five independent changes across the workspace layer. History toggle state lives in WorkspacePage and is passed into LeftSidebar. Email verification adds a two-step flow inside AuthModal only — no new files needed. All i18n strings go through translations.ts.
Tech Stack: React 18, TypeScript, Tailwind CSS (utility classes), Lucide React icons, existing http client in lib/api.ts
File Map
| File | Change |
|---|---|
src/components/layout/AppNavbar.tsx |
Remove logo icon + text + divider |
src/components/LeftSidebar.tsx |
Remove login button; add history toggle prop |
src/pages/WorkspacePage.tsx |
Add historyEnabled state; pass to sidebar; force mandatory modal after 3rd upload |
src/components/AuthModal.tsx |
Add send-code button, countdown, verification code field to signup tab |
src/lib/authService.ts |
Add sendEmailCode() method; update register() to send code |
src/types/api.ts |
Add SendEmailCodeRequest; add code to RegisterRequest |
src/lib/translations.ts |
Add new i18n keys for history toggle, verification code flow |
Task 1: Add i18n strings for new features
Files:
-
Modify:
src/lib/translations.ts -
Step 1: Add new keys to both
enandzhauth sections and sidebar section
In src/lib/translations.ts, find the en.auth block and add after oauthFailed:
sendCode: 'Send Code',
resendCode: 'Resend',
codeSent: 'Code sent',
verificationCode: 'Verification Code',
verificationCodePlaceholder: 'Enter 6-digit code',
verificationCodeRequired: 'Please enter the verification code.',
verificationCodeHint: 'Check your inbox for the 6-digit code.',
sendCodeFailed: 'Failed to send verification code, please retry.',
In en.sidebar, add after historyHeader:
historyToggle: 'Show History',
historyLoginRequired: 'Login to enable history',
In zh.auth, add after oauthFailed:
sendCode: '发送验证码',
resendCode: '重新发送',
codeSent: '验证码已发送',
verificationCode: '验证码',
verificationCodePlaceholder: '请输入 6 位验证码',
verificationCodeRequired: '请输入验证码。',
verificationCodeHint: '请查收邮箱中的 6 位验证码。',
sendCodeFailed: '发送验证码失败,请重试。',
In zh.sidebar, add after historyHeader:
historyToggle: '显示历史',
historyLoginRequired: '登录后开启历史记录',
- Step 2: Commit
git add src/lib/translations.ts
git commit -m "feat: add i18n keys for history toggle and email verification"
Task 2: Remove logo from AppNavbar
Files:
-
Modify:
src/components/layout/AppNavbar.tsx:38-50 -
Step 1: Remove logo image, text, and divider — keep only the Home icon link
Replace the entire {/* Left: Logo + Home link */} div (lines 38–51) with:
{/* Left: Home link */}
<div className="flex items-center gap-2">
<Link
to="/"
className="flex items-center gap-1.5 px-2 py-1 text-ink-muted hover:text-ink-secondary text-xs font-medium transition-colors rounded-md hover:bg-cream-200/60"
>
<Home size={13} />
<span className="hidden sm:inline">{t.marketing.nav.home}</span>
</Link>
</div>
- Step 2: Verify dev server renders correctly
npm run dev
Navigate to /app. Confirm the navbar no longer shows the TexPixel icon or text, only the Home link on the left.
- Step 3: Commit
git add src/components/layout/AppNavbar.tsx
git commit -m "feat: remove logo from workspace navbar"
Task 3: Remove login button from LeftSidebar and add history toggle prop
Files:
-
Modify:
src/components/LeftSidebar.tsx -
Step 1: Add
historyEnabledandonToggleHistoryto the props interface
Replace the existing LeftSidebarProps interface with:
interface LeftSidebarProps {
files: FileRecord[];
selectedFileId: string | null;
onFileSelect: (fileId: string) => void;
onUploadClick: () => void;
canUploadAnonymously: boolean;
onRequireAuth: () => void;
isCollapsed: boolean;
onToggleCollapse: () => void;
onUploadFiles: (files: File[]) => void;
hasMore: boolean;
loadingMore: boolean;
onLoadMore: () => void;
historyEnabled: boolean;
onToggleHistory: () => void;
}
- Step 2: Destructure the two new props in the function signature
Replace the destructuring block (the function params) to add historyEnabled and onToggleHistory:
export default function LeftSidebar({
files,
selectedFileId,
onFileSelect,
onUploadClick,
canUploadAnonymously,
onRequireAuth,
isCollapsed,
onToggleCollapse,
onUploadFiles,
hasMore,
loadingMore,
onLoadMore,
historyEnabled,
onToggleHistory,
}: LeftSidebarProps) {
- Step 3: Add
Toggleicon to lucide imports
Change the import line at the top from:
import { Upload, LogIn, LogOut, FileText, Clock, ChevronLeft, ChevronRight, History, MousePointerClick, FileUp, ClipboardPaste, Loader2 } from 'lucide-react';
to:
import { Upload, LogOut, FileText, Clock, ChevronLeft, ChevronRight, MousePointerClick, FileUp, ClipboardPaste, Loader2 } from 'lucide-react';
(Remove LogIn, History — no longer used in expanded view)
- Step 4: Remove the
showAuthModalstate and theAuthModalimport/usage from LeftSidebar
Remove these lines near the top of the function body:
const [showAuthModal, setShowAuthModal] = useState(false);
and the useEffect that sets showAuthModal to false on auth:
useEffect(() => {
if (isAuthenticated) {
setShowAuthModal(false);
}
}, [isAuthenticated]);
Also remove import AuthModal from './AuthModal'; at the top of the file, and at the bottom remove the {showAuthModal && <AuthModal onClose={() => setShowAuthModal(false)} />} JSX.
- Step 5: Replace the history header section with a toggle switch
Find the {/* Middle Area: History */} block. Replace the header div (the one with Clock icon and historyHeader text) with:
<div className="flex items-center justify-between text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3 px-2">
<div className="flex items-center gap-2">
<Clock size={14} />
<span>{t.sidebar.historyHeader}</span>
</div>
<button
onClick={onToggleHistory}
className={`relative w-8 h-4 rounded-full transition-colors duration-200 focus:outline-none ${
historyEnabled ? 'bg-blue-500' : 'bg-gray-300'
}`}
title={t.sidebar.historyToggle}
aria-pressed={historyEnabled}
>
<span
className={`absolute top-0.5 left-0.5 w-3 h-3 bg-white rounded-full shadow transition-transform duration-200 ${
historyEnabled ? 'translate-x-4' : 'translate-x-0'
}`}
/>
</button>
</div>
- Step 6: Gate the history list behind
historyEnabled
Replace the entire <div ref={listRef} ...> scrollable list with:
<div
ref={listRef}
onScroll={handleScroll}
className="flex-1 overflow-y-auto space-y-1 pr-2 -mr-2 custom-scrollbar"
>
{!historyEnabled ? (
<div className="text-center py-12 text-gray-400 text-sm">
<div className="mb-2 opacity-50"><FileText size={40} className="mx-auto" /></div>
{t.sidebar.historyToggle}
</div>
) : !user ? (
<div className="text-center py-12 text-gray-400 text-sm">
<div className="mb-2 opacity-50"><FileText size={40} className="mx-auto" /></div>
{t.sidebar.historyLoginRequired}
</div>
) : files.length === 0 ? (
<div className="text-center py-12 text-gray-400 text-sm">
<div className="mb-2 opacity-50"><FileText size={40} className="mx-auto" /></div>
{t.sidebar.noHistory}
</div>
) : (
<>
{files.map((file) => (
<button
key={file.id}
onClick={() => onFileSelect(file.id)}
className={`w-full p-3 rounded-lg text-left transition-all border group relative ${selectedFileId === file.id
? 'bg-blue-50 border-blue-200 shadow-sm'
: 'bg-white border-transparent hover:bg-gray-50 hover:border-gray-100'
}`}
>
<div className="flex items-start gap-3">
<div className={`p-2 rounded-lg ${selectedFileId === file.id ? 'bg-blue-100 text-blue-600' : 'bg-gray-100 text-gray-500 group-hover:bg-gray-200'}`}>
<FileText size={18} />
</div>
<div className="flex-1 min-w-0">
<p className={`text-sm font-medium truncate ${selectedFileId === file.id ? 'text-blue-900' : 'text-gray-700'}`}>
{file.filename}
</p>
<div className="flex items-center gap-2 mt-1">
<span className="text-xs text-gray-400">
{new Date(file.created_at).toLocaleDateString()}
</span>
<span className={`w-1.5 h-1.5 rounded-full ${file.status === 'completed' ? 'bg-green-500' :
file.status === 'processing' ? 'bg-yellow-500' : 'bg-red-500'
}`} />
</div>
</div>
</div>
</button>
))}
{loadingMore && (
<div className="flex items-center justify-center py-3 text-gray-400">
<Loader2 size={18} className="animate-spin" />
<span className="ml-2 text-xs">{t.common.loading}</span>
</div>
)}
{!hasMore && files.length > 0 && (
<div className="text-center py-3 text-xs text-gray-400">
{t.sidebar.noMore}
</div>
)}
</>
)}
</div>
- Step 7: Replace the bottom user/login area — remove login button, keep only logged-in user view
Replace the entire {/* Bottom Area: User/Login */} div with:
{/* Bottom Area: User info (only shown when logged in) */}
{user && (
<div className="p-4 border-t border-gray-100 bg-gray-50/30">
<div className="flex items-center gap-3 p-2 rounded-lg bg-white border border-gray-100 shadow-sm">
<div className="w-8 h-8 bg-gray-900 rounded-full flex items-center justify-center flex-shrink-0">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 12C14.21 12 16 10.21 16 8C16 5.79 14.21 4 12 4C9.79 4 8 5.79 8 8C8 10.21 9.79 12 12 12ZM12 14C9.33 14 4 15.34 4 18V20H20V18C20 15.34 14.67 14 12 14Z" fill="white" />
</svg>
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-900 truncate">{displayName}</p>
</div>
<button
onClick={() => signOut()}
className="p-1.5 text-gray-400 hover:text-red-500 hover:bg-red-50 rounded-md transition-colors"
title={t.common.logout}
>
<LogOut size={16} />
</button>
</div>
</div>
)}
- Step 8: Fix collapsed view — remove LogIn icon button
In the if (isCollapsed) return block, remove the bottom <button> that shows the LogIn icon:
<button
onClick={() => !user && setShowAuthModal(true)}
className="p-3 rounded-lg text-gray-600 hover:bg-gray-200 transition-colors mt-auto"
title={user ? 'Signed In' : t.common.login}
>
<LogIn size={20} />
</button>
Replace with nothing (delete that button entirely). The isAuthenticated import can stay for now.
- Step 9: Commit
git add src/components/LeftSidebar.tsx
git commit -m "feat: remove login button from sidebar, add history toggle"
Task 4: Wire history toggle state and mandatory auth in WorkspacePage
Files:
-
Modify:
src/pages/WorkspacePage.tsx -
Step 1: Add
historyEnabledstate
After the existing const [loadingMore, setLoadingMore] = useState(false); line, add:
const [historyEnabled, setHistoryEnabled] = useState(false);
- Step 2: Add
handleToggleHistorycallback
After the openAuthModal callback, add:
const handleToggleHistory = useCallback(() => {
if (!historyEnabled) {
// Turning on
if (!user) {
openAuthModal();
return;
}
setHistoryEnabled(true);
if (!hasLoadedFiles.current) {
hasLoadedFiles.current = true;
loadFiles();
}
} else {
setHistoryEnabled(false);
}
}, [historyEnabled, user, openAuthModal]);
- Step 3: Remove auto-load on auth — history is now opt-in
Find the useEffect that auto-calls loadFiles() when user becomes available:
useEffect(() => {
if (!initializing && user && !hasLoadedFiles.current) {
hasLoadedFiles.current = true;
loadFiles();
}
if (!user) {
hasLoadedFiles.current = false;
setFiles([]);
setSelectedFileId(null);
setCurrentPage(1);
setHasMore(false);
}
}, [initializing, user]);
Replace with (keep the reset on logout, remove the auto-load):
useEffect(() => {
if (!user) {
hasLoadedFiles.current = false;
setHistoryEnabled(false);
setFiles([]);
setSelectedFileId(null);
setCurrentPage(1);
setHasMore(false);
}
}, [user]);
- Step 4: Make the auth modal mandatory after 3rd upload
Add a new state after showAuthModal:
const [authModalMandatory, setAuthModalMandatory] = useState(false);
Change openAuthModal to accept an optional mandatory parameter:
const openAuthModal = useCallback((mandatory = false) => {
setAuthModalMandatory(mandatory);
setShowAuthModal(true);
}, []);
In handleUpload, after incrementGuestUsage(), add a mandatory modal trigger when the new count hits the limit:
if (!user && successfulUploads > 0) {
incrementGuestUsage();
// Force login after hitting the limit
setGuestUsageCount(prev => {
const next = prev + 1;
if (next >= GUEST_USAGE_LIMIT) {
openAuthModal(true);
}
return next;
});
}
Wait — incrementGuestUsage already increments. We need to check the new count after increment. Replace the if (!user && successfulUploads > 0) block at the end of handleUpload with:
if (!user && successfulUploads > 0) {
const nextCount = guestUsageCount + successfulUploads;
const newCount = Math.min(nextCount, GUEST_USAGE_LIMIT + 10);
localStorage.setItem(GUEST_USAGE_COUNT_KEY, String(newCount));
setGuestUsageCount(newCount);
if (newCount >= GUEST_USAGE_LIMIT) {
openAuthModal(true);
}
}
And remove the separate incrementGuestUsage call entirely (delete the incrementGuestUsage callback too).
- Step 5: Update the two places that check upload limit to pass
mandatory=false(keep non-mandatory for pre-upload checks)
The onUploadClick handler and paste handler already call openAuthModal() with no arg (defaults to false) — that stays as-is.
- Step 6: Update AuthModal JSX to pass
mandatoryprop and update close handler
Find {showAuthModal && (<AuthModal onClose={() => setShowAuthModal(false)} />)} and replace with:
{showAuthModal && (
<AuthModal
onClose={() => { setShowAuthModal(false); setAuthModalMandatory(false); }}
mandatory={authModalMandatory}
/>
)}
- Step 7: Pass
historyEnabledandonToggleHistoryto LeftSidebar
Find the <LeftSidebar JSX and add the two new props:
historyEnabled={historyEnabled}
onToggleHistory={handleToggleHistory}
- Step 8: After login via mandatory modal, auto-enable history
In the useEffect that watches user, add history auto-enable when user logs in while historyEnabled is still false — actually simpler to auto-enable history when user is set and they just came from a mandatory flow. Instead, just handle this in the useEffect that watches user change after modal closes:
Replace the logout-only effect from Step 3 with:
useEffect(() => {
if (user && !hasLoadedFiles.current && historyEnabled) {
// user logged in while history was already toggled on
hasLoadedFiles.current = true;
loadFiles();
}
if (!user) {
hasLoadedFiles.current = false;
setHistoryEnabled(false);
setFiles([]);
setSelectedFileId(null);
setCurrentPage(1);
setHasMore(false);
}
}, [user]);
- Step 9: Commit
git add src/pages/WorkspacePage.tsx
git commit -m "feat: history toggle state, mandatory auth after 3 guest uploads"
Task 5: Add email verification to API types and authService
Files:
-
Modify:
src/types/api.ts -
Modify:
src/lib/authService.ts -
Step 1: Update
RegisterRequestand addSendEmailCodeRequestinsrc/types/api.ts
Find the existing RegisterRequest interface:
export interface RegisterRequest {
email: string;
password: string;
}
Replace with:
export interface RegisterRequest {
email: string;
password: string;
code: string;
}
export interface SendEmailCodeRequest {
email: string;
}
- Step 2: Add
sendEmailCode()toauthServiceinsrc/lib/authService.ts
Add the import of SendEmailCodeRequest — it will be used below. Then add a new method inside export const authService = { ... } after the login method:
async sendEmailCode(email: string): Promise<void> {
await http.post<null>('/user/email/code', { email } satisfies SendEmailCodeRequest, { skipAuth: true });
},
Also update the register method signature to accept code:
async register(credentials: RegisterRequest): Promise<{ user: UserInfo; token: string; expiresAt: number }> {
const response = await http.post<AuthData>('/user/register', credentials, { skipAuth: true });
if (!response.data) {
throw new ApiError(-1, '注册失败,请重试');
}
return buildSession(response.data, credentials.email);
},
(Signature stays the same — just ensuring credentials now includes code via the updated type.)
- Step 3: Update the import in
authService.tsto includeSendEmailCodeRequest
Find:
import type {
AuthData,
GoogleAuthUrlData,
GoogleOAuthCallbackRequest,
LoginRequest,
RegisterRequest,
UserInfoData,
UserInfo,
} from '../types/api';
Replace with:
import type {
AuthData,
GoogleAuthUrlData,
GoogleOAuthCallbackRequest,
LoginRequest,
RegisterRequest,
SendEmailCodeRequest,
UserInfoData,
UserInfo,
} from '../types/api';
- Step 4: Commit
git add src/types/api.ts src/lib/authService.ts
git commit -m "feat: add sendEmailCode API and code field to RegisterRequest"
Task 6: Update AuthContext to pass code through signUp
Files:
-
Modify:
src/contexts/AuthContext.tsx -
Step 1: Update
signUpto accept and forwardcode
Find the signUp function/action in AuthContext.tsx. It currently calls authService.register({ email, password }). Update the signUp signature to accept a third code parameter and pass it:
signUp: async (email: string, password: string, code: string) => { ... }
Inside, change:
await authService.register({ email, password, code });
Also update the AuthContextValue interface (or wherever signUp type is declared) to:
signUp: (email: string, password: string, code: string) => Promise<{ error: string | null }>;
- Step 2: Commit
git add src/contexts/AuthContext.tsx
git commit -m "feat: thread verification code through AuthContext signUp"
Task 7: Add email verification UI to AuthModal
Files:
-
Modify:
src/components/AuthModal.tsx -
Step 1: Add verification code state variables
After the existing useState declarations in AuthModal, add:
const [verificationCode, setVerificationCode] = useState('');
const [codeSent, setCodeSent] = useState(false);
const [codeCountdown, setCodeCountdown] = useState(0);
const [sendingCode, setSendingCode] = useState(false);
const countdownRef = useRef<NodeJS.Timeout | null>(null);
Add useRef to the React import if not already there.
Also import authService at the top:
import { authService } from '../lib/authService';
- Step 2: Add
sendCodehandler
After the handleGoogleOAuth function, add:
const handleSendCode = async () => {
const normalizedEmail = email.trim();
if (!normalizedEmail || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizedEmail)) {
setFieldErrors(prev => ({ ...prev, email: t.auth.emailInvalid }));
return;
}
setSendingCode(true);
try {
await authService.sendEmailCode(normalizedEmail);
setCodeSent(true);
setCodeCountdown(60);
countdownRef.current = setInterval(() => {
setCodeCountdown(prev => {
if (prev <= 1) {
clearInterval(countdownRef.current!);
return 0;
}
return prev - 1;
});
}, 1000);
} catch {
setLocalError(t.auth.sendCodeFailed);
} finally {
setSendingCode(false);
}
};
Add a cleanup useEffect for the countdown interval:
useEffect(() => {
return () => {
if (countdownRef.current) clearInterval(countdownRef.current);
};
}, []);
- Step 3: Update
handleSubmitto validate code and pass it tosignUp
In the handleSubmit function, inside the if (mode === 'signup') validation block, add a check for the verification code:
if (!verificationCode.trim()) {
nextFieldErrors.verificationCode = t.auth.verificationCodeRequired;
}
Update fieldErrors type to include verificationCode:
const [fieldErrors, setFieldErrors] = useState<{
email?: string;
password?: string;
confirmPassword?: string;
verificationCode?: string;
}>({});
Update the signUp call to pass the code:
const result = mode === 'signup'
? await signUp(normalizedEmail, password, verificationCode.trim())
: await signIn(normalizedEmail, password);
- Step 4: Add Send Code button inline with the email field (signup mode only)
In the JSX, find the email <div style={s.fieldGroup}> block. Replace it with a version that adds the send-code button when in signup mode:
<div style={s.fieldGroup}>
<label htmlFor="auth-email" style={s.label}>{t.auth.email}</label>
<div style={{ display: 'flex', gap: '8px', alignItems: 'flex-start' }}>
<div style={{ flex: 1 }}>
<input
id="auth-email"
type="email"
value={email}
onChange={(e) => {
setEmail(e.target.value);
if (fieldErrors.email) setFieldErrors((prev) => ({ ...prev, email: undefined }));
}}
style={fieldErrors.email ? { ...s.input, ...s.inputError } : s.input}
placeholder="your@email.com"
required
disabled={isBusy}
/>
{fieldErrors.email && <p style={s.fieldError}>{fieldErrors.email}</p>}
{mode === 'signup' && <p style={s.fieldHint}>{t.auth.emailHint}</p>}
</div>
{mode === 'signup' && (
<button
type="button"
onClick={handleSendCode}
disabled={isBusy || sendingCode || codeCountdown > 0}
style={{
flexShrink: 0,
height: '42px',
padding: '0 12px',
background: codeCountdown > 0 ? '#F5DDD0' : '#C8622A',
color: codeCountdown > 0 ? '#AA9685' : 'white',
border: 'none',
borderRadius: '12px',
fontSize: '13px',
fontWeight: 600,
fontFamily: "'DM Sans', sans-serif",
cursor: codeCountdown > 0 || sendingCode ? 'not-allowed' : 'pointer',
whiteSpace: 'nowrap',
transition: 'all 0.2s',
}}
>
{codeCountdown > 0
? `${codeCountdown}s`
: codeSent
? t.auth.resendCode
: t.auth.sendCode}
</button>
)}
</div>
</div>
- Step 5: Add the verification code input field (signup mode, after confirm password)
After the confirm-password {mode === 'signup' && (...)} block, add:
{mode === 'signup' && (
<div style={s.fieldGroup}>
<label htmlFor="auth-code" style={s.label}>{t.auth.verificationCode}</label>
<input
id="auth-code"
type="text"
inputMode="numeric"
maxLength={6}
value={verificationCode}
onChange={(e) => {
setVerificationCode(e.target.value.replace(/\D/g, ''));
if (fieldErrors.verificationCode) setFieldErrors(prev => ({ ...prev, verificationCode: undefined }));
}}
style={fieldErrors.verificationCode ? { ...s.input, ...s.inputError } : s.input}
placeholder={t.auth.verificationCodePlaceholder}
disabled={isBusy}
/>
{fieldErrors.verificationCode
? <p style={s.fieldError}>{fieldErrors.verificationCode}</p>
: <p style={s.fieldHint}>{t.auth.verificationCodeHint}</p>
}
</div>
)}
- Step 6: Reset code state when switching to signin tab
In the setMode('signin') onClick handler, add resets:
onClick={() => {
setMode('signin');
setFieldErrors({});
setLocalError('');
setVerificationCode('');
setCodeSent(false);
setCodeCountdown(0);
if (countdownRef.current) clearInterval(countdownRef.current);
}}
- Step 7: Verify in browser — register flow should now require sending code first
npm run dev
Open /app, click register. Confirm:
- Email field has "Send Code" button
- Clicking it (with valid email) triggers send and starts countdown
- Verification code field appears below confirm-password
- Submitting without code shows validation error
- Login tab does not show the code field
- Step 8: Commit
git add src/components/AuthModal.tsx
git commit -m "feat: add email verification code step to registration flow"
Task 8: Final smoke test
- Step 1: Run type check
npm run typecheck
Expected: no errors
- Step 2: Run unit tests
npm run test
Expected: all pass (existing tests should still pass)
- Step 3: Manual end-to-end check
npm run dev
Verify:
- Workspace navbar has no logo — only Home link on left
- Sidebar bottom shows no login button when logged out
- History toggle is off by default, shows placeholder text
- Toggling history on while logged out opens auth modal
- After 3 guest uploads, mandatory auth modal appears (no close button)
- Register tab: Send Code button appears, countdown works, code field required
- Sign-in tab: no code field visible
- Logged-in user: history toggle on loads history; logout resets toggle to off