You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
5.9 KiB
210 lines
5.9 KiB
interface TimeLimitData { |
|
dailyLimit: number; // minutes (stored on server) |
|
dailyTimeUsed: number; // minutes (stored per-device in localStorage) |
|
lastResetDate: string; // ISO date string (YYYY-MM-DD) |
|
} |
|
|
|
const STORAGE_KEY = 'video_time_limit'; |
|
const DEFAULT_DAILY_LIMIT = 1; // 1 minute for testing |
|
|
|
// Cache for daily limit from server |
|
let cachedDailyLimit: number | null = null; |
|
let limitCacheTime: number = 0; |
|
const LIMIT_CACHE_DURATION = 5 * 60 * 1000; // 5 minutes |
|
|
|
/** |
|
* Get local date string in YYYY-MM-DD format (not UTC) |
|
* This ensures the daily reset happens at local midnight, not UTC midnight |
|
*/ |
|
function getLocalDateString(): string { |
|
const now = new Date(); |
|
const year = now.getFullYear(); |
|
const month = String(now.getMonth() + 1).padStart(2, '0'); |
|
const day = String(now.getDate()).padStart(2, '0'); |
|
return `${year}-${month}-${day}`; |
|
} |
|
|
|
/** |
|
* Get time limit data from localStorage (for usage tracking only) |
|
*/ |
|
function getTimeLimitData(): Omit<TimeLimitData, 'dailyLimit'> { |
|
try { |
|
const stored = localStorage.getItem(STORAGE_KEY); |
|
if (stored) { |
|
const data = JSON.parse(stored); |
|
return { |
|
dailyTimeUsed: data.dailyTimeUsed || 0, |
|
lastResetDate: data.lastResetDate || getLocalDateString() |
|
}; |
|
} |
|
} catch (e) { |
|
console.warn('Failed to parse time limit data from localStorage', e); |
|
} |
|
|
|
// Return default data |
|
return { |
|
dailyTimeUsed: 0, |
|
lastResetDate: getLocalDateString() |
|
}; |
|
} |
|
|
|
/** |
|
* Save time limit data to localStorage (for usage tracking only) |
|
*/ |
|
function saveTimeLimitData(data: Omit<TimeLimitData, 'dailyLimit'>): void { |
|
try { |
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); |
|
} catch (e) { |
|
console.warn('Failed to save time limit data to localStorage', e); |
|
} |
|
} |
|
|
|
/** |
|
* Check if we need to reset daily counter (new day) |
|
*/ |
|
function shouldResetDaily(): boolean { |
|
const data = getTimeLimitData(); |
|
const today = getLocalDateString(); |
|
return data.lastResetDate !== today; |
|
} |
|
|
|
/** |
|
* Reset daily counter if it's a new day |
|
*/ |
|
function resetIfNeeded(): void { |
|
if (shouldResetDaily()) { |
|
const data = getTimeLimitData(); |
|
data.dailyTimeUsed = 0; |
|
data.lastResetDate = getLocalDateString(); |
|
saveTimeLimitData(data); |
|
} |
|
} |
|
|
|
/** |
|
* Get current time limit configuration |
|
* Priority: Magic code settings > Server settings > Cached > Default |
|
*/ |
|
export async function getDailyLimit(): Promise<number> { |
|
// Check magic code settings first (highest priority) |
|
try { |
|
const { getMagicCodeSettings } = await import('./magicCodeService'); |
|
const magicCodeSettings = getMagicCodeSettings(); |
|
if (magicCodeSettings?.dailyTimeLimit !== null && magicCodeSettings.dailyTimeLimit !== undefined) { |
|
return magicCodeSettings.dailyTimeLimit; |
|
} |
|
} catch (error) { |
|
console.warn('Failed to check magic code settings:', error); |
|
} |
|
|
|
// Return cached value if still valid |
|
const now = Date.now(); |
|
if (cachedDailyLimit !== null && (now - limitCacheTime) < LIMIT_CACHE_DURATION) { |
|
return cachedDailyLimit; |
|
} |
|
|
|
try { |
|
const { settingsApi } = await import('./apiClient'); |
|
const response = await settingsApi.getTimeLimit(); |
|
const limit = response.data.dailyLimit; |
|
cachedDailyLimit = limit; |
|
limitCacheTime = now; |
|
return limit; |
|
} catch (error) { |
|
console.warn('Failed to fetch daily limit from server, using cached/default:', error); |
|
// Return cached value or default |
|
return cachedDailyLimit ?? DEFAULT_DAILY_LIMIT; |
|
} |
|
} |
|
|
|
/** |
|
* Set daily time limit (in minutes) on server |
|
*/ |
|
export async function setDailyLimit(minutes: number): Promise<void> { |
|
try { |
|
const { settingsApi } = await import('./apiClient'); |
|
await settingsApi.setTimeLimit(minutes); |
|
cachedDailyLimit = minutes; |
|
limitCacheTime = Date.now(); |
|
} catch (error) { |
|
console.error('Failed to set daily limit on server:', error); |
|
throw error; |
|
} |
|
} |
|
|
|
/** |
|
* Synchronous version for use in hooks (uses cached value or magic code settings) |
|
* Note: Magic code settings are checked synchronously from localStorage |
|
*/ |
|
export function getDailyLimitSync(): number { |
|
// Check magic code settings first (highest priority) |
|
// We can access localStorage synchronously |
|
try { |
|
const MAGIC_CODE_SETTINGS_KEY = 'magic_code_settings'; |
|
const stored = localStorage.getItem(MAGIC_CODE_SETTINGS_KEY); |
|
if (stored) { |
|
const magicCodeSettings = JSON.parse(stored); |
|
if (magicCodeSettings?.dailyTimeLimit !== null && magicCodeSettings.dailyTimeLimit !== undefined) { |
|
return magicCodeSettings.dailyTimeLimit; |
|
} |
|
} |
|
} catch (error) { |
|
// Ignore errors in sync context |
|
} |
|
|
|
return cachedDailyLimit ?? DEFAULT_DAILY_LIMIT; |
|
} |
|
|
|
/** |
|
* Get time used today (in minutes) - per device |
|
*/ |
|
export function getTimeUsedToday(): number { |
|
resetIfNeeded(); |
|
const data = getTimeLimitData(); |
|
return data.dailyTimeUsed; |
|
} |
|
|
|
/** |
|
* Get remaining time today (in minutes) |
|
* Note: Uses cached limit value |
|
*/ |
|
export function getRemainingTimeToday(): number { |
|
resetIfNeeded(); |
|
const limit = getDailyLimitSync(); |
|
const used = getTimeUsedToday(); |
|
return Math.max(0, limit - used); |
|
} |
|
|
|
/** |
|
* Check if daily limit has been reached |
|
* Note: Uses cached limit value |
|
*/ |
|
export function isLimitReached(): boolean { |
|
resetIfNeeded(); |
|
return getRemainingTimeToday() <= 0; |
|
} |
|
|
|
/** |
|
* Add time spent (in seconds) to the daily counter |
|
* Note: Uses cached limit value to cap the usage |
|
*/ |
|
export function addTimeSpent(seconds: number): void { |
|
resetIfNeeded(); |
|
const data = getTimeLimitData(); |
|
const minutesToAdd = seconds / 60; |
|
const limit = getDailyLimitSync(); |
|
data.dailyTimeUsed = Math.min( |
|
limit, |
|
data.dailyTimeUsed + minutesToAdd |
|
); |
|
saveTimeLimitData(data); |
|
} |
|
|
|
/** |
|
* Reset daily counter (for testing/admin purposes) |
|
*/ |
|
export function resetDailyCounter(): void { |
|
const data = getTimeLimitData(); |
|
data.dailyTimeUsed = 0; |
|
data.lastResetDate = getLocalDateString(); |
|
saveTimeLimitData(data); |
|
}
|
|
|