Skip to content

Commit

Permalink
Merge pull request #431 from wyne/performance-2
Browse files Browse the repository at this point in the history
Performance 2
  • Loading branch information
wyne authored May 6, 2024
2 parents fb67dd8 + b4665d0 commit 3c43008
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 63 deletions.
28 changes: 18 additions & 10 deletions redux/GamesSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,20 +270,27 @@ export const selectSortSelectorKey = (state: RootState, gameId: string) => {
return key !== undefined ? key : SortSelectorKey.ByScore;
};

export const selectPlayerColors = createSelector(
export const makeSelectPlayerColors = () => createSelector(
[
(state: RootState, playerId: string) => {
const gameId = selectAllGames(state).filter((game) => game.playerIds.includes(playerId))[0].id;
const paletteName = state.games.entities[gameId]?.palette;
(state: RootState, gameId: string | undefined): GameState | undefined => {
if (!gameId) {
return undefined;
}
return state.games.entities[gameId];
},
(state: RootState, gameId: string | undefined, playerId: string): string => playerId,
(state: RootState, gameId: string | undefined, playerId: string): string | undefined => state.players.entities[playerId]?.color,
],
(game, playerId, playerColor) => {
if (!game || !playerId) {
return ['#000000', '#FFFFFF'];
}

const playerColor = state.players.entities[playerId]?.color;
const paletteName = game.palette;
const playerIds = game.playerIds;

const playerIndex = state.games.entities[gameId]?.playerIds.indexOf(playerId) || 0;
const playerIndex = playerIds.indexOf(playerId);

return { paletteName, playerColor, playerIndex };
},
],
({ paletteName, playerColor, playerIndex }) => {
const palette = getPalette(paletteName || 'original') || getPalette('original');
const paletteBG = palette[playerIndex % palette.length];

Expand All @@ -299,6 +306,7 @@ export const selectPlayerColors = createSelector(
}
);


export const {
updateGame,
roundNext,
Expand Down
18 changes: 17 additions & 1 deletion redux/PlayersSlice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import crashlytics from '@react-native-firebase/crashlytics';
import { PayloadAction, createEntityAdapter, createSlice } from '@reduxjs/toolkit';
import { PayloadAction, createEntityAdapter, createSelector, createSlice } from '@reduxjs/toolkit';

import { RootState } from './store';

type RoundIndex = number;

Expand Down Expand Up @@ -72,3 +74,17 @@ export const {
selectIds: selectPlayerIds,
// Pass in a selector that returns the posts slice of state
} = playersAdapter.getSelectors((state: PlayersSlice) => state.players);

export const selectPlayerNameById = createSelector(
[
(state: RootState, playerId: string) => state.players.entities[playerId]
],
(player) => player?.playerName
);

export const selectPlayerScoreByRound = createSelector(
[
(state: RootState, playerId: string, round: number) => state.players.entities[playerId]?.scores[round]
],
(score) => score || 0
);
12 changes: 7 additions & 5 deletions src/components/Boards/FlexboxTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import React from 'react';
import { DimensionValue, StyleSheet } from 'react-native';
import Animated, { Easing, FadeIn } from 'react-native-reanimated';

import { selectPlayerColors } from '../../../redux/GamesSlice';
import { makeSelectPlayerColors } from '../../../redux/GamesSlice';
import { useAppSelector } from '../../../redux/hooks';
import { selectInteractionType } from '../../../redux/selectors';
import { selectCurrentGame, selectInteractionType } from '../../../redux/selectors';
import { interactionComponents } from '../Interactions/InteractionComponents';
import { InteractionType } from '../Interactions/InteractionType';
import AdditionTile from '../PlayerTiles/AdditionTile/AdditionTile';
Expand All @@ -20,7 +20,7 @@ interface Props {
height: number;
}

const FlexboxTile: React.FunctionComponent<Props> = ({
const FlexboxTile: React.FunctionComponent<Props> = React.memo(({
index,
width,
height,
Expand All @@ -32,8 +32,10 @@ const FlexboxTile: React.FunctionComponent<Props> = ({
if (!(width > 0 && height > 0)) return null;
if (Number.isNaN(width) || Number.isNaN(height)) return null;

const currentGameId = useAppSelector(state => selectCurrentGame(state)?.id);
const playerIndexLabel = useAppSelector(state => state.settings.showPlayerIndex);
const playerColors = useAppSelector(state => selectPlayerColors(state, playerId));
const selectPlayerColors = makeSelectPlayerColors();
const playerColors = useAppSelector(state => selectPlayerColors(state, currentGameId, playerId));
const [bg, fg] = playerColors;

const widthPerc: DimensionValue = `${(100 / cols)}%`;
Expand Down Expand Up @@ -66,7 +68,7 @@ const FlexboxTile: React.FunctionComponent<Props> = ({
</InteractionComponent>
</Animated.View>
);
};
});

const styles = StyleSheet.create({
playerCard: {
Expand Down
5 changes: 3 additions & 2 deletions src/components/PlayerListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { Icon, ListItem } from 'react-native-elements';

import { selectGameById, selectPlayerColors, updateGame } from '../../redux/GamesSlice';
import { makeSelectPlayerColors, selectGameById, updateGame } from '../../redux/GamesSlice';
import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { removePlayer, selectPlayerById } from '../../redux/PlayersSlice';
import { selectCurrentGame } from '../../redux/selectors';
Expand All @@ -31,7 +31,8 @@ const PlayerListItem: React.FunctionComponent<Props> = ({
const currentGameId = useAppSelector(state => selectCurrentGame(state)?.id);
const playerIds = useAppSelector(state => selectGameById(state, currentGameId || '')?.playerIds);
const player = useAppSelector(state => selectPlayerById(state, playerId));
const playerColors = useAppSelector(state => selectPlayerColors(state, playerId));
const selectPlayerColors = makeSelectPlayerColors();
const playerColors = useAppSelector(state => selectPlayerColors(state, currentGameId, playerId));

if (currentGameId == '' || currentGameId === undefined) return null;
if (playerIds === undefined) return null;
Expand Down
11 changes: 4 additions & 7 deletions src/components/Rounds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { LayoutChangeEvent, Platform, ScrollView, StyleSheet, View } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';

import { selectGameById, selectSortSelectorKey, setSortSelector } from '../../redux/GamesSlice';
import { selectGameById, setSortSelector } from '../../redux/GamesSlice';
import { useAppDispatch, useAppSelector } from '../../redux/hooks';

import PlayerNameColumn from './ScoreLog/PlayerNameColumn';
import RoundScoreColumn from './ScoreLog/RoundScoreColumn';
import { SortSelectorKey, sortSelectors } from './ScoreLog/SortHelper';
import { SortSelectorKey } from './ScoreLog/SortHelper';
import TotalScoreColumn from './ScoreLog/TotalScoreColumn';

interface Props {
Expand Down Expand Up @@ -61,9 +61,6 @@ const Rounds: React.FunctionComponent<Props> = ({ }) => {

const dispatch = useAppDispatch();

const sortSelectorKey = useAppSelector(state => selectSortSelectorKey(state, currentGameId));
const sortSelector = sortSelectors[sortSelectorKey];

const sortByPlayerIndex = () => {
dispatch(setSortSelector({ gameId: currentGameId, sortSelector: SortSelectorKey.ByIndex }));
};
Expand All @@ -88,10 +85,10 @@ const Rounds: React.FunctionComponent<Props> = ({ }) => {
{roundsIterator.map((item, round) => (
<View key={round} onLayout={e => onLayoutHandler(e, round)}>
<MemoizedRoundScoreColumn
sortSelector={sortSelector}
round={round}
key={round}
isCurrentRound={round == roundCurrent} />
isCurrentRound={round == roundCurrent}
/>
</View>
))}
</ScrollView>
Expand Down
11 changes: 7 additions & 4 deletions src/components/ScoreLog/PlayerNameColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Icon } from 'react-native-elements/dist/icons/Icon';

import { selectPlayerColors } from '../../../redux/GamesSlice';
import { makeSelectPlayerColors } from '../../../redux/GamesSlice';
import { useAppSelector } from '../../../redux/hooks';
import { selectPlayerById } from '../../../redux/PlayersSlice';
import { selectCurrentGame } from '../../../redux/selectors';
Expand All @@ -17,7 +17,9 @@ interface CellProps {
}

const PlayerNameCell: React.FunctionComponent<CellProps> = ({ index, playerId }) => {
const playerColors = useAppSelector(state => selectPlayerColors(state, playerId));
const selectPlayerColors = makeSelectPlayerColors();
const currentGameId = useAppSelector(state => selectCurrentGame(state)?.id);
const playerColors = useAppSelector(state => selectPlayerColors(state, currentGameId, playerId));
const playerName = useAppSelector(state => selectPlayerById(state, playerId)?.playerName);

return (
Expand Down Expand Up @@ -54,9 +56,10 @@ const PlayerHeaderCell: React.FunctionComponent = () => {
);
};

const MemoizedPlayerNameCell = React.memo(PlayerNameCell);

const PlayerNameColumn: React.FunctionComponent = () => {
const sortKey = useAppSelector(state => selectCurrentGame(state)?.sortSelectorKey);

const sortSelector = sortSelectors[sortKey || SortSelectorKey.ByIndex];
const sortedPlayerIds = useAppSelector(sortSelector);

Expand All @@ -65,7 +68,7 @@ const PlayerNameColumn: React.FunctionComponent = () => {
<PlayerHeaderCell />

{sortedPlayerIds.map((playerId, index) => (
playerId && <PlayerNameCell key={index} index={index} playerId={playerId} />
playerId && <MemoizedPlayerNameCell key={index} index={index} playerId={playerId} />
))}
</View>
);
Expand Down
12 changes: 3 additions & 9 deletions src/components/ScoreLog/RoundScoreCell.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { memo } from 'react';

import { Text, StyleSheet } from 'react-native';
import { StyleSheet, Text } from 'react-native';

import { useAppSelector } from '../../../redux/hooks';
import { selectPlayerById } from '../../../redux/PlayersSlice';
import { selectPlayerScoreByRound } from '../../../redux/PlayersSlice';

interface Props {
playerId: string;
Expand All @@ -12,13 +12,7 @@ interface Props {
}

const RoundScoreCell: React.FunctionComponent<Props> = ({ playerId, round, playerIndex }) => {
const scores = useAppSelector(state =>
selectPlayerById(state, playerId)?.scores
);

if (typeof scores == 'undefined') return null;

const scoreRound = scores[round] || 0;
const scoreRound = useAppSelector(state => selectPlayerScoreByRound(state, playerId, round));

return (
<Text key={playerIndex} style={[
Expand Down
10 changes: 5 additions & 5 deletions src/components/ScoreLog/RoundScoreColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,29 @@ import { LayoutChangeEvent, Text, TouchableWithoutFeedback, View } from 'react-n

import { updateGame } from '../../../redux/GamesSlice';
import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
import { RootState } from '../../../redux/store';
import { selectCurrentGame } from '../../../redux/selectors';

import RoundScoreCell from './RoundScoreCell';
import { SortSelectorKey, sortSelectors } from './SortHelper';

interface Props {
round: number;
isCurrentRound: boolean;
disabled?: boolean;
sortSelector: (state: RootState) => string[];
onLayout?: (event: LayoutChangeEvent, round: number) => void;
}

const RoundScoreColumn: React.FunctionComponent<Props> = ({
round,
isCurrentRound,
disabled = false,
sortSelector,
onLayout,
}) => {
const dispatch = useAppDispatch();

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

const currentGameId = useAppSelector(state => selectCurrentGame(state)?.id);
const sortKey = useAppSelector(state => selectCurrentGame(state)?.sortSelectorKey);
const sortSelector = sortSelectors[sortKey || SortSelectorKey.ByIndex];
const sortedPlayerIds = useAppSelector(sortSelector);

const onPressHandler = useCallback(async () => {
Expand Down
23 changes: 11 additions & 12 deletions src/components/ScoreLog/SortHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,30 @@ import { GameState } from '../../../redux/GamesSlice';
import { ScoreState, selectAllPlayers } from '../../../redux/PlayersSlice';
import { RootState } from '../../../redux/store';

export const selectPlayerIdsByIndex: SortSelector = createSelector(
export const selectSortedPlayerIdsByIndex = createSelector(
[
selectAllPlayers,
(state: RootState) => state.settings.currentGameId ? state.games.entities[state.settings.currentGameId] : undefined,
(state: RootState) => state.settings.currentGameId ? state.games.entities[state.settings.currentGameId]?.sortDirectionKey : undefined
],
(players: ScoreState[], currentGame: GameState | undefined) => {
if (!currentGame) return [];
(game: GameState | undefined, sortDirectionKey: SortDirectionKey | undefined) => {
if (!game) return [];

const playerIds = players.filter(player => currentGame.playerIds?.includes(player.id))
const playerIds = [...game.playerIds]
.sort((a, b) => {
if (currentGame?.playerIds == undefined) return 0;
return currentGame.playerIds.indexOf(a.id) - currentGame.playerIds.indexOf(b.id);
if (game?.playerIds == undefined) return 0;
return game.playerIds.indexOf(a) - game.playerIds.indexOf(b);
})
.map(player => player.id);
.map(player => player);

if (SortDirectionKey.Normal === currentGame.sortDirectionKey) {
if (SortDirectionKey.Normal === sortDirectionKey) {
return playerIds;
} else {
return playerIds.reverse();
}
}
);

export const selectPlayerIdsByScore: SortSelector = createSelector(
export const selectSortedPlayerIdsByScore: SortSelector = createSelector(
[
selectAllPlayers,
(state: RootState) => state.settings.currentGameId ? state.games.entities[state.settings.currentGameId] : undefined,
Expand Down Expand Up @@ -78,6 +77,6 @@ export enum SortDirectionKey {
}

export const sortSelectors = {
[SortSelectorKey.ByScore]: selectPlayerIdsByScore,
[SortSelectorKey.ByIndex]: selectPlayerIdsByIndex,
[SortSelectorKey.ByScore]: selectSortedPlayerIdsByScore,
[SortSelectorKey.ByIndex]: selectSortedPlayerIdsByIndex,
};
11 changes: 3 additions & 8 deletions src/screens/ShareScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ import { Button, Icon } from 'react-native-elements';
import { SafeAreaView } from 'react-native-safe-area-context';
import { captureRef } from 'react-native-view-shot';

import { selectGameById, selectSortSelectorKey } from '../../redux/GamesSlice';
import { selectGameById } from '../../redux/GamesSlice';
import { useAppSelector } from '../../redux/hooks';
import { selectCurrentGame } from '../../redux/selectors';
import PlayerNameColumn from '../components/ScoreLog/PlayerNameColumn';
import RoundScoreColumn from '../components/ScoreLog/RoundScoreColumn';
import { sortSelectors } from '../components/ScoreLog/SortHelper';
import TotalScoreColumn from '../components/ScoreLog/TotalScoreColumn';
import { systemBlue } from '../constants';

Expand Down Expand Up @@ -54,9 +53,6 @@ const ShareScreen: React.FunctionComponent<Props> = ({ navigation }) => {
await analytics().logEvent('share_image');
};

const sortSelectorKey = useAppSelector(state => selectSortSelectorKey(state, currentGameId));
const sortSelector = sortSelectors[sortSelectorKey];

return (
<SafeAreaView edges={['right', 'left']}
style={[styles.contentContainer, { height: 'auto' }]}>
Expand Down Expand Up @@ -98,12 +94,11 @@ const ShareScreen: React.FunctionComponent<Props> = ({ navigation }) => {
</Text>
</View>
<View style={{ flexDirection: 'row' }}>
<PlayerNameColumn sortSelector={sortSelector} sortSelectorKey={sortSelectorKey} />
<TotalScoreColumn sortSelector={sortSelector} sortSelectorKey={sortSelectorKey} />
<PlayerNameColumn />
<TotalScoreColumn />
{roundsIterator.map((item, round) => (
<View key={round}>
<RoundScoreColumn
sortSelector={sortSelector}
round={round}
key={round}
isCurrentRound={false}
Expand Down

0 comments on commit 3c43008

Please sign in to comment.