feat(core): bettery save mode (#12996)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced a battery save mode that can pause background sync and processing to conserve energy. * Battery save mode is automatically enabled when the app window loses focus and disabled when the window regains focus or is interacted with. * Available in both web and desktop (Electron) versions. * **Improvements** * Added user activity detection to optimize background operations for better battery efficiency. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -159,6 +159,14 @@ export class IndexerSyncImpl implements IndexerSync {
|
|||||||
this.remote = Object.values(this.peers.remotes).find(remote => !!remote);
|
this.remote = Object.values(this.peers.remotes).find(remote => !!remote);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enableBatterySaveMode() {
|
||||||
|
this.status.enableBatterySaveMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
disableBatterySaveMode() {
|
||||||
|
this.status.disableBatterySaveMode();
|
||||||
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
if (this.abort) {
|
if (this.abort) {
|
||||||
this.abort.abort(MANUALLY_STOP);
|
this.abort.abort(MANUALLY_STOP);
|
||||||
@@ -611,6 +619,10 @@ class IndexerSyncStatus {
|
|||||||
currentJob: string | null = null;
|
currentJob: string | null = null;
|
||||||
errorMessage: string | null = null;
|
errorMessage: string | null = null;
|
||||||
statusUpdatedSubject$ = new Subject<string | true>();
|
statusUpdatedSubject$ = new Subject<string | true>();
|
||||||
|
batterySaveMode: {
|
||||||
|
promise: Promise<void>;
|
||||||
|
resolve: () => void;
|
||||||
|
} | null = null;
|
||||||
|
|
||||||
state$ = new Observable<IndexerSyncState>(subscribe => {
|
state$ = new Observable<IndexerSyncState>(subscribe => {
|
||||||
const next = () => {
|
const next = () => {
|
||||||
@@ -685,6 +697,9 @@ class IndexerSyncStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async acceptJob(abort?: AbortSignal) {
|
async acceptJob(abort?: AbortSignal) {
|
||||||
|
if (this.batterySaveMode) {
|
||||||
|
await this.batterySaveMode.promise;
|
||||||
|
}
|
||||||
const job = await this.jobs.asyncPop(abort);
|
const job = await this.jobs.asyncPop(abort);
|
||||||
this.currentJob = job;
|
this.currentJob = job;
|
||||||
this.statusUpdatedSubject$.next(job);
|
this.statusUpdatedSubject$.next(job);
|
||||||
@@ -709,6 +724,18 @@ class IndexerSyncStatus {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enableBatterySaveMode() {
|
||||||
|
if (this.batterySaveMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.batterySaveMode = Promise.withResolvers();
|
||||||
|
}
|
||||||
|
|
||||||
|
disableBatterySaveMode() {
|
||||||
|
this.batterySaveMode?.resolve();
|
||||||
|
this.batterySaveMode = null;
|
||||||
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
// reset all state, except prioritySettings
|
// reset all state, except prioritySettings
|
||||||
this.isReadonly = false;
|
this.isReadonly = false;
|
||||||
|
|||||||
@@ -86,6 +86,21 @@ export class StoreManagerClient {
|
|||||||
connection.dispose();
|
connection.dispose();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enableBatterySaveMode() {
|
||||||
|
this.connections.forEach(connection => {
|
||||||
|
connection.store.enableBatterySaveMode().catch(err => {
|
||||||
|
console.error('error enabling battery save mode', err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
disableBatterySaveMode() {
|
||||||
|
this.connections.forEach(connection => {
|
||||||
|
connection.store.disableBatterySaveMode().catch(err => {
|
||||||
|
console.error('error disabling battery save mode', err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StoreClient {
|
export class StoreClient {
|
||||||
@@ -113,6 +128,13 @@ export class StoreClient {
|
|||||||
readonly blobFrontend: BlobFrontend;
|
readonly blobFrontend: BlobFrontend;
|
||||||
readonly awarenessFrontend: AwarenessFrontend;
|
readonly awarenessFrontend: AwarenessFrontend;
|
||||||
readonly indexerFrontend: IndexerFrontend;
|
readonly indexerFrontend: IndexerFrontend;
|
||||||
|
|
||||||
|
enableBatterySaveMode(): Promise<void> {
|
||||||
|
return this.client.call('sync.enableBatterySaveMode');
|
||||||
|
}
|
||||||
|
disableBatterySaveMode(): Promise<void> {
|
||||||
|
return this.client.call('sync.disableBatterySaveMode');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WorkerDocStorage implements DocStorage {
|
class WorkerDocStorage implements DocStorage {
|
||||||
|
|||||||
@@ -134,6 +134,35 @@ class StoreConsumer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly ENABLE_BATTERY_SAVE_MODE_DELAY = 1000;
|
||||||
|
private enableBatterySaveModeTimeout: NodeJS.Timeout | null = null;
|
||||||
|
private enabledBatterySaveMode = false;
|
||||||
|
|
||||||
|
enableBatterySaveMode() {
|
||||||
|
if (this.enableBatterySaveModeTimeout || this.enabledBatterySaveMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.enableBatterySaveModeTimeout = setTimeout(() => {
|
||||||
|
if (!this.enabledBatterySaveMode) {
|
||||||
|
this.indexerSync.enableBatterySaveMode();
|
||||||
|
this.enabledBatterySaveMode = true;
|
||||||
|
console.log('[BatterySaveMode] enabled');
|
||||||
|
}
|
||||||
|
}, this.ENABLE_BATTERY_SAVE_MODE_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
disableBatterySaveMode() {
|
||||||
|
if (this.enableBatterySaveModeTimeout) {
|
||||||
|
clearTimeout(this.enableBatterySaveModeTimeout);
|
||||||
|
this.enableBatterySaveModeTimeout = null;
|
||||||
|
}
|
||||||
|
if (this.enabledBatterySaveMode) {
|
||||||
|
this.indexerSync.disableBatterySaveMode();
|
||||||
|
this.enabledBatterySaveMode = false;
|
||||||
|
console.log('[BatterySaveMode] disabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private registerHandlers(consumer: OpConsumer<WorkerOps>) {
|
private registerHandlers(consumer: OpConsumer<WorkerOps>) {
|
||||||
const collectJobs = new Map<
|
const collectJobs = new Map<
|
||||||
string,
|
string,
|
||||||
@@ -285,6 +314,8 @@ class StoreConsumer {
|
|||||||
this.indexerSync.search$(table, query, options),
|
this.indexerSync.search$(table, query, options),
|
||||||
'indexerSync.subscribeAggregate': ({ table, query, field, options }) =>
|
'indexerSync.subscribeAggregate': ({ table, query, field, options }) =>
|
||||||
this.indexerSync.aggregate$(table, query, field, options),
|
this.indexerSync.aggregate$(table, query, field, options),
|
||||||
|
'sync.enableBatterySaveMode': () => this.enableBatterySaveMode(),
|
||||||
|
'sync.disableBatterySaveMode': () => this.disableBatterySaveMode(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,10 @@ interface GroupedWorkerOps {
|
|||||||
AggregateResult<any, any>,
|
AggregateResult<any, any>,
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
sync: {
|
||||||
|
enableBatterySaveMode: [void, void];
|
||||||
|
disableBatterySaveMode: [void, void];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type Values<T> = T extends { [k in keyof T]: any } ? T[keyof T] : never;
|
type Values<T> = T extends { [k in keyof T]: any } ? T[keyof T] : never;
|
||||||
|
|||||||
@@ -46,6 +46,15 @@ export function setupStoreManager(framework: Framework) {
|
|||||||
window.addEventListener('beforeunload', () => {
|
window.addEventListener('beforeunload', () => {
|
||||||
storeManagerClient.dispose();
|
storeManagerClient.dispose();
|
||||||
});
|
});
|
||||||
|
window.addEventListener('focus', () => {
|
||||||
|
storeManagerClient.disableBatterySaveMode();
|
||||||
|
});
|
||||||
|
window.addEventListener('click', () => {
|
||||||
|
storeManagerClient.disableBatterySaveMode();
|
||||||
|
});
|
||||||
|
window.addEventListener('blur', () => {
|
||||||
|
storeManagerClient.enableBatterySaveMode();
|
||||||
|
});
|
||||||
|
|
||||||
framework.impl(NbstoreProvider, {
|
framework.impl(NbstoreProvider, {
|
||||||
openStore(key, options) {
|
openStore(key, options) {
|
||||||
|
|||||||
@@ -41,6 +41,15 @@ if (
|
|||||||
window.addEventListener('beforeunload', () => {
|
window.addEventListener('beforeunload', () => {
|
||||||
storeManagerClient.dispose();
|
storeManagerClient.dispose();
|
||||||
});
|
});
|
||||||
|
window.addEventListener('focus', () => {
|
||||||
|
storeManagerClient.disableBatterySaveMode();
|
||||||
|
});
|
||||||
|
window.addEventListener('click', () => {
|
||||||
|
storeManagerClient.disableBatterySaveMode();
|
||||||
|
});
|
||||||
|
window.addEventListener('blur', () => {
|
||||||
|
storeManagerClient.enableBatterySaveMode();
|
||||||
|
});
|
||||||
|
|
||||||
const future = {
|
const future = {
|
||||||
v7_startTransition: true,
|
v7_startTransition: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user