Skip to content

Conversation

@xhwgood
Copy link
Contributor

@xhwgood xhwgood commented Aug 7, 2025

老哥,想加个计时器功能你看看咋样?
我在 packages/web/src/stores/setting.pc.ts 文件里加了两个变量,但在 vue 文件中读取的时候都是 undefined 是为什么?

Summary by CodeRabbit

  • New Features

    • Added a timer that tracks and displays elapsed reading time in the bottom bar, visible when enabled in settings.
    • Introduced a new settings option allowing users to show or hide the elapsed time display.
  • Improvements

    • Enhanced settings with a checkbox to control the visibility of the elapsed time indicator.

@coderabbitai
Copy link

coderabbitai bot commented Aug 7, 2025

Walkthrough

A user timer feature was introduced, tracking elapsed time and storing it in a reactive store. The timer is started in the content view and its value is displayed in the layout's bottom bar when enabled. New settings allow toggling the time display. Supporting store properties and formatting logic were added, with minor updates to types.

Changes

Cohort / File(s) Change Summary
Timer Hook Integration
packages/web/src/pages/common/timer.ts, packages/web/src/pages/vscode/content/index.vue
Introduced a userTimer hook that increments a timer in the settings store every second. The timer is initialized in the content page.
Timer Display and Formatting
packages/web/src/pages/vscode/layout/index.vue
Added a conditional elapsed time display in the bottom bar, formatting the timer value as hh:mm:ss when enabled in settings. Imported and used the settings store.
Timer Settings UI
packages/web/src/pages/vscode/settings/index.vue
Added a settings row with a checkbox to toggle the timer display, binding to the new store property and logging changes.
Settings Store Enhancements
packages/web/src/stores/setting.pc.ts
Made the font property optional in ReadStyle. Added timeIsShow (boolean) and readTime (number) as reactive state in the settings store, exposing them for use elsewhere.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ContentPage
    participant TimerHook
    participant SettingStore
    participant Layout
    participant SettingsPage

    User->>ContentPage: Loads page
    ContentPage->>TimerHook: Calls userTimer()
    TimerHook->>SettingStore: Every second, increment readTime

    User->>SettingsPage: Opens settings
    SettingsPage->>SettingStore: Toggles timeIsShow

    Layout->>SettingStore: Reads timeIsShow and readTime
    Layout->>Layout: Formats and displays elapsed time if timeIsShow is true
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Poem

🐇
Tick-tock goes the timer’s song,
Counting seconds all day long.
With settings checked, the time appears,
In the bar for bunny peers.
If you wish, just hide the trace—
But the code keeps perfect pace!
⏱️✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (1)
packages/web/src/pages/vscode/layout/index.vue (1)

43-43: Remove debug console.log statement.

Debug logging should be removed from production code.

-console.log('settingStore.readTime:', settingStore.readTime);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 563ea48 and a826a56.

📒 Files selected for processing (5)
  • packages/web/src/pages/common/timer.ts (1 hunks)
  • packages/web/src/pages/vscode/content/index.vue (2 hunks)
  • packages/web/src/pages/vscode/layout/index.vue (2 hunks)
  • packages/web/src/pages/vscode/settings/index.vue (2 hunks)
  • packages/web/src/stores/setting.pc.ts (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
packages/web/src/pages/common/timer.ts (1)
packages/web/src/stores/setting.pc.ts (1)
  • useSettingStore (45-92)
packages/web/src/stores/setting.pc.ts (1)
packages/web/src/utils/font.ts (1)
  • FontData (3-8)
🔇 Additional comments (2)
packages/web/src/stores/setting.pc.ts (1)

7-7: LGTM: Font property made optional.

Making the font property optional in ReadStyle is a reasonable change that provides more flexibility.

packages/web/src/pages/vscode/layout/index.vue (1)

35-39: LGTM: Store import and usage.

The store is properly imported and instantiated in the component setup.

Comment on lines +1 to +19
import { useSettingStore } from '@/stores/setting';
/** 启动和销毁计时器 hooks */
export const userTimer = () => {
const settingStore = useSettingStore();

const timerId = ref();

onMounted(() => {
timerId.value = setInterval(() => {
settingStore.readTime += 1;
}, 1000);
});

onBeforeUnmount(() => {
if (timerId.value) {
clearInterval(timerId.value);
}
});
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix missing imports and consider timer behavior.

Several critical issues need to be addressed:

  1. Missing Vue imports - This will cause runtime errors:
  2. Timer runs continuously - Consider pausing when user is inactive
  3. Function naming - userTimer should probably be useTimer

Apply this diff to fix the missing imports:

+import { ref, onMounted, onBeforeUnmount } from 'vue';
 import { useSettingStore } from '@/stores/setting';
-/** 启动和销毁计时器 hooks */
-export const userTimer = () => {
+
+/** 启动和销毁计时器 hook */
+export const useTimer = () => {
   const settingStore = useSettingStore();
   
   const timerId = ref();
   
   onMounted(() => {
     timerId.value = setInterval(() => {
       settingStore.readTime += 1;
     }, 1000);
   });
   
   onBeforeUnmount(() => {
     if (timerId.value) {
       clearInterval(timerId.value);
+      timerId.value = null;
     }
   });
 };

Consider adding pause/resume functionality for when the user is inactive:

export const useTimer = () => {
  const settingStore = useSettingStore();
  const timerId = ref<number | null>(null);
  const isActive = ref(true);

  const startTimer = () => {
    if (timerId.value) return;
    timerId.value = setInterval(() => {
      if (isActive.value) {
        settingStore.readTime += 1;
      }
    }, 1000);
  };

  const pauseTimer = () => {
    isActive.value = false;
  };

  const resumeTimer = () => {
    isActive.value = true;
  };

  // Add window focus/blur listeners
  onMounted(() => {
    startTimer();
    window.addEventListener('focus', resumeTimer);
    window.addEventListener('blur', pauseTimer);
  });

  onBeforeUnmount(() => {
    if (timerId.value) {
      clearInterval(timerId.value);
      timerId.value = null;
    }
    window.removeEventListener('focus', resumeTimer);
    window.removeEventListener('blur', pauseTimer);
  });
};
🤖 Prompt for AI Agents
In packages/web/src/pages/common/timer.ts lines 1 to 19, the code is missing
necessary Vue imports like ref, onMounted, and onBeforeUnmount, which causes
runtime errors. Rename the function from userTimer to useTimer for clarity.
Implement pause and resume functionality to stop the timer when the user is
inactive by tracking window focus and blur events, and ensure the timerId is
properly typed and cleared on unmount. Add event listeners for window focus and
blur in onMounted and remove them in onBeforeUnmount to manage timer activity
accordingly.

import { ContentType } from '@any-reader/rule-utils';
import { useContent, useTheme } from '@/pages/common/content';
import { useReadStore } from '@/stores/read';
import { userTimer } from '@/pages/common/timer';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Update import name and add error handling.

The import should be updated to match the corrected function name from the timer module.

-import { userTimer } from '@/pages/common/timer';
+import { useTimer } from '@/pages/common/timer';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { userTimer } from '@/pages/common/timer';
-import { userTimer } from '@/pages/common/timer';
+import { useTimer } from '@/pages/common/timer';
🤖 Prompt for AI Agents
In packages/web/src/pages/vscode/content/index.vue at line 56, update the import
statement to use the corrected function name from the timer module instead of
'userTimer'. Also, add appropriate error handling around the usage of this
imported function to handle potential failures gracefully.

useTheme(contentRef);
userTimer();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Update function call and consider error handling.

Update the function call to match the corrected name and consider adding error handling.

-userTimer();
+useTimer();

Consider wrapping the timer initialization with error handling:

try {
  useTimer();
} catch (error) {
  console.warn('Failed to initialize reading timer:', error);
}
🤖 Prompt for AI Agents
In packages/web/src/pages/vscode/content/index.vue at line 67, the function call
userTimer() should be updated to useTimer() to match the corrected function
name. Additionally, wrap the useTimer() call in a try-catch block to handle
potential errors gracefully by logging a warning message with the error details.

<div v-if="!hideBtmBar" class="flex gap-4 px-8 py-4">
<div v-if="!hideBtmBar" class="flex items-center gap-4 px-8 py-4">
<div class="codicon codicon-arrow-left vsc-toolbar-btn" @click="router.back()"></div>
<div v-if="settingStore.timeIsShow">已摸鱼:{{ showTIme }}</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Update template to use corrected computed property name.

Update the template binding to match the corrected computed property name.

-      <div v-if="settingStore.timeIsShow">已摸鱼:{{ showTIme }}</div>
+      <div v-if="settingStore.timeIsShow">已摸鱼:{{ showTime }}</div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div v-if="settingStore.timeIsShow">已摸鱼:{{ showTIme }}</div>
<div v-if="settingStore.timeIsShow">已摸鱼:{{ showTime }}</div>
🤖 Prompt for AI Agents
In packages/web/src/pages/vscode/layout/index.vue at line 13, the template uses
the computed property name "showTIme" which is incorrect. Update the template
binding to use the corrected computed property name exactly as defined in the
script, ensuring proper casing and spelling.

Comment on lines +44 to +52
const showTIme = computed(() => {
const readTime = settingStore.readTime;
if (readTime <= 0) return 0;
const hours = Math.floor(readTime / 3600);
const minutes = Math.floor((readTime % 3600) / 60);
const seconds = readTime % 60;
return `${hours}:${minutes}:${seconds}`;
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix typo in computed property name and improve formatting.

The computed property has a typo and the time formatting could be improved.

Apply this diff to fix the typo and improve formatting:

-const showTIme = computed(() => {
+const showTime = computed(() => {
   const readTime = settingStore.readTime;

   if (readTime <= 0) return 0;
   const hours = Math.floor(readTime / 3600);
   const minutes = Math.floor((readTime % 3600) / 60);
   const seconds = readTime % 60;
-  return `${hours}:${minutes}:${seconds}`;
+  return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const showTIme = computed(() => {
const readTime = settingStore.readTime;
if (readTime <= 0) return 0;
const hours = Math.floor(readTime / 3600);
const minutes = Math.floor((readTime % 3600) / 60);
const seconds = readTime % 60;
return `${hours}:${minutes}:${seconds}`;
});
const showTime = computed(() => {
const readTime = settingStore.readTime;
if (readTime <= 0) return 0;
const hours = Math.floor(readTime / 3600);
const minutes = Math.floor((readTime % 3600) / 60);
const seconds = readTime % 60;
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
});
🤖 Prompt for AI Agents
In packages/web/src/pages/vscode/layout/index.vue around lines 44 to 52, the
computed property name "showTIme" contains a typo and the time formatting lacks
leading zeros for minutes and seconds. Rename the property to "showTime" and
update the return statement to format hours, minutes, and seconds with leading
zeros (e.g., using padStart or equivalent) to ensure consistent time display.

Comment on lines +12 to +14
<SettingRow title="摸鱼时间">
<vscode-checkbox :checked="settingStore.timeIsShow" @input="handleInput">显示/隐藏</vscode-checkbox>
</SettingRow>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix checkbox binding and event handling.

The current implementation has issues with the two-way binding:

  1. The event handler only logs but doesn't update the store state
  2. Missing proper two-way binding pattern

Apply this diff to fix the checkbox binding:

-    <SettingRow title="摸鱼时间">
-      <vscode-checkbox :checked="settingStore.timeIsShow" @input="handleInput">显示/隐藏</vscode-checkbox>
-    </SettingRow>
+    <SettingRow title="摸鱼时间">
+      <vscode-checkbox 
+        :checked="settingStore.timeIsShow" 
+        @input="(event) => settingStore.timeIsShow = event.target.checked"
+      >
+        显示/隐藏
+      </vscode-checkbox>
+    </SettingRow>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<SettingRow title="摸鱼时间">
<vscode-checkbox :checked="settingStore.timeIsShow" @input="handleInput">显示/隐藏</vscode-checkbox>
</SettingRow>
<SettingRow title="摸鱼时间">
<vscode-checkbox
:checked="settingStore.timeIsShow"
@input="(event) => settingStore.timeIsShow = event.target.checked"
>
显示/隐藏
</vscode-checkbox>
</SettingRow>
🤖 Prompt for AI Agents
In packages/web/src/pages/vscode/settings/index.vue around lines 12 to 14, the
checkbox component uses a :checked prop and an @input event that only logs
without updating the store, breaking two-way binding. Replace :checked with
v-model bound to settingStore.timeIsShow and update the event handler to modify
the store state accordingly, ensuring proper two-way binding and reactive
updates.

Comment on lines +36 to +39
const handleInput = (event) => {
console.log('event:', event);
console.log('event.target.checked:', event.target.checked);
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove unused event handler.

The handleInput function is no longer needed after fixing the checkbox binding.

-const handleInput = (event) => {
-  console.log('event:', event);
-  console.log('event.target.checked:', event.target.checked);
-};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleInput = (event) => {
console.log('event:', event);
console.log('event.target.checked:', event.target.checked);
};
🤖 Prompt for AI Agents
In packages/web/src/pages/vscode/settings/index.vue around lines 36 to 39, the
handleInput function is no longer used after fixing the checkbox binding. Remove
the entire handleInput function to clean up unused code.

Comment on lines +69 to +70
const timeIsShow = ref<boolean>(true);
const readTime = ref<number>(0);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding timer state to persistent configuration.

The new timeIsShow and readTime variables are not included in the persistent data object that gets synchronized with the backend via readConfig/updateConfig. This means:

  1. Settings won't persist across application restarts
  2. Timer value will reset to 0 on page reload
  3. User preference for showing time won't be remembered

Consider moving these properties into the Setting type and data object:

export type Setting = {
  readStyle: ReadStyle;
  keyboardShortcuts: KeyboardShortcuts;
  sidebar: Sidebar;
  bookDir: string;
+ timeIsShow: boolean;
+ readTime: number;
};

export const useSettingStore = defineStore('setting', () => {
  const data = reactive<Setting>({
    readStyle: {
      // ...existing properties
    },
    keyboardShortcuts: {
      // ...existing properties  
    },
    sidebar: 'left',
-   bookDir: ''
+   bookDir: '',
+   timeIsShow: true,
+   readTime: 0
  });

-  /** 是否显示摸鱼时间 */
-  const timeIsShow = ref<boolean>(true);
-  const readTime = ref<number>(0);

  return {
    data,
-   readTime,
-   timeIsShow,
    sync
  };
});

Then access them via settingStore.data.timeIsShow and settingStore.data.readTime in components.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const timeIsShow = ref<boolean>(true);
const readTime = ref<number>(0);
export type Setting = {
readStyle: ReadStyle;
keyboardShortcuts: KeyboardShortcuts;
sidebar: Sidebar;
bookDir: string;
timeIsShow: boolean;
readTime: number;
};
export const useSettingStore = defineStore('setting', () => {
const data = reactive<Setting>({
readStyle: {
// ...existing properties
},
keyboardShortcuts: {
// ...existing properties
},
sidebar: 'left',
bookDir: '',
timeIsShow: true,
readTime: 0
});
// ...other store logic (e.g. readConfig, updateConfig, etc.)...
return {
data,
sync
};
});
🤖 Prompt for AI Agents
In packages/web/src/stores/setting.pc.ts around lines 69 to 70, the new reactive
variables timeIsShow and readTime are not part of the persistent data object
synchronized with the backend, causing their values and user preferences to
reset on reload. To fix this, add timeIsShow and readTime to the Setting type
and include them in the data object that is read and updated via readConfig and
updateConfig. Then update all references to these variables to access them
through settingStore.data.timeIsShow and settingStore.data.readTime to ensure
persistence across restarts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant