-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
feat: disable high power consumption effects on battery #14230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: canary
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { useEffect, useState } from 'react'; | ||
|
|
||
| export function usePowerState() { | ||
| const [onBattery, setOnBattery] = useState(false); | ||
|
|
||
| useEffect(() => { | ||
| window.__apis.getPowerState().then(setOnBattery); | ||
| }, []); | ||
|
|
||
| return onBattery; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import { useEffect, useState } from 'react'; | ||
|
|
||
| import { usePowerState } from './hooks/use-power-state'; | ||
|
|
||
| export const DesktopPowerStateSync = () => { | ||
| const onBattery = usePowerState(); | ||
| const [prefersReducedMotion, setPrefersReducedMotion] = useState( | ||
| () => window.matchMedia('(prefers-reduced-motion: reduce)').matches | ||
| ); | ||
|
|
||
| useEffect(() => { | ||
| const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); | ||
| const handleChange = (e: MediaQueryListEvent) => { | ||
| setPrefersReducedMotion(e.matches); | ||
| }; | ||
|
|
||
| // Modern browsers | ||
| if (mediaQuery.addEventListener) { | ||
| mediaQuery.addEventListener('change', handleChange); | ||
| return () => mediaQuery.removeEventListener('change', handleChange); | ||
| } | ||
| // Fallback for older browsers | ||
| else if (mediaQuery.addListener) { | ||
| mediaQuery.addListener(handleChange); | ||
| return () => mediaQuery.removeListener(handleChange); | ||
| } | ||
| }, []); | ||
|
|
||
| useEffect(() => { | ||
| const enableAnimations = !onBattery && !prefersReducedMotion; | ||
| document.documentElement.classList.toggle( | ||
| 'reduce-motion', | ||
| !enableAnimations | ||
| ); | ||
| }, [onBattery, prefersReducedMotion]); | ||
|
|
||
| return null; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,7 @@ import path from 'node:path'; | |
|
|
||
| import * as Sentry from '@sentry/electron/main'; | ||
| import { IPCMode } from '@sentry/electron/main'; | ||
| import { app, protocol } from 'electron'; | ||
| import { app, ipcMain, powerMonitor, protocol } from 'electron'; | ||
|
|
||
| import { createApplicationMenu } from './application-menu/create'; | ||
| import { buildType, isDev, overrideSession } from './config'; | ||
|
|
@@ -20,6 +20,8 @@ import { registerUpdater } from './updater'; | |
| import { launch } from './windows-manager/launcher'; | ||
| import { launchStage } from './windows-manager/stage'; | ||
|
|
||
| let isOnBatteryPower = false; | ||
|
|
||
| app.enableSandbox(); | ||
|
|
||
| if (isDev) { | ||
|
|
@@ -95,6 +97,26 @@ app.on('window-all-closed', () => { | |
| app.quit(); | ||
| }); | ||
|
|
||
| /** | ||
| * Monitor system power source changes | ||
| * Refs: https://www.electronjs.org/docs/latest/api/power-monitor | ||
| */ | ||
| powerMonitor.on('on-battery', () => { | ||
| isOnBatteryPower = true; | ||
| logger.info('Running on battery'); | ||
| }); | ||
|
|
||
| powerMonitor.on('on-ac', () => { | ||
| isOnBatteryPower = false; | ||
| logger.info('Running on AC power'); | ||
| }); | ||
|
|
||
| isOnBatteryPower = powerMonitor.isOnBatteryPower(); | ||
|
Comment on lines
+104
to
+114
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Short answer
Example
Notes
Sources 🏁 Script executed: # First, let's examine the actual file to understand the code context
head -120 packages/frontend/apps/electron/src/main/index.ts | tail -50Repository: toeverything/AFFiNE Length of output: 1126 🏁 Script executed: # Get more context to see app.whenReady() calls and overall structure
cat -n packages/frontend/apps/electron/src/main/index.ts | head -130Repository: toeverything/AFFiNE Length of output: 5290 🏁 Script executed: # Check if app.whenReady() is used elsewhere in the file
rg "app\.whenReady" packages/frontend/apps/electron/src/main/index.tsRepository: toeverything/AFFiNE Length of output: 45 Wrap powerMonitor listeners in The Electron powerMonitor API cannot be used until the app has emitted the ready event. Currently, the event listeners at lines 104-114 are registered at module scope during initialization, which executes before Move these listeners into an Example fix:app.whenReady().then(() => {
powerMonitor.on('on-battery', () => {
isOnBatteryPower = true;
logger.info('Running on battery');
});
powerMonitor.on('on-ac', () => {
isOnBatteryPower = false;
logger.info('Running on AC power');
});
isOnBatteryPower = powerMonitor.isOnBatteryPower();
});🤖 Prompt for AI Agents |
||
|
|
||
| ipcMain.handle('get-power-state', () => { | ||
| return isOnBatteryPower; | ||
| }); | ||
|
Comment on lines
+116
to
+118
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Power state changes are not propagated to the renderer. The IPC handler only returns the current state when queried, but there's no mechanism to notify the renderer when power state changes. This means:
💡 Proposed fix: Add event broadcasting to renderer+/**
+ * Broadcast power state changes to all renderer windows
+ */
+function broadcastPowerStateChange(onBattery: boolean) {
+ const { BrowserWindow } = require('electron');
+ BrowserWindow.getAllWindows().forEach(window => {
+ window.webContents.send('power-state-changed', onBattery);
+ });
+}
+
powerMonitor.on('on-battery', () => {
isOnBatteryPower = true;
logger.info('Running on battery');
+ broadcastPowerStateChange(true);
});
powerMonitor.on('on-ac', () => {
isOnBatteryPower = false;
logger.info('Running on AC power');
+ broadcastPowerStateChange(false);
});Then update the renderer's
|
||
|
|
||
| /** | ||
| * @see https://www.electronjs.org/docs/latest/api/app#event-activate-macos Event: 'activate' | ||
| */ | ||
|
|
||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Hook doesn't react to power state changes.
This hook queries the power state only once on mount and never updates. Combined with the main process not broadcasting power state changes, this means:
Additionally, there's no error handling for the IPC call, which could cause the component to silently fail.
🔧 Proposed fix: Add event listener for power state updates
import { useEffect, useState } from 'react'; export function usePowerState() { const [onBattery, setOnBattery] = useState(false); useEffect(() => { - window.__apis.getPowerState().then(setOnBattery); - }, []); + // Get initial state + window.__apis.getPowerState() + .then(setOnBattery) + .catch(err => { + console.error('Failed to get power state:', err); + }); + + // Listen for power state changes + const handlePowerStateChange = (_event: any, onBattery: boolean) => { + setOnBattery(onBattery); + }; + + window.electron?.ipcRenderer.on('power-state-changed', handlePowerStateChange); + + return () => { + window.electron?.ipcRenderer.removeListener('power-state-changed', handlePowerStateChange); + }; + }, []); return onBattery; }Note: This requires the main process changes suggested in
packages/frontend/apps/electron/src/main/index.tsto broadcast power state changes.