Skip to content

Commit 2e44ae0

Browse files
eureka0928a7m-1stWendong-Fan
authored
refactor: move privacy consent from ChatBox to Login/SignUp (#1239)
Co-authored-by: a7m-1st <Ahmed.jimi.awelkeir500@gmail.com> Co-authored-by: Wendong-Fan <133094783+Wendong-Fan@users.noreply.github.com>
1 parent caa5742 commit 2e44ae0

41 files changed

Lines changed: 277 additions & 1030 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/get_started/quick_start.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,6 @@ Click the gear icon in the top-right corner to open Settings. Here’s a brief o
153153

154154
![General](/docs/images/quickstart_settings_general.png)
155155

156-
### **Privacy**
157-
158-
Your privacy is important to us! Because Eigent agents operate on your desktop, they may need permissions to take screenshots or access local files to complete tasks. You can enable and manage all these permissions here.
159-
160-
![Privacy](/docs/images/quickstart_settings_privacy.png)
161-
162156
### **Models**
163157

164158
<aside>

server/app/model/user/privacy.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class UserPrivacySettings(BaseModel):
3232
access_local_software: bool | None = False
3333
access_your_address: bool | None = False
3434
password_storage: bool | None = False
35+
help_improve: bool | None = False
3536

3637
# Fields that must all be True for the user to proceed
3738
REQUIRED_FIELDS: ClassVar[list[str]] = [

src/assets/privacy_settings.png

-371 KB
Binary file not shown.

src/components/ChatBox/BottomBox/InputBox.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ export interface InputboxProps {
7676
textareaRef?: React.RefObject<HTMLTextAreaElement>;
7777
/** Allow drag and drop */
7878
allowDragDrop?: boolean;
79-
/** Privacy mode enabled */
80-
privacy?: boolean;
8179
/** Use cloud model in dev */
8280
useCloudModelInDev?: boolean;
8381
/** Callback when trigger is being created (for placeholder) */
@@ -139,7 +137,6 @@ export const Inputbox = ({
139137
className,
140138
textareaRef: externalTextareaRef,
141139
allowDragDrop = false,
142-
privacy = true,
143140
useCloudModelInDev = false,
144141
onTriggerCreating,
145142
onTriggerCreated,
@@ -275,7 +272,7 @@ export const Inputbox = ({
275272
};
276273

277274
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
278-
if (!allowDragDrop || !privacy || useCloudModelInDev) return;
275+
if (!allowDragDrop || useCloudModelInDev) return;
279276
if (!isFileDrag(e)) return;
280277
e.preventDefault();
281278
e.stopPropagation();
@@ -284,7 +281,7 @@ export const Inputbox = ({
284281
};
285282

286283
const handleDragEnter = (e: React.DragEvent<HTMLDivElement>) => {
287-
if (!allowDragDrop || !privacy || useCloudModelInDev) return;
284+
if (!allowDragDrop || useCloudModelInDev) return;
288285
if (!isFileDrag(e)) return;
289286
e.preventDefault();
290287
e.stopPropagation();
@@ -304,8 +301,7 @@ export const Inputbox = ({
304301
e.stopPropagation();
305302
setIsDragging(false);
306303
dragCounter.current = 0;
307-
if (!allowDragDrop || !privacy || useCloudModelInDev) return;
308-
304+
if (!allowDragDrop || useCloudModelInDev) return;
309305
try {
310306
const dropped = Array.from(e.dataTransfer?.files || []);
311307
if (dropped.length === 0) return;
@@ -524,7 +520,7 @@ export const Inputbox = ({
524520
size="icon"
525521
className="rounded-lg shadow-none"
526522
onClick={onAddFile}
527-
disabled={disabled || !privacy || useCloudModelInDev}
523+
disabled={disabled || useCloudModelInDev}
528524
>
529525
<Plus size={16} className="text-icon-primary" />
530526
</Button>
@@ -596,7 +592,6 @@ export const Inputbox = ({
596592
onFilesChange,
597593
onAddFile,
598594
disabled,
599-
privacy,
600595
useCloudModelInDev,
601596
}}
602597
onClose={() => handleExpandedDialogChange(false)}

src/components/ChatBox/index.tsx

Lines changed: 66 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
fetchPut,
1919
proxyFetchDelete,
2020
proxyFetchGet,
21-
proxyFetchPut,
2221
} from '@/api/http';
2322
import useChatStoreAdapter from '@/hooks/useChatStoreAdapter';
2423
import { generateUniqueId, replayActiveTask } from '@/lib';
@@ -29,7 +28,7 @@ import { AgentStep, ChatTaskStatus } from '@/types/constants';
2928
import { TriangleAlert } from 'lucide-react';
3029
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3130
import { useTranslation } from 'react-i18next';
32-
import { useNavigate, useSearchParams } from 'react-router-dom';
31+
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
3332
import { toast } from 'sonner';
3433
import BottomBox from './BottomBox';
3534
import { HeaderBox } from './HeaderBox';
@@ -43,12 +42,11 @@ export default function ChatBox(): JSX.Element {
4342

4443
const { t } = useTranslation();
4544
const textareaRef = useRef<HTMLTextAreaElement>(null);
46-
const [hasModel, setHasModel] = useState(true);
45+
const [hasModel, setHasModel] = useState(false);
46+
const [isConfigLoaded, setIsConfigLoaded] = useState(false);
4747
const scrollContainerRef = useRef<HTMLDivElement>(null);
48-
const [privacy, setPrivacy] = useState<any>(false);
4948
const [_hasSearchKey, setHasSearchKey] = useState<any>(false);
5049
const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
51-
// const [privacyDialogOpen, setPrivacyDialogOpen] = useState(false);
5250
const { modelType } = useAuthStore();
5351
const [useCloudModelInDev, setUseCloudModelInDev] = useState(false);
5452
useEffect(() => {
@@ -62,13 +60,43 @@ export default function ChatBox(): JSX.Element {
6260
setUseCloudModelInDev(false);
6361
}
6462
}, [modelType]);
65-
useEffect(() => {
66-
proxyFetchGet('/api/user/privacy')
67-
.then((res) => {
68-
setPrivacy(res.all_required_granted);
69-
})
70-
.catch((err) => console.error('Failed to fetch settings:', err));
7163

64+
const [searchParams, setSearchParams] = useSearchParams();
65+
const share_token = searchParams.get('share_token');
66+
const skill_prompt = searchParams.get('skill_prompt');
67+
68+
const handleSendRef = useRef<
69+
((messageStr?: string, taskId?: string) => Promise<void>) | null
70+
>(null);
71+
72+
const navigate = useNavigate();
73+
const location = useLocation();
74+
75+
// Shared function to check model configuration
76+
const checkModelConfig = useCallback(async () => {
77+
try {
78+
if (modelType === 'cloud') {
79+
// For cloud model, check if API key exists
80+
const res = await proxyFetchGet('/api/user/key');
81+
setHasModel(!!res.value);
82+
} else if (modelType === 'local' || modelType === 'custom') {
83+
// For local/custom model, check if provider exists
84+
const res = await proxyFetchGet('/api/providers', { prefer: true });
85+
const providerList = res.items || [];
86+
setHasModel(providerList.length > 0);
87+
} else {
88+
setHasModel(false);
89+
}
90+
} catch (err) {
91+
console.error('Failed to check model config:', err);
92+
setHasModel(false);
93+
} finally {
94+
setIsConfigLoaded(true);
95+
}
96+
}, [modelType]);
97+
98+
// Check model config on mount and when modelType changes
99+
useEffect(() => {
72100
proxyFetchGet('/api/configs')
73101
.then((configsRes) => {
74102
const configs = Array.isArray(configsRes) ? configsRes : [];
@@ -81,34 +109,29 @@ export default function ChatBox(): JSX.Element {
81109
if (_hasApiKey && _hasApiId) setHasSearchKey(true);
82110
})
83111
.catch((err) => console.error('Failed to fetch configs:', err));
84-
}, []);
85112

86-
// Refresh privacy status when dialog closes
87-
// useEffect(() => {
88-
// if (!privacyDialogOpen) {
89-
// proxyFetchGet("/api/user/privacy")
90-
// .then((res) => {
91-
// let _privacy = 0;
92-
// Object.keys(res).forEach((key) => {
93-
// if (!res[key]) {
94-
// _privacy++;
95-
// return;
96-
// }
97-
// });
98-
// setPrivacy(_privacy === 0 ? true : false);
99-
// })
100-
// .catch((err) => console.error("Failed to fetch settings:", err));
101-
// }
102-
// }, [privacyDialogOpen]);
103-
const [searchParams, setSearchParams] = useSearchParams();
104-
const share_token = searchParams.get('share_token');
105-
const skill_prompt = searchParams.get('skill_prompt');
113+
checkModelConfig();
114+
}, [modelType, checkModelConfig]);
106115

107-
const handleSendRef = useRef<
108-
((messageStr?: string, taskId?: string) => Promise<void>) | null
109-
>(null);
116+
// Re-check model config when returning from settings page
117+
useEffect(() => {
118+
// Check when location changes (user navigates)
119+
if (location.pathname === '/') {
120+
checkModelConfig();
121+
}
122+
}, [location.pathname, checkModelConfig]);
110123

111-
const navigate = useNavigate();
124+
// Also check when window gains focus (user returns from settings)
125+
useEffect(() => {
126+
const handleFocus = () => {
127+
checkModelConfig();
128+
};
129+
130+
window.addEventListener('focus', handleFocus);
131+
return () => {
132+
window.removeEventListener('focus', handleFocus);
133+
};
134+
}, [checkModelConfig]);
112135

113136
// Task time tracking
114137
const [taskTime, setTaskTime] = useState(
@@ -226,17 +249,15 @@ export default function ChatBox(): JSX.Element {
226249

227250
if (isTaskBusy) return true;
228251

229-
// Standard checks - check model first, then privacy
252+
// Standard checks - check model
230253
if (!hasModel) return true;
231-
if (!privacy) return true;
232254
if (useCloudModelInDev) return true;
233255
if (task.isContextExceeded) return true;
234256

235257
return false;
236258
}, [
237259
chatStore?.activeTaskId,
238260
chatStore?.tasks,
239-
privacy,
240261
hasModel,
241262
useCloudModelInDev,
242263
isTaskBusy,
@@ -257,10 +278,6 @@ export default function ChatBox(): JSX.Element {
257278
navigate('/history?tab=agents');
258279
return;
259280
}
260-
if (!privacy) {
261-
toast.error('Please accept the privacy policy first.');
262-
return;
263-
}
264281

265282
let _token: string = token.split('__')[0];
266283
let taskId: string = token.split('__')[1];
@@ -290,7 +307,7 @@ export default function ChatBox(): JSX.Element {
290307
}
291308
}
292309
},
293-
[chatStore, projectStore.activeProjectId, hasModel, privacy, navigate]
310+
[chatStore, projectStore.activeProjectId, hasModel, navigate]
294311
);
295312

296313
// Handle skill_prompt from URL - pre-fill message when navigating from Skills page
@@ -358,16 +375,12 @@ export default function ChatBox(): JSX.Element {
358375
const _taskId = taskId || chatStore.activeTaskId;
359376
if (message.trim() === '' && !messageStr) return;
360377

361-
// Check model first, then privacy
378+
// Check model configuration
362379
if (!hasModel) {
363380
toast.error('Please select a model first.');
364381
navigate('/history?tab=agents');
365382
return;
366383
}
367-
if (!privacy) {
368-
toast.error('Please accept the privacy policy first.');
369-
return;
370-
}
371384
const tempMessageContent = messageStr || message;
372385

373386
if (executionId && projectStore.activeProjectId) {
@@ -542,23 +555,6 @@ export default function ChatBox(): JSX.Element {
542555
setMessage('');
543556
}
544557
} else {
545-
if (!privacy) {
546-
const API_FIELDS = [
547-
'take_screenshot',
548-
'access_local_software',
549-
'access_your_address',
550-
'password_storage',
551-
];
552-
const requestData = {
553-
[API_FIELDS[0]]: true,
554-
[API_FIELDS[1]]: true,
555-
[API_FIELDS[2]]: true,
556-
[API_FIELDS[3]]: true,
557-
};
558-
proxyFetchPut('/api/user/privacy', requestData);
559-
setPrivacy(true);
560-
}
561-
562558
setTimeout(() => {
563559
scrollToBottom();
564560
}, 200);
@@ -670,11 +666,11 @@ export default function ChatBox(): JSX.Element {
670666
}, [projectStore]);
671667

672668
useEffect(() => {
673-
// Wait for both config and privacy to be loaded before handling share token
674-
if (share_token) {
669+
// Wait for config to be loaded before handling share token
670+
if (share_token && isConfigLoaded) {
675671
handleSendShare(share_token);
676672
}
677-
}, [share_token, handleSendShare]);
673+
}, [share_token, isConfigLoaded, handleSendShare]);
678674

679675
if (!chatStore) {
680676
return <div>Loading...</div>;
@@ -1053,7 +1049,6 @@ export default function ChatBox(): JSX.Element {
10531049
disabled: isInputDisabled,
10541050
textareaRef: textareaRef,
10551051
allowDragDrop: true,
1056-
privacy: privacy,
10571052
useCloudModelInDev: useCloudModelInDev,
10581053
}}
10591054
/>
@@ -1076,61 +1071,7 @@ export default function ChatBox(): JSX.Element {
10761071
</div>
10771072
</div>
10781073
) : null}
1079-
{hasModel && !privacy ? (
1080-
<div className="flex items-center gap-2">
1081-
<div
1082-
onClick={(e) => {
1083-
const target = e.target as HTMLElement;
1084-
if (target.tagName === 'A') {
1085-
return;
1086-
}
1087-
const API_FIELDS = [
1088-
'take_screenshot',
1089-
'access_local_software',
1090-
'access_your_address',
1091-
'password_storage',
1092-
];
1093-
const requestData = {
1094-
[API_FIELDS[0]]: true,
1095-
[API_FIELDS[1]]: true,
1096-
[API_FIELDS[2]]: true,
1097-
[API_FIELDS[3]]: true,
1098-
};
1099-
proxyFetchPut('/api/user/privacy', requestData);
1100-
setPrivacy(true);
1101-
}}
1102-
className="flex cursor-pointer items-center gap-1 rounded-md bg-surface-information px-sm py-xs"
1103-
>
1104-
<TriangleAlert
1105-
size={20}
1106-
className="text-icon-information"
1107-
/>
1108-
<span className="flex-1 text-xs font-medium leading-[20px] text-text-information">
1109-
{t('layout.by-messaging-eigent')}{' '}
1110-
<a
1111-
href="https://www.eigent.ai/terms-of-use"
1112-
target="_blank"
1113-
className="text-text-information underline"
1114-
onClick={(e) => e.stopPropagation()}
1115-
rel="noreferrer"
1116-
>
1117-
{t('layout.terms-of-use')}
1118-
</a>{' '}
1119-
{t('layout.and')}{' '}
1120-
<a
1121-
href="https://www.eigent.ai/privacy-policy"
1122-
target="_blank"
1123-
className="text-text-information underline"
1124-
onClick={(e) => e.stopPropagation()}
1125-
rel="noreferrer"
1126-
>
1127-
{t('layout.privacy-policy')}
1128-
</a>
1129-
.
1130-
</span>
1131-
</div>
1132-
</div>
1133-
) : (
1074+
{hasModel && (
11341075
<div className="mr-2 flex flex-col items-center gap-2">
11351076
{[
11361077
{
@@ -1210,7 +1151,6 @@ export default function ChatBox(): JSX.Element {
12101151
disabled: isInputDisabled,
12111152
textareaRef: textareaRef,
12121153
allowDragDrop: hasAnyMessages,
1213-
privacy: hasAnyMessages ? privacy : true,
12141154
useCloudModelInDev: useCloudModelInDev,
12151155
}}
12161156
/>

0 commit comments

Comments
 (0)