Skip to content

Commit

Permalink
Merge pull request #480 from wyne/color-analytics
Browse files Browse the repository at this point in the history
Permanently enable colors and add analytics
  • Loading branch information
wyne authored Aug 3, 2024
2 parents ebed161 + 269b6e6 commit 5a79d88
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 55 deletions.
2 changes: 1 addition & 1 deletion redux/GamesSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const gamesAdapter = createEntityAdapter({
const initialState = gamesAdapter.getInitialState({
});

const gameDefaults = {
export const gameDefaults = {
roundCurrent: 0,
roundTotal: 1,
locked: false,
Expand Down
8 changes: 1 addition & 7 deletions redux/SettingsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export interface SettingsState {
onboarded: string | undefined;
showPointParticles: boolean;
showPlayerIndex: boolean;
showColorPalettes?: boolean;
interactionType: InteractionType;
lastStoreReviewPrompt: number;
devMenuEnabled?: boolean;
Expand All @@ -23,7 +22,7 @@ export interface SettingsState {
rollingGameCounter?: number;
};

const initialState: SettingsState = {
export const initialState: SettingsState = {
home_fullscreen: false,
multiplier: 1,
addendOne: 1,
Expand All @@ -32,7 +31,6 @@ const initialState: SettingsState = {
onboarded: undefined,
showPointParticles: false,
showPlayerIndex: false,
showColorPalettes: false,
interactionType: InteractionType.SwipeVertical,
lastStoreReviewPrompt: 0,
appOpens: 0,
Expand All @@ -57,9 +55,6 @@ const settingsSlice = createSlice({
toggleShowPlayerIndex(state) {
state.showPlayerIndex = !state.showPlayerIndex;
},
toggleShowColorPalettes(state) {
state.showColorPalettes = !state.showColorPalettes;
},
setMultiplier(state, action: PayloadAction<number>) {
state.multiplier = action.payload;
},
Expand Down Expand Up @@ -107,7 +102,6 @@ export const {
setOnboardedVersion,
toggleShowPointParticles,
toggleShowPlayerIndex,
toggleShowColorPalettes,
setInteractionType,
setLastStoreReviewPrompt,
toggleDevMenuEnabled,
Expand Down
1 change: 0 additions & 1 deletion redux/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ const settingsPersistConfig = {
'onboarded',
'showPointParticles',
'showPlayerIndex',
'showColorPalettes',
'interactionType',
'lastStoreReviewPrompt',
'devMenuEnabled',
Expand Down
79 changes: 66 additions & 13 deletions src/components/Buttons/CheckButton.test.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,94 @@
import { configureStore } from '@reduxjs/toolkit';
import { fireEvent, render, waitFor } from '@testing-library/react-native';
import { Provider } from 'react-redux';

import gamesReducer, { gameDefaults } from '../../../redux/GamesSlice';
import settingsReducer, { initialState as settingsState } from '../../../redux/SettingsSlice';
import { useNavigationMock } from '../../../test/test-helpers';
import { logEvent } from '../../Analytics';

import CheckButton from './CheckButton';

jest.mock('../../Analytics');

const mockStore = () => {
return configureStore({
reducer: {
settings: settingsReducer,
games: gamesReducer,
},
preloadedState: {
settings: {
...settingsState,
currentGameId: '123'
},
games: {
entities: {
'123': {
...gameDefaults,
id: '123',
title: 'Game',
dateCreated: 1,
playerIds: [],
}
},
ids: ['123']
}
}
});
};

describe('CheckButton', () => {
const navigation = useNavigationMock();

it.skip('should navigate to Game screen when pressed', async () => {
const { getByRole } = render(<CheckButton navigation={navigation} route={{ key: 'Settings', name: 'Settings', params: { source: 'new_game' } }} />);
it('should navigate to Game screen when pressed', async () => {
const store = mockStore();

const { getByRole } = render(
<Provider store={store}>
<CheckButton navigation={navigation} route={{ key: 'Settings', name: 'Settings', params: { source: 'new_game' } }} />
</Provider>
);

const button = getByRole('button');
fireEvent.press(button);

await waitFor(() => {
fireEvent.press(button);
expect(navigation.navigate).toHaveBeenCalledWith('Game');
});
});
}, 10000);

it('should navigate back a screen when pressed', async () => {
const store = mockStore();

const { getByRole } = render(
<Provider store={store}>
<CheckButton navigation={navigation} route={{ key: 'Settings', name: 'Settings', params: { source: 'list_screen' } }} />
</Provider>
);

it.skip('should navigate back a screen when pressed', async () => {
const { getByRole } = render(<CheckButton navigation={navigation} route={{ key: 'Settings', name: 'Settings', params: { source: '' } }} />);
const button = getByRole('button');
fireEvent.press(button);

await waitFor(() => {
fireEvent.press(button);
expect(navigation.goBack).toHaveBeenCalled();
expect(navigation.navigate).toHaveBeenCalledWith('List');
});
});
}, 10000);

it('should log an analytics event when pressed', async () => {
const { getByRole } = render(<CheckButton navigation={navigation} />);
const button = getByRole('button');
const store = mockStore();

const { getByRole } = render(
<Provider store={store}>
<CheckButton navigation={navigation} />
</Provider>
);

const button = getByRole('button');
fireEvent.press(button);

await waitFor(() => {
expect(logEvent).toHaveBeenCalledWith('save_game');
expect(logEvent).toHaveBeenCalledWith('save_game', expect.any(Object));
});
});
}, 10000);
});
12 changes: 11 additions & 1 deletion src/components/Buttons/CheckButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { ParamListBase, RouteProp } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { Text } from 'react-native';

import { useAppSelector } from '../../../redux/hooks';
import { selectCurrentGame } from '../../../redux/selectors';
import { logEvent } from '../../Analytics';
import { systemBlue } from '../../constants';

Expand All @@ -20,10 +22,18 @@ interface Props {
}

const CheckButton: React.FunctionComponent<Props> = ({ navigation, route }) => {
const currentGame = useAppSelector(state => selectCurrentGame(state));
if (typeof currentGame == 'undefined') return null;

return (
<HeaderButton accessibilityLabel='Save Game' onPress={async () => {
await logEvent('save_game');
await logEvent('save_game', {
source: route?.params?.source,
gameId: currentGame?.id,
palette: currentGame.palette,
player_count: currentGame.playerIds.length,
});

if (route?.params?.source === 'list_screen') {
navigation.navigate('List');
} else {
Expand Down
12 changes: 10 additions & 2 deletions src/components/ColorPalettes/ColorSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
import { updatePlayer } from '../../../redux/PlayersSlice';
import { selectCurrentGame } from '../../../redux/selectors';
import { logEvent } from '../../Analytics';
import { getPalette, getPalettes } from '../../ColorPalette';

interface ColorSelectorProps {
Expand All @@ -27,18 +28,25 @@ const ColorButton: React.FC<{ color: string, playerColor: string | undefined; }>
const ColorSelector: React.FC<ColorSelectorProps> = ({ playerId }) => {
const colorPalettes = getPalettes();
const currentGameId = useAppSelector(state => selectCurrentGame(state)?.id);
const currentGame = useAppSelector(state => selectCurrentGame(state));
const currentPalette = useAppSelector(state => selectCurrentGame(state)?.palette);
const playerColor = useAppSelector(state => state.players.entities[playerId]?.color);

if (!currentGameId) return null;

const dispatch = useAppDispatch();

const tapColorHandler = (color: string) => {
const tapColorHandler = (color: string, inCurrentPalette: boolean = false) => {
dispatch(updatePlayer({
id: playerId,
changes: { color: color }
}));
logEvent('set_player_color', {
gameId: currentGameId,
palette: currentGame?.palette,
color,
inCurrentPalette,
});
};

return (
Expand All @@ -55,7 +63,7 @@ const ColorSelector: React.FC<ColorSelectorProps> = ({ playerId }) => {
getPalette(currentPalette).map((color, i) => (
<TouchableOpacity
key={'currentPalette' + i}
onPress={() => tapColorHandler(color)}
onPress={() => tapColorHandler(color, true)}
>
<ColorButton color={color} playerColor={playerColor} />
</TouchableOpacity>
Expand Down
5 changes: 5 additions & 0 deletions src/components/ColorPalettes/PaletteSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ScrollView, StyleSheet, TouchableOpacity } from 'react-native';
import { asyncSetGamePalette } from '../../../redux/GamesSlice';
import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
import { selectCurrentGame } from '../../../redux/selectors';
import { logEvent } from '../../Analytics';
import { getPalette, getPalettes } from '../../ColorPalette';

import PalettePreview from './PalettePreview';
Expand All @@ -27,6 +28,10 @@ const PaletteSelector: React.FunctionComponent = () => {
palette: palette,
})
);
logEvent('set_game_palette', {
game_id: currentGameId,
palette,
});
};

return (
Expand Down
6 changes: 1 addition & 5 deletions src/components/EditGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ const EditGame = ({ }) => {
}));
};

const showColorPalettes = useAppSelector(state => state.settings.showColorPalettes);

return (
<>
<View style={styles.inputContainer}>
Expand All @@ -75,9 +73,7 @@ const EditGame = ({ }) => {
</View>

<View style={{ marginHorizontal: 10 }}>
{showColorPalettes &&
<PaletteSelector />
}
<PaletteSelector />
</View>
</>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Sheets/GameSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ const GameSheet: React.FunctionComponent<Props> = ({ navigation, containerHeight
logEvent('edit_game', {
game_id: currentGameId
});
navigation.navigate('Settings');
navigation.navigate('Settings', { source: 'edit_game' });
}
}
/>
Expand Down
21 changes: 4 additions & 17 deletions src/screens/AppInfoScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Alert, Linking, Platform, ScrollView, StyleSheet, Switch, Text, View }
import { Button } from 'react-native-elements';

import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { toggleShowColorPalettes, toggleShowPlayerIndex, toggleShowPointParticles } from '../../redux/SettingsSlice';
import { toggleShowPlayerIndex, toggleShowPointParticles } from '../../redux/SettingsSlice';
import { logEvent } from '../Analytics';
import RotatingIcon from '../components/AppInfo/RotatingIcon';

Expand Down Expand Up @@ -41,7 +41,6 @@ const AppInfoScreen: React.FunctionComponent<Props> = ({ navigation }) => {

const showPointParticles = useAppSelector(state => state.settings.showPointParticles);
const showPlayerIndex = useAppSelector(state => state.settings.showPlayerIndex);
const showColorPalettes = useAppSelector(state => state.settings.showColorPalettes);
const devMenuEnabled = useAppSelector(state => state.settings.devMenuEnabled);
const installId = useAppSelector(state => state.settings.installId);

Expand All @@ -62,14 +61,6 @@ const AppInfoScreen: React.FunctionComponent<Props> = ({ navigation }) => {
installId
});
};
const toggleColorPalettesSwitch = () => {
dispatch(toggleShowColorPalettes());
logEvent('toggle_feature', {
feature: 'color_palettes',
value: !showColorPalettes,
installId
});
};

const alertWithVersion = async () => {
Alert.alert('ScorePad with Rounds\n' +
Expand All @@ -94,22 +85,18 @@ const AppInfoScreen: React.FunctionComponent<Props> = ({ navigation }) => {

<Section title="Features">
<SectionItem>
<SectionItemText text="Point Particle Effect" />
<SectionItemText text="Particle Effect (tap-only)" />
<Switch onValueChange={toggleParticleSwitch} value={showPointParticles} />
</SectionItem>
<SectionItem>
<SectionItemText text="Change Colors (Beta*)" />
<Switch onValueChange={toggleColorPalettesSwitch} value={showColorPalettes} />
<SectionItemText text="Player Numbers (Beta*)" />
<Switch onValueChange={togglePlayerIndexSwitch} value={showPlayerIndex} />
</SectionItem>
<SectionItem>
<SectionItemText text="*Beta features may change or be removed without warning." />
</SectionItem>
{devMenuEnabled && (
<>
<SectionItem>
<SectionItemText text="Player Numbers" />
<Switch onValueChange={togglePlayerIndexSwitch} value={showPlayerIndex} />
</SectionItem>
</>
)}
</Section>
Expand Down
10 changes: 3 additions & 7 deletions src/screens/EditPlayerScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ const EditPlayerScreen: React.FC<EditPlayerScreenProps> = ({
};

const inputRef = React.useRef<TextInput>(null);
const showColorPalettes = useAppSelector(state => state.settings.showColorPalettes);

return (
<ScrollView style={{ flex: 1 }}>
Expand Down Expand Up @@ -119,12 +118,9 @@ const EditPlayerScreen: React.FC<EditPlayerScreenProps> = ({
value={localPlayerName}
/>

{
showColorPalettes &&
<View style={{ marginHorizontal: 20 }}>
<ColorSelector playerId={playerId} />
</View>
}
<View style={{ marginHorizontal: 20 }}>
<ColorSelector playerId={playerId} />
</View>

</ScrollView>
);
Expand Down

0 comments on commit 5a79d88

Please sign in to comment.