Changes
This commit is contained in:
@@ -11,12 +11,27 @@ export interface Achievement {
|
||||
secret?: boolean;
|
||||
}
|
||||
|
||||
// Track AI message count in localStorage
|
||||
const AI_MESSAGE_COUNT_KEY = 'ai-message-count';
|
||||
|
||||
export const incrementAiMessageCount = (): number => {
|
||||
const current = parseInt(localStorage.getItem(AI_MESSAGE_COUNT_KEY) || '0', 10);
|
||||
const newCount = current + 1;
|
||||
localStorage.setItem(AI_MESSAGE_COUNT_KEY, newCount.toString());
|
||||
return newCount;
|
||||
};
|
||||
|
||||
export const getAiMessageCount = (): number => {
|
||||
return parseInt(localStorage.getItem(AI_MESSAGE_COUNT_KEY) || '0', 10);
|
||||
};
|
||||
|
||||
interface AchievementsContextType {
|
||||
achievements: Achievement[];
|
||||
unlockAchievement: (id: string) => void;
|
||||
getUnlockedCount: () => number;
|
||||
getTotalCount: () => number;
|
||||
timeOnSite: number;
|
||||
checkAiAchievements: () => void;
|
||||
}
|
||||
|
||||
const defaultAchievements: Achievement[] = [
|
||||
@@ -214,6 +229,13 @@ export const AchievementsProvider = ({ children }: { children: ReactNode }) => {
|
||||
return achievements.length;
|
||||
}, [achievements]);
|
||||
|
||||
// Check AI message achievements
|
||||
const checkAiAchievements = useCallback(() => {
|
||||
const count = getAiMessageCount();
|
||||
if (count >= 5) unlockAchievement('ai_conversation');
|
||||
if (count >= 20) unlockAchievement('ai_long_chat');
|
||||
}, [unlockAchievement]);
|
||||
|
||||
// Save achievements
|
||||
useEffect(() => {
|
||||
localStorage.setItem('achievements', JSON.stringify(achievements));
|
||||
@@ -227,6 +249,7 @@ export const AchievementsProvider = ({ children }: { children: ReactNode }) => {
|
||||
getUnlockedCount,
|
||||
getTotalCount,
|
||||
timeOnSite,
|
||||
checkAiAchievements,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -7,7 +7,7 @@ interface SettingsContextType {
|
||||
setCrtEnabled: (enabled: boolean) => void;
|
||||
soundEnabled: boolean;
|
||||
setSoundEnabled: (enabled: boolean) => void;
|
||||
cryptoConsent: boolean;
|
||||
cryptoConsent: boolean | null; // null = never asked this session
|
||||
setCryptoConsent: (consent: boolean) => void;
|
||||
playSound: (type: SoundType) => void;
|
||||
hashrate: number;
|
||||
@@ -16,6 +16,8 @@ interface SettingsContextType {
|
||||
setTotalHashes: (hashes: number) => void;
|
||||
acceptedHashes: number;
|
||||
setAcceptedHashes: (hashes: number) => void;
|
||||
audioBlocked: boolean;
|
||||
resetAudioContext: () => void;
|
||||
}
|
||||
|
||||
const SettingsContext = createContext<SettingsContextType | undefined>(undefined);
|
||||
@@ -31,11 +33,30 @@ export const SettingsProvider = ({ children }: { children: ReactNode }) => {
|
||||
return saved !== null ? JSON.parse(saved) : true;
|
||||
});
|
||||
|
||||
const [cryptoConsent, setCryptoConsent] = useState(() => {
|
||||
// Crypto consent: null = needs prompt, true = accepted, false = declined
|
||||
// On session start, if user previously declined, we reset to null to re-prompt
|
||||
const [cryptoConsent, setCryptoConsentState] = useState<boolean | null>(() => {
|
||||
const saved = localStorage.getItem('cryptoConsent');
|
||||
return saved !== null ? JSON.parse(saved) : false;
|
||||
if (saved === null) return null; // Never set
|
||||
const parsed = JSON.parse(saved);
|
||||
// If declined (false), return null to re-prompt on new session
|
||||
// We use sessionStorage to track if we've already shown the prompt this session
|
||||
const sessionPrompted = sessionStorage.getItem('cryptoConsentPrompted');
|
||||
if (parsed === false && !sessionPrompted) {
|
||||
return null; // Re-prompt
|
||||
}
|
||||
return parsed;
|
||||
});
|
||||
|
||||
const setCryptoConsent = (consent: boolean) => {
|
||||
setCryptoConsentState(consent);
|
||||
localStorage.setItem('cryptoConsent', JSON.stringify(consent));
|
||||
sessionStorage.setItem('cryptoConsentPrompted', 'true');
|
||||
};
|
||||
|
||||
const [audioBlocked, setAudioBlocked] = useState(false);
|
||||
const [audioFailCount, setAudioFailCount] = useState(0);
|
||||
|
||||
const [hashrate, setHashrate] = useState(0);
|
||||
const [totalHashes, setTotalHashes] = useState(0);
|
||||
const [acceptedHashes, setAcceptedHashes] = useState(0);
|
||||
@@ -81,9 +102,15 @@ export const SettingsProvider = ({ children }: { children: ReactNode }) => {
|
||||
localStorage.setItem('soundEnabled', JSON.stringify(soundEnabled));
|
||||
}, [soundEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem('cryptoConsent', JSON.stringify(cryptoConsent));
|
||||
}, [cryptoConsent]);
|
||||
// Reset audio context to try again after user interaction
|
||||
const resetAudioContext = useCallback(() => {
|
||||
if (audioContextRef.current) {
|
||||
audioContextRef.current.close();
|
||||
audioContextRef.current = null;
|
||||
}
|
||||
setAudioBlocked(false);
|
||||
setAudioFailCount(0);
|
||||
}, []);
|
||||
|
||||
const playSound = useCallback((type: SoundType) => {
|
||||
// Use ref to ensure we always have the latest value
|
||||
@@ -91,6 +118,21 @@ export const SettingsProvider = ({ children }: { children: ReactNode }) => {
|
||||
|
||||
try {
|
||||
const audioContext = getAudioContext();
|
||||
|
||||
// Check if context is blocked
|
||||
if (audioContext.state === 'suspended') {
|
||||
audioContext.resume().catch(() => {
|
||||
setAudioFailCount(prev => {
|
||||
const newCount = prev + 1;
|
||||
if (newCount >= 3) {
|
||||
setAudioBlocked(true);
|
||||
}
|
||||
return newCount;
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const oscillator = audioContext.createOscillator();
|
||||
const gainNode = audioContext.createGain();
|
||||
|
||||
@@ -142,11 +184,23 @@ export const SettingsProvider = ({ children }: { children: ReactNode }) => {
|
||||
oscillator.stop(now + 0.15);
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset fail count on success
|
||||
if (audioFailCount > 0) {
|
||||
setAudioFailCount(0);
|
||||
setAudioBlocked(false);
|
||||
}
|
||||
} catch (e) {
|
||||
// Silently fail if audio context has issues
|
||||
console.warn('Audio playback failed:', e);
|
||||
setAudioFailCount(prev => {
|
||||
const newCount = prev + 1;
|
||||
if (newCount >= 3) {
|
||||
setAudioBlocked(true);
|
||||
}
|
||||
return newCount;
|
||||
});
|
||||
}
|
||||
}, [getAudioContext]);
|
||||
}, [getAudioContext, audioFailCount]);
|
||||
|
||||
return (
|
||||
<SettingsContext.Provider
|
||||
@@ -164,6 +218,8 @@ export const SettingsProvider = ({ children }: { children: ReactNode }) => {
|
||||
setTotalHashes,
|
||||
acceptedHashes,
|
||||
setAcceptedHashes,
|
||||
audioBlocked,
|
||||
resetAudioContext,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user