Skip to content

Commit

Permalink
Fixed scanning problems and improved the flow (#36)
Browse files Browse the repository at this point in the history
Solved #35.
  • Loading branch information
Burzo authored Apr 5, 2024
1 parent fe9b204 commit 674bebc
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 74 deletions.
15 changes: 4 additions & 11 deletions src/components/AppDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import {
} from "react"
import { StyleSheet, View } from "react-native"
import { Drawer } from "react-native-drawer-layout"
import { Avatar, Button, Surface } from "react-native-paper"
import { Avatar, Surface } from "react-native-paper"
import { WWText } from "./ui/WWText"
import { useExtendedTheme } from "../theme"
import { useAuth } from "../providers/AuthProvider"
import { useSafeAreaInsets } from "react-native-safe-area-context"
import { getReadableVersion } from "react-native-device-info"
import { SideNavigation } from "./SideNavigation"

type DrawerContextProps = {
isOpen: boolean
Expand All @@ -25,14 +26,9 @@ export const useAppDrawer = () => useContext(DrawerContext)
export const AppDrawer = ({ children }: PropsWithChildren<unknown>) => {
const [isOpen, setIsOpen] = useState(false)
const { appPadding, spacing } = useExtendedTheme()
const { setIsLoggedIn, isLoggedIn } = useAuth()
const { isLoggedIn } = useAuth()
const { top } = useSafeAreaInsets()

const onLogout = () => {
setIsLoggedIn(false)
setIsOpen(false)
}

return (
<Drawer
swipeEnabled={isLoggedIn}
Expand All @@ -49,10 +45,7 @@ export const AppDrawer = ({ children }: PropsWithChildren<unknown>) => {
]}
>
<Avatar.Image source={require("../assets/avatar.png")} />
<WWText variant="bodyLarge">I'm empty at the moment.</WWText>
<Button icon="logout" onPress={onLogout}>
Logout
</Button>
<SideNavigation drawerControls={setIsOpen} />
<View style={styles.version}>
<WWText>Current version:</WWText>
<WWText style={[styles.versionText, { marginStart: spacing }]}>
Expand Down
4 changes: 3 additions & 1 deletion src/components/DeviceItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ type DeviceItemProps = {
item: ExtendedPeripheral
disconnect: (item: ExtendedPeripheral) => Promise<void>
go: (item: ExtendedPeripheral) => Promise<void>
disabled?: boolean
}

export const DeviceItem = React.memo(
({ item, disconnect, go }: DeviceItemProps) => {
({ item, disconnect, go, disabled }: DeviceItemProps) => {
return (
<TouchableRipple
style={styles.card}
onPress={() => go(item)}
onLongPress={() => disconnect(item)}
disabled={disabled}
>
<View
style={[
Expand Down
81 changes: 81 additions & 0 deletions src/components/SideNavigation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { StyleSheet, View } from "react-native"
import { Button } from "react-native-paper"
import { useAppNavigation } from "../hooks/useAppNavigation"
import { useAuth } from "../providers/AuthProvider"
import { useExtendedTheme } from "../theme"
import { Dispatch } from "react"

type Props = {
drawerControls: Dispatch<React.SetStateAction<boolean>>
}

export const SideNavigation = ({ drawerControls }: Props) => {
const navigation = useAppNavigation()
const { setIsLoggedIn } = useAuth()
const { spacing, colors, appPadding } = useExtendedTheme()

const goTo = (link: string) => {
navigation.navigate(link)
drawerControls(false)
}

const onLogout = () => {
setIsLoggedIn(false)
drawerControls(false)
}

return (
<View style={[styles.list, { marginVertical: appPadding }]}>
<Button
textColor={colors.onBackground}
style={[{ margin: spacing }, styles.link]}
icon="bell"
onPress={() => goTo("Notifications")}
>
Notifications
</Button>
<Button
textColor={colors.onBackground}
style={[{ margin: spacing }, styles.link]}
icon="account"
onPress={() => goTo("Profile")}
>
Profile
</Button>
<Button
textColor={colors.onBackground}
style={[{ margin: spacing }, styles.link]}
icon="cog"
onPress={() => goTo("Settings")}
>
Settings
</Button>
<Button
textColor={colors.onBackground}
style={[{ margin: spacing }, styles.link]}
icon="crowd"
onPress={() => goTo("CommunityDiscussion")}
>
Community discussion
</Button>
<Button
textColor={colors.onBackground}
style={[{ margin: spacing }, styles.link]}
icon="logout"
onPress={onLogout}
>
Sign out
</Button>
</View>
)
}

const styles = StyleSheet.create({
list: {
flex: 1,
alignItems: "flex-start",
},
link: {
margin: 10,
},
})
59 changes: 30 additions & 29 deletions src/hooks/useBleListeners.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export const useBleListeners = () => {
...DEFAULT_PERIPHERAL(peripheral.id),
device: peripheral,
name: peripheral.name,
signalLost: false,
}

/**
Expand All @@ -267,41 +268,41 @@ export const useBleListeners = () => {
const peripherals: Peripheral[] = await guard(() =>
BleManager.getDiscoveredPeripherals(),
)
const filteredPeripherals = peripherals.filter(
(p) => p.name === DEVICE_NAME,
const filteredPeripherals = peripherals.filter((p) =>
p.name?.includes(DEVICE_NAME),
)

if (Array.isArray(filteredPeripherals) && filteredPeripherals.length > 0) {
const notFoundAnymore: Peripheral[] = []
const notFoundAnymore: Peripheral[] = []

Object.keys(devicesRef.current).forEach((key) => {
const peripheral = devicesRef.current[key]
if (
!filteredPeripherals.find(
(filteredPeripheral) => filteredPeripheral.id === peripheral.id,
)
) {
Object.keys(devicesRef.current).forEach((key) => {
const peripheral = devicesRef.current[key]
if (
!filteredPeripherals.find(
(filteredPeripheral) => filteredPeripheral.id === peripheral.id,
)
) {
if (peripheral.connected) {
log(`Disconnecting device ${peripheral.id} after scan stopped.`)
disconnectDevice(peripheral)
notFoundAnymore.push(peripheral)
}
})

dispatch(
deviceSignalChanged({
data: [
...filteredPeripherals.map((peripheral) => ({
peripheral,
value: false,
})),
...notFoundAnymore.map((peripheral) => ({
peripheral,
value: true,
})),
],
}),
)
}
notFoundAnymore.push(peripheral)
}
})

dispatch(
deviceSignalChanged({
data: [
...filteredPeripherals.map((peripheral) => ({
peripheral,
value: false,
})),
...notFoundAnymore.map((peripheral) => ({
peripheral,
value: true,
})),
],
}),
)

dispatch(scanStop())
log("Scan stopped.")
Expand Down
30 changes: 30 additions & 0 deletions src/navigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,23 @@ import { Login } from "./screens/Login"
import { useAuth } from "../providers/AuthProvider"
import { AppLoading } from "./screens/AppLoading"
import { AppDrawer } from "../components/AppDrawer"
import { Notifications } from "./screens/Notifications"
import { CommunityDiscussion } from "./screens/CommunityDiscussion"
import { Profile } from "./screens/Profile"
import { Settings } from "./screens/Settings"

export interface RootStackParamList extends ParamListBase {
CommunityDiscussion: undefined
Notifications: undefined
Profile: undefined
Settings: undefined
Home: undefined
DeviceNavigator: { deviceId: string }
Terminal: { deviceId: string }
}

export type Routes = keyof RootStackParamList

export type AppParams<T extends keyof RootStackParamList> = RouteProp<
RootStackParamList,
T
Expand Down Expand Up @@ -110,6 +120,26 @@ export const MainNavigation = () => {
component={Home}
options={{ title: "Wildlife Watcher" }}
/>
<Stack.Screen
name="Notifications"
component={Notifications}
options={{ title: "Notifications" }}
/>
<Stack.Screen
name="CommunityDiscussion"
component={CommunityDiscussion}
options={{ title: "Community Discussion" }}
/>
<Stack.Screen
name="Profile"
component={Profile}
options={{ title: "Profile" }}
/>
<Stack.Screen
name="Settings"
component={Settings}
options={{ title: "Settings" }}
/>
<Stack.Screen
name="DeviceNavigator"
options={{ title: "Configure device" }}
Expand Down
13 changes: 13 additions & 0 deletions src/navigation/screens/CommunityDiscussion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { View } from "react-native"
import { WWScreenView } from "../../components/ui/WWScreenView"
import { WWText } from "../../components/ui/WWText"

export const CommunityDiscussion = () => {
return (
<WWScreenView>
<View>
<WWText variant="titleSmall">Community discussion screen.</WWText>
</View>
</WWScreenView>
)
}
44 changes: 30 additions & 14 deletions src/navigation/screens/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Button } from "react-native-paper"
import { WWText } from "../../components/ui/WWText"
import { WWScreenView } from "../../components/ui/WWScreenView"
import { log } from "../../utils/logger"
import { useIsFocused } from "@react-navigation/native"

export const Home = memo(() => {
const { isBleConnecting, startScan, connectDevice, disconnectDevice } =
Expand All @@ -19,17 +20,24 @@ export const Home = memo(() => {
const { isScanning } = useAppSelector((state) => state.scanning)
const navigation = useAppNavigation()
const { bottom } = useSafeAreaInsets()
const isFocused = useIsFocused()

const isBleBusy = isBleConnecting || isScanning

const devicesToDisplay = useMemo(() => {
return Object.values(devices).sort((a, b) => {
if (a.rssi && b.rssi) {
if (b.rssi === 127 || a.rssi === 127) return -1
return b.rssi - a.rssi
}
return -1
})
return Object.values(devices)
.sort((a, b) => {
if (a.rssi && b.rssi) {
if (b.rssi === 127 || a.rssi === 127) return -1
return b.rssi - a.rssi
}
return -1
})
.filter((device) => !device.signalLost)
}, [devices])

const isAnyDeviceConnecting = useMemo(() => {
return !!Object.values(devices).find((device) => device.loading)
}, [devices])

const disconnect = useCallback(
Expand Down Expand Up @@ -57,23 +65,26 @@ export const Home = memo(() => {
}

useEffect(() => {
startScan(2)
if (isFocused) {
startScan(10)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
}, [isFocused])

useEffect(() => {
const interval = setInterval(() => {
if (!isBleBusy) {
startScan(2)
startScan(10)
} else {
log("Scanning already taking place, skipping.")
}
}, 4 * 1000)
}, 15 * 1000)

return () => {
log("Clearing interval.")
clearInterval(interval)
}
}, [isScanning, isBleConnecting, isBleBusy, startScan])
}, [isScanning, isBleConnecting, isBleBusy, startScan, isFocused])

return (
<WWScreenView>
Expand All @@ -82,7 +93,7 @@ export const Home = memo(() => {
<View style={styles.headerView}>
<View style={styles.buttonRow}>
<Button mode="contained" onPress={scan} loading={isScanning}>
Scan
{isScanning ? "Scanning" : "Scan"}
</Button>
</View>
</View>
Expand All @@ -95,7 +106,12 @@ export const Home = memo(() => {
contentContainerStyle={styles.list}
data={devicesToDisplay}
renderItem={({ item }: { item: ExtendedPeripheral }) => (
<DeviceItem item={item} disconnect={disconnect} go={go} />
<DeviceItem
disabled={isAnyDeviceConnecting}
item={item}
disconnect={disconnect}
go={go}
/>
)}
keyExtractor={(item: ExtendedPeripheral) => item.id}
/>
Expand Down
13 changes: 13 additions & 0 deletions src/navigation/screens/Notifications.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { View } from "react-native"
import { WWScreenView } from "../../components/ui/WWScreenView"
import { WWText } from "../../components/ui/WWText"

export const Notifications = () => {
return (
<WWScreenView>
<View>
<WWText variant="titleSmall">Notifications screen.</WWText>
</View>
</WWScreenView>
)
}
Loading

0 comments on commit 674bebc

Please sign in to comment.