@symbiote-native/battery 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -32
- package/build/angular/services/battery-level.service/index.js +1 -1
- package/build/angular/services/battery-state.service/index.js +2 -2
- package/build/angular/services/low-power-mode.service/index.js +2 -2
- package/build/react/hooks/use-battery-state/index.js +1 -1
- package/build/react/hooks/use-low-power-mode/index.js +1 -1
- package/build/solid/index.d.ts +4 -0
- package/build/solid/index.js +10 -0
- package/build/solid/primitives/create-battery-level.d.ts +2 -0
- package/build/solid/primitives/create-battery-level.js +35 -0
- package/build/solid/primitives/create-battery-state.d.ts +3 -0
- package/build/solid/primitives/create-battery-state.js +21 -0
- package/build/solid/primitives/create-low-power-mode.d.ts +2 -0
- package/build/solid/primitives/create-low-power-mode.js +21 -0
- package/build/svelte/runes/use-battery-level.svelte.js +1 -1
- package/build-ngc/angular/services/battery-level.service/index.js +1 -1
- package/build-ngc/angular/services/battery-level.service/index.js.map +1 -1
- package/build-ngc/angular/services/battery-state.service/index.js +2 -2
- package/build-ngc/angular/services/battery-state.service/index.js.map +1 -1
- package/build-ngc/angular/services/low-power-mode.service/index.js +2 -2
- package/build-ngc/angular/services/low-power-mode.service/index.js.map +1 -1
- package/build-ngc/core/native-module.js.map +1 -1
- package/package.json +40 -14
- package/src/angular/services/battery-level.service/index.ts +11 -2
- package/src/angular/services/battery-state.service/index.ts +16 -3
- package/src/angular/services/low-power-mode.service/index.ts +15 -3
- package/src/core/native-module.ts +9 -2
- package/src/react/hooks/use-battery-level/index.ts +3 -1
- package/src/react/hooks/use-battery-state/index.ts +11 -3
- package/src/react/hooks/use-low-power-mode/index.ts +7 -2
- package/src/solid/index.ts +11 -0
- package/src/solid/primitives/create-battery-level.ts +45 -0
- package/src/solid/primitives/create-battery-state.ts +33 -0
- package/src/solid/primitives/create-low-power-mode.ts +30 -0
- package/src/svelte/runes/use-battery-level.svelte.ts +5 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Port of [`expo-battery`](https://docs.expo.dev/versions/latest/sdk/battery/) for
|
|
4
4
|
[SymbioteNative](../../README.md) — battery level and state, low-power-mode detection, reachable
|
|
5
|
-
from every adapter (React, Vue, Angular), not just React.
|
|
5
|
+
from every adapter (React, Vue, Svelte, Solid, Angular), not just React.
|
|
6
6
|
|
|
7
7
|
Built the same way as [`@symbiote-native/local-auth`](../local-auth) and
|
|
8
8
|
[`@symbiote-native/sensors`](../sensors), an `expo-modules-core`-based wrapper (see the
|
|
@@ -29,12 +29,12 @@ Unlike a plain RN native module, `expo-battery`'s native code is discovered by
|
|
|
29
29
|
into the native host app **once**, covering this package and every other `expo-modules-core`
|
|
30
30
|
package (`@symbiote-native/sensors`, `@symbiote-native/local-auth`) with zero further changes:
|
|
31
31
|
|
|
32
|
-
| Platform | Touches
|
|
33
|
-
|
|
34
|
-
| iOS
|
|
35
|
-
| iOS
|
|
36
|
-
| Android
|
|
37
|
-
| Android
|
|
32
|
+
| Platform | Touches |
|
|
33
|
+
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| iOS | `ios/Podfile` — add `use_expo_modules!` |
|
|
35
|
+
| iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
|
|
36
|
+
| Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
|
|
37
|
+
| Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
|
|
38
38
|
|
|
39
39
|
Full mechanics — the Podfile pieces that normally ship inside the `expo` package, the `expo`
|
|
40
40
|
peer-dependency exclusion list — live in the `symbiote-expo-native-module` skill. Reference
|
|
@@ -55,33 +55,44 @@ src/core/ Battery.ts — one-shot get*Async functions + addListene
|
|
|
55
55
|
src/react/hooks/ @symbiote-native/battery/react — useBatteryLevel, useBatteryState,
|
|
56
56
|
useLowPowerMode
|
|
57
57
|
src/vue/composables/ @symbiote-native/battery/vue — same three names, Vue lifecycle
|
|
58
|
+
src/svelte/runes/ @symbiote-native/battery/svelte — same three names, read as `.current`
|
|
59
|
+
src/solid/primitives/ @symbiote-native/battery/solid — createBatteryLevel, createBatteryState,
|
|
60
|
+
createLowPowerMode (each returns an Accessor)
|
|
58
61
|
src/angular/services/ @symbiote-native/battery/angular — BatteryLevelService, BatteryStateService,
|
|
59
62
|
LowPowerModeService (`.connect()` returns a Signal)
|
|
60
63
|
```
|
|
61
64
|
|
|
62
|
-
Each adapter's hook/composable/service is a thin lifecycle wrapper (seed from the
|
|
63
|
-
`get*Async` call, subscribe to the matching listener, unsubscribe on unmount/cleanup)
|
|
64
|
-
same `core` functions — the subscription and fallback logic is written once and shared
|
|
65
|
-
|
|
65
|
+
Each adapter's hook/composable/rune/primitive/service is a thin lifecycle wrapper (seed from the
|
|
66
|
+
one-shot `get*Async` call, subscribe to the matching listener, unsubscribe on unmount/cleanup)
|
|
67
|
+
over the same `core` functions — the subscription and fallback logic is written once and shared
|
|
68
|
+
by every adapter. Solid's naming differs on purpose: `create*`, not `use*`, which Solid reserves
|
|
69
|
+
for consuming something that already exists.
|
|
66
70
|
|
|
67
71
|
## Use it
|
|
68
72
|
|
|
69
73
|
```tsx
|
|
70
74
|
// React — examples/expo-react/screens/BatteryScreen.tsx
|
|
71
|
-
import {
|
|
75
|
+
import {
|
|
76
|
+
useBatteryLevel,
|
|
77
|
+
useBatteryState,
|
|
78
|
+
useLowPowerMode,
|
|
79
|
+
} from '@symbiote-native/battery/react';
|
|
72
80
|
import { BatteryState } from '@symbiote-native/battery';
|
|
73
81
|
|
|
74
82
|
function BatteryScreen() {
|
|
75
|
-
const batteryLevel = useBatteryLevel();
|
|
76
|
-
const batteryState = useBatteryState();
|
|
77
|
-
const lowPowerMode = useLowPowerMode();
|
|
83
|
+
const batteryLevel = useBatteryLevel(); // number, -1 until the first reading arrives
|
|
84
|
+
const batteryState = useBatteryState(); // BatteryState, UNKNOWN until the first reading
|
|
85
|
+
const lowPowerMode = useLowPowerMode(); // boolean, false until the first reading
|
|
78
86
|
|
|
79
|
-
const batteryLevelLabel =
|
|
87
|
+
const batteryLevelLabel =
|
|
88
|
+
batteryLevel < 0 ? 'unknown' : `${Math.round(batteryLevel * 100)}%`;
|
|
80
89
|
|
|
81
90
|
return (
|
|
82
91
|
<>
|
|
83
92
|
<Text>{batteryLevelLabel}</Text>
|
|
84
|
-
<Text>
|
|
93
|
+
<Text>
|
|
94
|
+
{batteryState === BatteryState.CHARGING ? 'Charging' : 'Not charging'}
|
|
95
|
+
</Text>
|
|
85
96
|
<Text>{lowPowerMode ? 'On' : 'Off'}</Text>
|
|
86
97
|
</>
|
|
87
98
|
);
|
|
@@ -92,14 +103,20 @@ function BatteryScreen() {
|
|
|
92
103
|
<!-- Vue — examples/expo-vue-sfc/screens/BatteryScreen.vue -->
|
|
93
104
|
<script setup lang="ts">
|
|
94
105
|
import { computed } from 'vue';
|
|
95
|
-
import {
|
|
106
|
+
import {
|
|
107
|
+
useBatteryLevel,
|
|
108
|
+
useBatteryState,
|
|
109
|
+
useLowPowerMode,
|
|
110
|
+
} from '@symbiote-native/battery/vue';
|
|
96
111
|
|
|
97
112
|
const batteryLevel = useBatteryLevel(); // Ref<number>
|
|
98
113
|
const batteryState = useBatteryState(); // Ref<BatteryState>
|
|
99
114
|
const lowPowerMode = useLowPowerMode(); // Ref<boolean>
|
|
100
115
|
|
|
101
116
|
const batteryLevelText = computed(() =>
|
|
102
|
-
batteryLevel.value < 0
|
|
117
|
+
batteryLevel.value < 0
|
|
118
|
+
? 'unknown'
|
|
119
|
+
: `${Math.round(batteryLevel.value * 100)}%`,
|
|
103
120
|
);
|
|
104
121
|
</script>
|
|
105
122
|
<template>
|
|
@@ -108,16 +125,81 @@ const batteryLevelText = computed(() =>
|
|
|
108
125
|
</template>
|
|
109
126
|
```
|
|
110
127
|
|
|
128
|
+
```svelte
|
|
129
|
+
<!-- Svelte — examples/expo-svelte/screens/BatteryScreen.svelte. Each rune returns a boxed
|
|
130
|
+
getter object, read as `.current`, since a bare `$state` doesn't survive being handed out
|
|
131
|
+
of a plain function. -->
|
|
132
|
+
<script lang="ts">
|
|
133
|
+
import {
|
|
134
|
+
useBatteryLevel,
|
|
135
|
+
useBatteryState,
|
|
136
|
+
useLowPowerMode,
|
|
137
|
+
} from '@symbiote-native/battery/svelte';
|
|
138
|
+
import { BatteryState } from '@symbiote-native/battery';
|
|
139
|
+
|
|
140
|
+
const batteryLevel = useBatteryLevel(); // { current: number }, -1 until the first reading
|
|
141
|
+
const batteryState = useBatteryState(); // { current: BatteryState }
|
|
142
|
+
const lowPowerMode = useLowPowerMode(); // { current: boolean }
|
|
143
|
+
|
|
144
|
+
const batteryLevelLabel = $derived(
|
|
145
|
+
batteryLevel.current < 0
|
|
146
|
+
? 'unknown'
|
|
147
|
+
: `${Math.round(batteryLevel.current * 100)}%`,
|
|
148
|
+
);
|
|
149
|
+
</script>
|
|
150
|
+
|
|
151
|
+
<Text>{batteryLevelLabel}</Text>
|
|
152
|
+
<Text>
|
|
153
|
+
{batteryState.current === BatteryState.CHARGING ? 'Charging' : 'Not charging'}
|
|
154
|
+
</Text>
|
|
155
|
+
<Text>{lowPowerMode.current ? 'On' : 'Off'}</Text>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
// Solid — an accessor per value; call it to read, so a component body that runs once still
|
|
160
|
+
// re-renders the leaf that reads it.
|
|
161
|
+
import {
|
|
162
|
+
createBatteryLevel,
|
|
163
|
+
createBatteryState,
|
|
164
|
+
createLowPowerMode,
|
|
165
|
+
} from '@symbiote-native/battery/solid';
|
|
166
|
+
import { BatteryState } from '@symbiote-native/battery';
|
|
167
|
+
|
|
168
|
+
function BatteryScreen() {
|
|
169
|
+
const batteryLevel = createBatteryLevel(); // Accessor<number>, -1 until the first reading
|
|
170
|
+
const batteryState = createBatteryState(); // Accessor<BatteryState>
|
|
171
|
+
const lowPowerMode = createLowPowerMode(); // Accessor<boolean>
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<>
|
|
175
|
+
<Text>
|
|
176
|
+
{batteryLevel() < 0
|
|
177
|
+
? 'unknown'
|
|
178
|
+
: `${Math.round(batteryLevel() * 100)}%`}
|
|
179
|
+
</Text>
|
|
180
|
+
<Text>
|
|
181
|
+
{batteryState() === BatteryState.CHARGING ? 'Charging' : 'Not charging'}
|
|
182
|
+
</Text>
|
|
183
|
+
<Text>{lowPowerMode() ? 'On' : 'Off'}</Text>
|
|
184
|
+
</>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
111
189
|
```ts
|
|
112
190
|
// Angular — examples/expo-angular/src/screens/BatteryScreen.ts
|
|
113
191
|
import { Component, inject } from '@angular/core';
|
|
114
|
-
import {
|
|
192
|
+
import {
|
|
193
|
+
BatteryLevelService,
|
|
194
|
+
BatteryStateService,
|
|
195
|
+
LowPowerModeService,
|
|
196
|
+
} from '@symbiote-native/battery/angular';
|
|
115
197
|
|
|
116
|
-
@Component({
|
|
198
|
+
@Component({/* ... */})
|
|
117
199
|
export class BatteryScreen {
|
|
118
|
-
readonly batteryLevel = inject(BatteryLevelService).connect();
|
|
119
|
-
readonly batteryState = inject(BatteryStateService).connect();
|
|
120
|
-
readonly lowPowerMode = inject(LowPowerModeService).connect();
|
|
200
|
+
readonly batteryLevel = inject(BatteryLevelService).connect(); // Signal<number>
|
|
201
|
+
readonly batteryState = inject(BatteryStateService).connect(); // Signal<BatteryState>
|
|
202
|
+
readonly lowPowerMode = inject(LowPowerModeService).connect(); // Signal<boolean>
|
|
121
203
|
|
|
122
204
|
batteryLevelLabel(): string {
|
|
123
205
|
const level = this.batteryLevel();
|
|
@@ -126,7 +208,7 @@ export class BatteryScreen {
|
|
|
126
208
|
}
|
|
127
209
|
```
|
|
128
210
|
|
|
129
|
-
Each of the
|
|
211
|
+
Each of the demo screens above also renders a one-shot capabilities card
|
|
130
212
|
(`isAvailableAsync()`, and Android-only `isBatteryOptimizationEnabledAsync()`) resolved directly
|
|
131
213
|
from `@symbiote-native/battery`'s core entry point — see the linked files for the full version.
|
|
132
214
|
The iOS Simulator reports the battery API as unavailable (`isAvailableAsync()` resolves `false`)
|
|
@@ -156,12 +238,33 @@ Plus `BatteryState` (enum), `PowerState`, `BatteryLevelEvent`, `BatteryStateEven
|
|
|
156
238
|
`PowerModeEvent` — ported from upstream's `Battery.types.ts`.
|
|
157
239
|
|
|
158
240
|
```ts
|
|
159
|
-
import {
|
|
241
|
+
import {
|
|
242
|
+
getBatteryLevelAsync,
|
|
243
|
+
addBatteryLevelListener,
|
|
244
|
+
} from '@symbiote-native/battery';
|
|
160
245
|
|
|
161
246
|
// framework-scoped entry points re-export the same free functions, plus a lifecycle
|
|
162
247
|
// hook/composable/service per listener:
|
|
163
|
-
import {
|
|
164
|
-
|
|
248
|
+
import {
|
|
249
|
+
useBatteryLevel,
|
|
250
|
+
useBatteryState,
|
|
251
|
+
useLowPowerMode,
|
|
252
|
+
} from '@symbiote-native/battery/react';
|
|
253
|
+
import {
|
|
254
|
+
useBatteryLevel,
|
|
255
|
+
useBatteryState,
|
|
256
|
+
useLowPowerMode,
|
|
257
|
+
} from '@symbiote-native/battery/vue';
|
|
258
|
+
import {
|
|
259
|
+
useBatteryLevel,
|
|
260
|
+
useBatteryState,
|
|
261
|
+
useLowPowerMode,
|
|
262
|
+
} from '@symbiote-native/battery/svelte';
|
|
263
|
+
import {
|
|
264
|
+
createBatteryLevel,
|
|
265
|
+
createBatteryState,
|
|
266
|
+
createLowPowerMode,
|
|
267
|
+
} from '@symbiote-native/battery/solid';
|
|
165
268
|
import {
|
|
166
269
|
BatteryLevelService,
|
|
167
270
|
BatteryStateService,
|
|
@@ -179,11 +282,12 @@ unsubscribes on unmount — mirroring upstream's own `useBatteryLevel`/`useBatte
|
|
|
179
282
|
No Fabric/Descriptor angle at all — battery is a pure async-function + `EventEmitter` listener
|
|
180
283
|
surface, never a view. Tests inject a fake native-module object in place of the real
|
|
181
284
|
`requireNativeModule` resolution (`src/core/battery.test.ts`,
|
|
182
|
-
`src/{react,vue,angular}/**/*.test.{ts,tsx}`, `vitest`), the same pattern
|
|
285
|
+
`src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`, `vitest`), the same pattern
|
|
183
286
|
`@symbiote-native/sensors` and `@symbiote-native/local-auth` use — no `installFabric()`, no
|
|
184
287
|
ViewConfig. Native rendering itself is verified on-device — see the parent
|
|
185
288
|
[README](../../README.md).
|
|
186
289
|
|
|
187
|
-
The Android/iOS native wiring is done across all
|
|
188
|
-
(`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-
|
|
189
|
-
this package isn't yet in the public, non-Expo
|
|
290
|
+
The Android/iOS native wiring is done across all six `examples/expo-*` canary apps
|
|
291
|
+
(`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-svelte`,
|
|
292
|
+
`examples/expo-solid`, `examples/expo-angular`); this package isn't yet in the public, non-Expo
|
|
293
|
+
`examples/react` canary.
|
|
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
32
32
|
}
|
|
33
33
|
return useValue ? value : void 0;
|
|
34
34
|
};
|
|
35
|
-
import { effect, inject, Injectable, Injector, signal } from '@angular/core';
|
|
35
|
+
import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
|
|
36
36
|
import { addBatteryLevelListener, getBatteryLevelAsync } from '../../../core/index.js';
|
|
37
37
|
// Angular twin of React's `useBatteryLevel` hook and Vue's `useBatteryLevel` composable.
|
|
38
38
|
// Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
|
|
@@ -32,8 +32,8 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
32
32
|
}
|
|
33
33
|
return useValue ? value : void 0;
|
|
34
34
|
};
|
|
35
|
-
import { effect, inject, Injectable, Injector, signal } from '@angular/core';
|
|
36
|
-
import { addBatteryStateListener, BatteryState, getBatteryStateAsync } from '../../../core/index.js';
|
|
35
|
+
import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
|
|
36
|
+
import { addBatteryStateListener, BatteryState, getBatteryStateAsync, } from '../../../core/index.js';
|
|
37
37
|
// Angular twin of React's `useBatteryState` hook and Vue's `useBatteryState` composable. See
|
|
38
38
|
// BatteryLevelService for the `connect()` pattern rationale.
|
|
39
39
|
//
|
|
@@ -32,8 +32,8 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
32
32
|
}
|
|
33
33
|
return useValue ? value : void 0;
|
|
34
34
|
};
|
|
35
|
-
import { effect, inject, Injectable, Injector, signal } from '@angular/core';
|
|
36
|
-
import { addLowPowerModeListener, isLowPowerModeEnabledAsync } from '../../../core/index.js';
|
|
35
|
+
import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
|
|
36
|
+
import { addLowPowerModeListener, isLowPowerModeEnabledAsync, } from '../../../core/index.js';
|
|
37
37
|
// Angular twin of React's `useLowPowerMode` hook and Vue's `useLowPowerMode` composable. See
|
|
38
38
|
// BatteryLevelService for the `connect()` pattern rationale.
|
|
39
39
|
//
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// use-battery-level's shape. Seeds the initial value with a one-shot getBatteryStateAsync()
|
|
3
3
|
// call before the first native event fires, matching upstream's own useBatteryState.
|
|
4
4
|
import { useEffect, useState } from 'react';
|
|
5
|
-
import { addBatteryStateListener, BatteryState, getBatteryStateAsync } from '../../../core/index.js';
|
|
5
|
+
import { addBatteryStateListener, BatteryState, getBatteryStateAsync, } from '../../../core/index.js';
|
|
6
6
|
export function useBatteryState() {
|
|
7
7
|
const [batteryState, setBatteryState] = useState(BatteryState.UNKNOWN);
|
|
8
8
|
useEffect(() => {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// isLowPowerModeEnabledAsync() call before the first native event fires, matching upstream's
|
|
4
4
|
// own useLowPowerMode.
|
|
5
5
|
import { useEffect, useState } from 'react';
|
|
6
|
-
import { addLowPowerModeListener, isLowPowerModeEnabledAsync } from '../../../core/index.js';
|
|
6
|
+
import { addLowPowerModeListener, isLowPowerModeEnabledAsync, } from '../../../core/index.js';
|
|
7
7
|
export function useLowPowerMode() {
|
|
8
8
|
const [lowPowerMode, setLowPowerMode] = useState(false);
|
|
9
9
|
useEffect(() => {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// @symbiote-native/battery/solid: the Solid entry over the framework-agnostic core. Battery's
|
|
2
|
+
// three subscription-backed values (level/state/low-power-mode) each get their own primitive,
|
|
3
|
+
// matching upstream's own useBatteryLevel/useBatteryState/useLowPowerMode being three separate
|
|
4
|
+
// hooks, not one combined hook — mirrors the lifecycle-bucket naming of
|
|
5
|
+
// adapters/solid/src/primitives (never `hooks/`/`composables/`/`runes/`, and `create*` rather
|
|
6
|
+
// than `use*`, which Solid reserves for consuming something that already exists).
|
|
7
|
+
export { createBatteryLevel } from './primitives/create-battery-level.js';
|
|
8
|
+
export { createBatteryState } from './primitives/create-battery-state.js';
|
|
9
|
+
export { createLowPowerMode } from './primitives/create-low-power-mode.js';
|
|
10
|
+
export * from '../core/index.js';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Solid lifecycle wiring over the framework-agnostic core (core/battery.ts) — the twin of
|
|
2
|
+
// react/hooks/use-battery-level, vue/composables/use-battery-level and
|
|
3
|
+
// svelte/runes/use-battery-level.svelte. See adapters/solid/src/primitives/create-color-scheme.ts
|
|
4
|
+
// for why the bucket is `primitives/` and the name is `create*` rather than `use*`.
|
|
5
|
+
//
|
|
6
|
+
// Returns an ACCESSOR, never a snapshot: a Solid component body runs ONCE, so a returned number
|
|
7
|
+
// would freeze at the level the screen mounted with.
|
|
8
|
+
//
|
|
9
|
+
// The listener is registered SYNCHRONOUSLY with the body — there is no useEffect/onMounted twin
|
|
10
|
+
// to defer it to, so nothing can arrive before the subscription exists. The seed stays a promise,
|
|
11
|
+
// which leaves one ordering hazard the React and Vue versions both have and neither guards: a
|
|
12
|
+
// native event can land BEFORE getBatteryLevelAsync() resolves, and the late seed would then
|
|
13
|
+
// overwrite the fresher reading with an older one. `hasNativeReading` drops the seed in that case
|
|
14
|
+
// — these two writers are ordered by latency, not by time, so last-write-wins is wrong here.
|
|
15
|
+
import { createSignal, onCleanup } from 'solid-js';
|
|
16
|
+
import { addBatteryLevelListener, getBatteryLevelAsync, } from '../../core/index.js';
|
|
17
|
+
// core/battery.ts's documented sentinel for "no reading yet / this device cannot report a level".
|
|
18
|
+
const UNKNOWN_BATTERY_LEVEL = -1;
|
|
19
|
+
export function createBatteryLevel() {
|
|
20
|
+
const [batteryLevel, setBatteryLevel] = createSignal(UNKNOWN_BATTERY_LEVEL);
|
|
21
|
+
let hasNativeReading = false;
|
|
22
|
+
const subscription = addBatteryLevelListener(event => {
|
|
23
|
+
hasNativeReading = true;
|
|
24
|
+
setBatteryLevel(event.batteryLevel);
|
|
25
|
+
});
|
|
26
|
+
getBatteryLevelAsync().then(level => {
|
|
27
|
+
if (!hasNativeReading) {
|
|
28
|
+
setBatteryLevel(level);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
onCleanup(() => {
|
|
32
|
+
subscription.remove();
|
|
33
|
+
});
|
|
34
|
+
return batteryLevel;
|
|
35
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Solid lifecycle wiring over the framework-agnostic core (core/battery.ts) — mirrors
|
|
2
|
+
// create-battery-level's shape, including the seed-vs-event ordering guard documented there.
|
|
3
|
+
import { createSignal, onCleanup } from 'solid-js';
|
|
4
|
+
import { addBatteryStateListener, BatteryState, getBatteryStateAsync, } from '../../core/index.js';
|
|
5
|
+
export function createBatteryState() {
|
|
6
|
+
const [batteryState, setBatteryState] = createSignal(BatteryState.UNKNOWN);
|
|
7
|
+
let hasNativeReading = false;
|
|
8
|
+
const subscription = addBatteryStateListener(event => {
|
|
9
|
+
hasNativeReading = true;
|
|
10
|
+
setBatteryState(event.batteryState);
|
|
11
|
+
});
|
|
12
|
+
getBatteryStateAsync().then(state => {
|
|
13
|
+
if (!hasNativeReading) {
|
|
14
|
+
setBatteryState(state);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
onCleanup(() => {
|
|
18
|
+
subscription.remove();
|
|
19
|
+
});
|
|
20
|
+
return batteryState;
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Solid lifecycle wiring over the framework-agnostic core (core/battery.ts) — mirrors
|
|
2
|
+
// create-battery-level's shape, including the seed-vs-event ordering guard documented there.
|
|
3
|
+
import { createSignal, onCleanup } from 'solid-js';
|
|
4
|
+
import { addLowPowerModeListener, isLowPowerModeEnabledAsync, } from '../../core/index.js';
|
|
5
|
+
export function createLowPowerMode() {
|
|
6
|
+
const [lowPowerMode, setLowPowerMode] = createSignal(false);
|
|
7
|
+
let hasNativeReading = false;
|
|
8
|
+
const subscription = addLowPowerModeListener(event => {
|
|
9
|
+
hasNativeReading = true;
|
|
10
|
+
setLowPowerMode(event.lowPowerMode);
|
|
11
|
+
});
|
|
12
|
+
isLowPowerModeEnabledAsync().then(enabled => {
|
|
13
|
+
if (!hasNativeReading) {
|
|
14
|
+
setLowPowerMode(enabled);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
onCleanup(() => {
|
|
18
|
+
subscription.remove();
|
|
19
|
+
});
|
|
20
|
+
return lowPowerMode;
|
|
21
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// the declaring module, so a raw `let x = $state(...)` handed out of a plain function arrives
|
|
16
16
|
// dead at the caller. `{ get current() { … } }` is the sanctioned way across, read as `.current`
|
|
17
17
|
// exactly like Vue's `Ref` is unwrapped via `.value`.
|
|
18
|
-
import { addBatteryLevelListener, getBatteryLevelAsync } from '../../core/index.js';
|
|
18
|
+
import { addBatteryLevelListener, getBatteryLevelAsync, } from '../../core/index.js';
|
|
19
19
|
// core/battery.ts's documented sentinel for "this device cannot report a battery level".
|
|
20
20
|
const UNKNOWN_BATTERY_LEVEL = -1;
|
|
21
21
|
export function useBatteryLevel() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { effect, inject, Injectable, Injector, signal } from '@angular/core';
|
|
1
|
+
import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
|
|
2
2
|
import { addBatteryLevelListener, getBatteryLevelAsync } from '../../../core';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
// Angular twin of React's `useBatteryLevel` hook and Vue's `useBatteryLevel` composable.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/battery-level.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/battery-level.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,MAAM,EACN,UAAU,EACV,QAAQ,EACR,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;;AAE9E,yFAAyF;AACzF,4FAA4F;AAC5F,+FAA+F;AAC/F,gCAAgC;AAChC,EAAE;AACF,mEAAmE;AACnE,sCAAsC;AACtC,EAAE;AACF,+FAA+F;AAC/F,+DAA+D;AAE/D,MAAM,OAAO,mBAAmB;IACb,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO;QACL,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC;yFAAC,CAAC;QAEhC,MAAM,CACJ,SAAS,CAAC,EAAE;YACV,oBAAoB,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9D,MAAM,YAAY,GAAG,uBAAuB,CAAC,KAAK,CAAC,EAAE,CACnD,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CACrC,CAAC;YACF,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC,EACD,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAC5B,CAAC;QAEF,OAAO,YAAY,CAAC,UAAU,EAAE,CAAC;IACnC,CAAC;uGAlBU,mBAAmB;2GAAnB,mBAAmB,cADN,MAAM;;2FACnB,mBAAmB;kBAD/B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { effect, inject, Injectable, Injector, signal } from '@angular/core';
|
|
2
|
-
import { addBatteryStateListener, BatteryState, getBatteryStateAsync } from '../../../core';
|
|
1
|
+
import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
|
|
2
|
+
import { addBatteryStateListener, BatteryState, getBatteryStateAsync, } from '../../../core';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
// Angular twin of React's `useBatteryState` hook and Vue's `useBatteryState` composable. See
|
|
5
5
|
// BatteryLevelService for the `connect()` pattern rationale.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/battery-state.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/battery-state.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,MAAM,EACN,UAAU,EACV,QAAQ,EACR,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,uBAAuB,EACvB,YAAY,EACZ,oBAAoB,GACrB,MAAM,eAAe,CAAC;;AAEvB,6FAA6F;AAC7F,6DAA6D;AAC7D,EAAE;AACF,mEAAmE;AACnE,sCAAsC;AAEtC,MAAM,OAAO,mBAAmB;IACb,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO;QACL,MAAM,YAAY,GAAG,MAAM,CAAe,YAAY,CAAC,OAAO;yFAAC,CAAC;QAEhE,MAAM,CACJ,SAAS,CAAC,EAAE;YACV,oBAAoB,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9D,MAAM,YAAY,GAAG,uBAAuB,CAAC,KAAK,CAAC,EAAE,CACnD,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CACrC,CAAC;YACF,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC,EACD,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAC5B,CAAC;QAEF,OAAO,YAAY,CAAC,UAAU,EAAE,CAAC;IACnC,CAAC;uGAlBU,mBAAmB;2GAAnB,mBAAmB,cADN,MAAM;;2FACnB,mBAAmB;kBAD/B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { effect, inject, Injectable, Injector, signal } from '@angular/core';
|
|
2
|
-
import { addLowPowerModeListener, isLowPowerModeEnabledAsync } from '../../../core';
|
|
1
|
+
import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
|
|
2
|
+
import { addLowPowerModeListener, isLowPowerModeEnabledAsync, } from '../../../core';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
// Angular twin of React's `useLowPowerMode` hook and Vue's `useLowPowerMode` composable. See
|
|
5
5
|
// BatteryLevelService for the `connect()` pattern rationale.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/low-power-mode.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/low-power-mode.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,MAAM,EACN,UAAU,EACV,QAAQ,EACR,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,eAAe,CAAC;;AAEvB,6FAA6F;AAC7F,6DAA6D;AAC7D,EAAE;AACF,mEAAmE;AACnE,sCAAsC;AAEtC,MAAM,OAAO,mBAAmB;IACb,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO;QACL,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK;yFAAC,CAAC;QAEnC,MAAM,CACJ,SAAS,CAAC,EAAE;YACV,0BAA0B,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;YACxE,MAAM,YAAY,GAAG,uBAAuB,CAAC,KAAK,CAAC,EAAE,CACnD,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CACrC,CAAC;YACF,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC,EACD,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAC5B,CAAC;QAEF,OAAO,YAAY,CAAC,UAAU,EAAE,CAAC;IACnC,CAAC;uGAlBU,mBAAmB;2GAAnB,mBAAmB,cADN,MAAM;;2FACnB,mBAAmB;kBAD/B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AASxD,MAAM,wBAAwB,GAAG,aAAa,CAAC;AA0B/C,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAC5C,wBAAwB,CACzB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/battery",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "expo-battery wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Battery level and state (charging/full/unplugged/unknown), low-power-mode detection, plus event listeners for level and state changes.",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "expo-battery wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Battery level and state (charging/full/unplugged/unknown), low-power-mode detection, plus event listeners for level and state changes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -13,6 +13,18 @@
|
|
|
13
13
|
"url": "https://github.com/OneEyed1366/symbiote-native/issues"
|
|
14
14
|
},
|
|
15
15
|
"author": "Andrey Prokopenko <psevdoproger@gmail.com>",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"react-native",
|
|
18
|
+
"symbiote-native",
|
|
19
|
+
"expo-battery",
|
|
20
|
+
"react",
|
|
21
|
+
"vue",
|
|
22
|
+
"svelte",
|
|
23
|
+
"solid",
|
|
24
|
+
"angular",
|
|
25
|
+
"battery-status",
|
|
26
|
+
"low-power-mode"
|
|
27
|
+
],
|
|
16
28
|
"type": "module",
|
|
17
29
|
"main": "./build/core/index.js",
|
|
18
30
|
"module": "./build/core/index.js",
|
|
@@ -34,6 +46,10 @@
|
|
|
34
46
|
"types": "./build/svelte/index.d.ts",
|
|
35
47
|
"default": "./build/svelte/index.js"
|
|
36
48
|
},
|
|
49
|
+
"./solid": {
|
|
50
|
+
"types": "./build/solid/index.d.ts",
|
|
51
|
+
"default": "./build/solid/index.js"
|
|
52
|
+
},
|
|
37
53
|
"./angular": {
|
|
38
54
|
"types": "./build-ngc/angular/index.d.ts",
|
|
39
55
|
"react-native": "./build-ngc/angular/index.js",
|
|
@@ -57,17 +73,19 @@
|
|
|
57
73
|
"expo-modules-core": "57.0.5"
|
|
58
74
|
},
|
|
59
75
|
"peerDependencies": {
|
|
60
|
-
"@symbiote-native/angular": ">=0.6.2",
|
|
61
|
-
"@symbiote-native/engine": ">=0.1.7",
|
|
62
|
-
"@symbiote-native/react": ">=0.2.8",
|
|
63
|
-
"@symbiote-native/svelte": ">=0.1.0",
|
|
64
|
-
"@symbiote-native/vue": ">=0.3.8",
|
|
65
76
|
"@angular/core": ">=20",
|
|
66
77
|
"@vue/runtime-core": "^3.5.13",
|
|
67
78
|
"react": ">=19.0.0",
|
|
68
79
|
"react-native": ">=0.86",
|
|
80
|
+
"solid-js": ">=1.9.0",
|
|
69
81
|
"svelte": ">=5.56.0",
|
|
70
|
-
"vue": ">=3.5.0"
|
|
82
|
+
"vue": ">=3.5.0",
|
|
83
|
+
"@symbiote-native/angular": "^1.0.0",
|
|
84
|
+
"@symbiote-native/engine": "^0.4.0",
|
|
85
|
+
"@symbiote-native/react": "^1.0.0",
|
|
86
|
+
"@symbiote-native/solid": "^1.0.0",
|
|
87
|
+
"@symbiote-native/svelte": "^1.0.0",
|
|
88
|
+
"@symbiote-native/vue": "^1.0.0"
|
|
71
89
|
},
|
|
72
90
|
"peerDependenciesMeta": {
|
|
73
91
|
"@symbiote-native/angular": {
|
|
@@ -76,6 +94,9 @@
|
|
|
76
94
|
"@symbiote-native/react": {
|
|
77
95
|
"optional": true
|
|
78
96
|
},
|
|
97
|
+
"@symbiote-native/solid": {
|
|
98
|
+
"optional": true
|
|
99
|
+
},
|
|
79
100
|
"@symbiote-native/svelte": {
|
|
80
101
|
"optional": true
|
|
81
102
|
},
|
|
@@ -91,6 +112,9 @@
|
|
|
91
112
|
"react": {
|
|
92
113
|
"optional": true
|
|
93
114
|
},
|
|
115
|
+
"solid-js": {
|
|
116
|
+
"optional": true
|
|
117
|
+
},
|
|
94
118
|
"svelte": {
|
|
95
119
|
"optional": true
|
|
96
120
|
},
|
|
@@ -106,14 +130,16 @@
|
|
|
106
130
|
"@types/react": "^19.2.0",
|
|
107
131
|
"@vue/runtime-core": "^3.5.13",
|
|
108
132
|
"react": "19.2.3",
|
|
133
|
+
"solid-js": "^1.9.14",
|
|
109
134
|
"svelte": "^5.56.0",
|
|
110
135
|
"typescript": "~6.0.0",
|
|
111
|
-
"@symbiote-native/angular": "0.
|
|
112
|
-
"@symbiote-native/engine": "0.
|
|
113
|
-
"@symbiote-native/
|
|
114
|
-
"@symbiote-native/
|
|
115
|
-
"@symbiote-native/
|
|
116
|
-
"@symbiote-native/
|
|
136
|
+
"@symbiote-native/angular": "1.0.0",
|
|
137
|
+
"@symbiote-native/engine": "0.4.0",
|
|
138
|
+
"@symbiote-native/react": "1.0.0",
|
|
139
|
+
"@symbiote-native/solid": "1.0.0",
|
|
140
|
+
"@symbiote-native/svelte": "1.0.0",
|
|
141
|
+
"@symbiote-native/test-utils": "0.3.0",
|
|
142
|
+
"@symbiote-native/vue": "1.0.0"
|
|
117
143
|
},
|
|
118
144
|
"scripts": {
|
|
119
145
|
"typecheck": "tsc --build",
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
effect,
|
|
3
|
+
inject,
|
|
4
|
+
Injectable,
|
|
5
|
+
Injector,
|
|
6
|
+
signal,
|
|
7
|
+
type Signal,
|
|
8
|
+
} from '@angular/core';
|
|
2
9
|
import { addBatteryLevelListener, getBatteryLevelAsync } from '../../../core';
|
|
3
10
|
|
|
4
11
|
// Angular twin of React's `useBatteryLevel` hook and Vue's `useBatteryLevel` composable.
|
|
@@ -21,7 +28,9 @@ export class BatteryLevelService {
|
|
|
21
28
|
effect(
|
|
22
29
|
onCleanup => {
|
|
23
30
|
getBatteryLevelAsync().then(level => batteryLevel.set(level));
|
|
24
|
-
const subscription = addBatteryLevelListener(event =>
|
|
31
|
+
const subscription = addBatteryLevelListener(event =>
|
|
32
|
+
batteryLevel.set(event.batteryLevel),
|
|
33
|
+
);
|
|
25
34
|
onCleanup(() => subscription.remove());
|
|
26
35
|
},
|
|
27
36
|
{ injector: this.injector },
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
effect,
|
|
3
|
+
inject,
|
|
4
|
+
Injectable,
|
|
5
|
+
Injector,
|
|
6
|
+
signal,
|
|
7
|
+
type Signal,
|
|
8
|
+
} from '@angular/core';
|
|
9
|
+
import {
|
|
10
|
+
addBatteryStateListener,
|
|
11
|
+
BatteryState,
|
|
12
|
+
getBatteryStateAsync,
|
|
13
|
+
} from '../../../core';
|
|
3
14
|
|
|
4
15
|
// Angular twin of React's `useBatteryState` hook and Vue's `useBatteryState` composable. See
|
|
5
16
|
// BatteryLevelService for the `connect()` pattern rationale.
|
|
@@ -16,7 +27,9 @@ export class BatteryStateService {
|
|
|
16
27
|
effect(
|
|
17
28
|
onCleanup => {
|
|
18
29
|
getBatteryStateAsync().then(state => batteryState.set(state));
|
|
19
|
-
const subscription = addBatteryStateListener(event =>
|
|
30
|
+
const subscription = addBatteryStateListener(event =>
|
|
31
|
+
batteryState.set(event.batteryState),
|
|
32
|
+
);
|
|
20
33
|
onCleanup(() => subscription.remove());
|
|
21
34
|
},
|
|
22
35
|
{ injector: this.injector },
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
effect,
|
|
3
|
+
inject,
|
|
4
|
+
Injectable,
|
|
5
|
+
Injector,
|
|
6
|
+
signal,
|
|
7
|
+
type Signal,
|
|
8
|
+
} from '@angular/core';
|
|
9
|
+
import {
|
|
10
|
+
addLowPowerModeListener,
|
|
11
|
+
isLowPowerModeEnabledAsync,
|
|
12
|
+
} from '../../../core';
|
|
3
13
|
|
|
4
14
|
// Angular twin of React's `useLowPowerMode` hook and Vue's `useLowPowerMode` composable. See
|
|
5
15
|
// BatteryLevelService for the `connect()` pattern rationale.
|
|
@@ -16,7 +26,9 @@ export class LowPowerModeService {
|
|
|
16
26
|
effect(
|
|
17
27
|
onCleanup => {
|
|
18
28
|
isLowPowerModeEnabledAsync().then(enabled => lowPowerMode.set(enabled));
|
|
19
|
-
const subscription = addLowPowerModeListener(event =>
|
|
29
|
+
const subscription = addLowPowerModeListener(event =>
|
|
30
|
+
lowPowerMode.set(event.lowPowerMode),
|
|
31
|
+
);
|
|
20
32
|
onCleanup(() => subscription.remove());
|
|
21
33
|
},
|
|
22
34
|
{ injector: this.injector },
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { requireNativeModule } from 'expo-modules-core';
|
|
2
2
|
import type { EventSubscription } from 'expo-modules-core';
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
BatteryLevelEvent,
|
|
5
|
+
BatteryState,
|
|
6
|
+
BatteryStateEvent,
|
|
7
|
+
PowerModeEvent,
|
|
8
|
+
} from './types';
|
|
4
9
|
|
|
5
10
|
const EXPO_BATTERY_MODULE_NAME = 'ExpoBattery';
|
|
6
11
|
|
|
@@ -28,4 +33,6 @@ export type INativeBatteryModule = {
|
|
|
28
33
|
): EventSubscription;
|
|
29
34
|
};
|
|
30
35
|
|
|
31
|
-
export const expoBattery = requireNativeModule<INativeBatteryModule>(
|
|
36
|
+
export const expoBattery = requireNativeModule<INativeBatteryModule>(
|
|
37
|
+
EXPO_BATTERY_MODULE_NAME,
|
|
38
|
+
);
|
|
@@ -11,7 +11,9 @@ export function useBatteryLevel(): number {
|
|
|
11
11
|
|
|
12
12
|
useEffect(() => {
|
|
13
13
|
getBatteryLevelAsync().then(setBatteryLevel);
|
|
14
|
-
const subscription = addBatteryLevelListener(event =>
|
|
14
|
+
const subscription = addBatteryLevelListener(event =>
|
|
15
|
+
setBatteryLevel(event.batteryLevel),
|
|
16
|
+
);
|
|
15
17
|
return () => subscription.remove();
|
|
16
18
|
}, []);
|
|
17
19
|
|
|
@@ -2,14 +2,22 @@
|
|
|
2
2
|
// use-battery-level's shape. Seeds the initial value with a one-shot getBatteryStateAsync()
|
|
3
3
|
// call before the first native event fires, matching upstream's own useBatteryState.
|
|
4
4
|
import { useEffect, useState } from 'react';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
addBatteryStateListener,
|
|
7
|
+
BatteryState,
|
|
8
|
+
getBatteryStateAsync,
|
|
9
|
+
} from '../../../core';
|
|
6
10
|
|
|
7
11
|
export function useBatteryState(): BatteryState {
|
|
8
|
-
const [batteryState, setBatteryState] = useState<BatteryState>(
|
|
12
|
+
const [batteryState, setBatteryState] = useState<BatteryState>(
|
|
13
|
+
BatteryState.UNKNOWN,
|
|
14
|
+
);
|
|
9
15
|
|
|
10
16
|
useEffect(() => {
|
|
11
17
|
getBatteryStateAsync().then(setBatteryState);
|
|
12
|
-
const subscription = addBatteryStateListener(event =>
|
|
18
|
+
const subscription = addBatteryStateListener(event =>
|
|
19
|
+
setBatteryState(event.batteryState),
|
|
20
|
+
);
|
|
13
21
|
return () => subscription.remove();
|
|
14
22
|
}, []);
|
|
15
23
|
|
|
@@ -3,14 +3,19 @@
|
|
|
3
3
|
// isLowPowerModeEnabledAsync() call before the first native event fires, matching upstream's
|
|
4
4
|
// own useLowPowerMode.
|
|
5
5
|
import { useEffect, useState } from 'react';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
addLowPowerModeListener,
|
|
8
|
+
isLowPowerModeEnabledAsync,
|
|
9
|
+
} from '../../../core';
|
|
7
10
|
|
|
8
11
|
export function useLowPowerMode(): boolean {
|
|
9
12
|
const [lowPowerMode, setLowPowerMode] = useState(false);
|
|
10
13
|
|
|
11
14
|
useEffect(() => {
|
|
12
15
|
isLowPowerModeEnabledAsync().then(setLowPowerMode);
|
|
13
|
-
const subscription = addLowPowerModeListener(event =>
|
|
16
|
+
const subscription = addLowPowerModeListener(event =>
|
|
17
|
+
setLowPowerMode(event.lowPowerMode),
|
|
18
|
+
);
|
|
14
19
|
return () => subscription.remove();
|
|
15
20
|
}, []);
|
|
16
21
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// @symbiote-native/battery/solid: the Solid entry over the framework-agnostic core. Battery's
|
|
2
|
+
// three subscription-backed values (level/state/low-power-mode) each get their own primitive,
|
|
3
|
+
// matching upstream's own useBatteryLevel/useBatteryState/useLowPowerMode being three separate
|
|
4
|
+
// hooks, not one combined hook — mirrors the lifecycle-bucket naming of
|
|
5
|
+
// adapters/solid/src/primitives (never `hooks/`/`composables/`/`runes/`, and `create*` rather
|
|
6
|
+
// than `use*`, which Solid reserves for consuming something that already exists).
|
|
7
|
+
|
|
8
|
+
export { createBatteryLevel } from './primitives/create-battery-level';
|
|
9
|
+
export { createBatteryState } from './primitives/create-battery-state';
|
|
10
|
+
export { createLowPowerMode } from './primitives/create-low-power-mode';
|
|
11
|
+
export * from '../core';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Solid lifecycle wiring over the framework-agnostic core (core/battery.ts) — the twin of
|
|
2
|
+
// react/hooks/use-battery-level, vue/composables/use-battery-level and
|
|
3
|
+
// svelte/runes/use-battery-level.svelte. See adapters/solid/src/primitives/create-color-scheme.ts
|
|
4
|
+
// for why the bucket is `primitives/` and the name is `create*` rather than `use*`.
|
|
5
|
+
//
|
|
6
|
+
// Returns an ACCESSOR, never a snapshot: a Solid component body runs ONCE, so a returned number
|
|
7
|
+
// would freeze at the level the screen mounted with.
|
|
8
|
+
//
|
|
9
|
+
// The listener is registered SYNCHRONOUSLY with the body — there is no useEffect/onMounted twin
|
|
10
|
+
// to defer it to, so nothing can arrive before the subscription exists. The seed stays a promise,
|
|
11
|
+
// which leaves one ordering hazard the React and Vue versions both have and neither guards: a
|
|
12
|
+
// native event can land BEFORE getBatteryLevelAsync() resolves, and the late seed would then
|
|
13
|
+
// overwrite the fresher reading with an older one. `hasNativeReading` drops the seed in that case
|
|
14
|
+
// — these two writers are ordered by latency, not by time, so last-write-wins is wrong here.
|
|
15
|
+
import { createSignal, onCleanup, type Accessor } from 'solid-js';
|
|
16
|
+
import {
|
|
17
|
+
addBatteryLevelListener,
|
|
18
|
+
getBatteryLevelAsync,
|
|
19
|
+
type EventSubscription,
|
|
20
|
+
} from '../../core';
|
|
21
|
+
|
|
22
|
+
// core/battery.ts's documented sentinel for "no reading yet / this device cannot report a level".
|
|
23
|
+
const UNKNOWN_BATTERY_LEVEL = -1;
|
|
24
|
+
|
|
25
|
+
export function createBatteryLevel(): Accessor<number> {
|
|
26
|
+
const [batteryLevel, setBatteryLevel] = createSignal(UNKNOWN_BATTERY_LEVEL);
|
|
27
|
+
let hasNativeReading = false;
|
|
28
|
+
|
|
29
|
+
const subscription: EventSubscription = addBatteryLevelListener(event => {
|
|
30
|
+
hasNativeReading = true;
|
|
31
|
+
setBatteryLevel(event.batteryLevel);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
getBatteryLevelAsync().then(level => {
|
|
35
|
+
if (!hasNativeReading) {
|
|
36
|
+
setBatteryLevel(level);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
onCleanup(() => {
|
|
41
|
+
subscription.remove();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return batteryLevel;
|
|
45
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Solid lifecycle wiring over the framework-agnostic core (core/battery.ts) — mirrors
|
|
2
|
+
// create-battery-level's shape, including the seed-vs-event ordering guard documented there.
|
|
3
|
+
import { createSignal, onCleanup, type Accessor } from 'solid-js';
|
|
4
|
+
import {
|
|
5
|
+
addBatteryStateListener,
|
|
6
|
+
BatteryState,
|
|
7
|
+
getBatteryStateAsync,
|
|
8
|
+
type EventSubscription,
|
|
9
|
+
} from '../../core';
|
|
10
|
+
|
|
11
|
+
export function createBatteryState(): Accessor<BatteryState> {
|
|
12
|
+
const [batteryState, setBatteryState] = createSignal<BatteryState>(
|
|
13
|
+
BatteryState.UNKNOWN,
|
|
14
|
+
);
|
|
15
|
+
let hasNativeReading = false;
|
|
16
|
+
|
|
17
|
+
const subscription: EventSubscription = addBatteryStateListener(event => {
|
|
18
|
+
hasNativeReading = true;
|
|
19
|
+
setBatteryState(event.batteryState);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
getBatteryStateAsync().then(state => {
|
|
23
|
+
if (!hasNativeReading) {
|
|
24
|
+
setBatteryState(state);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
onCleanup(() => {
|
|
29
|
+
subscription.remove();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return batteryState;
|
|
33
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Solid lifecycle wiring over the framework-agnostic core (core/battery.ts) — mirrors
|
|
2
|
+
// create-battery-level's shape, including the seed-vs-event ordering guard documented there.
|
|
3
|
+
import { createSignal, onCleanup, type Accessor } from 'solid-js';
|
|
4
|
+
import {
|
|
5
|
+
addLowPowerModeListener,
|
|
6
|
+
isLowPowerModeEnabledAsync,
|
|
7
|
+
type EventSubscription,
|
|
8
|
+
} from '../../core';
|
|
9
|
+
|
|
10
|
+
export function createLowPowerMode(): Accessor<boolean> {
|
|
11
|
+
const [lowPowerMode, setLowPowerMode] = createSignal(false);
|
|
12
|
+
let hasNativeReading = false;
|
|
13
|
+
|
|
14
|
+
const subscription: EventSubscription = addLowPowerModeListener(event => {
|
|
15
|
+
hasNativeReading = true;
|
|
16
|
+
setLowPowerMode(event.lowPowerMode);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
isLowPowerModeEnabledAsync().then(enabled => {
|
|
20
|
+
if (!hasNativeReading) {
|
|
21
|
+
setLowPowerMode(enabled);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
onCleanup(() => {
|
|
26
|
+
subscription.remove();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return lowPowerMode;
|
|
30
|
+
}
|
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
// the declaring module, so a raw `let x = $state(...)` handed out of a plain function arrives
|
|
16
16
|
// dead at the caller. `{ get current() { … } }` is the sanctioned way across, read as `.current`
|
|
17
17
|
// exactly like Vue's `Ref` is unwrapped via `.value`.
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
addBatteryLevelListener,
|
|
20
|
+
getBatteryLevelAsync,
|
|
21
|
+
type EventSubscription,
|
|
22
|
+
} from '../../core';
|
|
19
23
|
|
|
20
24
|
// core/battery.ts's documented sentinel for "this device cannot report a battery level".
|
|
21
25
|
const UNKNOWN_BATTERY_LEVEL = -1;
|