Skip to content

Commit

Permalink
completed UI refactor, added audio settings, testing performance opti…
Browse files Browse the repository at this point in the history
…mizations
  • Loading branch information
StefanTodoran committed Mar 16, 2023
1 parent 0f5ea08 commit 0753f3c
Show file tree
Hide file tree
Showing 22 changed files with 150 additions and 110 deletions.
46 changes: 31 additions & 15 deletions App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useEffect, useRef, useState } from 'react';
import { StyleSheet, View, Image, Dimensions, Animated, BackHandler, ScrollView, SafeAreaView, Pressable } from 'react-native';

import { colors, graphics, nextTheme } from './Theme';
import MenuButton from './components/MenuButton';
import { GlobalContext } from './GlobalContext';
import AsyncStorage from '@react-native-async-storage/async-storage';
import PlayPage from './pages/PlayPage';
Expand Down Expand Up @@ -54,6 +53,11 @@ export default function App() {
try {
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem(storageKey, jsonValue);

// let checkValue = await AsyncStorage.getItem(storageKey);
// checkValue = checkValue != null ? JSON.parse(checkValue) : null;
// return value == checkValue;

return true;
} catch (err) {
console.log("\n\n(ERROR) >>> SAVING ERROR:\n", err);
Expand Down Expand Up @@ -85,6 +89,7 @@ export default function App() {
const [dragSensitivity, setSensitivity] = useState(60);
const [doubleTapDelay, setTapDelay] = useState(250);
const [curTheme, setCurTheme] = useState("purple");
const [playAudio, setAudioMode] = useState(true);

// These functions are used by children (probably only the settings page)
// to modify App's state, and we use them to abstract that away from those
Expand All @@ -93,15 +98,25 @@ export default function App() {
const toggleDarkMode = () => {
setDarkMode(current => !current);
}
const toggleAudioMode = () => {
setAudioMode(current => !current);
}

useEffect(() => {
NavigationBar.setBackgroundColorAsync(!darkMode ? "white" : colors.NEAR_BLACK);
}, [darkMode, curTheme]);

// We use this to avoid rewriting the settings on app start (since
// the setting writing useEffect will trigger on first render).
const didReadSettings = useRef(false);

async function readSettingsFromStorage() {
const storedDarkMode = await getData("isAppDarkMode", "boolean", false);
setDarkMode(storedDarkMode);


const storedAudioMode = await getData("appAudioMode", "boolean", true);
setAudioMode(storedAudioMode);

setSensitivity(await getData("appDragSensitivity", "number", 60));
setTapDelay(await getData("appDoubleTapDelay", "number", 250));

Expand All @@ -111,14 +126,18 @@ export default function App() {
theme = nextTheme();
}
setCurTheme(theme);
didReadSettings.current = true;
}

async function writeSettingsToStorage() {
storeData(darkMode, "isAppDarkMode");
storeData(dragSensitivity, "appDragSensitivity");
storeData(doubleTapDelay, "appDoubleTapDelay");
storeData(curTheme, "appTheme");
}
useEffect(() => {
if (didReadSettings.current) {
storeData(darkMode, "isAppDarkMode");
storeData(playAudio, "appAudioMode");
storeData(dragSensitivity, "appDragSensitivity");
storeData(doubleTapDelay, "appDoubleTapDelay");
storeData(curTheme, "appTheme");
}
}, [darkMode, playAudio, dragSensitivity, doubleTapDelay, curTheme]);

useEffect(() => {
readSettingsFromStorage();
Expand All @@ -134,30 +153,27 @@ export default function App() {

useEffect(() => {
const backAction = () => {
if (true) { // TODO: change this to be only when in game
return true;
}
return false;
return scrollEnabled;
}
BackHandler.addEventListener("hardwareBackPress", backAction);

return () =>
BackHandler.removeEventListener("hardwareBackPress", backAction);
}, [page]); // TODO: update this
}, [scrollEnabled]); // TODO: update this

if (!fontsLoaded) {
return null;
}

return (
<GlobalContext.Provider value={{ darkMode, dragSensitivity, doubleTapDelay }}>
<GlobalContext.Provider value={{ darkMode, dragSensitivity, doubleTapDelay, playAudio }}>
<SafeAreaView style={{ flex: 1, backgroundColor: (darkMode) ? colors.NEAR_BLACK : "white" }}>
{/* PAGE CONTENT */}
<ScrollView horizontal pagingEnabled showsHorizontalScrollIndicator={false} onScroll={(evt) => {
setPageState(Math.round(evt.nativeEvent.contentOffset.x / win.width));
}} scrollEnabled={scrollEnabled} ref={scrollRef}>
<View style={styles.page}>
<HomePage darkModeCallback={toggleDarkMode} setThemeCallback={setCurTheme}
<HomePage darkModeCallback={toggleDarkMode} setThemeCallback={setCurTheme} audioModeCallback={toggleAudioMode}
setSensitivityCallback={setSensitivity} setTapDelayCallback={setTapDelay}></HomePage>
</View>
<View style={styles.page}>
Expand Down
20 changes: 18 additions & 2 deletions Game.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,22 @@ const level_ten = [
[4, 4, 0, 0, 0, 0, 5, 4],
[0, 4, 0, 4, 0, 0, 0, 5],
];
const level_eleven = [];
const level_eleven = [
[4, 4, 5, 0, 0, 6, 1, 4],
[5, 0, 5, 4, 0, 5, 0, 0],
[0, 5, 0, 5, 1, 0, 0, 0],
[0, 4, 0, 5, 0, 1, 0, 8],
[1, 0, 1, 4, 6, 1, 4, 1],
[0, 4, 0, 1, 1, 0, 4, 1],
[0, 1, 4, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 0, 0],
[4, 5, 4, 0, 7, 4, 5, 0],
[0, 1, 1, 1, 1, 1, 0, 0],
[0, 1, 4, 6, 0, 1, 0, 4],
[5, 4, 5, 0, 5, 0, 0, 4],
[4, 0, 3, 4, 0, 0, "b:41", 1],
[4, 0, 5, 5, 4, 0, 2, 6],
];
const level_twenty = [
[1, 6, 5, 1, 4, 0, 4, 0],
[5, 4, 0, 3, 5, 0, 4, 4],
Expand Down Expand Up @@ -249,7 +264,8 @@ export async function importStoredLevels() {
"isAppDarkMode",
"appDragSensitivity",
"appDoubleTapDelay",
"appTheme"
"appTheme",
"appAudioMode"
];

for (let i = 0; i < keys.length; i++) {
Expand Down
3 changes: 3 additions & 0 deletions GlobalContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ export const GlobalContext = createContext({});
* A number representing how many miliseconds delay of tolerance are allowed between two taps for it to
* count as a double tap.
*
* @param {number} playAudio
* A boolean representing whether the app should play sound effects or not.
*
*/
8 changes: 8 additions & 0 deletions Theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const blue_graphics = {
LEFT_ICON: require('./assets/blue_theme/left_icon.png'),
RIGHT_ICON: require('./assets/blue_theme/right_icon.png'),
SUPPORT_ICON: require('./assets/blue_theme/support_icon.png'),
AUDIO_ON_ICON: require('./assets/blue_theme/audio_on_icon.png'),
AUDIO_OFF_ICON: require('./assets/blue_theme/audio_off_icon.png'),

HOME_OUTLINED_ICON: require('./assets/blue_theme/home_outlined_icon.png'),
HOME_FILLED_ICON: require('./assets/blue_theme/home_filled_icon.png'),
Expand Down Expand Up @@ -90,6 +92,8 @@ const orange_graphics = {
LEFT_ICON: require('./assets/orange_theme/left_icon.png'),
RIGHT_ICON: require('./assets/orange_theme/right_icon.png'),
SUPPORT_ICON: require('./assets/orange_theme/support_icon.png'),
AUDIO_ON_ICON: require('./assets/orange_theme/audio_on_icon.png'),
AUDIO_OFF_ICON: require('./assets/orange_theme/audio_off_icon.png'),

HOME_OUTLINED_ICON: require('./assets/orange_theme/home_outlined_icon.png'),
HOME_FILLED_ICON: require('./assets/orange_theme/home_filled_icon.png'),
Expand Down Expand Up @@ -154,6 +158,8 @@ const purple_graphics = {
LEFT_ICON: require('./assets/purple_theme/left_icon.png'),
RIGHT_ICON: require('./assets/purple_theme/right_icon.png'),
SUPPORT_ICON: require('./assets/purple_theme/support_icon.png'),
AUDIO_ON_ICON: require('./assets/purple_theme/audio_on_icon.png'),
AUDIO_OFF_ICON: require('./assets/purple_theme/audio_off_icon.png'),

HOME_OUTLINED_ICON: require('./assets/purple_theme/home_outlined_icon.png'),
HOME_FILLED_ICON: require('./assets/purple_theme/home_filled_icon.png'),
Expand Down Expand Up @@ -219,6 +225,8 @@ const green_graphics = {
LEFT_ICON: require('./assets/green_theme/left_icon.png'),
RIGHT_ICON: require('./assets/green_theme/right_icon.png'),
SUPPORT_ICON: require('./assets/green_theme/support_icon.png'),
AUDIO_ON_ICON: require('./assets/green_theme/audio_on_icon.png'),
AUDIO_OFF_ICON: require('./assets/green_theme/audio_off_icon.png'),

HOME_OUTLINED_ICON: require('./assets/green_theme/home_outlined_icon.png'),
HOME_FILLED_ICON: require('./assets/green_theme/home_filled_icon.png'),
Expand Down
Binary file added assets/blue_theme/audio_off_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/blue_theme/audio_on_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/green_theme/audio_off_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/green_theme/audio_on_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/orange_theme/audio_off_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/orange_theme/audio_on_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/purple_theme/audio_off_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/purple_theme/audio_on_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 7 additions & 2 deletions components/MenuButton.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Pressable, Text, StyleSheet, Image, Dimensions } from 'react-native';
import React, { useEffect, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import { Audio } from 'expo-av';
import { colors } from '../Theme';
import { GlobalContext } from '../GlobalContext';
const width = Dimensions.get('window').width;

/**
Expand All @@ -20,6 +21,8 @@ const width = Dimensions.get('window').width;
* @param {boolean} allowOverflow Whether number of lines for the button text should cap at 1.
*/
export default function MenuButton({ onPress, onLongPress, value, label, icon, invisible, disabled, allowOverflow }) {
const { darkMode, dragSensitivity, doubleTapDelay, playAudio } = useContext(GlobalContext);

const [pressed, setPressedState] = useState(false);
const [sound, setSound] = useState();
async function playSound() {
Expand All @@ -36,7 +39,9 @@ export default function MenuButton({ onPress, onLongPress, value, label, icon, i
const pressedFn = () => {
if (!!onPress) {
onPress(value);
playSound();
if (playAudio) {
playSound();
}
}
}

Expand Down
13 changes: 8 additions & 5 deletions components/Selector.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Pressable, View, Text, StyleSheet, Image, Dimensions } from 'react-native';
import React, { useEffect, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import { Audio } from 'expo-av';
import { colors, graphics } from '../Theme';
import { GlobalContext } from '../GlobalContext';
const win = Dimensions.get('window');

/**
Expand All @@ -17,6 +18,8 @@ const win = Dimensions.get('window');
* @param {boolean} disabled Whether or not the button can be pressed (changes appearance).
*/
export default function Selector({ label, onNext, onPrev, nextDisabled, prevDisabled }) {
const { darkMode, dragSensitivity, doubleTapDelay, playAudio } = useContext(GlobalContext);

const [nextPressed, setNextPressed] = useState(false);
const [prevPressed, setPrevPressed] = useState(false);
const [sound, setSound] = useState();
Expand All @@ -33,9 +36,9 @@ export default function Selector({ label, onNext, onPrev, nextDisabled, prevDisa
return (
<View style={styles.outerContainer}>
<Pressable disabled={prevDisabled} touchSoundDisabled={true} onPress={() => {
playSound();
if (playAudio) { playSound(); }
onPrev();
}}
}} hitSlop={15}
onPressIn={() => {setPrevPressed(true)}} onPressOut={() => {setPrevPressed(false)}}>
<Image style={styles.icon(prevPressed, prevDisabled)} source={graphics.LEFT_ICON} />
</Pressable>
Expand All @@ -44,9 +47,9 @@ export default function Selector({ label, onNext, onPrev, nextDisabled, prevDisa
...styles.label, color: colors.MAIN_COLOR,
}}>{label}</Text>
<Pressable disabled={nextDisabled} touchSoundDisabled={true} onPress={() => {
playSound();
if (playAudio) { playSound(); }
onNext();
}}
}} hitSlop={15}
onPressIn={() => {setNextPressed(true)}} onPressOut={() => {setNextPressed(false)}}>
<Image style={styles.icon(nextPressed, nextDisabled)} source={graphics.RIGHT_ICON} />
</Pressable>
Expand Down
5 changes: 2 additions & 3 deletions pages/CreateLevel.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,16 +343,15 @@ export default function CreateLevel({ pageCallback, levelCallback, level, storeL
</Text>
</View>
<View style={styles.row}>
<MenuButton onPress={testLevel} label="Playtest" icon={graphics.PLAYER} disabled={index === -1} />
<MenuButton onPress={saveLevelToStorage} label="Save Level" icon={graphics.SAVE_ICON} disabled={level.name === ""} />
<MenuButton onPress={loadLevelFromStorage} label="Load Level" icon={graphics.LOAD_ICON} disabled={index === -1} />
</View>
<View style={styles.row}>
<MenuButton onLongPress={deleteLevelFromStorage} label="Delete Level (Long Press)" icon={graphics.DELETE_ICON} allowOverflow disabled={index === -1} />
<MenuButton onLongPress={() => { storeLevelCallback(createLevelObj("", "", null)); }} label="Clear Level (Long Press)" icon={graphics.HAMMER_ICON} allowOverflow />
</View>
<View style={styles.row}>
{/* <MenuButton onPress={shareLevel} label="Share Level" icon={graphics.SHARE_ICON} disabled={index === -1} /> */}
<MenuButton onPress={loadLevelFromStorage} label="Load Level" icon={graphics.LOAD_ICON} disabled={index === -1} />
<MenuButton onPress={testLevel} label="Playtest" icon={graphics.PLAYER} disabled={index === -1} />
<MenuButton onPress={pageCallback} value={false} label="To Menu" icon={graphics.DOOR} />
</View>
</Animated.View>}
Expand Down
4 changes: 2 additions & 2 deletions pages/HomePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import HowToPlay from '../pages/HowToPlay';
import MenuButton from '../components/MenuButton';
const win = Dimensions.get('window');

export default function HomePage({ darkModeCallback, setThemeCallback, setSensitivityCallback, setTapDelayCallback }) {
export default function HomePage({ darkModeCallback, setThemeCallback, audioModeCallback, setSensitivityCallback, setTapDelayCallback }) {
const { darkMode } = useContext(GlobalContext);

const anim = useRef(new Animated.Value(0)).current;
Expand Down Expand Up @@ -40,7 +40,7 @@ export default function HomePage({ darkModeCallback, setThemeCallback, setSensit
} else if (modalOpen === "how") {
content = <HowToPlay pageCallback={setModalOpen} />;
} else if (modalOpen === "settings") {
content = <Settings pageCallback={setModalOpen} darkModeCallback={darkModeCallback}
content = <Settings pageCallback={setModalOpen} darkModeCallback={darkModeCallback} audioModeCallback={audioModeCallback}
setThemeCallback={setThemeCallback} setSensitivityCallback={setSensitivityCallback} setTapDelayCallback={setTapDelayCallback} />;
}

Expand Down
5 changes: 2 additions & 3 deletions pages/HowToPlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ export default function HowToPlay({ pageCallback }) {
</View>

<Text style={styles.text(darkMode)}>
There are, however, a number of obstacles in your way. The first one are doors. All doors
start off locked, and must be unlocked with a key. Any key can unlock any door, but each key
is single use.
The first one are doors. Any key can unlock any door, but each key
is single use. There are, however, a number of obstacles in your way.
</Text>
<View style={styles.row}>
<Image style={styles.icon} source={graphics.DOOR} />
Expand Down
7 changes: 3 additions & 4 deletions pages/LevelSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,11 @@ export default function LevelSelect({ pageCallback, levelCallback }) {
<View style={{ marginTop: 35, marginBottom: 15 }}>
<Selector
onNext={() => { pageChange(page + 1) }} nextDisabled={!levels[pageEnd]}
onPrev={() => { pageChange(page - 1) }} prevDisabled={page === 0}
onPrev={() => { pageChange(Math.max(0, page - 1)) }} prevDisabled={page === 0}
// For some reason fast clicks in succession can move page below zero before
// disabling kicks in, unless we add this min and max force.
label={`Page #${page + 1}`} />
</View>
<View style={styles.buttonsRow}>
<MenuButton onPress={pageCallback} value={false} label="Go Back" icon={graphics.DOOR} />
</View>
</>
);
}
Expand Down
Loading

0 comments on commit 0753f3c

Please sign in to comment.