Skip to content
Merged
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
42 changes: 36 additions & 6 deletions src/components/performance/PrefetchingEngine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React, { useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { isSlowConnection } from '../../utils/performanceUtils';
import { useStore } from '../../store/stateManager';

interface PrefetchingEngineProps {
strategies?: ('hover' | 'proximity' | 'intent')[];
Expand All @@ -13,33 +14,62 @@ interface PrefetchingEngineProps {
*/
const PrefetchingEngine: React.FC<PrefetchingEngineProps> = ({ strategies = ['hover'] }) => {
const router = useRouter();
const prefetchingEnabled = useStore((state) => state.user.preferences.prefetching);

const handleIntent = useCallback(
(href: string) => {
if (isSlowConnection()) {
if (!prefetchingEnabled || isSlowConnection()) {
return;
}
router.prefetch(href);
},
[router],
[router, prefetchingEnabled],
);

useEffect(() => {
if (!strategies.includes('hover')) return;
if (!prefetchingEnabled || isSlowConnection() || !strategies.includes('hover')) return;

const prefetched = new Set<string>();
let hoverTimeout: NodeJS.Timeout;

const handleMouseOver = (e: MouseEvent) => {
const target = e.target as HTMLElement;
const link = target.closest('a');

if (link && link.href && link.origin === window.location.origin) {
const href = link.pathname;
handleIntent(href);
if (prefetched.has(href)) return;

clearTimeout(hoverTimeout);
hoverTimeout = setTimeout(() => {
handleIntent(href);
prefetched.add(href);
}, 50); // 50ms intent delay
}
};

const handleTouchStart = (e: TouchEvent) => {
const target = e.target as HTMLElement;
const link = target.closest('a');

if (link && link.href && link.origin === window.location.origin) {
const href = link.pathname;
if (!prefetched.has(href)) {
handleIntent(href);
prefetched.add(href);
}
}
};

document.addEventListener('mouseover', handleMouseOver);
return () => document.removeEventListener('mouseover', handleMouseOver);
}, [strategies, handleIntent]);
document.addEventListener('touchstart', handleTouchStart, { passive: true });

return () => {
document.removeEventListener('mouseover', handleMouseOver);
document.removeEventListener('touchstart', handleTouchStart);
clearTimeout(hoverTimeout);
};
}, [strategies, handleIntent, prefetchingEnabled]);

// Proximity strategy could be implemented with Intersection Observer on all links

Expand Down
16 changes: 16 additions & 0 deletions src/components/profile/PreferencesSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ export default function PreferencesSection() {
</label>
</div>
</div>
<div>
<span className="block text-sm font-medium text-gray-700 mb-2">Performance</span>
<label className="flex items-center space-x-3 cursor-pointer">
<input
type="checkbox"
{...register('prefetching')}
className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
/>
<div className="flex flex-col">
<span className="text-gray-700">Enable Smart Prefetching</span>
<span className="text-xs text-gray-500">
Automatically load pages before you click. Disabled on slow connections.
</span>
</div>
</label>
</div>
</div>
</div>
</div>
Expand Down
24 changes: 19 additions & 5 deletions src/components/profile/ProfileEditForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useProfileUpdate } from '../../hooks/useProfileUpdate';
import { User, Mail, FileText } from 'lucide-react';
import ImageUploader from '../shared/ImageUploader';
import PreferencesSection from './PreferencesSection';
import { useStore } from '../../store/stateManager';

// Schema definition
const profileSchema = z.object({
Expand All @@ -19,24 +20,29 @@ const profileSchema = z.object({
push: z.boolean(),
}),
theme: z.enum(['light', 'dark']),
prefetching: z.boolean(),
});

type ProfileFormData = z.infer<typeof profileSchema>;

export default function ProfileEditForm() {
const { updateProfile, isLoading } = useProfileUpdate();
const user = useStore((state) => state.user);
const setPreferences = useStore((state) => state.setPreferences);
const setUser = useStore((state) => state.setUser);

const methods = useForm<ProfileFormData>({
resolver: zodResolver(profileSchema),
defaultValues: {
name: 'John Doe',
email: 'john@example.com',
name: user.name || 'John Doe',
email: user.id ? 'user@example.com' : 'john@example.com', // Placeholder if no user
bio: 'Lifelong learner and enthusiast.',
notifications: {
email: true,
email: user.preferences.notifications,
push: false,
},
theme: 'light',
theme: user.preferences.theme,
prefetching: user.preferences.prefetching,
},
});

Expand All @@ -48,7 +54,15 @@ export default function ProfileEditForm() {
} = methods;

const onSubmit = async (data: ProfileFormData) => {
await updateProfile(data);
const success = await updateProfile(data);
if (success) {
setUser({ name: data.name });
setPreferences({
theme: data.theme,
notifications: data.notifications.email,
prefetching: data.prefetching,
});
}
};

const handleImageSelect = (file: File) => {
Expand Down
12 changes: 7 additions & 5 deletions src/store/stateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import { stateLogger } from './devTools';
interface UserState {
id: string | null;
name: string | null;
preferences: {
theme: 'light' | 'dark';
language: string;
notifications: boolean;
};
preferences: {
theme: 'light' | 'dark';
language: string;
notifications: boolean;
prefetching: boolean;
};
}

interface AppState {
Expand Down Expand Up @@ -49,6 +50,7 @@ export const useStore = create<StoreState>()(
theme: 'light' as 'light' | 'dark',
language: 'en',
notifications: true,
prefetching: true,
},
},
app: {
Expand Down
Loading