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

Color Palettes #429

Merged
merged 18 commits into from
May 5, 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
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