Skip to content
This repository has been archived by the owner on Nov 27, 2020. It is now read-only.

Commit

Permalink
chore: normalize data across the app (closes #9)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulocf92 committed Mar 14, 2020
1 parent 70da942 commit 1365547
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 164 deletions.
27 changes: 18 additions & 9 deletions src/pages/HuntList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ import {
} from '~/store/modules/recipe/actions';

export default function HuntList() {
const storedRecipeIds = useSelector(state => state.recipe.recipeIds);
const storedRecipes = useSelector(state => state.recipe.recipes);
const isLoading = useSelector(state => state.recipe.loading);
const dispatch = useDispatch();

const [recipes, setRecipes] = useState([]);
const [recipes, setRecipes] = useState();
const [recipeIds, setRecipeIds] = useState([]);
const [loading, setLoading] = useState(true);

// Load recipes from AsyncStorage when screen is focused
Expand All @@ -36,10 +38,14 @@ export default function HuntList() {
setRecipes(storedRecipes);
}, [storedRecipes]);

useEffect(() => {
setRecipeIds(storedRecipeIds);
}, [storedRecipeIds]);

useEffect(() => setLoading(isLoading), [isLoading]);

function handleClear() {
if (recipes.length) {
if (recipeIds.length) {
Alert.alert('Clear recipes', 'Clear all recipes stored in the device?', [
{
text: 'No',
Expand Down Expand Up @@ -67,8 +73,7 @@ export default function HuntList() {
}

async function handleSelected({ current }) {
const recipeExists = !!recipes.filter(recipe => recipe.id === current.id)
.length;
const recipeExists = !!recipes[current.id];

if (recipeExists) {
Alert.alert(
Expand Down Expand Up @@ -110,15 +115,19 @@ export default function HuntList() {
onChangeSelected={handleSelected}
/>
<List
key={recipes.length}
data={recipes}
keyExtractor={item => String(item.id)}
key={recipeIds.length}
data={recipeIds}
keyExtractor={(_, idx) => String(idx)}
renderItem={({ item }) => (
<HuntItem data={item} onDelete={handleDeleteRecipe} />
<HuntItem data={recipes[item]} onDelete={handleDeleteRecipe} />
)}
/>

<ClearButton onPress={handleClear} enabled={!!recipes.length} lightText>
<ClearButton
onPress={handleClear}
enabled={!!recipeIds.length}
lightText
>
Clear all
</ClearButton>
</Container>
Expand Down
20 changes: 11 additions & 9 deletions src/pages/RecipeDetail/Recipe/Ingredient/CrystalCluster/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ export default function CrystalCluster({ cluster, onUpdateCrystal }) {
const [complete, setComplete] = useState(false);

useEffect(() => {
const completion = !!cluster.filter(
crystal => crystal.progress === crystal.totalRequired,
const { crystals, ids } = cluster;
const completion = !!ids.filter(
id => crystals[id].progress === crystals[id].totalRequired,
).length;

setComplete(completion);
Expand Down Expand Up @@ -44,10 +45,10 @@ export default function CrystalCluster({ cluster, onUpdateCrystal }) {
locations={[0, 0.8]}
colors={statusColors}
>
{cluster.map(crystal => (
<CrystalData key={crystal.id}>
<CrystalQty>{crystal.totalRequired}</CrystalQty>
<CrystalIcon source={{ uri: crystal.icon }} />
{cluster.ids.map(id => (
<CrystalData key={cluster.crystals[id].id}>
<CrystalQty>{cluster.crystals[id].totalRequired}</CrystalQty>
<CrystalIcon source={{ uri: cluster.crystals[id].icon }} />
</CrystalData>
))}
</Crystal>
Expand All @@ -57,12 +58,13 @@ export default function CrystalCluster({ cluster, onUpdateCrystal }) {
}

CrystalCluster.propTypes = {
cluster: PropTypes.arrayOf(
PropTypes.shape({
cluster: PropTypes.shape({
crystals: PropTypes.shape({
id: PropTypes.number,
totalAmount: PropTypes.number,
icon: PropTypes.string,
}),
).isRequired,
ids: PropTypes.arrayOf(PropTypes.number),
}).isRequired,
onUpdateCrystal: PropTypes.func.isRequired,
};
9 changes: 8 additions & 1 deletion src/pages/RecipeDetail/Recipe/Ingredient/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,14 @@ Ingredient.propTypes = {
icon: PropTypes.string,
leaf: PropTypes.bool,
}).isRequired,
crystals: PropTypes.arrayOf(PropTypes.shape()),
crystals: PropTypes.shape({
crystals: PropTypes.shape({
id: PropTypes.number,
totalAmount: PropTypes.number,
icon: PropTypes.string,
}),
ids: PropTypes.arrayOf(PropTypes.number),
}),
treePath: PropTypes.arrayOf(PropTypes.number).isRequired,
onUpdateProgress: PropTypes.func.isRequired,
};
Expand Down
83 changes: 49 additions & 34 deletions src/pages/RecipeDetail/Recipe/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ export default function Recipe({ route }) {

// Load single recipe upon mounting
useEffect(() => {
// Load recipe tree
dispatch(loadSingleRecipeRequest(recipe.id));
}, [dispatch, recipe.id]);

Expand Down Expand Up @@ -122,7 +121,7 @@ export default function Recipe({ route }) {

const item = updateRecipeProgress(
recipeTree,
traversalPath,
traversalPath.slice(),
amount,
isCrystal,
);
Expand All @@ -139,11 +138,11 @@ export default function Recipe({ route }) {
// Successively reduce path until we achieve parent item containing
// amount of crystals
const parent = traversalPath.reduce(
(acc, i) => acc.children[i],
(acc, id) => acc.ingredients[id],
recipeTree,
);

uniqueProgress = item.uniqueProgress + parent.crystals.length * amount;
uniqueProgress = item.uniqueProgress + parent.crystalIds.length * amount;
} else {
uniqueProgress = item.uniqueProgress + increase;
}
Expand All @@ -154,37 +153,50 @@ export default function Recipe({ route }) {
}

function renderIngredient(ingredient, parentCrystals, treePath = []) {
const { ingredients, ingredientIds } = ingredient;

return (
<View>
{ingredient.children.map((item, idx) => (
<RecipeTreeRow key={item.id} spacing={ingredient.depth === 0}>
<Ingredient
item={item}
crystals={idx === 0 ? parentCrystals : null}
onUpdateProgress={handleUpdateProgress}
treePath={[...treePath, idx]}
/>

{item.children && (
<>
<Svg
width="56"
height={item.svgHeight}
viewBox={`0 0 60 ${item.svgHeight}`}
>
<Path
d={item.svgGraph}
fill="none"
stroke="#888"
strokeWidth="2"
/>
</Svg>

{renderIngredient(item, item.crystals, [...treePath, idx])}
</>
)}
</RecipeTreeRow>
))}
{ingredientIds.map((id, idx) => {
const item = ingredients[id];

return (
<RecipeTreeRow key={item.id} spacing={ingredient.depth === 0}>
<Ingredient
item={item}
crystals={idx === 0 ? parentCrystals : null}
onUpdateProgress={handleUpdateProgress}
treePath={[...treePath, item.id]}
/>

{item.ingredients && (
<>
<Svg
width="56"
height={item.svgHeight}
viewBox={`0 0 60 ${item.svgHeight}`}
>
<Path
d={item.svgGraph}
fill="none"
stroke="#888"
strokeWidth="2"
/>
</Svg>

{renderIngredient(
item,
{
ids: item.crystalIds,
crystals: item.crystals,
},
[...treePath, item.id],
)}
</>
)}
</RecipeTreeRow>
);
})}
</View>
);
}
Expand All @@ -203,7 +215,10 @@ export default function Recipe({ route }) {
{recipeTree && (
<RecipeTreeContainer>
<RecipeTree>
{renderIngredient(recipeTree, recipeTree.crystals)}
{renderIngredient(recipeTree, {
ids: recipeTree.crystalIds,
crystals: recipeTree.crystals,
})}
</RecipeTree>
</RecipeTreeContainer>
)}
Expand Down
8 changes: 4 additions & 4 deletions src/store/modules/recipe/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ export function storeRecipeRequest(id) {
};
}

export function storeRecipeSuccess(recipe) {
export function storeRecipeSuccess(recipe, sortedIds) {
return {
type: '@recipe/STORE_RECIPE_SUCCESS',
payload: { recipe },
payload: { recipe, sortedIds },
};
}

Expand All @@ -44,10 +44,10 @@ export function deleteRecipeRequest(id) {
};
}

export function deleteRecipeSuccess(id) {
export function deleteRecipeSuccess(id, newIds) {
return {
type: '@recipe/DELETE_RECIPE_SUCCESS',
payload: { id },
payload: { id, newIds },
};
}

Expand Down
34 changes: 14 additions & 20 deletions src/store/modules/recipe/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import produce from 'immer';

const INITIAL_STATE = {
count: 0,
recipes: [],
recipes: {},
recipeIds: [],
loading: false,
editing: {
item: null,
Expand All @@ -21,6 +22,7 @@ export default function user(state = INITIAL_STATE, action) {
case '@recipe/LOAD_RECIPES_SUCCESS': {
draft.recipes = action.payload.recipes;
draft.count = action.payload.count;
draft.recipeIds = action.payload.recipeIds;
draft.editing = { item: null, baseItems: [] };
draft.loading = false;
draft.refresh = false;
Expand All @@ -40,11 +42,13 @@ export default function user(state = INITIAL_STATE, action) {
break;
}
case '@recipe/STORE_RECIPE_SUCCESS': {
const recipes = [...draft.recipes, action.payload.recipe].sort((a, b) =>
a.name.localeCompare(b.name),
);
const recipes = {
...draft.recipes,
[action.payload.recipe.id]: action.payload.recipe,
};

draft.recipes = recipes;
draft.recipeIds = action.payload.sortedIds;
draft.count += 1;
draft.loading = false;
break;
Expand All @@ -55,20 +59,13 @@ export default function user(state = INITIAL_STATE, action) {
break;
}
case '@recipe/DELETE_RECIPE_SUCCESS': {
const idx = draft.recipes.findIndex(
recipe => recipe.id === action.payload.id,
);

if (idx >= 0) {
draft.recipes.splice(idx, 1);
draft.count -= 1;
}

delete draft.recipes[action.payload.id];
draft.recipeIds = action.payload.newIds;
draft.count -= 1;
draft.loading = false;
break;
}
case '@recipe/EDIT_RECIPE_ITEM': {
// const deepCopy = JSON.parse(JSON.stringify(action.payload.item));
draft.editing.item = action.payload.item;
break;
}
Expand Down Expand Up @@ -99,7 +96,8 @@ export default function user(state = INITIAL_STATE, action) {
}
case '@recipe/CLEAR_RECIPES_SUCCESS': {
draft.count = 0;
draft.recipes = [];
draft.recipes = {};
draft.recipeIds = [];
draft.loading = false;
break;
}
Expand All @@ -108,12 +106,8 @@ export default function user(state = INITIAL_STATE, action) {
break;
}
case '@recipe/RESET_RECIPE_PROGRESS_SUCCESS': {
const idx = draft.recipes.findIndex(
recipe => recipe.id === action.payload.id,
);

// Reset this recipe's unique leaves' progress
draft.recipes[idx].uniqueProgress = 0;
draft.recipes[action.payload.id].uniqueProgress = 0;

draft.loading = false;
break;
Expand Down
Loading

0 comments on commit 1365547

Please sign in to comment.