Skip to content

Commit

Permalink
chore: Add QA settings screen
Browse files Browse the repository at this point in the history
  • Loading branch information
Ahmed-Ali committed Jun 23, 2024
1 parent 6e575d0 commit 35501b8
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 31 deletions.
14 changes: 11 additions & 3 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ContentNavigator } from '@screens';
import { Storage } from '@services';
import { appTheme } from '@utils';
import { CustomerIO } from 'customerio-reactnative';
import FlashMessage from 'react-native-flash-message';
import FlashMessage, { showMessage } from 'react-native-flash-message';

export default function App({ moduleName }: { moduleName: string }) {
const [isLoading, setIsLoading] = React.useState(true);
Expand Down Expand Up @@ -48,8 +48,16 @@ export default function App({ moduleName }: { moduleName: string }) {
CustomerIO.clearIdentify();
},
onTrackEvent: (eventPayload) => {
console.log('Tracking event', eventPayload);
CustomerIO.track(eventPayload.name, eventPayload.properties);
if (CustomerIO.isInitialized()) {
console.log('Tracking event', eventPayload);
CustomerIO.track(eventPayload.name, eventPayload.properties);
} else {
showMessage({
message: 'CustomerIO not initialized',
description: 'Please set the CustomerIO config',
type: 'danger',
});
}
},
onProfileAttributes(attributes) {
console.log('Setting profile attributes', attributes);
Expand Down
9 changes: 8 additions & 1 deletion example/src/components/navigation-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,19 @@ import {
export const NavigationButton = ({
onPress,
iconSource,
onLongPress,
}: {
onPress: () => void;
onLongPress?: () => void;
iconSource: ImageSourcePropType;
}) => {
return (
<TouchableOpacity onPress={onPress}>
<TouchableOpacity
onPress={onPress}
onLongPress={() => {
onLongPress?.();
}}
>
<Image
source={iconSource}
tintColor={Colors.bodySecondaryText}
Expand Down
1 change: 1 addition & 0 deletions example/src/components/settings-nav-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const SettingsNavButton = () => {
return (
<NavigationButton
onPress={() => navigation.navigate('Settings')}
onLongPress={() => navigation.navigate('QA Settings')}
iconSource={require('@assets/images/settings.png')}
/>
);
Expand Down
2 changes: 2 additions & 0 deletions example/src/navigation/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const LoginScreenName = 'Login' as const;
export const TrackScreenName = 'Track' as const;
export const CustomProfileAttrScreenName = 'Profile Attributes' as const;
export const CustomDeviceAttrScreenName = 'Device Attributes' as const;
export const QASettingsScreenName = 'QA Settings' as const;

export type NavigationStackParamList = {
[SettingsScreenName]: undefined;
Expand All @@ -18,6 +19,7 @@ export type NavigationStackParamList = {
[TrackScreenName]: undefined;
[CustomProfileAttrScreenName]: undefined;
[CustomDeviceAttrScreenName]: undefined;
[QASettingsScreenName]: undefined;
};

export type NavigationProps = NavigationProp<NavigationStackParamList>;
Expand Down
6 changes: 6 additions & 0 deletions example/src/screens/content-navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
LoginScreenName,
NavigationCallbackContext,
NavigationStackParamList,
QASettingsScreenName,
SettingsScreenName,
TrackScreenName,
} from '@navigation';
Expand All @@ -17,6 +18,7 @@ import {
CustomProfileAttrScreen,
HomeScreen,
LogingScreen,
QASettingsScreen,
SettingsScreen,
TrackScreen,
} from '@screens';
Expand Down Expand Up @@ -53,6 +55,10 @@ export const ContentNavigator = ({ moduleName }: { moduleName: string }) => {
}}
>
<Stack.Screen name={SettingsScreenName} component={SettingsScreen} />
<Stack.Screen
name={QASettingsScreenName}
component={QASettingsScreen}
/>
<Stack.Screen
name={HomeScreenName}
component={HomeScreen}
Expand Down
13 changes: 6 additions & 7 deletions example/src/screens/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import { BuildInfoText, Button, Profile } from '@components';
import {
CustomDeviceAttrScreenName,
CustomProfileAttrScreenName,
NavigationCallbackContext,
NavigationScreenProps,
} from '@navigation';
import { Storage } from '@services';
import { CustomerIO } from 'customerio-reactnative';
import React, { useState } from 'react';
import React, { useContext, useState } from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import { showMessage } from 'react-native-flash-message';

export const HomeScreen = ({
navigation,
}: NavigationScreenProps<'Customer.io'>) => {
const [user] = useState(Storage.instance.getUser());
const { onTrackEvent } = useContext(NavigationCallbackContext);
return (
<>
<ScrollView>
Expand All @@ -25,11 +25,10 @@ export const HomeScreen = ({
<Button
title="Send Random Event"
onPress={() => {
showMessage({
message: 'Random event sent',
type: 'success',
onTrackEvent({
name: 'random_event',
properties: { random: Math.random() },
});
CustomerIO.track('random_event', { random: Math.random() });
}}
/>
<Button
Expand Down
1 change: 1 addition & 0 deletions example/src/screens/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export * from './content-navigator';
export * from './custom-attribute';
export * from './home';
export * from './login';
export * from './qa-settings';
export * from './settings';
export * from './track';
83 changes: 83 additions & 0 deletions example/src/screens/qa-settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
Button,
ButtonExperience,
LargeBoldText,
TextField,
} from '@components';
import { QASettings, Storage } from '@services';
import React, { useState } from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import { showMessage } from 'react-native-flash-message';

const validateSettings = (config: QASettings) => {
if (!config.apiHost || !config.cdnHost) {
throw Error('CDN and API host values are missing');
} else if (!config.cdnHost) {
throw Error('CDB Host value is missing');
} else if (!config.apiHost) {
throw Error('API Host value is missing');
}
};

export const QASettingsScreen = () => {
const [config, setConfig] = useState<QASettings>(
Storage.instance.getQaConfig()
);

return (
<ScrollView>
<View style={styles.container}>
<LargeBoldText>CustomerIO QA Settings</LargeBoldText>
<TextField
onChangeText={(cdnHost) => {
setConfig({ ...config, cdnHost });
}}
label="CDN Host"
placeholder="Enter the CDN Host"
defaultValue={config.cdnHost ?? ''}
/>

<TextField
onChangeText={(apiHost) => {
setConfig({ ...config, apiHost });
}}
label="API Host"
placeholder="Enter the API host"
defaultValue={config.apiHost ?? ''}
/>

<Button
title="Save"
experience={ButtonExperience.callToAction}
disabled={!config.apiHost || !config.cdnHost}
onPress={async () => {
try {
validateSettings(config);
Storage.instance.setQaConfig(config);

showMessage({
message: 'QA settings saved successfully',
type: 'success',
});
} catch (error: unknown) {
showMessage({
message: (error as Error).message,
type: 'warning',
});
return;
}
}}
/>
</View>
</ScrollView>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
flexDirection: 'column',
gap: 16,
},
});
13 changes: 3 additions & 10 deletions example/src/screens/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,9 @@ import React, { useState } from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import { showMessage } from 'react-native-flash-message';

const defaultCioConfig: Partial<CioConfig> = {
region: CioRegion.US,
logLevel: CioLogLevel.Error,
trackApplicationLifecycleEvents: true,
};

export const SettingsScreen = () => {
const [config, setConfig] = useState<Partial<CioConfig>>(
Storage.instance.getCioConfig() ?? defaultCioConfig
Storage.instance.getCioConfig() ?? Storage.instance.getDefaultCioConfig()
);

return (
Expand Down Expand Up @@ -119,7 +113,7 @@ export const SettingsScreen = () => {
CustomerIO.initialize(config as CioConfig);
showMessage({
message:
'CustomerIO settings saved successfully CustomerIO.initialize() has been called with the new settings',
'CustomerIO settings saved successfully and CustomerIO.initialize() has been called with the new settings',
type: 'success',
});
}
Expand All @@ -130,8 +124,7 @@ export const SettingsScreen = () => {
title="Reset to Default"
experience={ButtonExperience.secondary}
onPress={() => {
setConfig(defaultCioConfig);
Storage.instance.setCioConfig(defaultCioConfig as CioConfig);
Storage.instance.resetCioConfig();
showMessage({
message: 'CustomerIO settings has been reset!',
type: 'success',
Expand Down
76 changes: 66 additions & 10 deletions example/src/services/storage.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { User } from '@utils';
import { CioConfig } from 'customerio-reactnative';
import { CioConfig, CioLogLevel, CioRegion } from 'customerio-reactnative';

const USER_STORAGE_KEY = 'user';
const CIO_CONFIG_STORAGE_KEY = 'cioConfig';

export type QASettings = {
cdnHost: string;
apiHost: string;
};

type Config = Partial<CioConfig> & { qa: QASettings };

const defaultConfig: Config = {
region: CioRegion.US,
logLevel: CioLogLevel.Error,
trackApplicationLifecycleEvents: true,
qa: {
cdnHost: 'https://cdp.customer.io/v1',
apiHost: 'https://cdp.customer.io/v1',
},
};

export class Storage {
private user: User | null = null;
private cioConfig: CioConfig | null = null;
private config: Config | null = null;

static readonly instance: Storage = new Storage();

private constructor() {}

readonly loadAll = async () => {
if (!this.user || !this.cioConfig) {
if (!this.user || !this.config) {
await this.loadFromStorage();
}
};
Expand All @@ -24,8 +41,9 @@ export class Storage {
const cioConfigJsonPayload = await AsyncStorage.getItem(
CIO_CONFIG_STORAGE_KEY
);

this.user = userJsonPayload ? JSON.parse(userJsonPayload) : null;
this.cioConfig = cioConfigJsonPayload
this.config = cioConfigJsonPayload
? JSON.parse(cioConfigJsonPayload)
: null;
};
Expand All @@ -40,17 +58,55 @@ export class Storage {
};

readonly removeUser = async () => {
await AsyncStorage.removeItem(USER_STORAGE_KEY);
this.user = null;
await AsyncStorage.removeItem(USER_STORAGE_KEY);
};

readonly setCioConfig = async (cioConfig: CioConfig) => {
const config = cioConfig as Config;
this.config = { ...config, qa: config.qa ?? defaultConfig.qa };
await AsyncStorage.setItem(
CIO_CONFIG_STORAGE_KEY,
JSON.stringify(this.config)
);
};

readonly getCioConfig = (): CioConfig => {
return this.config as CioConfig;
};

readonly setCioConfig = async (config: CioConfig) => {
await AsyncStorage.setItem(CIO_CONFIG_STORAGE_KEY, JSON.stringify(config));
this.cioConfig = config;
readonly getDefaultCioConfig = (): Partial<CioConfig> => {
return defaultConfig;
};

readonly getCioConfig = () => {
return this.cioConfig;
readonly resetCioConfig = async () => {
if (this.config === null) {
this.config = defaultConfig;
} else {
this.config = { ...this.config, ...defaultConfig, qa: this.config.qa };
}

await AsyncStorage.setItem(
CIO_CONFIG_STORAGE_KEY,
JSON.stringify(this.config)
);
};

readonly getQaConfig = (): QASettings => {
return this.config?.qa ?? defaultConfig.qa;
};

readonly setQaConfig = async (qa: QASettings) => {
if (this.config === null) {
this.config = { ...defaultConfig, qa };
} else {
this.config = { ...this.config, qa };
}

await AsyncStorage.setItem(
CIO_CONFIG_STORAGE_KEY,
JSON.stringify(this.config)
);
};

readonly clear = async () => {
Expand Down

0 comments on commit 35501b8

Please sign in to comment.