Browse Source

fix(checkin): prevent visual flicker when loading check-in component

- Add initialLoaded state to track first data load completion
- Set isCollapsed to null initially, determined after data loads
- Show loading state on button and description text before data arrives
- Remove auto-collapse effect that caused visual flicker
- Add i18n translations for loading states (en/fr/ja/ru/vi/zh)

Fixes issue where component would collapse/expand after data loads,
causing visual flicker when navigating to personal settings page.
RedwindA 2 months ago
parent
commit
a0328b2f5e

+ 33 - 22
web/src/components/settings/personal/cards/CheckinCalendar.jsx

@@ -53,8 +53,10 @@ const CheckinCalendar = ({ t, status }) => {
   const [currentMonth, setCurrentMonth] = useState(
     new Date().toISOString().slice(0, 7),
   );
-  // 折叠状态:如果已签到则默认折叠
-  const [isCollapsed, setIsCollapsed] = useState(true);
+  // 初始加载状态,用于避免折叠状态闪烁
+  const [initialLoaded, setInitialLoaded] = useState(false);
+  // 折叠状态:null 表示未确定(等待首次加载)
+  const [isCollapsed, setIsCollapsed] = useState(null);
 
   // 创建日期到额度的映射,方便快速查找
   const checkinRecordsMap = useMemo(() => {
@@ -77,17 +79,31 @@ const CheckinCalendar = ({ t, status }) => {
 
   // 获取签到状态
   const fetchCheckinStatus = async (month) => {
+    const isFirstLoad = !initialLoaded;
     setLoading(true);
     try {
       const res = await API.get(`/api/user/checkin?month=${month}`);
       const { success, data, message } = res.data;
       if (success) {
         setCheckinData(data);
+        // 首次加载时,根据签到状态设置折叠状态
+        if (isFirstLoad) {
+          setIsCollapsed(data.stats?.checked_in_today ?? false);
+          setInitialLoaded(true);
+        }
       } else {
         showError(message || t('获取签到状态失败'));
+        if (isFirstLoad) {
+          setIsCollapsed(false);
+          setInitialLoaded(true);
+        }
       }
     } catch (error) {
       showError(t('获取签到状态失败'));
+      if (isFirstLoad) {
+        setIsCollapsed(false);
+        setInitialLoaded(true);
+      }
     } finally {
       setLoading(false);
     }
@@ -121,15 +137,6 @@ const CheckinCalendar = ({ t, status }) => {
     }
   }, [status?.checkin_enabled, currentMonth]);
 
-  // 当签到状态加载完成后,根据是否已签到设置折叠状态
-  useEffect(() => {
-    if (checkinData.stats?.checked_in_today) {
-      setIsCollapsed(true);
-    } else {
-      setIsCollapsed(false);
-    }
-  }, [checkinData.stats?.checked_in_today]);
-
   // 如果签到功能未启用,不显示组件
   if (!status?.checkin_enabled) {
     return null;
@@ -200,11 +207,13 @@ const CheckinCalendar = ({ t, status }) => {
               )}
             </div>
             <div className='text-xs text-gray-500 dark:text-gray-400'>
-              {checkinData.stats?.checked_in_today
-                ? t('今日已签到,累计签到') +
-                  ` ${checkinData.stats?.total_checkins || 0} ` +
-                  t('天')
-                : t('每日签到可获得随机额度奖励')}
+              {!initialLoaded
+                ? t('正在加载签到状态...')
+                : checkinData.stats?.checked_in_today
+                  ? t('今日已签到,累计签到') +
+                    ` ${checkinData.stats?.total_checkins || 0} ` +
+                    t('天')
+                  : t('每日签到可获得随机额度奖励')}
             </div>
           </div>
         </div>
@@ -213,18 +222,20 @@ const CheckinCalendar = ({ t, status }) => {
           theme='solid'
           icon={<Gift size={16} />}
           onClick={doCheckin}
-          loading={checkinLoading}
-          disabled={checkinData.stats?.checked_in_today}
+          loading={checkinLoading || !initialLoaded}
+          disabled={!initialLoaded || checkinData.stats?.checked_in_today}
           className='!bg-green-600 hover:!bg-green-700'
         >
-          {checkinData.stats?.checked_in_today
-            ? t('今日已签到')
-            : t('立即签到')}
+          {!initialLoaded
+            ? t('加载中...')
+            : checkinData.stats?.checked_in_today
+              ? t('今日已签到')
+              : t('立即签到')}
         </Button>
       </div>
 
       {/* 可折叠内容 */}
-      <Collapsible isOpen={!isCollapsed} keepDOM>
+      <Collapsible isOpen={isCollapsed === false} keepDOM>
         {/* 签到统计 */}
         <div className='grid grid-cols-3 gap-3 mb-4 mt-4'>
           <div className='text-center p-2.5 bg-slate-50 dark:bg-slate-800 rounded-lg'>

+ 2 - 0
web/src/i18n/locales/en.json

@@ -2192,6 +2192,8 @@
     "每日签到可获得随机额度奖励": "Daily check-in rewards random quota",
     "今日已签到": "Checked in today",
     "立即签到": "Check in now",
+    "加载中...": "Loading...",
+    "正在加载签到状态...": "Loading check-in status...",
     "获取签到状态失败": "Failed to get check-in status",
     "签到成功!获得": "Check-in successful! Received",
     "签到失败": "Check-in failed",

+ 2 - 0
web/src/i18n/locales/fr.json

@@ -2241,6 +2241,8 @@
     "每日签到可获得随机额度奖励": "L'enregistrement quotidien récompense un quota aléatoire",
     "今日已签到": "Enregistré aujourd'hui",
     "立即签到": "S'enregistrer maintenant",
+    "加载中...": "Chargement...",
+    "正在加载签到状态...": "Chargement du statut d'enregistrement...",
     "获取签到状态失败": "Échec de la récupération du statut d'enregistrement",
     "签到成功!获得": "Enregistrement réussi ! Reçu",
     "签到失败": "Échec de l'enregistrement",

+ 2 - 0
web/src/i18n/locales/ja.json

@@ -2140,6 +2140,8 @@
     "每日签到可获得随机额度奖励": "毎日のチェックインでランダムなクォータ報酬を獲得できます",
     "今日已签到": "本日チェックイン済み",
     "立即签到": "今すぐチェックイン",
+    "加载中...": "読み込み中...",
+    "正在加载签到状态...": "チェックイン状態を読み込み中...",
     "获取签到状态失败": "チェックイン状態の取得に失敗しました",
     "签到成功!获得": "チェックイン成功!獲得",
     "签到失败": "チェックインに失敗しました",

+ 2 - 0
web/src/i18n/locales/ru.json

@@ -2251,6 +2251,8 @@
     "每日签到可获得随机额度奖励": "Ежедневная регистрация награждает случайной квотой",
     "今日已签到": "Зарегистрирован сегодня",
     "立即签到": "Зарегистрироваться сейчас",
+    "加载中...": "Загрузка...",
+    "正在加载签到状态...": "Загрузка статуса регистрации...",   
     "获取签到状态失败": "Не удалось получить статус регистрации",
     "签到成功!获得": "Регистрация успешна! Получено",
     "签到失败": "Регистрация не удалась",

+ 2 - 0
web/src/i18n/locales/vi.json

@@ -2751,6 +2751,8 @@
     "每日签到可获得随机额度奖励": "Đăng nhập hàng ngày để nhận phần thưởng hạn mức ngẫu nhiên",
     "今日已签到": "Đã đăng nhập hôm nay",
     "立即签到": "Đăng nhập ngay",
+    "加载中...": "Đang tải...",
+    "正在加载签到状态...": "Đang tải trạng thái đăng nhập...",
     "获取签到状态失败": "Không thể lấy trạng thái đăng nhập",
     "签到成功!获得": "Đăng nhập thành công! Đã nhận",
     "签到失败": "Đăng nhập thất bại",

+ 2 - 0
web/src/i18n/locales/zh.json

@@ -2218,6 +2218,8 @@
     "每日签到可获得随机额度奖励": "每日签到可获得随机额度奖励",
     "今日已签到": "今日已签到",
     "立即签到": "立即签到",
+    "加载中...": "加载中...",
+    "正在加载签到状态...": "正在加载签到状态...",
     "获取签到状态失败": "获取签到状态失败",
     "签到成功!获得": "签到成功!获得",
     "签到失败": "签到失败",