Skip to content

Latest commit

 

History

History
354 lines (287 loc) · 10.5 KB

File metadata and controls

354 lines (287 loc) · 10.5 KB
noteId 26067490b54a11f0ad8f4f365506ecb8
tags

Library Borrowing System - Complete Implementation

Overview

Implemented full library borrowing system with:

  • Borrow books from library pool
  • Return books to library pool
  • Track active borrows with expiry time
  • Display expiry countdown on book cards

Key Features Implemented

1. "Not Available" Tag

  • Shows red "Not Available" badge when all books are borrowed
  • Shows green "Available" badge when books are available
  • Located at top-right corner of book card

2. Borrowing System

  • Uses borrowFromPool(tokenId) from library pool contract
  • One book per user per title
  • Automatically assigns rental period (default: 3 days)
  • Returns recordId for tracking

3. Return System

  • Uses returnMyBorrow(tokenId) from library pool contract
  • User can return before expiry
  • Auto-deleted when expired

4. Active Borrows Tracking

  • Uses getActiveBorrows(address user) to fetch all user's active borrows
  • Returns array of BorrowView:
    • recordId: Unique borrow record identifier
    • tokenId: Book ID
    • expiry: Unix timestamp when borrow expires

5. Expiry Display

  • Shows countdown timer: "2d 5h left", "3h 45m left", or "30m left"
  • Blue badge under availability info
  • Updates based on expiry timestamp from contract
  • Shows "Expired" when time runs out

Contract Functions Used

Library Pool Contract (0xA31D6d3f2a6C5fBA99E451CCAAaAdf0bca12cbF0)

Read Functions:

// Get all active borrows for a user
getActiveBorrows(address user) 
 returns BorrowView[] { recordId, tokenId, expiry }

// Check if user can use this book (has active borrow)
usableBalanceOf(address user, uint256 tokenId) 
 returns uint256 (1 if borrowed, 0 if not)

// Get available books (total - borrowed)
previewAvailability(uint256 tokenId) 
 returns (uint256 available, uint256 frozenNow)

Write Functions:

// Borrow a book
borrowFromPool(uint256 tokenId) 
 returns uint256 recordId

// Return borrowed book
returnMyBorrow(uint256 tokenId) 
 void

Main Contract (0xC12F333f41D7cedB209F24b303287531Bb05Bc67)

// Get total stock in library
balanceOf(address libraryPool, uint256 tokenId) 
 returns uint256

UI Flow

State 1: Book Available, Not Borrowed

┌──────────────────────────┐
│ Cover    [Available]     │  ← Green badge
├──────────────────────────┤
│ Book Title               │
│ Author                   │
│ 🟢 Availability: 4/5     │
│                          │
│ [Borrow]                 │  ← Black button
└──────────────────────────┘

State 2: Book Available, User Has Borrowed

┌──────────────────────────┐
│ Cover    [Available]     │  ← Green badge
├──────────────────────────┤
│ Book Title               │
│ Author                   │
│ 🟢 Availability: 3/5     │
│ 🔵 Due: 2d 5h left       │  ← NEW: Expiry countdown
│                          │
│ [Read Book] [Return]     │  ← Two buttons
└──────────────────────────┘

State 3: All Books Borrowed

┌──────────────────────────┐
│ Cover [Not Available]    │  ← Red badge
├──────────────────────────┤
│ Book Title               │
│ Author                   │
│ 🔴 Availability: 0/5     │
│                          │
│ [Not available]          │  ← Disabled button
└──────────────────────────┘

State 4: User Borrowed, Near Expiry

┌──────────────────────────┐
│ Cover    [Available]     │
├──────────────────────────┤
│ Book Title               │
│ Author                   │
│ 🟢 Availability: 3/5     │
│ 🔵 Due: 45m left         │  ← Urgent countdown
│                          │
│ [Read Book] [Return]     │
└──────────────────────────┘

Button States

Borrow Button (Not borrowed + Available)

  • Black background (bg-zinc-900)
  • White text
  • Icon: Book reader
  • Action: Calls borrowFromPool()
  • Shows "Borrowing..." during transaction

Read Book + Return Buttons (Already borrowed)

  • Read Book: Black, full width left
    • Opens EPUB reader at /read-book/{bookId}
  • Return: White with black border, compact right
    • Calls returnMyBorrow()
    • Shows "Returning..." during transaction

Not Available Button (No stock)

  • Gray background (disabled state)
  • Gray text
  • Icon: Slash
  • Non-clickable

Status Messages

Shown above buttons with color coding:

Loading/Processing:

  • Blue background (bg-blue-50)
  • "Borrowing book..." / "Returning book..."

Success:

  • Green background (bg-green-50)
  • "Book borrowed successfully!" / "Book returned successfully!"
  • Auto-clears after 2 seconds

Error:

  • Red background (bg-red-50)
  • "Error: [error message]"

Expiry Time Formatting

function formatExpiryTime(expiryTimestamp: number) {
  const timeLeft = expiryTimestamp - now;
  
  if (timeLeft <= 0) return "Expired";
  if (days > 0) return `${days}d ${hours}h left`;
  if (hours > 0) return `${hours}h ${minutes}m left`;
  return `${minutes}m left`;
}

Examples:

  • 172800 seconds → "2d 0h left"
  • 12600 seconds → "3h 30m left"
  • 1800 seconds → "30m left"
  • -100 seconds → "Expired"

Data Flow

On Page Load:

1. CivilibBookCard mounts
2. Fetches totalStock from main contract
3. Fetches frozenNow from library pool
4. Calculates availableBooks = totalStock - frozenNow
5. CivilibAccessButton calls getActiveBorrows(user)
6. Checks if user borrowed this book
7. If yes: displays expiry time

On Borrow:

1. User clicks "Borrow" button
2. Set loading = true, show "Borrowing book..."
3. Call borrowFromPool(tokenId)
4. Wait for transaction confirmation
5. Wait 2 seconds for blockchain update
6. Call getActiveBorrows(user) to get recordId & expiry
7. Update UI: show "Read Book" + "Return" buttons
8. Display expiry countdown badge
9. Notify parent (CivilibBookCard) with expiry timestamp

On Return:

1. User clicks "Return" button
2. Set loading = true, show "Returning book..."
3. Call returnMyBorrow(tokenId)
4. Wait for transaction confirmation
5. Wait 2 seconds
6. Update UI: show "Borrow" button again
7. Remove expiry badge
8. Notify parent with null expiry

Files Modified

1. src/components/civilib/CivilibBookCard.tsx

Changes:

  • Added "Not Available" red badge (complementing green "Available")
  • Added userBorrowExpiry state for tracking expiry
  • Added formatExpiryTime() helper function
  • Added blue expiry badge display: "Due: Xd Xh left"
  • Changed badge container to flex-col for vertical stacking
  • Pass onBorrowStatusChange callback to CivilibAccessButton

2. src/components/civilib/CivilibAccessButton.tsx

Major Refactor:

Removed:

  • Old functions: hasAccess(), accessRegistry(), rentAccess()
  • Main contract imports

Added:

  • Library pool contract imports
  • getActiveBorrows() for checking borrow status
  • borrowFromPool() for borrowing
  • returnMyBorrow() for returning
  • onBorrowStatusChange callback prop
  • Status message display
  • Loading states
  • Two-button layout for borrowed state

New Props:

  • onBorrowStatusChange?: (expiry: number | null) => void

New States:

  • hasBorrowed: boolean
  • borrowRecord: BorrowRecord | null
  • loading: boolean
  • statusMessage: string

3. src/library-pool.abi.ts (Already created earlier)

  • Exports library pool address
  • Exports library pool ABI

Environment Setup

Contract Addresses:

  • Main Contract: 0xC12F333f41D7cedB209F24b303287531Bb05Bc67
  • Library Pool: 0xA31D6d3f2a6C5fBA99E451CCAAaAdf0bca12cbF0

Network: Base Sepolia Testnet

Testing Checklist

Availability Display:

  • Green "Available" badge when books available
  • Red "Not Available" badge when all borrowed
  • No badge when book not in collection
  • Correct availability count (X/Y format)

Borrowing:

  • "Borrow" button visible when available
  • Status message shows during transaction
  • Button disabled during loading
  • Transaction calls borrowFromPool()
  • UI updates after successful borrow
  • Expiry time fetched and displayed

Expiry Display:

  • Blue badge shows countdown
  • Format: "Xd Xh left" or "Xh Xm left"
  • Updates when borrow status changes
  • Hidden when no active borrow

Return:

  • "Return" button visible when borrowed
  • Status message shows during transaction
  • Transaction calls returnMyBorrow()
  • UI updates after successful return
  • Expiry badge removed
  • "Borrow" button reappears

Error Handling:

  • Shows error message on failed borrow
  • Shows error message on failed return
  • Graceful handling of contract errors
  • Console logs for debugging

Edge Cases:

  • No wallet connected
  • Book not in collection (totalStock = 0)
  • All books borrowed (availableBooks = 0)
  • Multiple books by same user

Known Limitations

  1. RecordId Tracking: borrowRecord state is set but not currently used. Could be used for advanced features like viewing borrow history.

  2. Real-time Updates: Expiry countdown is set once and doesn't tick down in real-time. Could add setInterval for live countdown.

  3. Auto-refresh: After expiry, user needs to refresh page to see updated status. Could add periodic polling.

Future Enhancements

  1. Live Countdown: Update expiry every minute
  2. Borrow History: Show all past borrows
  3. Renewal: Extend borrow period
  4. Notifications: Alert when book due soon
  5. Waitlist: Queue when all books borrowed
  6. EAS Integration: On-chain attestations for borrows

Success Metrics

Full borrowing flow implementedExpiry tracking working
UI responsive to borrow stateStatus messages clearError handling robustBuild succeeds without blocking errors

Ready for testing on Base Sepolia testnet! 🚀