Skip to content

Commit

Permalink
Merge pull request #429 from wyne/colors
Browse files Browse the repository at this point in the history
Color Palettes
  • Loading branch information
wyne authored May 5, 2024
2 parents 8e06c07 + 6741086 commit fb67dd8
Show file tree
Hide file tree
Showing 17 changed files with 305 additions and 125 deletions.
8 changes: 4 additions & 4 deletions Contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ For android, use JDK 17.
```zsh
npx react-native-clean-project
npx expo prebuild
eas build --profile development-simulator --platform ios --local
eas build --profile development-simulator --platform android --local
eas build:run -p ios # select expo build from above
eas build:run -p android # select expo build from above
npx eas build --profile development-simulator --platform ios --local
npx eas build --profile development-simulator --platform android --local
npx eas build:run -p ios # select expo build from above
npx eas build:run -p android # select expo build from above
npx expo start --dev-client
```

Expand Down
99 changes: 86 additions & 13 deletions redux/GamesSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getPalette } from '../src/ColorPalette';
import { SortDirectionKey, SortSelectorKey } from '../src/components/ScoreLog/SortHelper';
import logger from '../src/Logger';

import { playerAdd, selectPlayerById } from './PlayersSlice';
import { playerAdd, selectPlayerById, updatePlayer } from './PlayersSlice';
import { setCurrentGameId } from './SettingsSlice';
import { RootState } from './store';

Expand All @@ -34,6 +34,7 @@ const initialState = gamesAdapter.getInitialState({
locked: false,
sortSelectorKey: SortSelectorKey.ByIndex,
sortDirectionKey: SortDirectionKey.Normal,
palette: 'original',
});

const gamesSlice = createSlice({
Expand Down Expand Up @@ -98,7 +99,8 @@ const gamesSlice = createSlice({
playerIds: action.payload.playerIds,
}
});
}
},

}
});

Expand Down Expand Up @@ -159,6 +161,35 @@ export const asyncRematchGame = createAsyncThunk(
}
);

export const asyncSetGamePalette = createAsyncThunk(
'games/setpalette',
async (
{ gameId, palette }: { gameId: string, palette: string; },
{ dispatch, getState }
) => {
// Update game
dispatch(updateGame({
id: gameId,
changes: {
palette: palette,
}
}));
// Get palette colors
const paletteColors = getPalette(palette);

const game = selectGameById(getState() as RootState, gameId);

// Update players
game?.playerIds.forEach((playerId) => {
const color = paletteColors[game.playerIds.indexOf(playerId) % paletteColors.length];
dispatch(updatePlayer({
id: playerId,
changes: { color: color }
}));
});
}
);

export const asyncCreateGame = createAsyncThunk(
'games/create',
async (
Expand All @@ -172,20 +203,24 @@ export const asyncCreateGame = createAsyncThunk(
playerIds.push(Crypto.randomUUID());
}

playerIds.forEach((playerId) => {
const paletteName = initialState.palette;
const paletteColors = getPalette(paletteName);

playerIds.forEach((playerId, index) => {
const color = paletteColors[index % paletteColors.length];
dispatch(playerAdd({
id: playerId,
playerName: `Player ${playerIds.indexOf(playerId) + 1}`,
scores: [0],
color: color,
}));
});

dispatch(gameSave({
...initialState,
id: newGameId,
title: `Game ${gameCount + 1}`,
dateCreated: Date.now(),
roundCurrent: 0,
roundTotal: 1,
playerIds: playerIds,
}));

Expand All @@ -199,22 +234,60 @@ export const asyncCreateGame = createAsyncThunk(
}
);

export const addPlayer = createAsyncThunk(
'games/addplayer',
async (
{ gameId, playerName }: { gameId: string, playerName: string; },
{ dispatch, getState }
) => {
const playerId = Crypto.randomUUID();
const s = getState() as RootState;
const paletteName = s.games.entities[gameId]?.palette;
const palette = getPalette(paletteName || 'original');
const playerIndex = s.games.entities[gameId]?.playerIds.length || 0;
const paletteColor = palette[playerIndex % palette.length];

dispatch(playerAdd({
id: playerId,
playerName: playerName,
scores: [0],
color: paletteColor,
}));

dispatch(updateGame({
id: gameId,
changes: {
playerIds: [...selectGameById(getState() as RootState, gameId)?.playerIds || [], playerId],
}
}));

return playerId;
}
);

export const selectSortSelectorKey = (state: RootState, gameId: string) => {
const key = selectGameById(state, gameId)?.sortSelectorKey;
return key !== undefined ? key : SortSelectorKey.ByScore;
};

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
[
(state: RootState, playerId: string) => {
const gameId = selectAllGames(state).filter((game) => game.playerIds.includes(playerId))[0].id;
const paletteName = state.games.entities[gameId]?.palette;

const palette = getPalette(paletteName || 'original');
const playerColor = state.players.entities[playerId]?.color;

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

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

const bg = palette[playerIndex % palette.length];
const bg = playerColor || paletteBG;

const blackContrast = getContrastRatio(bg, '#000').number;
const whiteContrast = getContrastRatio(bg, '#fff').number;
Expand Down
8 changes: 7 additions & 1 deletion redux/SettingsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface SettingsState {
showColorPalettes?: boolean;
interactionType: InteractionType;
lastStoreReviewPrompt: number;
devMenuEnabled?: boolean;
};

const initialState: SettingsState = {
Expand All @@ -26,8 +27,9 @@ const initialState: SettingsState = {
addendTwo: 10,
currentGameId: undefined,
onboarded: undefined,
showPointParticles: true,
showPointParticles: false,
showPlayerIndex: false,
showColorPalettes: false,
interactionType: InteractionType.SwipeVertical,
lastStoreReviewPrompt: 0,
};
Expand Down Expand Up @@ -72,6 +74,9 @@ const settingsSlice = createSlice({
setLastStoreReviewPrompt(state, action: PayloadAction<number>) {
state.lastStoreReviewPrompt = action.payload;
},
toggleDevMenuEnabled(state) {
state.devMenuEnabled = !state.devMenuEnabled;
}
}
});

Expand All @@ -87,6 +92,7 @@ export const {
toggleShowColorPalettes,
setInteractionType,
setLastStoreReviewPrompt,
toggleDevMenuEnabled,
} = settingsSlice.actions;

export default settingsSlice.reducer;
68 changes: 21 additions & 47 deletions src/ColorPalette.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ const palettes: PaletteType = {
'#755647',
'#925561',
],
'c': [
'#88498f',
'#779fa1',
'#e0cba8',
'#ff6542',
'#564154'
],
'd': [
'#f8ffe5',
'#06d6a0',
'#1b9aaa',
'#ef476f',
'#ffc43d'
],
'pastel': [
'#f9d5e5',
'#eeac99',
Expand All @@ -25,13 +39,19 @@ const palettes: PaletteType = {
'#f6416c',
],
'dark': [
'#011627',
'#fdfffc',
'#2ec4b6',
'#e71d36',
'#ff9f1c',
'#f3722c',
],
'f': [
'#fcaa67',
'#b0413e',
'#ffffc7',
'#548687',
'#473335'
],
'grey': [
'#f8f9fa',
'#e9ecef',
Expand All @@ -44,52 +64,6 @@ const palettes: PaletteType = {
'#212529',
'#000000',
],
'a': [
'#114b5f',
'#456990',
'#e4fde1',
'#f45b69',
'#6b2737'
],
'b': [
'#c25858',
'#01497c',
],
'c': [
'#88498f',
'#779fa1',
'#e0cba8',
'#ff6542',
'#564154'
],
'd': [
'#f8ffe5',
'#06d6a0',
'#1b9aaa',
'#ef476f',
'#ffc43d'
],
'e': [
'#1f2041',
'#4b3f72',
'#ffc857',
'#119da4',
'#19647e'
],
'f': [
'#fcaa67',
'#b0413e',
'#ffffc7',
'#548687',
'#473335'
],
'g': [
'#ffa400',
'#009ffd',
'#2a2a72',
'#232528',
'#eaf6ff'
]
};

export const getPalettes = (): string[] => {
Expand Down
31 changes: 26 additions & 5 deletions src/components/AppInfo/RotatingIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import Animated, {
withTiming
} from 'react-native-reanimated';

import { useAppDispatch } from '../../../redux/hooks';
import { toggleDevMenuEnabled } from '../../../redux/SettingsSlice';

const RotatingIcon: React.FunctionComponent = ({ }) => {
const dispatch = useAppDispatch();

const rotation = useSharedValue(0);
const rotationCount = useSharedValue(1);
const animatedStyles = useAnimatedStyle(() => {
Expand All @@ -21,12 +26,28 @@ const RotatingIcon: React.FunctionComponent = ({ }) => {
};
});

return <TouchableWithoutFeedback onPress={async () => {
rotationCount.value = rotationCount.value + 1;
rotation.value = withTiming((rotationCount.value * 90), { duration: 1000, easing: Easing.elastic(1) });
let holdCallback: NodeJS.Timeout;
const onPressIn = () => {
holdCallback = setTimeout(() => {
dispatch(toggleDevMenuEnabled());
// spring expand animate the Animated.View
}, 5000);
};

const onPressOut = () => {
if (holdCallback == null) return;
clearTimeout(holdCallback);
};

return <TouchableWithoutFeedback
onPressIn={onPressIn}
onPressOut={onPressOut}
onPress={async () => {
rotationCount.value = rotationCount.value + 1;
rotation.value = withTiming((rotationCount.value * 90), { duration: 1000, easing: Easing.elastic(1) });

await analytics().logEvent('app_icon');
}}>
await analytics().logEvent('app_icon');
}}>
<Animated.View style={[animatedStyles]}>
<Image source={require('../../../assets/icon.png')}
contentFit='contain'
Expand Down
2 changes: 1 addition & 1 deletion src/components/Boards/FlexboxBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const FlexboxBoard: React.FC<FlexboxBoardProps> = () => {

const playerCount = playerIds.length;

const desiredAspectRatio = 0.8;
const desiredAspectRatio = 1;

const layoutHandler = (e: LayoutChangeEvent) => {
const { width, height } = e.nativeEvent.layout;
Expand Down
5 changes: 2 additions & 3 deletions src/components/Boards/FlexboxTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Animated, { Easing, FadeIn } from 'react-native-reanimated';

import { selectPlayerColors } from '../../../redux/GamesSlice';
import { useAppSelector } from '../../../redux/hooks';
import { selectCurrentGame, selectInteractionType } from '../../../redux/selectors';
import { selectInteractionType } from '../../../redux/selectors';
import { interactionComponents } from '../Interactions/InteractionComponents';
import { InteractionType } from '../Interactions/InteractionType';
import AdditionTile from '../PlayerTiles/AdditionTile/AdditionTile';
Expand All @@ -32,9 +32,8 @@ 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, currentGameId || '', index || 0));
const playerColors = useAppSelector(state => selectPlayerColors(state, playerId));
const [bg, fg] = playerColors;

const widthPerc: DimensionValue = `${(100 / cols)}%`;
Expand Down
Loading

0 comments on commit fb67dd8

Please sign in to comment.