Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add spells by class #1

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions components/AllCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@ import { useAppDispatch, useAppSelector } from "../stores/hooks";


import Card from "./Card";
import { createNewCard, setActiveCardCreator } from "../stores/thunks";
import { createNewCard, getClassSpellsThunk, setActiveCardCreator, removeAllCards } from "../stores/thunks";
import { useState } from "react";
import { PcClass } from "../utils/SpellApiService";
import PreloadedClassList from "./PreloadedClassList";

function AllCards() {
const cardsData = useAppSelector((state: RootState) => state.cards.list);
const dispatch = useAppDispatch();

const [classListOpen, setClassListOpen] = useState(false);

const openListOfClasses = () => {
setClassListOpen(true)
}

const cards = cardsData.map((c, index) => {
const selector = setActiveCardCreator(index);
Expand All @@ -23,8 +32,28 @@ function AllCards() {
return(
<>
<div>
<button className="print:hidden uppercase font-mono leading-6 px-3 rounded-md text-white bg-green-500 hover:bg-green-600" onClick={() => dispatch(createNewCard())}>add card</button>
<button className="print:hidden uppercase font-mono leading-6 px-3 ml-2 rounded-md bg-blue-700 hover:bg-blue-800 text-white" onClick={() => print()}>print cards</button>
<button className="print:hidden uppercase font-mono leading-6 px-3 rounded-md text-white bg-green-500 hover:bg-green-600"
onClick={() => dispatch(createNewCard())}>
add card
</button>

<button className="print:hidden uppercase font-mono leading-6 px-3 ml-2 rounded-md text-white bg-green-500 hover:bg-green-600"
onClick={() => openListOfClasses()}>
add SRD cards by class
</button>

<button className="print:hidden uppercase font-mono leading-6 px-3 ml-2 rounded-md bg-blue-700 hover:bg-blue-800 text-white"
onClick={() => print()}>
print cards
</button>

<button className="print:hidden uppercase font-mono leading-6 px-3 ml-2 rounded-md bg-red-700 hover:bg-red-800 text-white"
onClick={() => dispatch(removeAllCards())}>
remove all cards
</button>

{ classListOpen && <PreloadedClassList closeList={() => setClassListOpen(false)} getClassSpells={(c: PcClass, l: number) => dispatch(getClassSpellsThunk(c, l))} /> }

</div>
<div className="flex-wrap flex flex-grow">
{ cards }
Expand Down
9 changes: 6 additions & 3 deletions components/FormElements/SpellNameField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function SuggestedSpells({
allSrdSpells: SrdType[],
acceptSuggestion: Function,
}) {
let message = 'Start typing, and I\'ll look for your spell in the SRD database';
let message = <p className="text-xs">Start typing, and I&rsquo;ll look for your spell in the SRD database</p>;
let possibleSpells: SrdType[] = [];
if (input.length < 2) {
possibleSpells = [];
Expand All @@ -24,7 +24,10 @@ function SuggestedSpells({
}

if(possibleSpells.length === 0 && input.length >= 2) {
message = 'Custom spell!';
const searchQuery = input.toLowerCase().split(" ").join("+");
message = <p className="text-xs">
Custom spell! <a className="text-blue-700" href={`https://www.google.com/search?q=dnd+5e+${searchQuery}`} rel="noreferrer" target="_blank">Google &ldquo;dnd 5e {input}&rdquo;</a>
</p>
}

const select = (sp: SrdType) => {
Expand All @@ -42,7 +45,7 @@ function SuggestedSpells({

return (
<div className="block">
<p className="text-xs">{ message }</p>
{ message }

{ spellsList.length > 0 && (<div className="border border-b-0">
{ spellsList }
Expand Down
57 changes: 57 additions & 0 deletions components/PreloadedClassList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useState } from "react";
import { PcClass } from "../utils/SpellApiService";
import { buttonClasses } from "../utils/constants";

type PreloadedClassListProps = {
getClassSpells: Function,
closeList: Function
}

function PreloadedClassList({
getClassSpells,
closeList
}: PreloadedClassListProps) {
const [maxLevel, setMaxLevel] = useState(9);

const maxLevelUp = () => {
if(maxLevel == 9) {
return;
}

setMaxLevel(maxLevel + 1);
}

const maxLevelDown = () => {
if(maxLevel == 0) {
return;
}

setMaxLevel(maxLevel - 1);
}

const classes: PcClass[] = ['barbarian', 'bard', 'cleric', 'druid', 'fighter', 'monk', 'paladin', 'ranger', 'rogue', 'sorcerer', 'warlock', 'wizard']
return (
<div className="flex flex-wrap items-center print:hidden mb-2 p-3 pt-1 rounded-sm bg-blue-50">
<p className="text-sm uppercase tracking-wide w-full">Import SRD Spells</p>
<div className="order-3 self-start">
<button className="print:hidden uppercase font-mono text-xs leading-6 px-3 rounded-md bg-slate-300 hover:bg-slate-400" onClick={() => closeList()}>never mind</button>
</div>
<ul className="flex-grow flex flex-wrap max-w-2xl">
{ classes.map((c) =>
<li key={`${c}-spells`} className="mr-2">
<button onClick={() => getClassSpells(c, maxLevel)} className={buttonClasses}>{c}</button>
</li>
)}
</ul>

<div className="flex flex-grow flex-wrap md:justify-center pb-2 mt-2 md:-mt-3">
<p className="text-xs w-full md:text-center">Maximum Spell Level</p>
<button onClick={() => maxLevelDown()} className={buttonClasses}>-</button>
<p className="text-md mr-1 font-sans">{maxLevel}</p>
<button onClick={() => maxLevelUp()} className={buttonClasses}>+</button>
</div>
</div>
)
}

export default PreloadedClassList
4 changes: 2 additions & 2 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function App() {
useEffect(() => {
if(typeof window !== 'undefined') {
dispatch(initCards(JSON.parse(localStorage.getItem("cards") || "[]")))
}
}
});

const appDescription = "Generating printable PDFs of your DnD spell cards, with an SRD API and CSS! Wow I love acronyms.";
Expand All @@ -31,7 +31,7 @@ function App() {
}

return (
<div className="container mx-auto p-4 flex flex-col flex-grow">
<div className="container mx-auto p-4 print:p-0 flex flex-col flex-grow">
<Head>
<title>5E SPELL CARD PRINTABLE GENERATOR</title>
<meta property="og:title" content="5E SPELL CARD PRINTABLE GENERATOR" key="title" />
Expand Down
5 changes: 4 additions & 1 deletion stores/cardsReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ const cardsSlice = createSlice({
remove: (state, action: PayloadAction<number>) => {
state.list.splice(action.payload, 1);
},
removeAll: (state) => {
state.list = [];
},
edit: (state, action: PayloadAction<EditPayload>) => {
state.list[action.payload.index] = action.payload.card;
},
Expand All @@ -37,5 +40,5 @@ const cardsSlice = createSlice({
}
});

export const { add, remove, edit, initCards } = cardsSlice.actions;
export const { add, remove, edit, initCards, removeAll } = cardsSlice.actions;
export default cardsSlice.reducer;
4 changes: 2 additions & 2 deletions stores/localStorageMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createListenerMiddleware, isAnyOf } from "@reduxjs/toolkit";
import { add, edit, remove } from "./cardsReducer";
import { add, edit, remove, removeAll } from "./cardsReducer";
import { RootState } from "./rootReducer";

export const listenerMiddleware = createListenerMiddleware();

listenerMiddleware.startListening({
matcher: isAnyOf(add, remove, edit),
matcher: isAnyOf(add, remove, edit, removeAll),
effect: (action, listenerApi) =>
localStorage.setItem(
'cards',
Expand Down
28 changes: 26 additions & 2 deletions stores/thunks.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { EditorState } from "draft-js";
import { blankCard } from "../utils/constants";
import { SrdType, ValidRichTextKeys } from "../utils/models";
import SpellApiService from "../utils/SpellApiService";
import { add, edit } from "./cardsReducer";
import SpellApiService, { PcClass } from "../utils/SpellApiService";
import { add, edit, removeAll } from "./cardsReducer";
import { RootState } from "./rootReducer";
import { AppThunk } from "./store";
import { reset, set, setSpells, updateActiveCard } from "./uiStateReducer";
Expand Down Expand Up @@ -33,6 +33,14 @@ export const setActiveCardCreator = (newIndex: number): AppThunk => {
}
}

export const removeAllCards = (): AppThunk => {
return (dispatch) => {
console.log("hey!!!")
dispatch(removeAll());
dispatch(set(-1));
}
}

export const updateRichTextThunkCreator = (propName: ValidRichTextKeys, newState: EditorState): AppThunk => {
return (dispatch, getState) => {
const activeCard = getState().ui.activeCardData;
Expand All @@ -54,3 +62,19 @@ export const fetchSpells = (): AppThunk => {
dispatch(setSpells(spells.results));
}
}


export const getClassSpellsThunk = (c: PcClass, maxLevel: number): AppThunk => {
return async (dispatch) => {
const newData = await SpellApiService.getListByClass(c);

newData.results.map(async (spell) => {
const i = spell.index;
const fullSpell = await SpellApiService.get(i);

if(fullSpell.level <= maxLevel) {
dispatch(add(fullSpell));
}
});
}
}
7 changes: 7 additions & 0 deletions utils/SpellApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,19 @@ type SrdSpellsReponse = {
count: number
}

export type PcClass = 'barbarian' | 'bard' | 'cleric' | 'druid' | 'fighter' | 'monk' | 'paladin' | 'ranger' | 'rogue' | 'sorcerer' | 'warlock' | 'wizard'

const SpellApiService = {

getList: async (): Promise<SrdSpellsReponse> => {
const list = await fetch('https://www.dnd5eapi.co/api/spells');
return list.json();
},

getListByClass: async (className: PcClass): Promise<SrdSpellsReponse> => {
const list = await fetch('https://www.dnd5eapi.co/api/classes/' + className + '/spells');
return list.json();
},

convertDamagePerLevel: (apiResponse: Record<string, any>): Record<number, string> => {
if(!apiResponse["damage"] || !apiResponse["damage"]["damage_at_character_level"]) {
Expand Down