@@ -10,6 +10,7 @@ | |||
}, | |||
"dependencies": { | |||
"@emotion/core": "^10.0.35", | |||
"@emotion/styled": "^10.0.27", | |||
"classnames": "^2.2.6", | |||
"discoteque": "^1.1.7", | |||
"emotion": "^10.0.27", | |||
@@ -19,6 +20,7 @@ | |||
"react-redux": "^7.2.1", | |||
"react-toastify": "^6.0.8", | |||
"redux": "^4.0.5", | |||
"redux-thunk": "^2.3.0", | |||
"typescript": "^4.0.3", | |||
"webpack": "^4.44.2", | |||
"webpack-dev-server": "^3.11.0" | |||
@@ -0,0 +1,55 @@ | |||
import React, { FC } from 'react'; | |||
import styled from '@emotion/styled'; | |||
import { IActor } from 'discoteque/lib/engine/types'; | |||
import { colors } from 'discoteque/lib/assets/style-vars'; | |||
const CharacterListContainer = styled.div` | |||
height: 100%; | |||
width: 100%; | |||
overflow-x: auto; | |||
`; | |||
const CharacterListItem = styled.div` | |||
border-bottom: 1px solid black; | |||
padding: 15px; | |||
&:last-of-type { | |||
border-bottom: none; | |||
} | |||
& > img { | |||
width: 100px; | |||
margin-right: 15px; | |||
float: left; | |||
} | |||
& > h3 { | |||
margin: 0 0 15px 0; | |||
} | |||
& > p { | |||
margin: 0; | |||
} | |||
`; | |||
type Props = { | |||
actorDescriptions: Record<string, string>; | |||
actors: Record<string, IActor>; | |||
knownActors: string[]; | |||
} | |||
const Characters: FC<Props> = ({ actors, knownActors, actorDescriptions }) => { | |||
return ( | |||
<CharacterListContainer> | |||
{knownActors.map(actorId => { | |||
const actor = actors[actorId]; | |||
const desc = actorDescriptions[actorId]; | |||
return ( | |||
<CharacterListItem> | |||
{actor.image && (<img src={actor.image} />)} | |||
<h3>{actor.name}</h3> | |||
{desc && <p>{desc}</p>} | |||
</CharacterListItem> | |||
); | |||
})} | |||
</CharacterListContainer> | |||
) | |||
}; | |||
export default Characters; |
@@ -1,5 +1,5 @@ | |||
import React, { FC, useState } from 'react'; | |||
import { useDispatch } from 'react-redux'; | |||
import { useDispatch, useSelector } from 'react-redux'; | |||
import SkillTree from 'discoteque/lib/components/SkillTree'; | |||
@@ -8,28 +8,68 @@ type Tabs = 'tasks' | 'characters' | 'skills'; | |||
import cn from 'classnames'; | |||
import * as styles from './styles'; | |||
import { setShowCustom } from 'discoteque/lib/engine/lib/store'; | |||
import TaskList from '../Tasks'; | |||
import taskInfo from '@/lib/tasks'; | |||
import styled from '@emotion/styled'; | |||
import Characters from '../Characters'; | |||
import { NodeMap, IActor } from 'discoteque/lib/engine/types'; | |||
import { stateSelector, setMenuTab, menuTabSelector } from '@/lib/store'; | |||
const TabMenu: FC = () => { | |||
const TopMenuContainer = styled.div` | |||
display: flex; | |||
flex-direction: row; | |||
border-bottom: 2px solid black; | |||
height: 40px; | |||
` | |||
const actorDescriptions = { | |||
char_helper: 'A very friendly fellow! Lover of helping others and very absurd guestions', | |||
} | |||
type TabMenuProps = { | |||
nodeMap: NodeMap<any, any, any>; | |||
} | |||
const TabMenu: FC<TabMenuProps> = ({ nodeMap }) => { | |||
const dispatch = useDispatch(); | |||
const [openTab, setOpenTab] = useState<Tabs>('tasks'); | |||
const openTasks = () => setOpenTab('tasks'); | |||
const openSkills = () => setOpenTab('skills'); | |||
const openChars = () => setOpenTab('characters'); | |||
const state = useSelector(stateSelector); | |||
const openTab = useSelector(menuTabSelector); | |||
const setTab = (tabName: string) => () => dispatch(setMenuTab(tabName)); | |||
const openTasks = setTab('tasks'); | |||
const openSkills = setTab('skills'); | |||
const openChars = setTab('characters'); | |||
const closeMenu = () => dispatch(setShowCustom(false)); | |||
const actors = { | |||
'char_helper': nodeMap['char_helper'] as IActor, | |||
} | |||
return ( | |||
<div className={styles.menuContainer}> | |||
<ul className={styles.menuTabList}> | |||
<li className={styles.menuClose} onClick={closeMenu}>X</li> | |||
<li className={cn({ [styles.active]: openTab === 'tasks' })} onClick={openTasks}>Tasks</li> | |||
<li className={cn({ [styles.active]: openTab === 'skills' })} onClick={openSkills}>Skills</li> | |||
<li className={cn({ [styles.active]: openTab === 'characters' })} onClick={openChars}>Characters</li> | |||
</ul> | |||
<TopMenuContainer> | |||
<a className={styles.menuClose} onClick={closeMenu}>X</a> | |||
<ul className={styles.menuTabList}> | |||
<li className={cn({ [styles.active]: openTab === 'skills' })} onClick={openSkills}><a>Skills</a></li> | |||
<li className={cn({ [styles.active]: openTab === 'tasks' })} onClick={openTasks}><a>Tasks</a></li> | |||
<li className={cn({ [styles.active]: openTab === 'characters' })} onClick={openChars}><a>Characters</a></li> | |||
</ul> | |||
</TopMenuContainer> | |||
{openTab === 'skills' && ( | |||
<SkillTree /> | |||
)} | |||
{openTab === 'tasks' && ( | |||
<TaskList taskList={state.db.tasks} taskInfo={taskInfo} /> | |||
)} | |||
{openTab === 'characters' && ( | |||
<Characters | |||
actors={actors} | |||
knownActors={state.db.characters} | |||
actorDescriptions={actorDescriptions} | |||
/> | |||
)} | |||
</div> | |||
); | |||
} | |||
@@ -15,11 +15,12 @@ export const menuTabList = css` | |||
list-style: none; | |||
padding: 0; | |||
margin: 0; | |||
overflow-y: auto; | |||
display: flex; | |||
flex-direction: row; | |||
justify-content: space-betwee; | |||
box-sizing: border-box; | |||
border-bottom: 2px dashed ${colors.MainBgColorDark}; | |||
width: 100%; | |||
& > li { | |||
width: 100%; | |||
text-align: center; | |||
@@ -35,14 +36,19 @@ export const menuTabList = css` | |||
`; | |||
export const menuClose = css` | |||
flex-basis: 40px; | |||
max-width: 40px !important; | |||
padding: 0px !important; | |||
height: 40px !important; | |||
min-height: 40px !important; | |||
line-height: 40px; | |||
font-size: 40px; | |||
box-sizing: border-box; | |||
border-right: 1px solid ${colors.MainBgColorDark}; | |||
box-sizing: content-box; | |||
border-right: 2px solid ${colors.MainBgColor}; | |||
text-align: center; | |||
cursor: pointer; | |||
color: ${colors.MainBgColor}; | |||
background-color: ${colors.MainBgColorDark}; | |||
`; | |||
export const active = "active"; |
@@ -0,0 +1,91 @@ | |||
import React, { FC } from "react"; | |||
import styled from '@emotion/styled'; | |||
import { css } from "emotion"; | |||
export type Task = { | |||
id: string; | |||
stages: number[]; | |||
stagesComplete: number[]; | |||
} | |||
export type TaskInfo<KT extends string = string> = Record<KT, { | |||
name: string; | |||
description?: string; | |||
stages: string[]; | |||
}>; | |||
type Props = { | |||
taskList: Task[], | |||
taskInfo: TaskInfo, | |||
} | |||
const TaskListContainer = styled.div` | |||
overflow-x: auto; | |||
height: 100%; | |||
`; | |||
const Task = styled.div` | |||
padding: 15px; | |||
& > i { | |||
margin: 0; | |||
margin-bottom: 15px; | |||
display: inline-block; | |||
} | |||
& > h3 { | |||
margin: 0; | |||
} | |||
`; | |||
const TaskStages = styled.ul` | |||
list-style: none; | |||
margin: 0; | |||
padding: 0; | |||
& > li::before { | |||
content: '—'; | |||
margin-right: 5px; | |||
} | |||
`; | |||
const taskStageComplete = css` | |||
margin-bottom: 5px; | |||
text-decoration: line-through; | |||
`; | |||
const TaskStage = styled.li` | |||
margin-bottom: 5px; | |||
&::before { | |||
content: ''; | |||
} | |||
` | |||
const TaskList: FC<Props> = ({ taskList, taskInfo }) => { | |||
return ( | |||
<TaskListContainer> | |||
{taskList.map(task => ( | |||
<Task key={task.id}> | |||
<h3>{taskInfo[task.id].name}</h3> | |||
{taskInfo[task.id].description && ( | |||
<i>{taskInfo[task.id].description}</i> | |||
)} | |||
<TaskStages> | |||
{task.stages.map(stage => ( | |||
<TaskStage | |||
key={stage} | |||
className={ | |||
(task.stagesComplete || []).includes(stage) | |||
? taskStageComplete | |||
: undefined | |||
} | |||
> | |||
{taskInfo[task.id].stages[stage]} | |||
</TaskStage> | |||
))} | |||
</TaskStages> | |||
</Task> | |||
))} | |||
</TaskListContainer> | |||
); | |||
} | |||
export default TaskList; |
@@ -1,6 +1,6 @@ | |||
import React from 'react'; | |||
import { IGameState, setState, reducer } from '@/lib/store'; | |||
import { IGameState, setState, reducer, addKnownCharacter, setTask } from '@/lib/store'; | |||
import { INode, IActor, EngineConfig, ILineOption, ILocation, RendererFN, ToolbarOptionsFN } from 'discoteque/lib/engine/types'; | |||
import { setShowCustom } from 'discoteque/lib/engine/lib/store'; | |||
@@ -15,8 +15,12 @@ const nodes: INode<IGameState>[] = [ | |||
kind: 'node', | |||
next: 'pre_choice', | |||
lines: [ | |||
{ actorId: 'char_helper', text: "Hi!" }, | |||
(_, _g, dispatch) => dispatch(lockSkills()) && null, | |||
{ actorId: 'char_helper', text: "Hi!" } , | |||
(_, _g, dispatch) => { | |||
dispatch(lockSkills()); | |||
dispatch(addKnownCharacter('char_helper')(dispatch)); | |||
return { actorId: 'char_helper', text: 'I\'m Helper! I help people play this demo. (Not really...)' }; | |||
}, | |||
{ actorId: 'char_helper', text: "Lets's start by advancing some lines!" }, | |||
{ actorId: 'char_helper', text: "This should be easy enough for you!" }, | |||
{ actorId: 'char_helper', text: "See?" }, | |||
@@ -29,6 +33,8 @@ const nodes: INode<IGameState>[] = [ | |||
{ actorId: 'char_helper', text: "You could also try going outside." }, | |||
({ skillPoints }, _, dispatch) => { | |||
awardSkill(dispatch, skillPoints); | |||
dispatch(setTask("choice", { stages: [0] })(dispatch)); | |||
dispatch(setTask("outside", { stages: [0] })(dispatch)); | |||
return { actorId: 'char_helper', text: "By the way, here's a skill point just for you!" } | |||
}, | |||
{ actorId: 'char_helper', text: "You can spend it using \"Skills\" menu in \"Options\", in the upper left corner! " }, | |||
@@ -256,6 +262,12 @@ const nodes: INode<IGameState>[] = [ | |||
// { actorId: 'char_helper', text: '' }, | |||
const actors: IActor<IGameState>[] = [ | |||
{ 'id': 'char_helper', kind: 'actor', name: 'Helper', image: require('@/assets/images/user.png').default, lines: [ | |||
(_, { haveTalkedToHelper }, dispatch) => { | |||
if (!haveTalkedToHelper) { | |||
dispatch(setState({ haveTalkedToHelper: true })); | |||
dispatch(setTask('outside', { stagesComplete: [0, 1, 2] })(dispatch)); | |||
} | |||
}, | |||
{ actorId: 'char_helper', text: 'Back again, huh?' }, | |||
{ actorId: 'char_helper', text: 'Then I guess you\'re ready to pick right from left now.', }, | |||
], next: 'dialogue_helper' }, | |||
@@ -264,8 +276,11 @@ const actors: IActor<IGameState>[] = [ | |||
const locations: ILocation<IGameState>[] = [ | |||
{ 'id': 'outside', kind: 'location', name: 'Great Outdoors', lines: [ | |||
(_, _gameState, dispatch) => { | |||
dispatch(setState({ beenOutside: true })); | |||
(_, { beenOutside }, dispatch) => { | |||
if (!beenOutside) { | |||
dispatch(setState({ beenOutside: true })); | |||
dispatch(setTask("outside", { stages: [0, 1], stagesComplete: [0] })(dispatch)); | |||
} | |||
return { text: 'You are standing in an open field, west of house.' }; | |||
}, | |||
{ text: 'The sea of green extends into all directions, as far as your eye can see.' }, | |||
@@ -275,6 +290,12 @@ const locations: ILocation<IGameState>[] = [ | |||
] }, | |||
] }, | |||
{ 'id': 'inside', kind: 'location', name: 'Inside House', lines: [ | |||
(_, { beenInside }, dispatch) => { | |||
if (!beenInside) { | |||
dispatch(setState({ beenInside: true })); | |||
dispatch(setTask('outside', { stages: [0, 1, 2], stagesComplete: [0, 1] })(dispatch)); | |||
} | |||
}, | |||
{ text: 'You are standing inside a small wooden house. The setup feels familiar... ' }, | |||
{ text: 'You almost expect there to be a trapdoor to a great underground empire.' }, | |||
{ text: 'You can see Helper here as well. They are standing in the corner, gesturing you to come and talk to them.' }, | |||
@@ -308,9 +329,10 @@ const locations: ILocation<IGameState>[] = [ | |||
interface IGameConfig extends EngineConfig<IGameState, undefined, GameSkills> { | |||
} | |||
const renderFn: RendererFN<IGameState, undefined, GameSkills> = () => { | |||
const renderFn: RendererFN<IGameState, undefined, GameSkills> = (engine) => { | |||
const nodeMap = engine.config.nodes.reduce((acc, node) => ({ ...acc, [node.id]: node }), {}); | |||
return ( | |||
<Menu /> | |||
<Menu nodeMap={nodeMap} /> | |||
); | |||
}; | |||
@@ -1,36 +1,78 @@ | |||
import * as R from 'ramda'; | |||
import { createStore, Action } from 'redux'; | |||
import { createStore, Action, Dispatch } from 'redux'; | |||
import { ILine } from 'discoteque/lib/engine/types'; | |||
import { Task } from '@/components/Tasks'; | |||
import { toast } from 'discoteque/lib/engine/lib/utils'; | |||
import { setShowCustom } from 'discoteque/lib/engine/lib/store'; | |||
export interface IGameState { | |||
visitedLeft: number; | |||
askedAboutChoice: boolean; | |||
visitedRight: number; | |||
beenOutside: boolean; | |||
lookedAtTrapdoor: boolean, | |||
talkedAboutTrapdoor: boolean, | |||
readMailbox: boolean, | |||
beenInside: boolean; | |||
haveTalkedToHelper: boolean; | |||
lookedAtTrapdoor: boolean; | |||
talkedAboutTrapdoor: boolean; | |||
readMailbox: boolean; | |||
ui: { menuTab: string } | |||
db: { | |||
characters: string[], | |||
tasks: Task[], | |||
} | |||
backlog: ILine<IGameState>[]; | |||
} | |||
export interface IGameStateAction extends Action { | |||
type: 'PATCH-GAME' | 'INIT-GAME' | 'RESET-GAME'; | |||
type: 'INIT' | 'PATCH-GAME' | 'INIT-GAME' | 'RESET-GAME' | 'SET-TASK' | 'ADD-KNOWN-CHARACTER' | 'SET-MENU-TAB'; | |||
state?: IGameState; | |||
tab?: string; | |||
task?: { | |||
id: string; | |||
data: Partial<Task>; | |||
} | |||
character?: string; | |||
} | |||
export const setState = (state: object): IGameStateAction => ({ type: 'PATCH-GAME', state: (state as IGameState) }); | |||
export const initState = (state: IGameState): IGameStateAction => ({ type: 'INIT-GAME', state }); | |||
export const resetState = (): IGameStateAction => ({ type: 'RESET-GAME' }) | |||
export const setMenuTab = (tab: string): IGameStateAction => ({ type: 'SET-MENU-TAB', tab }); | |||
export const setTask = (id: string, data: Partial<Task>) => (dispatch: Dispatch): IGameStateAction => { | |||
toast.info("Task list updated", { onClick: () => { | |||
dispatch(setMenuTab('tasks')); | |||
dispatch(setShowCustom(true)); | |||
} }); | |||
return { | |||
type: 'SET-TASK', task: { id, data }, | |||
}; | |||
}; | |||
export const addKnownCharacter = (character: string) => (dispatch: Dispatch): IGameStateAction => { | |||
toast.info("Character database updated", { onClick: () => { | |||
dispatch(setMenuTab('characters')); | |||
dispatch(setShowCustom(true)); | |||
} }); | |||
return { | |||
type: 'ADD-KNOWN-CHARACTER', character, | |||
}; | |||
}; | |||
const defaultState: IGameState = { | |||
visitedLeft: 0, | |||
visitedRight: 0, | |||
askedAboutChoice: false, | |||
beenOutside: false, | |||
beenInside: false, | |||
haveTalkedToHelper: false, | |||
lookedAtTrapdoor: false, | |||
talkedAboutTrapdoor: false, | |||
readMailbox: false, | |||
ui: { menuTab: 'skills' }, | |||
db: { | |||
characters: [], | |||
tasks: [], | |||
}, | |||
backlog: [], | |||
}; | |||
@@ -40,13 +82,40 @@ export const reducer = (state: IGameState = defaultState, action: IGameStateActi | |||
return ({ ...state, ...action.state }); | |||
case 'INIT-GAME': | |||
return action.state || state; | |||
case 'INIT': | |||
return (action as any).data.gameState || state; | |||
case 'RESET-GAME': | |||
return defaultState; | |||
default: | |||
return state; | |||
case 'SET-TASK': | |||
if (action.task) { | |||
const newTasks = [ | |||
R.mergeDeepRight<Partial<Task>, Partial<Task>>( | |||
state.db.tasks.find(task => task.id === action.task?.id) || { id: action.task.id }, | |||
action.task.data, | |||
), | |||
...state.db.tasks.filter(task => task.id !== action.task?.id), | |||
] as Task[]; | |||
const statePatch: Partial<IGameState> = { db: { ...state.db, tasks: newTasks } }; | |||
return R.mergeDeepRight<IGameState, Partial<IGameState>>( | |||
state, | |||
statePatch, | |||
) as IGameState; | |||
} | |||
case 'ADD-KNOWN-CHARACTER': | |||
return R.mergeDeepRight(state, { | |||
db: { characters: [ ...state.db.characters, action.character ].filter(R.identity) } | |||
}) as IGameState; | |||
case 'SET-MENU-TAB': | |||
if (action.tab) { | |||
return R.mergeDeepRight(state, { | |||
ui: { menuTab: action.tab } | |||
}); | |||
} | |||
} | |||
return state; | |||
} | |||
export const stateSelector: (state: object) => IGameState = R.propOr(defaultState, 'game'); | |||
export const menuTabSelector: (state: object) => string = R.pathOr(defaultState.ui.menuTab, ['game', 'ui', 'menuTab']); | |||
export default createStore(reducer); |
@@ -0,0 +1,29 @@ | |||
import { TaskInfo } from "@/components/Tasks"; | |||
const tasks: TaskInfo = { | |||
trapdoor: { | |||
name: "Find the trap door", | |||
description: "\"You're here to find a famous trap door leading to adventure and treasure!\"", | |||
stages: [ | |||
"Find the trap door", | |||
] | |||
}, | |||
choice: { | |||
name: "Make a choice", | |||
description: "\"Helper wants me to pick right form left. I should do that probably... Or maybe hold off for a moment.\"", | |||
stages: [ | |||
"Make the choice", | |||
], | |||
}, | |||
outside: { | |||
name: "Go outside", | |||
description: "\"Helper mentioned Outside. It's a good idea to check it out.\"", | |||
stages: [ | |||
"Go outside", | |||
"Check the house", | |||
"Talk to helper" | |||
], | |||
} | |||
} | |||
export default tasks; |
@@ -150,7 +150,7 @@ | |||
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" | |||
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== | |||
"@emotion/is-prop-valid@^0.8.8": | |||
"@emotion/is-prop-valid@0.8.8", "@emotion/is-prop-valid@^0.8.8": | |||
version "0.8.8" | |||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" | |||
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== | |||
@@ -178,6 +178,24 @@ | |||
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5" | |||
integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA== | |||
"@emotion/styled-base@^10.0.27": | |||
version "10.0.31" | |||
resolved "https://registry.yarnpkg.com/@emotion/styled-base/-/styled-base-10.0.31.tgz#940957ee0aa15c6974adc7d494ff19765a2f742a" | |||
integrity sha512-wTOE1NcXmqMWlyrtwdkqg87Mu6Rj1MaukEoEmEkHirO5IoHDJ8LgCQL4MjJODgxWxXibGR3opGp1p7YvkNEdXQ== | |||
dependencies: | |||
"@babel/runtime" "^7.5.5" | |||
"@emotion/is-prop-valid" "0.8.8" | |||
"@emotion/serialize" "^0.11.15" | |||
"@emotion/utils" "0.11.3" | |||
"@emotion/styled@^10.0.27": | |||
version "10.0.27" | |||
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.27.tgz#12cb67e91f7ad7431e1875b1d83a94b814133eaf" | |||
integrity sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q== | |||
dependencies: | |||
"@emotion/styled-base" "^10.0.27" | |||
babel-plugin-emotion "^10.0.27" | |||
"@emotion/stylis@0.8.5", "@emotion/stylis@^0.8.4": | |||
version "0.8.5" | |||
resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" | |||
@@ -4073,6 +4091,11 @@ readdirp@~3.4.0: | |||
dependencies: | |||
picomatch "^2.2.1" | |||
redux-thunk@^2.3.0: | |||
version "2.3.0" | |||
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" | |||
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== | |||
redux@^4.0.0, redux@^4.0.5: | |||
version "4.0.5" | |||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" | |||
@@ -177,6 +177,22 @@ | |||
sha1 = "894374bea39ec30f489bbfc3438192b9774d32e5"; | |||
}; | |||
} | |||
{ | |||
name = "_emotion_styled_base___styled_base_10.0.31.tgz"; | |||
path = fetchurl { | |||
name = "_emotion_styled_base___styled_base_10.0.31.tgz"; | |||
url = "https://registry.yarnpkg.com/@emotion/styled-base/-/styled-base-10.0.31.tgz"; | |||
sha1 = "940957ee0aa15c6974adc7d494ff19765a2f742a"; | |||
}; | |||
} | |||
{ | |||
name = "_emotion_styled___styled_10.0.27.tgz"; | |||
path = fetchurl { | |||
name = "_emotion_styled___styled_10.0.27.tgz"; | |||
url = "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.27.tgz"; | |||
sha1 = "12cb67e91f7ad7431e1875b1d83a94b814133eaf"; | |||
}; | |||
} | |||
{ | |||
name = "_emotion_stylis___stylis_0.8.5.tgz"; | |||
path = fetchurl { | |||
@@ -4505,6 +4521,14 @@ | |||
sha1 = "9fdccdf9e9155805449221ac645e8303ab5b9ada"; | |||
}; | |||
} | |||
{ | |||
name = "redux_thunk___redux_thunk_2.3.0.tgz"; | |||
path = fetchurl { | |||
name = "redux_thunk___redux_thunk_2.3.0.tgz"; | |||
url = "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz"; | |||
sha1 = "51c2c19a185ed5187aaa9a2d08b666d0d6467622"; | |||
}; | |||
} | |||
{ | |||
name = "redux___redux_4.0.5.tgz"; | |||
path = fetchurl { | |||