171 lines
5.9 KiB
JavaScript
171 lines
5.9 KiB
JavaScript
// src/hooks/usePermissionGuide.js
|
|
/**
|
|
* 通知权限引导管理 Hook
|
|
*
|
|
* 功能:
|
|
* - 管理多个引导场景的显示状态
|
|
* - 使用 localStorage 持久化记录
|
|
* - 支持定期提醒策略
|
|
*/
|
|
|
|
import { useState, useCallback, useEffect } from 'react';
|
|
import { logger } from '../utils/logger';
|
|
|
|
// 引导场景类型
|
|
export const GUIDE_TYPES = {
|
|
WELCOME: 'welcome', // 首次登录欢迎引导
|
|
COMMUNITY: 'community', // 社区功能引导
|
|
FIRST_FOLLOW: 'first_follow', // 首次关注事件引导
|
|
PERIODIC: 'periodic', // 定期提醒
|
|
};
|
|
|
|
// localStorage 键名
|
|
const STORAGE_KEYS = {
|
|
SHOWN_GUIDES: 'notification_guides_shown',
|
|
LAST_PERIODIC: 'notification_last_periodic_prompt',
|
|
TOTAL_PROMPTS: 'notification_total_prompts',
|
|
};
|
|
|
|
// 定期提醒间隔(毫秒)
|
|
const PERIODIC_INTERVAL = 3 * 24 * 60 * 60 * 1000; // 3 天
|
|
const MAX_PERIODIC_PROMPTS = 3; // 最多提醒 3 次
|
|
|
|
/**
|
|
* 权限引导管理 Hook
|
|
*/
|
|
export function usePermissionGuide() {
|
|
const [shownGuides, setShownGuides] = useState(() => {
|
|
try {
|
|
const stored = localStorage.getItem(STORAGE_KEYS.SHOWN_GUIDES);
|
|
return stored ? JSON.parse(stored) : [];
|
|
} catch (error) {
|
|
logger.error('usePermissionGuide', 'Failed to load shown guides', error);
|
|
return [];
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 检查是否应该显示某个引导
|
|
* @param {string} guideType - 引导类型
|
|
* @returns {boolean}
|
|
*/
|
|
const shouldShowGuide = useCallback((guideType) => {
|
|
// 已经显示过的引导不再显示
|
|
if (shownGuides.includes(guideType)) {
|
|
return false;
|
|
}
|
|
|
|
// 特殊逻辑:定期提醒
|
|
if (guideType === GUIDE_TYPES.PERIODIC) {
|
|
try {
|
|
const lastPrompt = localStorage.getItem(STORAGE_KEYS.LAST_PERIODIC);
|
|
const totalPrompts = parseInt(localStorage.getItem(STORAGE_KEYS.TOTAL_PROMPTS) || '0', 10);
|
|
|
|
// 超过最大提醒次数
|
|
if (totalPrompts >= MAX_PERIODIC_PROMPTS) {
|
|
logger.debug('usePermissionGuide', 'Periodic prompts limit reached', { totalPrompts });
|
|
return false;
|
|
}
|
|
|
|
// 未到提醒间隔
|
|
if (lastPrompt) {
|
|
const elapsed = Date.now() - parseInt(lastPrompt, 10);
|
|
if (elapsed < PERIODIC_INTERVAL) {
|
|
logger.debug('usePermissionGuide', 'Periodic interval not reached', {
|
|
elapsed: Math.round(elapsed / 1000 / 60 / 60), // 小时
|
|
required: Math.round(PERIODIC_INTERVAL / 1000 / 60 / 60)
|
|
});
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
} catch (error) {
|
|
logger.error('usePermissionGuide', 'Failed to check periodic guide', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}, [shownGuides]);
|
|
|
|
/**
|
|
* 标记引导已显示
|
|
* @param {string} guideType - 引导类型
|
|
*/
|
|
const markGuideAsShown = useCallback((guideType) => {
|
|
try {
|
|
// 更新状态
|
|
setShownGuides(prev => {
|
|
if (prev.includes(guideType)) {
|
|
return prev;
|
|
}
|
|
const updated = [...prev, guideType];
|
|
// 持久化
|
|
localStorage.setItem(STORAGE_KEYS.SHOWN_GUIDES, JSON.stringify(updated));
|
|
logger.info('usePermissionGuide', 'Guide marked as shown', { guideType });
|
|
return updated;
|
|
});
|
|
|
|
// 特殊处理:定期提醒
|
|
if (guideType === GUIDE_TYPES.PERIODIC) {
|
|
localStorage.setItem(STORAGE_KEYS.LAST_PERIODIC, String(Date.now()));
|
|
|
|
const totalPrompts = parseInt(localStorage.getItem(STORAGE_KEYS.TOTAL_PROMPTS) || '0', 10);
|
|
localStorage.setItem(STORAGE_KEYS.TOTAL_PROMPTS, String(totalPrompts + 1));
|
|
|
|
logger.info('usePermissionGuide', 'Periodic prompt recorded', {
|
|
totalPrompts: totalPrompts + 1
|
|
});
|
|
}
|
|
} catch (error) {
|
|
logger.error('usePermissionGuide', 'Failed to mark guide as shown', error);
|
|
}
|
|
}, []);
|
|
|
|
/**
|
|
* 重置所有引导(用于测试或用户主动重置)
|
|
*/
|
|
const resetAllGuides = useCallback(() => {
|
|
try {
|
|
localStorage.removeItem(STORAGE_KEYS.SHOWN_GUIDES);
|
|
localStorage.removeItem(STORAGE_KEYS.LAST_PERIODIC);
|
|
localStorage.removeItem(STORAGE_KEYS.TOTAL_PROMPTS);
|
|
setShownGuides([]);
|
|
logger.info('usePermissionGuide', 'All guides reset');
|
|
} catch (error) {
|
|
logger.error('usePermissionGuide', 'Failed to reset guides', error);
|
|
}
|
|
}, []);
|
|
|
|
/**
|
|
* 获取定期提醒的统计信息(用于调试)
|
|
*/
|
|
const getPeriodicStats = useCallback(() => {
|
|
try {
|
|
const lastPrompt = localStorage.getItem(STORAGE_KEYS.LAST_PERIODIC);
|
|
const totalPrompts = parseInt(localStorage.getItem(STORAGE_KEYS.TOTAL_PROMPTS) || '0', 10);
|
|
|
|
return {
|
|
lastPromptTime: lastPrompt ? new Date(parseInt(lastPrompt, 10)) : null,
|
|
totalPrompts,
|
|
remainingPrompts: MAX_PERIODIC_PROMPTS - totalPrompts,
|
|
nextPromptTime: lastPrompt
|
|
? new Date(parseInt(lastPrompt, 10) + PERIODIC_INTERVAL)
|
|
: new Date(),
|
|
};
|
|
} catch (error) {
|
|
logger.error('usePermissionGuide', 'Failed to get periodic stats', error);
|
|
return null;
|
|
}
|
|
}, []);
|
|
|
|
return {
|
|
shouldShowGuide,
|
|
markGuideAsShown,
|
|
resetAllGuides,
|
|
getPeriodicStats,
|
|
shownGuides,
|
|
};
|
|
}
|