-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Slices are more better for reactivity than reacts useReducer.
- Loading branch information
1 parent
0e44689
commit 5d77c1f
Showing
14 changed files
with
491 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import { Card } from './Card'; | ||
|
||
export interface Group { | ||
readonly id: string; | ||
readonly cardIds: ReadonlySet<Card['id']>; | ||
id: string; | ||
cardIds: Set<Card['id']>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { PayloadAction } from '@reduxjs/toolkit'; | ||
import { Deck, DeckItem } from '../context/universe/Deck'; | ||
import { Universe } from './universeSlice'; | ||
|
||
export function createCards( | ||
state: Universe, | ||
{ | ||
payload, | ||
}: PayloadAction<{ | ||
targetDeckId: Deck['id']; | ||
targetIndex: number; | ||
names: string[]; | ||
}>, | ||
): void { | ||
const newCards = payload.names.map((name) => ({ | ||
id: crypto.randomUUID(), | ||
name, | ||
})); | ||
|
||
const deck = state.decks.find((deck) => deck.id === payload.targetDeckId); | ||
|
||
if (!deck) | ||
throw new Error( | ||
`Target deck ${payload.targetDeckId} not found when creating cards`, | ||
); | ||
|
||
deck.items.splice( | ||
payload.targetIndex, | ||
0, | ||
...newCards.map( | ||
(card): DeckItem => ({ | ||
type: 'card', | ||
cardId: card.id, | ||
}), | ||
), | ||
); | ||
|
||
state.cards.push(...newCards); | ||
} | ||
|
||
export function destroyCards( | ||
state: Universe, | ||
{ | ||
payload, | ||
}: PayloadAction<{ | ||
cardIds: string[]; | ||
}>, | ||
): void { | ||
const cardIds = new Set(payload.cardIds); | ||
|
||
for (const id of cardIds) { | ||
const card = state.cards.find((card) => card.id === id); | ||
|
||
if (!card) | ||
throw new Error(`Card ${id} not found when destroying cards`); | ||
|
||
cardIds.add(id); | ||
} | ||
|
||
// Remove all the cards from the global card list | ||
state.cards = state.cards.filter((card) => !cardIds.has(card.id)); | ||
|
||
// Remove the cards from the groups. If removed from a group, instances of that group need to be removed from decks | ||
const groupItemsRemovedById: Record<string, number> = {}; | ||
const emptyGroupIds = new Set<string>(); | ||
for (const group of state.groups) { | ||
const toRemove = group.cardIds.intersection(cardIds); | ||
|
||
if (toRemove.size > 0) { | ||
group.cardIds = group.cardIds.difference(toRemove); | ||
groupItemsRemovedById[group.id] = toRemove.size; | ||
if (group.cardIds.size === 0) { | ||
emptyGroupIds.add(group.id); | ||
} | ||
} | ||
} | ||
state.groups = state.groups.filter((group) => !emptyGroupIds.has(group.id)); | ||
|
||
// Remove the cards from the decks, and remove instances of groups that have also had cards removed | ||
for (const deck of state.decks) { | ||
deck.items = deck.items.filter((c) => { | ||
if (c.type === 'card' && cardIds.has(c.cardId)) return false; | ||
if (c.type === 'group') { | ||
if (groupItemsRemovedById[c.groupId] > 0) { | ||
groupItemsRemovedById[c.groupId]--; | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import { PayloadAction } from '@reduxjs/toolkit'; | ||
import { Card } from '../context/universe/Card'; | ||
import CardUtil from '../context/universe/CardUtil'; | ||
import { Deck, DeckItem } from '../context/universe/Deck'; | ||
import { Group } from '../context/universe/Group'; | ||
import { Universe } from './universeSlice'; | ||
|
||
export function createDeck( | ||
state: Universe, | ||
{ | ||
payload: { id }, | ||
}: PayloadAction<{ | ||
id: Deck['id']; | ||
}>, | ||
): void { | ||
if (state.decks.some((deck) => deck.id === id)) { | ||
throw new Error(`Deck ${id} already exists`); | ||
} | ||
|
||
state.decks.push({ | ||
id, | ||
items: [], | ||
}); | ||
} | ||
|
||
export function moveCard( | ||
state: Universe, | ||
{ | ||
payload: { fromDeckId, fromIndex, toDeckId, toIndex, count }, | ||
}: PayloadAction<{ | ||
fromDeckId: Deck['id']; | ||
fromIndex: number; | ||
toDeckId: Deck['id']; | ||
toIndex: number; | ||
count: number; | ||
}>, | ||
): void { | ||
const fromDeck = state.decks.find((deck) => deck.id === fromDeckId); | ||
|
||
if (!fromDeck) { | ||
throw new Error(`Deck ${fromDeckId} not found when moving card`); | ||
} | ||
|
||
const toDeck = state.decks.find((deck) => deck.id === toDeckId); | ||
|
||
if (!toDeck) { | ||
throw new Error(`Deck ${toDeckId} not found when moving card`); | ||
} | ||
|
||
const cards = fromDeck.items.splice(fromIndex, count); | ||
|
||
toDeck.items.splice(toIndex, 0, ...cards); | ||
} | ||
|
||
export function shuffleDeck( | ||
state: Universe, | ||
{ | ||
payload: { deckId }, | ||
}: PayloadAction<{ | ||
deckId: Deck['id']; | ||
}>, | ||
): void { | ||
const deck = state.decks.find((deck) => deck.id === deckId); | ||
|
||
if (!deck) { | ||
throw new Error(`Deck ${deckId} not found when shuffling`); | ||
} | ||
|
||
const uniqueCardNames = new Set( | ||
deck.items.flatMap((item) => { | ||
if (item.type === 'card') | ||
return CardUtil.getCardName(state, item.cardId); | ||
|
||
const group = state.groups.find( | ||
(group) => group.id === item.groupId, | ||
)!; | ||
return Array.from(group.cardIds).map((cardId) => | ||
CardUtil.getCardName(state, cardId), | ||
); | ||
}), | ||
); | ||
|
||
if (uniqueCardNames.size <= 0) { | ||
return; | ||
} | ||
|
||
const numberOfItemsFromEachGroup: Record<string, number> = {}; | ||
for (const item of deck.items) { | ||
if (item.type === 'group') { | ||
numberOfItemsFromEachGroup[item.groupId] = | ||
(numberOfItemsFromEachGroup[item.groupId] ?? 0) + 1; | ||
} | ||
} | ||
|
||
const allGroupsInThisDeckAreFullyInThisDeck = Array.from( | ||
Object.entries(numberOfItemsFromEachGroup), | ||
).every(([groupId, count]) => { | ||
const group = state.groups.find((group) => group.id === groupId); | ||
|
||
if (!group) return false; | ||
|
||
if (count === group.cardIds.size) return true; | ||
|
||
return false; | ||
}); | ||
|
||
if (!allGroupsInThisDeckAreFullyInThisDeck) { | ||
throw new Error( | ||
'Cannot shuffle deck with incomplete groups. Some items in this deck come from shuffle groups that have cards elsewhere, entanglement is not supported', | ||
); | ||
} | ||
|
||
const groupsToDelete = Object.keys(numberOfItemsFromEachGroup); | ||
|
||
const cardsInNewGroup: Card['id'][] = []; | ||
|
||
for (const item of deck.items) { | ||
if (item.type === 'card') { | ||
cardsInNewGroup.push(item.cardId); | ||
} | ||
} | ||
|
||
for (const groupId of groupsToDelete) { | ||
const group = state.groups.find((group) => group.id === groupId)!; | ||
|
||
cardsInNewGroup.push(...group.cardIds); | ||
} | ||
|
||
const newGroup: Group = { | ||
id: crypto.randomUUID(), | ||
cardIds: new Set(cardsInNewGroup), | ||
}; | ||
|
||
state.groups = [ | ||
...state.groups.filter((group) => !groupsToDelete.includes(group.id)), | ||
newGroup, | ||
]; | ||
|
||
deck.items = cardsInNewGroup.map( | ||
(): DeckItem => ({ | ||
type: 'group', | ||
groupId: newGroup.id, | ||
}), | ||
); | ||
} |
Oops, something went wrong.