Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
07da3a8
Remove DB from server
jonty007 Apr 16, 2025
135fca9
update chess-contracts to support OP_2 OP_# multisig
jonty007 Apr 16, 2025
5b49244
update lib
jonty007 Apr 16, 2025
ad441b8
update chess-app using OP_2 OP_3 flow to clain win
jonty007 Apr 16, 2025
fa19b72
lib build
jonty007 Apr 16, 2025
43a623e
lib build
jonty007 Apr 16, 2025
8306ad8
lint fix
jonty007 Apr 24, 2025
604d831
merge commit
jonty007 Apr 24, 2025
534f787
peer to peer chess game
jonty007 May 12, 2025
5e45df3
remove API_BASE_URL as its not required
jonty007 May 12, 2025
3fc34e0
removed vite api url
jonty007 May 12, 2025
44b3843
UX fixes
jonty007 May 13, 2025
b700dac
remove logs
jonty007 May 13, 2025
da9e411
logs for subscription check
jonty007 May 14, 2025
041cf67
adding logs for subscription test
jonty007 May 16, 2025
6b782bc
upgraded typescript to latest
jonty007 May 19, 2025
a817c7d
UI/UX improvements
jonty007 May 27, 2025
cf64d17
Remove comment
jonty007 Jun 8, 2025
2b29c95
Update ChessBoard.tsx
ClemensLey Jun 9, 2025
e5f680d
Updates chess-app README.md
ltardivo Jun 9, 2025
1c32e53
Fix the UI/UX
jonty007 Jun 11, 2025
ec68f8f
merge commit
jonty007 Jun 13, 2025
ffdbeb9
UI/UX fixes for chess app
jonty007 Jun 18, 2025
0d6b6dd
UI/UX fixes
jonty007 Jun 19, 2025
cccf0b6
UI/UX changes, removed my-games and moved features to home
jonty007 Jun 20, 2025
b7e54cd
Challenge wrapper and UI/UX improvements
jonty007 Jun 25, 2025
b4992a5
Challenge notification UX
jonty007 Jun 26, 2025
181aaf2
staging merge
jonty007 Jul 3, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ packages/**/chain-setup/**/db-data
# Logs
logs
*.log
.parcel-cache

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
Expand Down
923 changes: 364 additions & 559 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"path": "^0.12.7",
"ts-node": "^10.9.2",
"turbo": "^2.1.3",
"typescript": "^5.5.3",
"typescript": "^5.8.2",
"url": "^0.11.3"
},
"optionalDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"jsdom": "^25.0.0",
"postcss": "^8.4.44",
"tailwindcss": "^3.4.10",
"typescript": "^5.5.3",
"typescript": "^5.8.2",
"typescript-eslint": "^8.0.1",
"vite": "^5.4.1",
"vitest": "^2.0.5"
Expand Down
3 changes: 2 additions & 1 deletion packages/chess-app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
VITE_CHAIN=LTC
VITE_NETWORK=regtest
VITE_URL=http://127.0.0.1:1031
VITE_API_BASE_URL=http://127.0.0.1:4000
VITE_PATH=m/44'/1'/0'

# Application Port
Expand All @@ -11,3 +10,5 @@ VITE_PORT=1032
# Smart Contract Locations
# Run 'npm run deploy' to generate module specifiers
VITE_CHESS_GAME_MOD_SPEC=
VITE_CHESS_USER_MOD_SPEC=
VITE_CHESS_CHALLENGE_MOD_SPEC=
14 changes: 9 additions & 5 deletions packages/chess-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@ Start a Bitcoin Computer Node in the package `node`. Then copy the `.env.example
cp .env.example .env
```

Then start the chess server in the package `chess-server` and run the deploy script there.

### Test
Deploy the smart contracts in the package `chess-contracts` and follow the instructions there.

```
npm test
npm run deploy
```

### Start the App
Start the App

```
npm start
```

### Test

```
npm test
```
4 changes: 3 additions & 1 deletion packages/chess-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"react": "^18.3.1",
"react-chessboard": "^4.7.2",
"react-dom": "^18.3.1",
"react-icons": "^5.2.1",
"url": "^0.11.4"
},
"devDependencies": {
Expand All @@ -51,9 +52,10 @@
"postcss": "^8.4.44",
"rollup-plugin-node-polyfills": "^0.2.1",
"tailwindcss": "^3.4.10",
"typescript": "^5.5.3",
"typescript": "^5.8.2",
"typescript-eslint": "^8.0.1",
"vite": "^5.4.1",
"vite-plugin-node-polyfills": "^0.23.0",
"vitest": "^2.0.5"
}
}
3 changes: 2 additions & 1 deletion packages/chess-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Auth, UtilsContext, Wallet, ComputerContext } from '@bitcoin-computer/c
import { ChessBoard } from './components/ChessBoard'

import { Navbar } from './components/Navbar'
import { Home } from './components/Home'

export default function App() {
const [computer] = useState(Auth.getComputer())
Expand All @@ -24,7 +25,7 @@ export default function App() {
<Navbar />
<div className="w-full h-screen bg-white border-gray-200 dark:bg-gray-900 px-4 lg:px-24">
<Routes>
<Route path="/" element={<ChessBoard />} />
<Route path="/" element={<Home />} />
<Route path="/game/:id" element={<ChessBoard />} />
<Route path="*" element={<Navigate to="/" replace={true} />} />
</Routes>
Expand Down
119 changes: 119 additions & 0 deletions packages/chess-app/src/components/ChallengesList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { User } from '@bitcoin-computer/chess-contracts'
import { Modal } from '@bitcoin-computer/components'
import { useCallback, useEffect, useRef, useState } from 'react'
import { HiRefresh } from 'react-icons/hi'
import { creaetUserModal } from './CreateUser'
import { startGameModal } from './StartGame'

export type ChallengeType = {
challengeId: string
new: boolean
}

export const ChallengeList = ({
challenges,
setChallengeId,
user,
refreshList,
}: {
challenges: ChallengeType[]
setChallengeId: React.Dispatch<React.SetStateAction<string>>
user: User | null
refreshList: () => Promise<void>
}) => {
const [items, setItems] = useState<ChallengeType[]>([])
const [hasMore, setHasMore] = useState(true)
const [loading, setLoading] = useState(false)
const scrollContainerRef = useRef<HTMLDivElement | null>(null)
const itemsPerPage = 12

const fetchMoreItems = useCallback(
async (offset: number): Promise<ChallengeType[]> => {
return challenges ? challenges.slice(offset, offset + itemsPerPage) : []
},
[itemsPerPage, challenges],
)

const loadMoreItems = useCallback(async () => {
if (loading || !hasMore) return

setLoading(true)
const newItems = await fetchMoreItems(items.length)

setItems((prev) => [...prev, ...newItems])
if (newItems.length < itemsPerPage) setHasMore(false) // Stop fetching when no more items are available
setLoading(false)
}, [loading, hasMore, items, fetchMoreItems])

const handleScroll = () => {
const container = scrollContainerRef.current
if (container) {
const bottomReached = container.scrollTop + container.clientHeight >= container.scrollHeight
if (bottomReached) {
loadMoreItems()
}
}
}

useEffect(() => {
// Initial fetch without relying on scroll
const initialFetch = async () => {
setLoading(true)
const initialItems = await fetchMoreItems(0)
setItems(initialItems)
if (initialItems.length < itemsPerPage) setHasMore(false)
setLoading(false)
}

initialFetch()
}, [fetchMoreItems])

const openModal = (item: string) => {
if (!user) {
Modal.showModal(creaetUserModal)
return
}
setChallengeId(item)
Modal.showModal(startGameModal)
}

return (
<div className="w-full h-full overflow-hidden flex flex-col bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg">
<div className="flex justify-center mt-2 mb-2">
<h3 className="text-xl font-bold text-gray-500 dark:text-gray-400">
<span className="hover:text-gray-700 dark:hover:text-gray-200 font-extrabold cursor-pointer">
My Challenges
</span>{' '}
<HiRefresh
onClick={refreshList}
className="w-4 h-4 ml-1 mb-1 inline cursor-pointer hover:text-slate-700 dark:hover:text-slate-100"
/>
</h3>
</div>
<div
ref={scrollContainerRef}
className="overflow-auto flex-1 max-h-[calc(100vh-9rem)]"
onScroll={handleScroll}
>
<ul className="space-y-2 p-4">
{items.map((item, index) => (
<li
key={index}
className="relative p-2 bg-gray-100 dark:bg-gray-700 rounded shadow text-gray-800 dark:text-gray-200 cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-600"
title={item.challengeId}
onClick={() => openModal(item.challengeId)}
>
<span className="truncate block">{item.challengeId}</span>
{item.new && (
<div className="absolute inline-flex w-3 h-3 bg-red-500 rounded-full top-0 right-0 -mt-1 -mr-1 z-10"></div>
)}
</li>
))}
</ul>
{loading && (
<div className="text-center text-gray-500 dark:text-gray-400 p-4">Loading...</div>
)}
</div>
</div>
)
}
62 changes: 62 additions & 0 deletions packages/chess-app/src/components/ChallengesListWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ChessChallengeTxWrapper, User } from '@bitcoin-computer/chess-contracts'
import { ComputerContext } from '@bitcoin-computer/components'
import { useContext, useEffect, useState } from 'react'
import { VITE_CHESS_CHALLENGE_MOD_SPEC } from '../constants/modSpecs'
import { ChallengeList, ChallengeType } from './ChallengesList'
import { StartGameModal } from './StartGame'

export const ChallengeListWrapper = ({ user }: { user: User | null }) => {
const [challenges, setChallenges] = useState<ChallengeType[]>([])
const computer = useContext(ComputerContext)
const [challengeId, setChallengeId] = useState('')

const getChallenges = async () => {
const challengeRevs = await computer.query({
mod: VITE_CHESS_CHALLENGE_MOD_SPEC,
publicKey: computer.getPublicKey(),
})

const availableChallenges: ChallengeType[] = []

const challengeSyncPromises: Promise<ChessChallengeTxWrapper>[] = []

challengeRevs.forEach((rev) => {
challengeSyncPromises.push(computer.sync(rev) as Promise<ChessChallengeTxWrapper>)
})

const challengesList = await Promise.all(challengeSyncPromises)

challengesList.forEach((challenge) => {
availableChallenges.push({ challengeId: challenge._id, new: !challenge.accepted })
})

return availableChallenges
}
const refreshList = async () => {
const availableChallenges: ChallengeType[] = await getChallenges()

setChallenges(availableChallenges.reverse())
}

useEffect(() => {
// Initial fetch without relying on scroll
const fetch = async () => {
const availableChallenges: ChallengeType[] = await getChallenges()

setChallenges(availableChallenges.reverse())
}
fetch()
}, [])

return (
<>
<ChallengeList
user={user}
refreshList={refreshList}
challenges={challenges}
setChallengeId={setChallengeId}
/>
<StartGameModal challengeId={challengeId} />
</>
)
}
Loading