This commit is contained in:
lucadruda 2021-05-10 15:50:36 +02:00
Родитель 528d0bce43
Коммит 468822868b
14 изменённых файлов: 354 добавлений и 239 удалений

Просмотреть файл

@ -5,7 +5,7 @@ import React, {
useCallback,
useContext,
} from 'react';
import {View, Platform, Alert} from 'react-native';
import { View, Platform, Alert } from 'react-native';
import Settings from './Settings';
import {
NavigationContainer,
@ -14,7 +14,7 @@ import {
useTheme,
RouteProp,
} from '@react-navigation/native';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import {
Screens,
NavigationScreens,
@ -33,7 +33,7 @@ import {
NavigationPages,
// ChartType,
} from 'types';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import {
LogsProvider,
StorageProvider,
@ -43,10 +43,10 @@ import {
} from 'contexts';
import LogoLight from './assets/IoT-Plug-And-Play_Dark.svg';
import LogoDark from './assets/IoT-Plug-And-Play_Light.svg';
import {Icon} from 'react-native-elements';
import {createStackNavigator} from '@react-navigation/stack';
import {Text} from './components/typography';
import {Welcome} from './Welcome';
import { Icon } from 'react-native-elements';
import { createStackNavigator } from '@react-navigation/stack';
import { Text } from './components/typography';
import { Welcome } from './Welcome';
import Logs from './Logs';
import {
IIcon,
@ -60,9 +60,9 @@ import {
useThemeMode,
} from 'hooks';
import FileUpload from './FileUpload';
import {Registration} from './Registration';
import { Registration } from './Registration';
import CardView from './CardView';
import {Loader} from './components/loader';
import { Loader } from './components/loader';
import {
IIoTCCommand,
IIoTCCommandResponse,
@ -73,8 +73,8 @@ import {
// import { AVAILABLE_SENSORS } from 'sensors';
import Torch from 'react-native-torch';
import Chart from 'Chart';
import Strings, {resolveString} from 'strings';
import {Option} from 'components/options';
import Strings, { resolveString } from 'strings';
import { Option } from 'components/options';
import Options from 'components/options';
import HeaderCloseButton from 'components/headerCloseButton';
@ -106,15 +106,15 @@ export default function App() {
}
const Navigation = React.memo(() => {
const {mode, type: themeType, setThemeMode} = useThemeMode();
const { mode, type: themeType, setThemeMode } = useThemeMode();
const [simulated] = useSimulation();
const {credentials, initialized} = useContext(StorageContext);
const { credentials, initialized } = useContext(StorageContext);
const [deliveryInterval, setDeliveryInterval] = useDeliveryInterval();
const [connect, , , {client, loading}] = useConnectIoTCentralClient();
const [connect, , , { client, loading }] = useConnectIoTCentralClient();
useEffect(() => {
if (credentials && initialized && !client) {
connect(credentials, {restore: true});
connect(credentials, { restore: true });
}
}, [connect, client, credentials, initialized]);
@ -122,7 +122,7 @@ const Navigation = React.memo(() => {
<NavigationContainer theme={mode === 'dark' ? DarkTheme : DefaultTheme}>
<Stack.Navigator
initialRouteName={simulated || client ? Pages.ROOT : Pages.REGISTRATION}
screenOptions={({navigation, route}) => {
screenOptions={({ navigation, route }) => {
const defaultOptions = {
gestureEnabled: false,
headerBackTitleVisible: false,
@ -145,42 +145,42 @@ const Navigation = React.memo(() => {
<Stack.Screen
name={Pages.REGISTRATION}
component={Registration}
// options={({ route, navigation }) => {
// if (!route.params || !(route.params as any).previousScreen) {
// return {
// headerTitle: () => null,
// headerLeft: () => <Logo />,
// headerRight: () => <Profile navigate={navigation.navigate} />,
// };
// }
// return {};
// }}
// options={({ navigation }: { navigation: NavigationProperty }) => {
// return {
// stackAnimation: 'flip',
// headerTitle: Platform.select({
// ios: undefined,
// android: '',
// }),
// headerLeft: () => (
// <BackButton goBack={navigation.goBack} title="Settings" />
// ),
// headerRight: () => (null)
// }
// }}
// options={({ route, navigation }) => {
// if (!route.params || !(route.params as any).previousScreen) {
// return {
// headerTitle: () => null,
// headerLeft: () => <Logo />,
// headerRight: () => <Profile navigate={navigation.navigate} />,
// };
// }
// return {};
// }}
// options={({ navigation }: { navigation: NavigationProperty }) => {
// return {
// stackAnimation: 'flip',
// headerTitle: Platform.select({
// ios: undefined,
// android: '',
// }),
// headerLeft: () => (
// <BackButton goBack={navigation.goBack} title="Settings" />
// ),
// headerRight: () => (null)
// }
// }}
/>
<Stack.Screen
name={Pages.INSIGHT}
component={Chart}
options={({route}) => {
options={({ route }) => {
let data = {};
if (route.params) {
const params = route.params as NavigationParams;
if (params.title) {
data = {...data, headerTitle: params.title};
data = { ...data, headerTitle: params.title };
}
if (params.backTitle) {
data = {...data, headerBackTitle: params.backTitle};
data = { ...data, headerBackTitle: params.backTitle };
}
}
return data;
@ -203,7 +203,7 @@ const Navigation = React.memo(() => {
/>
<Stack.Screen
name={Pages.THEME}
options={({navigation}: {navigation: NavigationProperty}) => ({
options={({ navigation }: { navigation: NavigationProperty }) => ({
stackAnimation: 'flip',
headerTitle: Platform.select({
ios: undefined,
@ -244,7 +244,7 @@ const Navigation = React.memo(() => {
</Stack.Screen>
<Stack.Screen
name={Pages.INTERVAL}
options={({navigation}: {navigation: NavigationProperty}) => ({
options={({ navigation }: { navigation: NavigationProperty }) => ({
stackAnimation: 'flip',
headerTitle: Platform.select({
ios: undefined,
@ -300,11 +300,11 @@ const Navigation = React.memo(() => {
const Root = React.memo<{
route: RouteProp<
Record<string, NavigationParams & {previousScreen?: string}>,
Record<string, NavigationParams & { previousScreen?: string }>,
`Root`
>;
navigation: PagesNavigator;
}>(({navigation}) => {
}>(({ navigation }) => {
const [, append] = useLogger();
const [sensors, addSensorListener, removeSensorListener] = useSensors();
// const [healths, addHealthListener, removeHealthListener] = useHealth();
@ -313,6 +313,7 @@ const Root = React.memo<{
properties,
updateProperty,
} = useProperties();
const [simulated] = useSimulation();
const onConnectionRefresh = useCallback(
async (client: IoTCClient) => {
@ -320,7 +321,7 @@ const Root = React.memo<{
await client.sendProperty({
[PROPERTY]: {
__t: 'c',
...properties.reduce((obj, p) => ({...obj, [p.id]: p.value}), {}),
...properties.reduce((obj, p) => ({ ...obj, [p.id]: p.value }), {}),
},
});
},
@ -328,7 +329,7 @@ const Root = React.memo<{
);
const [iotcentralClient] = useIoTCentralClient(onConnectionRefresh);
const iconsRef = useRef<{[x in ScreenNames]: IIcon}>({
const iconsRef = useRef<{ [x in ScreenNames]: IIcon }>({
[Screens.TELEMETRY_SCREEN]: Platform.select({
ios: {
name: 'stats-chart-outline',
@ -383,8 +384,8 @@ const Root = React.memo<{
async (componentName: string, id: string, value: any) => {
if (iotcentralClient && iotcentralClient.isConnected()) {
await iotcentralClient.sendTelemetry(
{[id]: value},
{'$.sub': componentName},
{ [id]: value },
{ '$.sub': componentName },
);
}
},
@ -443,7 +444,7 @@ const Root = React.memo<{
const onPropUpdate = useCallback(
async (prop: IIoTCProperty) => {
let {name, value} = prop;
let { name, value } = prop;
if (value.__t === 'c') {
// inside a component: TODO: change sdk
name = Object.keys(value).filter(v => v !== '__t')[0];
@ -494,10 +495,12 @@ const Root = React.memo<{
iotcentralClient.fetchTwin();
} else {
// device has been disconnected. reset to registration
navigation.reset({
index: 1, // as per issue: https://github.com/react-navigation/react-navigation/issues/7839
routes: [{name: Pages.REGISTRATION}],
});
if (!simulated) {
navigation.reset({
index: 1, // as per issue: https://github.com/react-navigation/react-navigation/issues/7839
routes: [{ name: Pages.REGISTRATION }],
});
}
}
return () => {
@ -520,18 +523,19 @@ const Root = React.memo<{
// sendHealthHandler,
sendTelemetryHandler,
navigation,
simulated
]);
return (
<Tab.Navigator
key="tab"
tabBarOptions={Platform.select({
android: {safeAreaInsets: {bottom: 0}},
android: { safeAreaInsets: { bottom: 0 } },
})}>
<Tab.Screen
name={Screens.TELEMETRY_SCREEN}
options={{
tabBarIcon: ({color, size}) => (
tabBarIcon: ({ color, size }) => (
<TabBarIcon icon={icons.Telemetry} color={color} size={size} />
),
}}>
@ -549,54 +553,54 @@ const Root = React.memo<{
<Tab.Screen
name={Screens.PROPERTIES_SCREEN}
options={{
tabBarIcon: ({color, size}) => (
tabBarIcon: ({ color, size }) => (
<TabBarIcon icon={icons.Properties} color={color} size={size} />
),
}}>
{propertiesLoading
? () => (
<Loader
message={Strings.Client.Properties.Loading}
visible={true}
style={{flex: 1, justifyContent: 'center'}}
/>
)
<Loader
message={Strings.Client.Properties.Loading}
visible={true}
style={{ flex: 1, justifyContent: 'center' }}
/>
)
: () => (
<CardView
items={properties}
componentName="Property"
onEdit={async (item, value) => {
try {
await iotcentralClient?.sendProperty({
[PROPERTY]: {__t: 'c', [item.id]: value},
});
Alert.alert(
'Property',
resolveString(
Strings.Client.Properties.Delivery.Success,
item.name,
),
[{text: 'OK'}],
);
} catch (e) {
Alert.alert(
'Property',
resolveString(
Strings.Client.Properties.Delivery.Failure,
item.name,
),
[{text: 'OK'}],
);
}
}}
/>
)}
<CardView
items={properties}
componentName="Property"
onEdit={async (item, value) => {
try {
await iotcentralClient?.sendProperty({
[PROPERTY]: { __t: 'c', [item.id]: value },
});
Alert.alert(
'Property',
resolveString(
Strings.Client.Properties.Delivery.Success,
item.name,
),
[{ text: 'OK' }],
);
} catch (e) {
Alert.alert(
'Property',
resolveString(
Strings.Client.Properties.Delivery.Failure,
item.name,
),
[{ text: 'OK' }],
);
}
}}
/>
)}
</Tab.Screen>
<Tab.Screen
name={Screens.FILE_UPLOAD_SCREEN}
component={FileUpload}
options={{
tabBarIcon: ({color, size}) => (
tabBarIcon: ({ color, size }) => (
<TabBarIcon
icon={icons['Image Upload']}
color={color}
@ -609,7 +613,7 @@ const Root = React.memo<{
name={Screens.LOGS_SCREEN}
component={Logs}
options={{
tabBarIcon: ({color, size}) => (
tabBarIcon: ({ color, size }) => (
<TabBarIcon icon={icons.Logs} color={color} size={size} />
),
}}
@ -626,28 +630,31 @@ const getCardView = (items: ItemProps[], name: string, detail: boolean) => ({
<CardView
items={items}
componentName={name}
// TEMP: temporary disabled charts
// onItemPress={
// detail
// ? item => {
// navigation.navigate('Insight', {
// chartType:
// item.id === AVAILABLE_SENSORS.GEOLOCATION
// ? ChartType.MAP
// : ChartType.DEFAULT,
// currentValue: item.value,
// telemetryId: item.id,
// title: camelToName(item.id),
// backTitle: 'Telemetry',
// });
// }
// : undefined
// }
onItemLongPress={(item) => {
item.enable(!item.enabled);
}}
// TEMP: temporary disabled charts
// onItemPress={
// detail
// ? item => {
// navigation.navigate('Insight', {
// chartType:
// item.id === AVAILABLE_SENSORS.GEOLOCATION
// ? ChartType.MAP
// : ChartType.DEFAULT,
// currentValue: item.value,
// telemetryId: item.id,
// title: camelToName(item.id),
// backTitle: 'Telemetry',
// });
// }
// : undefined
// }
/>
);
const Logo = React.memo(() => {
const {colors, dark} = useTheme();
const { colors, dark } = useTheme();
return (
<View
style={{
@ -675,19 +682,19 @@ const Logo = React.memo(() => {
);
});
const Profile = React.memo((props: {navigate: any}) => {
const {colors} = useTheme();
const Profile = React.memo((props: { navigate: any }) => {
const { colors } = useTheme();
return (
<View style={{marginHorizontal: 10}}>
<View style={{ marginHorizontal: 10 }}>
<Icon
style={{marginEnd: 20}}
style={{ marginEnd: 20 }}
name={
Platform.select({
ios: 'settings-outline',
android: 'settings',
}) as string
}
type={Platform.select({ios: 'ionicon', android: 'material'})}
type={Platform.select({ ios: 'ionicon', android: 'material' })}
color={colors.text}
onPress={() => {
props.navigate('Settings');
@ -697,8 +704,8 @@ const Profile = React.memo((props: {navigate: any}) => {
);
});
const TabBarIcon = React.memo<{icon: IIcon; color: string; size: number}>(
({icon, color, size}) => {
const TabBarIcon = React.memo<{ icon: IIcon; color: string; size: number }>(
({ icon, color, size }) => {
return (
<Icon
name={icon ? icon.name : 'home'}

Просмотреть файл

@ -1,7 +1,10 @@
import { useTheme } from '@react-navigation/native';
import React from 'react';
import {View, FlatList} from 'react-native';
import {ItemProps} from 'types';
import {Card} from './components/card';
import { View, FlatList } from 'react-native';
import { BottomSheet, ListItem } from 'react-native-elements';
import Strings from 'strings';
import { ItemProps } from 'types';
import { Card } from './components/card';
type CardPressCallback = (item: ItemProps) => void | Promise<void>;
type CardEditCallback = (item: ItemProps, value: any) => void | Promise<void>;
@ -10,15 +13,64 @@ const CardView = React.memo<{
items: ItemProps[];
componentName?: string;
onItemPress?: CardPressCallback;
onItemLongPress?: CardPressCallback;
onEdit?: CardEditCallback;
}>(({items, onItemPress, componentName, onEdit}) => {
}>(({ items, onItemPress, onItemLongPress, componentName, onEdit }) => {
const [bottomItem, setBottomItem] = React.useState<ItemProps | undefined>(undefined);
const { colors } = useTheme();
const styles = React.useMemo(
() => ({
listItem: {
backgroundColor: colors.card,
},
listItemText: {
color: colors.text,
},
closeItemText: {
color: 'gray',
},
}),
[colors],
);
const onCardLongPress = React.useCallback<CardPressCallback>((item) => {
console.log('qua');
setBottomItem(item);
}, [setBottomItem]);
return (
<View style={{flex: 1, paddingVertical: 10}}>
<View style={{ flex: 1, paddingVertical: 10 }}>
<FlatList
numColumns={items.length > 4 ? 2 : 1}
data={items}
renderItem={getCard(componentName, onItemPress, onEdit)}
renderItem={getCard(componentName, onItemPress, onCardLongPress, onEdit)}
/>
<BottomSheet
isVisible={bottomItem !== undefined}
containerStyle={{ backgroundColor: 'rgba(0.5, 0.25, 0, 0.7)' }}
modalProps={{}}>
<ListItem
containerStyle={styles.listItem}>
<ListItem.Content>
<ListItem.Title style={styles.listItemText}>
{bottomItem?.name}
</ListItem.Title>
</ListItem.Content>
</ListItem>
<ListItem
onPress={async () => {
await onItemLongPress?.(bottomItem!);
// close sheet
setBottomItem(undefined);
}}
containerStyle={styles.listItem}>
<ListItem.Content>
<ListItem.Title style={styles.closeItemText}>
{bottomItem?.enabled ? Strings.Core.DisableSensor : Strings.Core.EnableSensor}
</ListItem.Title>
</ListItem.Content>
</ListItem>
</BottomSheet>
</View>
);
});
@ -26,8 +78,9 @@ const CardView = React.memo<{
const getCard = (
componentName?: string,
onItemPress?: CardPressCallback,
onItemLongPress?: CardPressCallback,
onEdit?: CardEditCallback,
) => ({item, index}: {item: ItemProps; index: number}) => (
) => ({ item, index }: { item: ItemProps; index: number }) => (
<Card
key={`${componentName ?? 'card'}-${index}`}
title={item.name}
@ -37,8 +90,8 @@ const getCard = (
enabled={item.enabled}
editable={(item as any).editable}
icon={item.icon}
onToggle={() => item.enable(!item.enabled)}
onLongPress={e => console.log('longpress')} // edit card
// onToggle={() => item.enable(!item.enabled)}
onLongPress={onItemLongPress && onItemLongPress.bind(null, item)} // edit card
onEdit={onEdit?.bind(null, item)}
onPress={
item.enabled && onItemPress ? onItemPress.bind(null, item) : undefined

Просмотреть файл

@ -15,8 +15,6 @@ import {
ScrollView,
KeyboardAvoidingView,
} from 'react-native';
import { Link, Name, Text, Detail } from './components/typography';
import { Button, CheckBox } from 'react-native-elements';
import { useScreenDimensions } from './hooks/layout';
import {
getFocusedRouteNameFromRoute,
@ -24,8 +22,7 @@ import {
useNavigation,
useTheme,
} from '@react-navigation/native';
import { Loader } from './components/loader';
import { ConnectionOptions, useConnectIoTCentralClient } from './hooks/iotc';
import { ConnectionOptions, useConnectIoTCentralClient, useSimulation } from './hooks/iotc';
import {
NavigationParams,
NavigationProperty,
@ -33,15 +30,13 @@ import {
PagesNavigator,
StyleDefinition,
} from './types';
import QRCodeScanner, { Event } from './components/qrcodeScanner';
import Strings from 'strings';
import { createStackNavigator } from '@react-navigation/stack';
import Form, { FormItem, FormValues } from 'components/form';
import { Form, FormItem, FormValues, HeaderCloseButton, QRCodeScanner, Event, Loader, Button, Link, Name, Text, ButtonGroup, ButtonGroupItem } from 'components';
import { useBoolean, usePrevious } from 'hooks/common';
import { Buffer } from 'buffer';
import { computeKey } from 'react-native-azure-iotcentral-client';
import { StorageContext } from 'contexts';
import HeaderCloseButton from 'components/headerCloseButton';
const Stack = createStackNavigator();
const screens = {
@ -80,7 +75,7 @@ export const Registration = React.memo<{
}, [parentNavigator, route]);
useEffect(() => {
if (!loading && previousLoading && client && client.isConnected()) {
if ((!loading && previousLoading && client && client.isConnected())) {
parentNavigator?.navigate(Pages.ROOT);
}
}, [client, loading, parentNavigator, previousLoading]);
@ -230,8 +225,8 @@ const ManualConnect = React.memo<{ navigation: PagesNavigator }>(
flex: 4,
},
footer: {
paddingTop: 40,
marginBottom: 100,
paddingTop: 20,
marginBottom: 30,
},
}),
[orientation],
@ -264,21 +259,32 @@ const ManualConnect = React.memo<{ navigation: PagesNavigator }>(
}
}, [setChecked, credentials, checked]);
const connectionTypes = useMemo<ButtonGroupItem[]>(() => ([
{
id: 'dps',
label: Strings.Registration.Manual.Body.ConnectionType.Dps
},
{
id: 'cstring',
label: Strings.Registration.Manual.Body.ConnectionType.CString
}
]), []);
const formItems = useMemo<FormItem[]>(() => {
if (checked === 'dps') {
return [
{
id: 'deviceId',
label: 'Device Id',
placeHolder: 'Enter a unique ID to identify this device',
label: Strings.Registration.Manual.DeviceId.Label,
placeHolder: Strings.Registration.Manual.DeviceId.PlaceHolder,
multiline: false,
readonly,
value: credentials?.deviceId,
},
{
id: 'scopeId',
label: 'ID Scope',
placeHolder: 'Enter your provisioning service ID',
label: Strings.Registration.Manual.ScopeId.Label,
placeHolder: Strings.Registration.Manual.ScopeId.PlaceHolder,
multiline: false,
readonly,
value: credentials?.scopeId,
@ -296,15 +302,15 @@ const ManualConnect = React.memo<{ navigation: PagesNavigator }>(
label: Strings.Registration.Manual.KeyTypes.Device,
},
],
label: 'Key type',
label: Strings.Registration.Manual.KeyTypes.Label,
multiline: false,
readonly,
value: credentials?.keyType,
},
{
id: 'authKey',
label: 'Shared access signature (SAS) key',
placeHolder: 'Enter or paste SAS key',
label: Strings.Registration.Manual.SASKey.Label,
placeHolder: Strings.Registration.Manual.SASKey.PlaceHolder,
multiline: true,
readonly,
value: credentials?.authKey,
@ -333,7 +339,7 @@ const ManualConnect = React.memo<{ navigation: PagesNavigator }>(
style={style.scroll}
keyboardShouldPersistTaps="handled">
<View style={style.header}>
<Detail>
<Text>
{Strings.Registration.Manual.Header}
<Link
onPress={() => {
@ -341,7 +347,7 @@ const ManualConnect = React.memo<{ navigation: PagesNavigator }>(
}}>
{Strings.Registration.Manual.StartHere.Title}
</Link>
</Detail>
</Text>
</View>
<View style={style.body}>
<Name>
@ -350,35 +356,14 @@ const ManualConnect = React.memo<{ navigation: PagesNavigator }>(
: Strings.Registration.Manual.Body.ConnectionType.Title}
</Name>
<View style={{ flex: 1 }}>
<CheckBox
containerStyle={{
marginStart: 0,
backgroundColor: undefined,
borderWidth: 0,
}}
disabled={readonly}
checkedIcon="dot-circle-o"
uncheckedIcon="circle-o"
checkedColor={readonly ? 'gray' : undefined}
uncheckedColor={readonly ? 'gray' : undefined}
checked={checked === 'dps'}
title={Strings.Registration.Manual.Body.ConnectionType.Dps}
onPress={() => setChecked('dps')}
/>
<CheckBox
containerStyle={{
marginStart: 0,
backgroundColor: undefined,
borderWidth: 0,
}}
disabled={readonly}
checkedIcon="dot-circle-o"
uncheckedIcon="circle-o"
checkedColor={readonly ? 'gray' : undefined}
uncheckedColor={readonly ? 'gray' : undefined}
checked={checked === 'cstring'}
title={Strings.Registration.Manual.Body.ConnectionType.CString}
onPress={() => setChecked('cstring')}
<ButtonGroup
readonly={readonly}
items={connectionTypes}
containerStyle={{ marginVertical: 10 }}
onCheckedChange={choiceId =>
setChecked(choiceId as any)
}
defaultCheckedId='dps'
/>
</View>
<View style={{ flex: 2 }}>
@ -397,7 +382,6 @@ const ManualConnect = React.memo<{ navigation: PagesNavigator }>(
<>
<Button
key="register-new-device"
type={Platform.select({ ios: 'clear', android: 'solid' })}
title={Strings.Registration.Manual.RegisterNew.Title}
onPress={() => {
Alert.alert(
@ -427,7 +411,6 @@ const ManualConnect = React.memo<{ navigation: PagesNavigator }>(
/>
<Button
key="clear-device-credentials"
type="clear"
title={Strings.Registration.Clear}
titleStyle={{ color: 'red' }}
/>
@ -435,7 +418,6 @@ const ManualConnect = React.memo<{ navigation: PagesNavigator }>(
) : (
<Button
key="connect-device-btn"
type={Platform.select({ ios: 'clear', android: 'solid' })}
title={Strings.Registration.Manual.Footer.Connect}
onPress={setStartSubmit.True}
/>
@ -456,9 +438,8 @@ const EmptyClient = React.memo<{
}>(({ navigation }) => {
return (
<View style={style.container}>
<Text style={style.header}>{Strings.Registration.Header}</Text>
<Text style={style.header}><Name>{Strings.Registration.Header.Welcome}</Name>{Strings.Registration.Header.Text}</Text>
<Button
type="clear"
title="Scan QR code"
onPress={() => navigation.navigate(screens.QR)}
/>
@ -478,7 +459,7 @@ const style = StyleSheet.create({
flex: 1,
justifyContent: 'space-around',
alignItems: 'center',
marginHorizontal: 30,
marginHorizontal: 20,
},
header: {},
footer: {

Просмотреть файл

@ -131,7 +131,7 @@ export default function Settings() {
icon: dark ? 'sync-outline' : 'sync',
action: {
type: 'switch',
fn: async val => {
fn: async (val) => {
await simulate(val);
},
},
@ -185,6 +185,23 @@ const RightElement = React.memo<{
const Root = React.memo<{ items: ProfileItem[]; colors: any; dark: boolean }>(
({ items, colors, dark }) => {
const nav = useNavigation<PagesNavigator>();
const [simulated] = useSimulation();
React.useEffect(
() =>
nav.addListener('beforeRemove', (e) => {
// Prevent default behavior of leaving the screen
e.preventDefault();
if (simulated) {
nav.navigate(Pages.ROOT);
}
else {
nav.dispatch(e.data.action);
}
}),
[nav, simulated]
);
return (
<ScrollView style={{ flex: 1 }}>
{items.map((item, index) => (

Просмотреть файл

@ -1,15 +1,15 @@
import React, {useEffect, useCallback, useContext, useMemo} from 'react';
import {View} from 'react-native';
import React, { useEffect, useCallback, useContext, useMemo } from 'react';
import { View } from 'react-native';
import LogoLight from './assets/IoT-Plug-And-Play_Dark.svg';
import LogoDark from './assets/IoT-Plug-And-Play_Light.svg';
import * as Animatable from 'react-native-animatable';
import {defaults} from './contexts/defaults';
import { defaults } from './contexts/defaults';
import DeviceInfo from 'react-native-device-info';
import {StateUpdater, StyleDefinition, ThemeMode} from './types';
import { StateUpdater, StyleDefinition, ThemeMode } from './types';
import ProgressCircleSnail from 'react-native-progress/CircleSnail';
import {useScreenDimensions} from './hooks/layout';
import {StorageContext, ThemeContext} from 'contexts';
import {Name} from 'components/typography';
import { useScreenDimensions } from './hooks/layout';
import { StorageContext, ThemeContext } from 'contexts';
import { Name } from 'components/typography';
const animations = {
slideOutLogo: {
@ -36,10 +36,10 @@ export function Welcome(props: {
title: string;
setInitialized: StateUpdater<boolean>;
}) {
const {setInitialized, title} = props;
const {read, initialized} = useContext(StorageContext);
const {screen} = useScreenDimensions();
const {mode, theme} = useContext(ThemeContext);
const { setInitialized, title } = props;
const { read, initialized } = useContext(StorageContext);
const { screen } = useScreenDimensions();
const { mode, theme } = useContext(ThemeContext);
const style = useMemo<StyleDefinition>(
() => ({
@ -54,6 +54,7 @@ export function Welcome(props: {
},
name: {
color: theme.textColor,
fontSize: 20
},
spinner: {
marginTop: 50,

Просмотреть файл

@ -0,0 +1,9 @@
import React from 'react';
import { Platform } from 'react-native';
import { Button as ElButton, ButtonProps } from 'react-native-elements';
const Button = React.memo<ButtonProps>(({ containerStyle, ...props }) => {
return <ElButton type={Platform.OS === 'ios' ? 'clear' : 'solid'} containerStyle={[{ minWidth: 200 }, containerStyle]} {...props} />
});
export default Button;

Просмотреть файл

@ -1,6 +1,6 @@
import React from 'react';
import {View} from 'react-native';
import {CheckBox} from 'react-native-elements';
import { View, ViewStyle } from 'react-native';
import { CheckBox } from 'react-native-elements';
export type ButtonGroupItem = {
id: string;
@ -10,9 +10,10 @@ export type ButtonGroupItem = {
const ButtonGroup = React.memo<{
items: ButtonGroupItem[];
onCheckedChange: (id: string) => void | Promise<void>;
containerStyle?: ViewStyle;
defaultCheckedId?: string;
readonly?: boolean;
}>(({items, onCheckedChange, defaultCheckedId, readonly}) => {
}>(({ items, onCheckedChange, defaultCheckedId, readonly, containerStyle }) => {
const ids = items.map(i => i.id);
const [checked, setChecked] = React.useState<typeof ids[number]>(
defaultCheckedId ?? ids[0],
@ -20,7 +21,7 @@ const ButtonGroup = React.memo<{
return (
<View
style={{flex: 1, marginVertical: 20}}
style={[{ flex: 1 }, containerStyle]}
key={`btnGroup-${Math.random()}`}>
{items.map((item, index) => (
<CheckBox

Просмотреть файл

@ -6,8 +6,7 @@ import {
ColorValue,
TouchableOpacity,
TouchableOpacityProps,
ViewStyle,
Platform,
ViewStyle
} from 'react-native';
import { Text, Name, Headline, getRandomColor, bytesToSize } from './typography';
import { DataType } from 'types';
@ -38,6 +37,7 @@ export function Card(
unit,
icon,
onPress,
onLongPress,
dataType,
...otherProps
} = props;
@ -76,8 +76,9 @@ export function Card(
containerStyle,
]}
{...otherProps}
disabled={!onPress}
onPress={onPress}>
disabled={!onPress && !onLongPress}
onPress={onPress}
onLongPress={onLongPress}>
<View style={{ flex: 1, position: 'relative' }}>
{enabled && (
<View

Просмотреть файл

@ -1,9 +1,9 @@
import { useTheme } from '@react-navigation/native';
import React from 'react';
import { View } from 'react-native';
import { Platform, View } from 'react-native';
import { Input } from 'react-native-elements';
import ButtonGroup from './buttonGroup';
import { Name, normalize } from './typography';
import { Text, Name, normalize } from './typography';
export type FormItem = {
id: string;
@ -64,17 +64,20 @@ const Form = React.memo<FormProps>(
{items.map((item, index) => {
if (item.choices && item.choices.length > 0) {
return (
<ButtonGroup
readonly={item.readonly}
key={`formitem-${index}`}
items={item.choices}
onCheckedChange={choiceId =>
setValues(current => ({ ...current, [item.id]: choiceId }))
}
defaultCheckedId={
item.choices.find(i => i.default === true)?.id
}
/>
<View key={`formitem-${index}`}>
<Text style={{ fontSize: normalize(17), fontWeight: 'bold', color: colors.text, paddingLeft: 10 }}>{item.label}</Text>
<ButtonGroup
readonly={item.readonly}
containerStyle={{ marginBottom: 10 }}
items={item.choices}
onCheckedChange={choiceId =>
setValues(current => ({ ...current, [item.id]: choiceId }))
}
defaultCheckedId={
item.choices.find(i => i.default === true)?.id
}
/>
</View>
);
}
return (
@ -83,8 +86,11 @@ const Form = React.memo<FormProps>(
multiline={item.multiline}
value={values[item.id]}
label={item.label}
labelStyle={{ color: colors.text, paddingBottom: 10 }}
disabled={item.readonly}
inputStyle={{ fontSize: normalize(14), color: colors.text }}
numberOfLines={item.multiline ? 6 : 1}
inputContainerStyle={Platform.OS === 'android' && { borderWidth: 0.5, borderColor: colors.border }}
inputStyle={{ fontSize: normalize(14), color: colors.text, paddingTop: 0, paddingBottom: 0, textAlignVertical: item.multiline ? 'top' : 'center' }}
placeholderTextColor={dark ? '#444' : '#BBB'}
onChangeText={text =>
setValues(current => ({ ...current, [item.id]: text }))

16
src/components/index.ts Normal file
Просмотреть файл

@ -0,0 +1,16 @@
export * from './button';
export { default as Button } from './button';
export * from './buttonGroup';
export { default as ButtonGroup } from './buttonGroup';
export * from './card';
export * from './form';
export { default as Form } from './form';
export { default as HeaderCloseButton } from './headerCloseButton';
export * from './headerCloseButton';
export * from './loader';
export * from './map';
export * from './options';
export { default as QRCodeScanner } from './qrcodeScanner';
export * from './qrcodeScanner';
export * from './screenview';
export * from './typography';

Просмотреть файл

@ -1,9 +1,9 @@
import React from 'react';
import {Platform, StyleSheet, View} from 'react-native';
import {Icon} from 'react-native-elements';
import { Platform, StyleSheet, View } from 'react-native';
import { Icon } from 'react-native-elements';
import Scanner from 'react-native-qrcode-scanner';
import {BarCodeReadEvent} from 'react-native-camera';
import {Text} from './typography';
import { BarCodeReadEvent } from 'react-native-camera';
import { Text } from './typography';
interface QRCodeScannerProps {
height: number;
@ -40,7 +40,7 @@ export default class QRCodeScanner
constructor(props: IQRCodeProps) {
super(props);
({onRead: this.onRead, onClose: this.onClose} = props);
({ onRead: this.onRead, onClose: this.onClose } = props);
this.qrCodeRef = null;
}
@ -62,6 +62,9 @@ export default class QRCodeScanner
<Scanner
ref={sc => (this.qrCodeRef = sc)}
onRead={this.onRead}
containerStyle={{
marginTop: -80
}}
topViewStyle={{
position: 'absolute',
zIndex: 2,
@ -78,7 +81,7 @@ export default class QRCodeScanner
<View
key="left"
style={{
height: this.props.height,
height: this.props.height + 80,
backgroundColor: 'rgba(0,0,0,.5)',
width: sideWidth,
left: 0,
@ -88,7 +91,7 @@ export default class QRCodeScanner
<View
key="right"
style={{
height: this.props.height,
height: this.props.height + 80,
backgroundColor: 'rgba(0,0,0,.5)',
width: sideWidth,
right: 0,
@ -101,7 +104,7 @@ export default class QRCodeScanner
marginHorizontal: sideWidth,
width: this.props.markerSize,
backgroundColor: 'rgba(0,0,0,.5)',
height: verticals + 10,
height: verticals + 40,
top: 0,
position: 'absolute',
}}></View>
@ -111,8 +114,8 @@ export default class QRCodeScanner
marginHorizontal: sideWidth,
width: this.props.markerSize,
backgroundColor: 'rgba(0,0,0,.5)',
height: verticals,
bottom: -10,
height: verticals + 40,
bottom: -80,
position: 'absolute',
}}></View>
{this.onClose && (
@ -137,7 +140,7 @@ export default class QRCodeScanner
customMarker={
<View>
<QRCodeMask width={this.props.markerSize} color={'black'} />
<Text style={{...style.center, textAlign: 'center'}}>
<Text style={{ ...style.center, textAlign: 'center' }}>
Move closer to scan
</Text>
</View>
@ -149,18 +152,18 @@ export default class QRCodeScanner
zIndex: 2,
bottom: 100,
}}
cameraStyle={{height: this.props.height + 20, width: this.props.width}}
cameraStyle={{ height: this.props.height + 80, width: this.props.width }}
/>
);
}
}
function QRCodeMask(props: {width: number; color: string}) {
const {width: markerWidth, color} = props;
function QRCodeMask(props: { width: number; color: string }) {
const { width: markerWidth, color } = props;
const sectorWidth = markerWidth / 5;
return (
<View
style={{position: 'relative', width: markerWidth, height: markerWidth}}>
style={{ position: 'relative', width: markerWidth, height: markerWidth }}>
<View
key="top-left"
style={{

Просмотреть файл

@ -206,9 +206,9 @@ export function useConnectIoTCentralClient(): [
export function useSimulation(): [boolean, (val: boolean) => Promise<void>] {
const { save, simulated } = useContext(StorageContext);
const setSimulated = async (simulated: boolean) => {
save({ simulated });
};
const setSimulated = useCallback(async (simulated: boolean) => {
await save({ simulated });
}, [save]);
return [simulated, setSimulated];
}

Просмотреть файл

@ -4,6 +4,8 @@ const Strings = {
Close: 'Close',
Cancel: 'Cancel',
Loading: 'Loading...',
DisableSensor: 'Disable sensor',
EnableSensor: 'Enable sensor'
},
Settings: {
Title: 'Settings',
@ -44,8 +46,10 @@ const Strings = {
},
},
Registration: {
Header:
'Welcome! Connect your phone to the Azure IoT cloud and experience the simplicity of IoT Plug and Play in just a few steps.',
Header: {
Welcome: 'Welcome! ',
Text: 'Connect your phone to the Azure IoT cloud and experience the simplicity of IoT Plug and Play in just a few steps.'
},
Footer: 'Need help getting started? ',
StartHere: {
Title: 'Start here',
@ -57,6 +61,18 @@ const Strings = {
Manual: {
Title: 'Manually connect',
Header: 'Need help locating this information? ',
DeviceId: {
Label: 'Device Id',
PlaceHolder: 'Enter a unique ID to identify this device'
},
ScopeId: {
Label: 'ID scope',
PlaceHolder: 'Enter your DPS ID scope'
},
SASKey: {
Label: 'Shared access signature (SAS) key',
PlaceHolder: 'Enter or paste your SAS key'
},
Registered: 'Registered using:',
RegisterNew: {
Title: 'Register as a new device',
@ -82,12 +98,13 @@ const Strings = {
ConnectionInfo: 'Connection info',
},
KeyTypes: {
Group: 'Application key',
Label: 'Authentication',
Group: 'Group key',
Device: 'Device key',
},
},
Connection: {
Loading: 'Connecting client...',
Loading: 'Connecting to Azure IoT...',
},
Clear: 'Clear registration',
},

Просмотреть файл

@ -33,6 +33,9 @@
],
"sensors/*":[
"sensors/*"
],
"components/*":[
"components/*"
]
}
},