Skip to content

Commit

Permalink
Allow selecting Friends, Foes, and Banners
Browse files Browse the repository at this point in the history
There's not yet a page where that does anything.

Refactored the UpgradedBasicNemesisCardTile to a NemesisCardTile which can show any nemesis card, and can show the ability inline instead of requiring a modal.

Used the UniqueStarter tile even though relevant Friend and Foe cards aren't unique starters.
  • Loading branch information
Torgen authored and on3iro committed Dec 11, 2024
1 parent 2f0ff3b commit df5cac1
Show file tree
Hide file tree
Showing 86 changed files with 2,266 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/Redux/Store/Settings/Expansions/Banners/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const BANNERS_DB_KEY = 'banners-3.27'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createAction } from '@martin_hotell/rex-tils'

export const actions = {
noOp: () => createAction('@@REDUX_LOOP/ENFORCE_DEFAULT_HANDLING'),
}
4 changes: 4 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/content/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { actions } from './actions'
export { initialState, Reducer } from './reducer'
export { selectors } from './selectors'
export * from './types'
23 changes: 23 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/content/reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { LoopReducer } from 'redux-loop'

import AERData from 'aer-data/src/index'

import { Action, State } from './types'

export const initialState: State = {
ENG: AERData.normalizedData.ENG.banners,
FR: AERData.normalizedData.FR.banners,
PL: AERData.normalizedData.PL.banners,
DE: AERData.normalizedData.DE.banners,
}

export const Reducer: LoopReducer<State, Action> = (
state: State = initialState,
action: Action
) => {
switch (action.type) {
default: {
return state
}
}
}
20 changes: 20 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/content/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { BannersContentStateSlice } from './types'
import { createSelector } from 'reselect'

import { selectors as LanguageSelectors } from '../../Languages'
import { getContentByIdWithLanguageFallback } from '../../helpers'

const getContent = (state: BannersContentStateSlice) =>
state.Settings.Expansions.Banners.content

const getId = (_: unknown, props: { id: string }) => props.id

const getById = createSelector(
[LanguageSelectors.getLanguagesByExpansion, getContent, getId],
getContentByIdWithLanguageFallback
)

export const selectors = {
getContent,
getById,
}
28 changes: 28 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/content/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ActionsUnion } from '@martin_hotell/rex-tils'

import * as types from 'aer-types/types'

import { actions } from './actions'
import { LanguageKey } from '../../types'

type LanguageContent = { [id: string]: types.ICard }

export type State = Readonly<
{
[key in LanguageKey]: LanguageContent
}
>

export enum ActionTypes {}

export type Action = ActionsUnion<typeof actions>

export type BannersContentStateSlice = {
Settings: {
Expansions: {
Banners: {
content: State
}
}
}
}
5 changes: 5 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/ids/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createAction } from '@martin_hotell/rex-tils'

export const actions = {
noOp: () => createAction('@@REDUX_LOOP/ENFORCE_DEFAULT_HANDLING'),
}
4 changes: 4 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/ids/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { actions } from './actions'
export { initialState, Reducer } from './reducer'
export { selectors } from './selectors'
export * from './types'
18 changes: 18 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/ids/reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { LoopReducer } from 'redux-loop'

import AERData from 'aer-data/src/index'

import { Action, State } from './types'

export const initialState: State = AERData.normalizedData.ENG.bannerIds

export const Reducer: LoopReducer<State, Action> = (
state: State = initialState,
action: Action
) => {
switch (action.type) {
default: {
return state
}
}
}
7 changes: 7 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/ids/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { BannerIdsStateSlice } from './types'

const getIds = (state: BannerIdsStateSlice) => state.Settings.Expansions.Banners.ids

export const selectors = {
getIds,
}
19 changes: 19 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/ids/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ActionsUnion } from '@martin_hotell/rex-tils'

import { actions } from './actions'

export type State = string[]

export enum ActionTypes {}

export type Action = ActionsUnion<typeof actions>

export type BannerIdsStateSlice = {
Settings: {
Expansions: {
Banners: {
ids: string[]
}
}
}
}
190 changes: 190 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import { combineReducers, reduceReducers, loop, Cmd } from 'redux-loop'
import { createSelector } from 'reselect'
import { createAction, ActionsUnion } from '@martin_hotell/rex-tils'

import * as types from 'aer-types/types'

import {
getEntitiesByIdListWithLanguageFallback,
getContentByIdWithLanguageFallback,
} from '../helpers'
import { selectors as LanguageSelectors } from '../Languages'
import {
Action as ExpansionAction,
ActionTypes as ExpansionActionTypes,
} from '..'

import * as Content from './content'
import * as Selected from './selected'
import * as Ids from './ids'

import { setSelectedBannersToDB } from './selected/sideEffects'

///////////
// STATE //
///////////

export type State = {
selected: Selected.State
ids: Ids.State
content: Content.State
}

export const initialState: State = {
selected: Selected.initialState,
ids: Ids.initialState,
content: Content.initialState,
}

/////////////
// ACTIONS //
/////////////

export enum ActionTypes {
TOGGLE_ALL = 'Settings/Expansions/Banners/TOGGLE_ALL',
}

export const mainActions = {
toggleAll: (expansionId: string, toggleType: types.ToggleType) =>
createAction(ActionTypes.TOGGLE_ALL, {
expansionId,
toggleType,
}),
}

export type MainAction = ActionsUnion<typeof mainActions>

export const actions = {
selected: Selected.actions,
ids: Ids.actions,
content: Content.actions,
main: mainActions,
}

export type Action = Selected.Action | MainAction

/////////////
// REDUCER //
/////////////

export const Reducer = reduceReducers(
combineReducers<State>({
selected: Selected.Reducer,
ids: Ids.Reducer,
content: Content.Reducer,
}),
(state: State, action: Action | ExpansionAction) => {
switch (action.type) {
case ExpansionActionTypes.TOGGLE_ALL_EXPANSION_CONTENT:
case ActionTypes.TOGGLE_ALL: {
const { expansionId, toggleType } = action.payload

const allBannersOfExpansion = state.ids.filter(
(id) => state.content.ENG[id].expansion === expansionId
)

const newSelected = toggleType === 'deselect' ? [] : allBannersOfExpansion

const newSelectedBanners =
newSelected.length === 0
? state.selected.filter(
(id) => state.content.ENG[id].expansion !== expansionId
)
: [...new Set([...state.selected, ...newSelected])]

const newState = {
...state,
selected: newSelectedBanners,
}

return loop(
newState,
Cmd.run(setSelectedBannersToDB, {
args: [newState.selected],
successActionCreator: actions.selected.setToDBSuccessful,
failActionCreator: actions.selected.setToDBFailed,
})
)
}

default: {
return state
}
}
}
)

///////////////
// SELECTORS //
///////////////

// Primitive

const getExpansionId = (_: unknown, props: { expansionId: string }) =>
props.expansionId
const getIdList = (_: unknown, props: { bannerIds: string[] }) => props.bannerIds

// Memoized

const getBannerIdsByExpansionId = createSelector(
[Content.selectors.getContent, Ids.selectors.getIds, getExpansionId],
(content, ids, expansionId) =>
ids.filter((id) => content.ENG[id].expansion === expansionId)
)

const getBannersByExpansionId = createSelector(
[
Content.selectors.getContent,
getBannerIdsByExpansionId,
LanguageSelectors.getSelectedLanguageByExpansionId,
],
getEntitiesByIdListWithLanguageFallback
)

const getSelectedBanners = createSelector(
[
Content.selectors.getContent,
Selected.selectors.getSelected,
LanguageSelectors.getLanguagesByExpansion,
],
(content, selectedIds, languages) =>
selectedIds.map((id) =>
getContentByIdWithLanguageFallback(languages, content, id)
)
)

const getBannersByIdList = createSelector(
[
Content.selectors.getContent,
getIdList,
LanguageSelectors.getLanguagesByExpansion,
],
(content, idList, languages) =>
idList.map((id) =>
getContentByIdWithLanguageFallback(languages, content, id)
)
)

const getSelectedBannersByExpansionId = createSelector(
[getSelectedBanners, getBannersByExpansionId],
(selectedBannerIds, expansionBannerIds) => {
return selectedBannerIds.filter((id) => expansionBannerIds.includes(id))
}
)

const getAllBannersOfExpansionSelected = createSelector(
[getSelectedBannersByExpansionId, getBannersByExpansionId],
(selectedBannerIds, allBannerIds) =>
allBannerIds.every((id) => selectedBannerIds.includes(id))
)

export const selectors = {
selected: Selected.selectors,
ids: Ids.selectors,
content: Content.selectors,
getSelectedBanners,
getBannersByIdList,
getBannersByExpansionId,
getSelectedBannersByExpansionId,
getAllBannersOfExpansionSelected,
}
17 changes: 17 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/selected/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createAction } from '@martin_hotell/rex-tils'

import { ActionTypes, State } from './types'

export const actions = {
noOp: () => createAction('@@REDUX_LOOP/ENFORCE_DEFAULT_HANDLING'),
toggle: (id: string) => createAction(ActionTypes.TOGGLE_BANNER, id),
setToDB: (state: State) => createAction(ActionTypes.SET_TO_DB, state),
setToDBSuccessful: () => createAction(ActionTypes.SET_TO_DB_SUCCESS),
setToDBFailed: (error: Object) =>
createAction(ActionTypes.SET_TO_DB_FAILURE, error),
fetchFromDB: () => createAction(ActionTypes.FETCH_FROM_DB),
fetchFromDBSuccessful: (selectedCards: unknown) =>
createAction(ActionTypes.FETCH_FROM_DB_SUCCESS, selectedCards),
fetchFromDBFailed: (error: Object) =>
createAction(ActionTypes.FETCH_FROM_DB_FAILURE, error),
}
4 changes: 4 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/selected/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { actions } from './actions'
export { initialState, Reducer } from './reducer'
export { selectors } from './selectors'
export * from './types'
36 changes: 36 additions & 0 deletions src/Redux/Store/Settings/Expansions/Banners/selected/reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { LoopReducer } from 'redux-loop'

import AERData from 'aer-data/src/index'

import { Action, ActionTypes, State } from './types'

import * as reducerHelpers from './reducerHelpers'

export const initialState: State = AERData.normalizedData.ENG.bannerIds

export const Reducer: LoopReducer<State, Action> = (
state: State = initialState,
action: Action
) => {
switch (action.type) {
case ActionTypes.TOGGLE_BANNER: {
return reducerHelpers.toggle(state, action)
}

case ActionTypes.FETCH_FROM_DB: {
return reducerHelpers.fetchFromDb(state)
}

case ActionTypes.FETCH_FROM_DB_SUCCESS: {
return reducerHelpers.fetchFromDbSuccess(state, action)
}

case ActionTypes.FETCH_FROM_DB_FAILURE: {
return state
}

default: {
return state
}
}
}
Loading

0 comments on commit df5cac1

Please sign in to comment.