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

The (Editor and Collection) Merge #108

Merged
merged 52 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
cc89c49
factored out components used in editor and collection. preparing to r…
eviterin Jan 19, 2024
1a23a29
Moved more save and cancel logic from editor to collection
eviterin Jan 19, 2024
6dfca99
Migrated save and load functionality from editor.
eviterin Jan 19, 2024
d66d902
Deleted 'editor.tsx'
eviterin Jan 19, 2024
69ef672
Renamed components for sanity
eviterin Jan 19, 2024
0ff3fc6
Grouped state variables by their function
eviterin Jan 19, 2024
a368f86
refactored variable names
eviterin Jan 19, 2024
e711cea
consistent css
eviterin Jan 19, 2024
d2f0de7
Fixed all warnings with eslint and removed semicolons.
eviterin Jan 22, 2024
b6cb6a3
Renamed dir 'editor' to 'collection'
eviterin Jan 23, 2024
67636a1
renamed import to reflect directory name update
eviterin Jan 23, 2024
e7fa296
Allow player to save empty deck
eviterin Jan 23, 2024
c85e050
Component wrapping 'Link' added to carry the '?index=' parameter used…
eviterin Jan 23, 2024
da488da
Removed unused css
eviterin Jan 24, 2024
9fd6a0b
renamed queryParamLink to just link.
eviterin Jan 27, 2024
36e70d5
updated comment on how to carry over the index query param
eviterin Jan 27, 2024
05d0817
minor typo
eviterin Jan 27, 2024
8b21444
minor typo
eviterin Jan 27, 2024
d4f508b
file renames and import updates
eviterin Jan 27, 2024
109500a
readded the props to to keep css.
eviterin Jan 27, 2024
168087a
added deck name validity check for just spaces
eviterin Jan 27, 2024
3080773
added trim and set deck name to be valid as an initial state
eviterin Jan 27, 2024
b1ce4ba
removed unneeded default props
eviterin Jan 27, 2024
de6fc70
Added custom tailwind box shadow
eviterin Jan 27, 2024
474592a
Dynamic card resize in collectionDisplay as per Norswap's recipe
eviterin Jan 27, 2024
0c01ce5
remove semicolons
eviterin Jan 28, 2024
f431eba
extended FablePage type for the Collection Component props ot prevent…
eviterin Jan 28, 2024
c141bd0
decks stored in collection instead of _app
eviterin Jan 28, 2024
3d2223b
use Deck type instead of card[]
eviterin Jan 28, 2024
99c3b1e
made explicit types
eviterin Jan 28, 2024
46956eb
made sure the index param stays
eviterin Jan 30, 2024
102fc6d
corrected error message
eviterin Jan 28, 2024
634a867
add queryParamLink
eviterin Feb 7, 2024
f029ccd
remove extra import
eviterin Feb 7, 2024
a270ef7
better card art selection
eviterin Feb 20, 2024
2661a57
Removed redundant comments
eviterin Feb 20, 2024
70c9423
deck names...
eviterin Feb 20, 2024
7705e70
Use our own button components and moved them down to their own row
eviterin Feb 20, 2024
aa8b578
flex the deck panel
eviterin Feb 20, 2024
f859439
made text readable by humans
eviterin Feb 20, 2024
9a8eaf9
use our own buttons
eviterin Feb 20, 2024
345996f
Made deck name buttons scale to daddy component width
eviterin Feb 20, 2024
3294eab
fixes
eviterin Feb 20, 2024
661c69f
add .vs and .vscode to gitignore
eviterin Jan 23, 2024
1c06d68
removed fallback art for cards
eviterin Feb 21, 2024
3a83806
quick fix starting index is 1
eviterin Feb 22, 2024
36e35ae
Improved flexing the deck panel
eviterin Feb 22, 2024
42f25f7
Fix bug where deck was not editable
eviterin Feb 26, 2024
928aded
make check
norswap Feb 27, 2024
60aff3f
Deck display no longer crashes if the player does not own any decks
eviterin Feb 27, 2024
3cf1f44
Removed unnessecarily complex props from collection and resolved a bu…
eviterin Feb 27, 2024
0f1f0aa
Removed unused imports
eviterin Feb 27, 2024
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Environment variables
*.env
.vscode
.vs

# MacOS
.DS_Store
Expand Down
Empty file added packages/webapp/.vs/slnx.sqlite
Empty file.
4 changes: 4 additions & 0 deletions packages/webapp/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"javascript.validate.enable": false,
"typescript.validate.enable": false
}
2 changes: 1 addition & 1 deletion packages/webapp/src/actions/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export function defaultContractWriteErrorHandling(err: ContractWriteError): fals

setError({
title: "Contract execution error",
message: `Transaction reverted (${err.args.functionName}) ${signatureMsg}.`
message: `Transaction reverted (${err.args.functionName}) ${signatureMsg}. `
+ `Please report to ${GIT_ISSUES}`,
buttons: [DISMISS_BUTTON]
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react'
import Image from 'next/image'
import { Card } from 'src/store/types'
import { MintDeckModal } from 'src/components/modals/mintDeckModal'
import { testCards } from 'src/utils/card-list'

interface CardCollectionDisplayProps {
cards: Card[]
isHydrated: boolean
setSelectedCard: (card: Card | null) => void
onCardToggle: (card: Card) => void
selectedCards: Card[]
isEditing: boolean
}

const CardCollectionDisplay: React.FC<CardCollectionDisplayProps> = ({ cards, isHydrated, setSelectedCard, selectedCards, onCardToggle, isEditing }) => {
return (
<>
<div className="col-span-7 flex rounded-xl border overflow-y-auto">
{isHydrated && cards.length === 0 && (
<div className="flex flex-row w-full justify-center items-center">
<MintDeckModal/>
</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whenever clicking some of the filter items in the left panel, this is shown. Clearly not what we want (because the filtered set size is 0).

The condition here should be changed to display this modal only if the full card collection (not the filtered one) is empty.

Btw, I want to refactor the left panel according to this later: #93 and remove these filter buttons. But I think it's good to keep the generic filtering ability. This (refactor left panel) is for a further PR!

)}

{isHydrated && cards.length > 0 && (
<div className="flex flex-wrap justify-around overflow-y-auto pb-4">
{cards.map((card, index) => (
<div
key={card.id}
className={`m-4 bg-slate-900/50 ${
selectedCards.some(c => c.id === card.id) ? 'shadow-highlight shadow-orange-300' : ''
} hover:bg-slate-800 rounded-lg p-4 border-4 border-slate-900 grow w-[220px] max-w-[330px]`}
onMouseEnter={() => setSelectedCard(card)}
onClick={() => {
if (isEditing) {
onCardToggle(card)
}
}}
>
<Image className="aspect-square" src={testCards.find(tc => Number(tc.id) === index + 1)?.image || ""} alt={card.lore.name} width={256} height={256} />
<div className="text-center">{card.lore.name}</div>
<div className="flex items-end justify-between p-2 relative">
<div className="flex items-center justify-center h-8 w-8 rounded-full bg-yellow-400 text-gray-900 font-bold text-lg absolute bottom-[-16px]">{card.stats.attack}</div>
<div className="flex items-center justify-center h-8 w-8 rounded-full bg-red-600 text-gray-900 font-bold text-lg absolute bottom-[-16px] right-3">{card.stats.defense}</div>
</div>
</div>
))}
</div>
)}
</div>
</>
)
}

export default CardCollectionDisplay
36 changes: 36 additions & 0 deletions packages/webapp/src/components/collection/deckList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react'
import Link from "src/components/link"
import { Deck } from 'src/store/types'
import { Button } from "src/components/ui/button"

interface DeckCollectionDisplayProps {
decks: Deck[]
onDeckSelect: (deckID: number) => void
}

const DeckCollectionDisplay: React.FC<DeckCollectionDisplayProps> = ({ decks, onDeckSelect }) => {
return (
<div className="w-full flex flex-col items-center p-3">
{/* New Deck Button */}
<div>
<Button variant="secondary" className="border-2 border-yellow-500 normal-case hover:scale-105 font-fable text-xl hover:border-yellow-400">
<Link href={"/collection?newDeck=true"}>
New Deck →
</Link>
</Button>
</div>

{/* Deck Buttons */}
{decks.map((deck, deckID) => (
<Button variant="secondary" width="full" className="border-2 border-yellow-500 normal-case hover:scale-105 font-fable text-xl hover:border-yellow-400"
key={deckID}
onClick={() => onDeckSelect(deckID)}
>
{deck.name}
</Button>
))}
</div>
)
}

export default DeckCollectionDisplay
88 changes: 88 additions & 0 deletions packages/webapp/src/components/collection/deckPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { useState } from 'react'
import { Deck, Card } from 'src/store/types'
import Image from 'next/image'
import { testCards } from 'src/utils/card-list'
import { Button } from "src/components/ui/button"

interface DeckConstructionPanelProps {
deck: Deck
selectedCards: Card[]
onCardSelect: (card: Card) => void
onSave: (deck: Deck) => void
onCancel: () => void
}


const DeckConstructionPanel : React.FC<DeckConstructionPanelProps> = ({ deck, selectedCards = [], onCardSelect, onSave, onCancel }) => {
const [ deckName, setDeckName ] = useState(deck.name)
const [ deckNameValid, setIsDeckNameValid ] = useState(false)

const nameValid = (name: string) => name.trim().length > 0

const handleDeckNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const newName = event.target.value
setDeckName(event.target.value)
setIsDeckNameValid(nameValid(newName))
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's wrap this function in a debounce. Debounce basically holds off on firing the callback until the value stabilizes for a certain amount of time. So if you're typing in the input box pretty quickly, it would only fire after you stop typing.

Here's a good visualization of debounce vs throttle (which rate limits the amount of time the callback is called instead): https://web.archive.org/web/20220117092326/http://demo.nimius.net/debounce_throttle/

You can use the debounce feature in lodash, which we already have as a dependency: https://lodash.com/docs/4.17.15#debounce


const handleSave = () => {
if(!nameValid(deckName)) return

const newDeck = {
name: deckName.trim(),
cards: selectedCards
}

onSave(newDeck)
}

return (
<div className="flex flex-col items-center w-full p-3 overflow-y-auto overflow-x-hidden">
{/* Deck Name Input */}
<div className="flex flex-wrap gap-2 justify-center w-full">
<input
type="text"
value={deckName}
onChange={handleDeckNameChange}
style={{ outline: deckNameValid ? "none" : "2px solid red" }}
className="flex-shrink min-w-0 px-2 py-2 border rounded-md text-black bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent m-1.5 placeholder-gray-700 flex-basis[auto] max-w-full"
placeholder="Deck name"
/>
eviterin marked this conversation as resolved.
Show resolved Hide resolved
</div>

{/* Save and Cancel Buttons */}
<div className="flex flex-wrap gap-2 justify-center w-full">
<Button variant="secondary" className="border-2 border-yellow-500 normal-case hover:scale-105 font-fable text-xl hover:border-yellow-400" onClick={handleSave}>
✓Save
</Button>
<Button variant="secondary" className="border-2 border-yellow-500 normal-case hover:scale-105 font-fable text-xl hover:border-yellow-400" onClick={onCancel}>
✕Cancel
</Button>
</div>
eviterin marked this conversation as resolved.
Show resolved Hide resolved

{/* List of Cards in the Deck */}
<div className="mt-4 w-full">
{selectedCards.length > 0 ? (
selectedCards.map((card, index) => (
<div
key={index}
className="p-2 cursor-pointer hover:bg-gray-100"
onClick={() => onCardSelect(card)}
>
<div className="flex items-center space-x-3">
<Image src={testCards.find(tc => tc.id === Number(card.id))?.image || '/card_art/1.jpg'} alt="Card art" width={40} height={40} className="object-cover rounded-full" />
<span className="font-medium">{card.lore.name}</span>
</div>
</div>
))
) : (
<div className="p-4 text-center text-gray-300">
Click on cards to add them to the deck.
</div>
)}
</div>
</div>
)
}

export default DeckConstructionPanel
84 changes: 84 additions & 0 deletions packages/webapp/src/components/collection/filterPanel.tsx
eviterin marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react'
import Image from 'next/image'
import { Card } from 'src/store/types'

interface FilterPanelProps {
effects: string[]
types: string[]
effectMap: { [key: string]: boolean }
typeMap: { [key: string]: boolean }
handleEffectClick: (index: number) => void
handleTypeClick: (index: number) => void
handleInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void
selectedCard: Card | null
}

const FilterPanel: React.FC<FilterPanelProps> = ({
effects,
types,
effectMap,
typeMap,
handleEffectClick,
handleTypeClick,
handleInputChange,
selectedCard
}) => {
const cardName = selectedCard?.lore.name || "Select a card"
const cardFlavor = selectedCard?.lore.flavor || "Select a card to see its details"

return (
<div className="flex col-span-3 rounded-xl border overflow-y-auto">
<div className="overflow-y-auto">
{/* Search */}
<h2 className="text-2xl font-bold text-white m-1.5">Search</h2>
<div>
<input
type="text"
onChange={handleInputChange}
className="px-4 py-2 border rounded-md text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent m-1.5"
placeholder="Search by name" />
</div>

{/* Effects */}
<h3 className="text-xl font-bold text-white m-1.5">Effects</h3>
<div className="flex flex-wrap gap-2">
{effects.map((effect, index) => (
<button
key={index}
onClick={() => handleEffectClick(index)}
className={`text-white font-bold py-2 px-2 rounded m-1.5 ${effectMap[effect] ? 'bg-purple-900' : 'bg-gray-500'}`}>
{effect}
</button>)
)}
</div>

{/* Types */}
<h3 className="text-xl font-bold text-white m-1">Types</h3>
<div className="flex flex-wrap gap-2">
{types.map((type, index) => (
<button
key={index}
onClick={() => handleTypeClick(index)}
className={`text-white font-bold py-2 px-2 rounded m-1 ${typeMap[type] ? 'bg-purple-900' : 'bg-gray-500'}`}>
{type}
</button>)
)}
</div>

{/* todo @eviterin: makes sense to add a filter for the card collection display to only show one of each card. */}
eviterin marked this conversation as resolved.
Show resolved Hide resolved

{/* Selected Card Display */}
<div className="pb-5">
<h2 className="text-3xl font-bold text-white m-1.5">Card details</h2>
<div className="m-4 bg-slate-900/50 rounded-lg p-4 border-4 border-slate-900">
<Image src="/card_art/0.jpg" alt={cardName} width={256} height={256} className="m-auto" />
<div className="text-center">{cardName}</div>
</div>
<div className="text-center m-2">{cardFlavor}</div>
</div>
</div>
</div>
)
}

export default FilterPanel
31 changes: 31 additions & 0 deletions packages/webapp/src/components/link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react"
import { useRouter } from "next/router"
import Link from "next/link"

interface QueryParamLinkProps {
children: React.ReactNode
href: string
}

/**
* A Link component wrapper that appends a 'index' query parameter to the URL in development mode.
* This is used to persist state across navigation during testing.
*/
const QueryParamLink : React.FC<QueryParamLinkProps> = ({ children, href }) => {
const router = useRouter()

let url = href

if (process.env.NODE_ENV === "development") {
const index = parseInt(router.query.index as string)
if (index !== undefined && !isNaN(index) && 0 <= index && index <= 9)
url += (url.includes("?") ? "&" : "?") + `index=${index}`
}
return (
<Link href={url}>
{children}
</Link>
)
}

export default QueryParamLink
8 changes: 6 additions & 2 deletions packages/webapp/src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ const buttonVariants = cva(
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
width: {
full: "w-full",
auto: "w-auto",
}
},
defaultVariants: {
variant: "default",
Expand All @@ -41,11 +45,11 @@ export interface ButtonProps
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
({ className, variant, size, width, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
className={cn(buttonVariants({ variant, size, width, className }))}
ref={ref}
{...props}
/>
Expand Down
2 changes: 1 addition & 1 deletion packages/webapp/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* @module constants
*/

export const GIT_REPO = "https://github.com/norswap/0xFable"
export const GIT_REPO = "https://github.com/0xFableOrg/0xFable"
export const GIT_ISSUES = `${GIT_REPO}/issues`

/** Proof generation timeout (in seconds) for the proof of the initial hand. */
Expand Down
Loading