Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permanently enable colors and add analytics #480

Merged
merged 4 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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