Skip to content

Commit

Permalink
feat: add sensor/useBattery hook
Browse files Browse the repository at this point in the history
  • Loading branch information
jo0ger committed Oct 29, 2019
1 parent d7fca12 commit 97ec0a9
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 2 deletions.
56 changes: 56 additions & 0 deletions src/hooks/sensor/useBattery/demo.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { storiesOf } from '@storybook/vue'
import { createComponent } from '@vue/composition-api'
import Doc from '../../../__stories__/components/Doc'
import { useBattery } from '../../..'

const Demo = createComponent({
setup() {
const battery = useBattery()
return { battery }
},
render() {
const { battery } = this

if (!battery.isSupported) {
return (
<div>
<strong>Battery sensor</strong>: <span>not supported</span>
</div>
)
}

if (!battery.fetched) {
return (
<div>
<strong>Battery sensor</strong>: <span>supported</span> <br />
<strong>Battery state</strong>: <span>fetching</span>
</div>
)
}

return (
<div>
<strong>Battery sensor</strong>:&nbsp;&nbsp; <span>supported</span>{' '}
<br />
<strong>Battery state</strong>: <span>fetched</span> <br />
<strong>Charge level</strong>:&nbsp;&nbsp;{' '}
<span>{(battery.level * 100).toFixed(0)}%</span> <br />
<strong>Charging</strong>:&nbsp;&nbsp;{' '}
<span>{battery.charging ? 'yes' : 'no'}</span> <br />
<strong>Charging time</strong>:&nbsp;&nbsp;
<span>
{battery.chargingTime ? battery.chargingTime : 'finished'}
</span>{' '}
<br />
<strong>Discharging time</strong>:&nbsp;&nbsp;{' '}
<span>{battery.dischargingTime}</span>
</div>
)
}
})

const Docs = () => <Doc md={require('./doc.md')}></Doc>

storiesOf('Sensor|useBattery', module)
.add('Docs', () => Docs as any)
.add('Demo', () => Demo)
90 changes: 90 additions & 0 deletions src/hooks/sensor/useBattery/doc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# useBattery

Vue hook that tracks device battery state.

> **Note:** current `BatteryManager` API state is obsolete.
> Although it may still work in some browsers, its use is discouraged since it could be removed at any time.
## Usage

```jsx
import { createComponent } from '@vue/composition-api'
import { useBattery } from 'vuses'

const Demo = createComponent({
setup() {
const battery = useBattery()
return { battery }
},
render() {
const { battery } = this

if (!battery.isSupported) {
return (
<div>
<strong>Battery sensor</strong>: <span>not supported</span>
</div>
)
}

if (!battery.fetched) {
return (
<div>
<strong>Battery sensor</strong>: <span>supported</span> <br />
<strong>Battery state</strong>: <span>fetching</span>
</div>
)
}

return (
<div>
<strong>Battery sensor</strong>:&nbsp;&nbsp; <span>supported</span>{' '}
<br />
<strong>Battery state</strong>: <span>fetched</span> <br />
<strong>Charge level</strong>:&nbsp;&nbsp;{' '}
<span>{(battery.level * 100).toFixed(0)}%</span> <br />
<strong>Charging</strong>:&nbsp;&nbsp; <span>
{battery.charging ? 'yes' : 'no'}
</span> <br />
<strong>Charging time</strong>:&nbsp;&nbsp;
<span>
{battery.chargingTime ? battery.chargingTime : 'finished'}
</span> <br />
<strong>Discharging time</strong>:&nbsp;&nbsp;{' '}
<span>{battery.dischargingTime}</span>
</div>
)
}
})
```

## Reference

```typescript
interface BatteryState {
charging: boolean
chargingTime: number
dischargingTime: number
level: number
}

type FetchedBatteryState = BatteryState & {
isSupported: boolean
fetched: boolean
}

type UseBatteryState =
| { isSupported: false } // Battery API is not supported
| FetchedBatteryState // battery API supported

function useBattery(): UseBatteryState
```

## ReturnValue

- **`isSupported`**_`: boolean`_ - whether browser/devise supports BatteryManager;
- **`fetched`**_`: boolean`_ - whether battery state is fetched;
- **`level`**_`: number`_ - representing the system's battery charge level scaled to a value between 0.0 and 1.0.
- **`charging`**_`: boolean`_ - indicating whether or not the battery is currently being charged.
- **`dischargingTime`**_`: number`_ - remaining time in seconds until the battery is completely discharged and the system will suspend.
- **`chargingTime`**_`: number`_ - remaining time in seconds until the battery is fully charged, or 0 if the battery is already fully charged.
96 changes: 96 additions & 0 deletions src/hooks/sensor/useBattery/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { reactive, onMounted, onUnmounted } from '@vue/composition-api'
import {
checkBrowser,
addEventListener,
removeEventListener,
warn
} from '../../../utils'

export interface BatteryState {
charging: boolean
chargingTime: number
dischargingTime: number
level: number
}

type FetchedBatteryState = BatteryState & {
isSupported: boolean
fetched: boolean
}

type UseBatteryState =
| { isSupported: false } // Battery API is not supported
| FetchedBatteryState // battery API supported

export interface BatteryManager extends Readonly<BatteryState>, EventTarget {
onchargingchange: VoidFunction
onchargingtimechange: VoidFunction
ondischargingtimechange: VoidFunction
onlevelchange: VoidFunction
}

interface NavigatorWithPossibleBattery extends Navigator {
getBattery?: () => Promise<BatteryManager>
}

const nav: NavigatorWithPossibleBattery | undefined =
typeof navigator === 'object' ? navigator : undefined
const isBatteryApiSupported = nav && typeof nav.getBattery === 'function'

export default function useBattery(): UseBatteryState {
checkBrowser(useBattery.name)

if (!isBatteryApiSupported) {
return { isSupported: false }
}

const state = reactive<FetchedBatteryState>({
isSupported: true,
fetched: false,
charging: false,
chargingTime: 0,
dischargingTime: 0,
level: 0
})

let mounted = true
let battery: BatteryManager | null = null

const update = () => {
if (!mounted || !battery) {
return
}
state.isSupported = true
state.fetched = true
state.level = battery.level
state.charging = battery.charging
state.dischargingTime = battery.dischargingTime
state.chargingTime = battery.chargingTime
}

onMounted(() => {
nav!.getBattery!()
.then((bm: BatteryManager) => {
if (!mounted) return
battery = bm
addEventListener(battery, 'chargingchange', update)
addEventListener(battery, 'chargingtimechange', update)
addEventListener(battery, 'dischargingtimechange', update)
addEventListener(battery, 'levelchange', update)
update()
})
.catch(warn)
})

onUnmounted(() => {
mounted = false
if (battery) {
removeEventListener(battery, 'chargingchange', update)
removeEventListener(battery, 'chargingtimechange', update)
removeEventListener(battery, 'dischargingtimechange', update)
removeEventListener(battery, 'levelchange', update)
}
})

return state
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { default as useWindowSize } from './hooks/sensor/useWindowSize'
export { default as useWindowScroll } from './hooks/sensor/useWindowScroll'
export { default as useGeolocation } from './hooks/sensor/useGeolocation'
export { default as useMouse } from './hooks/sensor/useMouse'
export { default as useBattery } from './hooks/sensor/useBattery'

// Side Effect Hooks
export { default as useTitle } from './hooks/sideEffect/useTitle'
Expand Down
5 changes: 3 additions & 2 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isRef, Ref } from '@vue/composition-api'
import { BatteryManager } from '../hooks/sensor/useBattery'

export const isBrowser = typeof window === 'object'

Expand Down Expand Up @@ -34,7 +35,7 @@ export const checkBrowser = (ctx = '') => {
}

export const addEventListener = (
el: Element | Window | PermissionStatus | Document,
el: Element | Window | PermissionStatus | Document | BatteryManager,
event: string,
handler: EventListener,
options?: AddEventListenerOptions
Expand All @@ -43,7 +44,7 @@ export const addEventListener = (
}

export const removeEventListener = (
el: Element | Window | PermissionStatus | Document,
el: Element | Window | PermissionStatus | Document | BatteryManager,
event: string,
handler: EventListener,
options?: EventListenerOptions
Expand Down

0 comments on commit 97ec0a9

Please sign in to comment.