diff --git a/redux/GamesSlice.ts b/redux/GamesSlice.ts index 4a8ae356..7c22d3e6 100644 --- a/redux/GamesSlice.ts +++ b/redux/GamesSlice.ts @@ -1,7 +1,9 @@ import analytics from '@react-native-firebase/analytics'; import { PayloadAction, createAsyncThunk, createEntityAdapter, createSelector, createSlice } from '@reduxjs/toolkit'; +import { getContrastRatio } from 'colorsheet'; import * as Crypto from 'expo-crypto'; +import { getPalette } from '../src/ColorPalette'; import logger from '../src/Logger'; import { ScoreState, playerAdd, selectAllPlayers, selectPlayerById } from './PlayersSlice'; @@ -185,6 +187,28 @@ export const selectSortedPlayers = createSelector( } ); +const selectPaletteName = (state: RootState, gameId: string) => state.games.entities[gameId]?.palette; +const selectPlayerIndex = (_: RootState, __: string, playerIndex: number) => playerIndex; + +export const selectPlayerColors = createSelector( + [selectPaletteName, selectPlayerIndex], + (paletteName, playerIndex) => { + // TODO: Get player color if it exists + + const palette = getPalette(paletteName || 'original'); + + const bg = palette[playerIndex % palette.length]; + + const blackContrast = getContrastRatio(bg, '#000').number; + const whiteContrast = getContrastRatio(bg, '#fff').number; + + // +1 to give a slight preference to white + const fg = blackContrast >= whiteContrast + 1 ? '#000000' : '#FFFFFF'; + + return [bg, fg]; + } +); + export const { updateGame, roundNext, diff --git a/src/ColorPalette.ts b/src/ColorPalette.ts index d85ca41a..e2b886b6 100644 --- a/src/ColorPalette.ts +++ b/src/ColorPalette.ts @@ -1,9 +1,5 @@ -import { getContrastRatio } from 'colorsheet'; - -import { updateGame } from '../redux/GamesSlice'; import { useAppDispatch, useAppSelector } from '../redux/hooks'; import { selectPlayerById, updatePlayer } from '../redux/PlayersSlice'; -import { selectCurrentGame } from '../redux/selectors'; type PaletteType = Record; @@ -106,23 +102,6 @@ export const getPalette = (name: string): string[] => { return palettes[name]; }; -export const getPlayerColors = (index: number): [string, string] => { - const palette = Object.keys(palettes)[0 % Object.keys(palettes).length]; - - // TODO: Get player color if it exists - - const length = palettes[palette].length; - const bg = palettes[palette][index % length]; - - const blackContrast = getContrastRatio(bg, '#000').number; - const whiteContrast = getContrastRatio(bg, '#fff').number; - - // +1 to give a slight preference to white - const fg = blackContrast >= whiteContrast + 1 ? '#000000' : '#FFFFFF'; - - return [bg, fg]; -}; - export const setPlayerColor = (playerId: string, color: string) => { const dispatch = useAppDispatch(); @@ -137,16 +116,3 @@ export const setPlayerColor = (playerId: string, color: string) => { } })); }; - -export const setGamePalette = (gameId: string, palette: string) => { - const dispatch = useAppDispatch(); - - const game = useAppSelector(selectCurrentGame); - if (typeof game == 'undefined') return; - dispatch(updateGame({ - id: game.id, - changes: { - palette: palette, - } - })); -}; diff --git a/src/components/Boards/FlexboxBoard.tsx b/src/components/Boards/FlexboxBoard.tsx index 505e9784..43578cfb 100644 --- a/src/components/Boards/FlexboxBoard.tsx +++ b/src/components/Boards/FlexboxBoard.tsx @@ -5,7 +5,6 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import { useAppSelector } from '../../../redux/hooks'; import { selectCurrentGame } from '../../../redux/selectors'; -import { getPlayerColors } from '../../ColorPalette'; import { bottomSheetHeight } from '../../components/Sheets/GameSheet'; import FlexboxTile from './FlexboxTile'; @@ -97,8 +96,6 @@ const FlexboxBoard: React.FC = () => { = ({ index, - color, - fontColor, width, height, cols, @@ -35,7 +32,10 @@ const FlexboxTile: React.FunctionComponent = ({ 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, currentGameId || '', index || 0)); + const [bg, fg] = playerColors; const widthPerc: DimensionValue = `${(100 / cols)}%`; const heightPerc: DimensionValue = `${(100 / rows)}%`; @@ -50,16 +50,16 @@ const FlexboxTile: React.FunctionComponent = ({ style={[ styles.playerCard, { - backgroundColor: color, + backgroundColor: bg, width: widthPerc, height: heightPerc, borderBottomLeftRadius: playerIndexLabel ? 7 : undefined, }]}> - - + + void; -} - const MemoizedColorCircle = React.memo(PalettePreview); -const PaletteSelector: React.FunctionComponent = () => { +const PaletteSelector: React.FunctionComponent = () => { const colorPalettes = getPalettes(); + const currentGameId = useAppSelector(state => selectCurrentGame(state)?.id); + const currentPalette = useAppSelector(state => selectCurrentGame(state)?.palette); + + const dispatch = useAppDispatch(); + + if (!currentGameId) return null; + + const onSelect = (palette: string) => { + dispatch(updateGame({ + id: currentGameId, + changes: { + palette: palette, + } + })); + }; return ( @@ -24,9 +37,9 @@ const PaletteSelector: React.FunctionComponent = () => { style={[ styles.palette, ]} - // onPress={() => onSelect(palette)} + onPress={() => onSelect(palette)} > - + ))} diff --git a/src/components/PlayerListItem.tsx b/src/components/PlayerListItem.tsx index 78acbec2..ed03e192 100644 --- a/src/components/PlayerListItem.tsx +++ b/src/components/PlayerListItem.tsx @@ -6,11 +6,10 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { Icon, ListItem } from 'react-native-elements'; -import { updateGame } from '../../redux/GamesSlice'; +import { selectGameById, selectPlayerColors, updateGame } from '../../redux/GamesSlice'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { removePlayer, selectPlayerById } from '../../redux/PlayersSlice'; import { selectCurrentGame } from '../../redux/selectors'; -import { getPlayerColors } from '../ColorPalette'; interface Props { playerId: string; @@ -29,10 +28,13 @@ const PlayerListItem: React.FunctionComponent = ({ navigation, edit, }) => { - const currentGame = useAppSelector(selectCurrentGame); + 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, currentGameId || '', index || 0)); - if (typeof currentGame == 'undefined') return null; + if (currentGameId == '' || currentGameId === undefined) return null; + if (playerIds === undefined) return null; if (typeof index == 'undefined') return null; const dispatch = useAppDispatch(); @@ -60,16 +62,16 @@ const PlayerListItem: React.FunctionComponent = ({ removePlayerHandler(); await analytics().logEvent('remove_player', { - game_id: currentGame.id, + game_id: currentGameId, player_index: index, }); }; const removePlayerHandler = () => { dispatch(updateGame({ - id: currentGame.id, + id: currentGameId, changes: { - playerIds: currentGame.playerIds.filter((id) => id != playerId), + playerIds: playerIds.filter((id) => id != playerId), } })); dispatch(removePlayer(playerId)); @@ -104,7 +106,7 @@ const PlayerListItem: React.FunctionComponent = ({ diff --git a/src/components/ScoreLog/PlayerNameColumn.tsx b/src/components/ScoreLog/PlayerNameColumn.tsx index a8a5c5e5..c966c6bf 100644 --- a/src/components/ScoreLog/PlayerNameColumn.tsx +++ b/src/components/ScoreLog/PlayerNameColumn.tsx @@ -1,28 +1,35 @@ import React from 'react'; -import { createSelector } from '@reduxjs/toolkit'; import { StyleSheet, Text, View } from 'react-native'; import { Icon } from 'react-native-elements/dist/icons/Icon'; +import { selectPlayerColors } from '../../../redux/GamesSlice'; import { useAppSelector } from '../../../redux/hooks'; +import { selectPlayerById } from '../../../redux/PlayersSlice'; import { selectCurrentGame } from '../../../redux/selectors'; -import { RootState } from '../../../redux/store'; -import { getPlayerColors } from '../../ColorPalette'; import { systemBlue } from '../../constants'; -const selectPlayerEntities = (state: RootState) => state.players.entities; +interface CellProps { + index: number; + playerId: string; +} -const selectPlayerIds = (_: RootState, playerIds: string[]) => playerIds; +const PlayerNameCell: React.FunctionComponent = ({ index, playerId }) => { + const currentGameId = useAppSelector(state => selectCurrentGame(state)?.id); + const playerColors = useAppSelector(state => selectPlayerColors(state, currentGameId || '', index || 0)); + const playerName = useAppSelector(state => selectPlayerById(state, playerId)?.playerName); -const selectPlayerNamesByIds = createSelector( - [selectPlayerEntities, selectPlayerIds], - (players, playerIds) => playerIds.map(id => players[id]?.playerName) -); + return ( + + {playerName} + + ); +}; const PlayerNameColumn: React.FunctionComponent = () => { const playerIds = useAppSelector(state => selectCurrentGame(state)?.playerIds) || []; - const playerNames = useAppSelector(state => selectPlayerNamesByIds(state, playerIds)); - return ( @@ -33,12 +40,8 @@ const PlayerNameColumn: React.FunctionComponent = () => { size={19} color='white' /> - {playerNames.map((name, index) => ( - - {name} - + {playerIds.map((playerId, index) => ( + ))} );