Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
532dc61
Add address book storage utility with localStorage
Mosas2000 May 15, 2026
bca85ff
Add address validation utilities
Mosas2000 May 15, 2026
a1b3873
Create main AddressBook component
Mosas2000 May 15, 2026
9cf09de
Add AddressBookEntry component for displaying entries
Mosas2000 May 15, 2026
1af4704
Add AddressBookForm component for adding and editing entries
Mosas2000 May 15, 2026
23f3572
Add AddressBookSearch component for filtering entries
Mosas2000 May 15, 2026
fbe7984
Add import and export functionality for address book
Mosas2000 May 15, 2026
3d4a662
Integrate address book into SendTip component
Mosas2000 May 15, 2026
67ad476
Add address book route configuration
Mosas2000 May 15, 2026
f26db03
Add address book route to application routing
Mosas2000 May 15, 2026
14c8eea
Apply Tailwind CSS styling to address book components
Mosas2000 May 15, 2026
14db06f
Add comprehensive tests for address book storage layer
Mosas2000 May 15, 2026
af19947
Add validation tests for address book entries
Mosas2000 May 15, 2026
12c4cad
Add component tests for AddressBook
Mosas2000 May 15, 2026
a7b1f7a
Integrate analytics tracking for address book operations
Mosas2000 May 15, 2026
c0d9d57
Add comprehensive documentation for address book feature
Mosas2000 May 15, 2026
5431a3f
Include address book metrics in analytics summary
Mosas2000 May 15, 2026
59bf26f
Enhance accessibility with ARIA labels and semantic HTML
Mosas2000 May 15, 2026
1556bd2
Add fallback copy mechanism for older browsers
Mosas2000 May 15, 2026
510952a
Add input sanitization and length enforcement
Mosas2000 May 15, 2026
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
202 changes: 202 additions & 0 deletions frontend/docs/ADDRESS_BOOK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Address Book Feature

## Overview

The Address Book feature allows users to save and manage frequently used Stacks addresses with custom labels and notes. This improves the user experience by reducing the need to manually enter or copy-paste addresses for recurring transactions.

## Features

### Core Functionality

- **Add Addresses**: Save Stacks addresses with custom labels and optional notes
- **Edit Addresses**: Update labels, addresses, or notes for existing entries
- **Delete Addresses**: Remove addresses from the address book
- **Search**: Filter addresses by label, address, or notes
- **Import/Export**: Backup and restore address book data via JSON

### User Interface

- **Full Mode**: Complete address book management interface with all features
- **Compact Mode**: Streamlined view for quick address selection (used in SendTip component)

## Usage

### Adding an Address

1. Navigate to the Address Book page
2. Click "Add Address" button
3. Fill in the form:
- **Label** (required): A friendly name for the address (max 50 characters)
- **Address** (required): A valid Stacks mainnet or testnet address
- **Notes** (optional): Additional information about the address (max 200 characters)
4. Click "Add" to save

### Editing an Address

1. Find the address entry in the list
2. Click the "Edit" button
3. Modify the fields as needed
4. Click "Update" to save changes

### Deleting an Address

1. Find the address entry in the list
2. Click the "Delete" button
3. Confirm the deletion in the dialog

### Searching Addresses

1. Use the search input at the top of the address book
2. Type any part of the label, address, or notes
3. The list will filter in real-time
4. Click the "X" button to clear the search

### Importing Addresses

1. Click "Import/Export" button
2. Switch to the "Import" tab
3. Either:
- Click "Choose File" to select a JSON file
- Paste JSON data directly into the textarea
4. Click "Import"
5. Duplicate addresses will be skipped automatically

### Exporting Addresses

1. Click "Import/Export" button
2. Stay on the "Export" tab
3. Either:
- Click "Download as JSON" to save a file
- Click "Copy to Clipboard" to copy the data

### Using Address Book in SendTip

1. Navigate to the Send Tip page
2. The address book appears in compact mode below the recipient field
3. Click on any saved address to auto-fill the recipient field
4. The label will also be displayed for reference

## Data Storage

- Address book data is stored in browser localStorage
- Storage key: `tipstream_address_book`
- Data persists across browser sessions
- Clearing browser data will remove saved addresses

## Validation Rules

### Label
- Required field
- Maximum 50 characters
- Can contain any characters

### Address
- Required field
- Must be a valid Stacks address format
- Supports both mainnet (SP) and testnet (ST) addresses
- Must be 41 characters long
- Cannot be a duplicate of an existing address in the book

### Notes
- Optional field
- Maximum 200 characters
- Can contain any characters

## Analytics Tracking

The following events are tracked for analytics:

- `addressBookAdded`: When a new address is added
- `addressBookUpdated`: When an address is edited
- `addressBookDeleted`: When an address is removed
- `addressBookImported`: When addresses are imported (includes count)
- `addressBookExported`: When addresses are exported
- `addressBookSearched`: When the search field is used
- `addressBookSelected`: When an address is selected in compact mode

## Technical Implementation

### Components

- **AddressBook**: Main component with full functionality
- **AddressBookEntry**: Individual address entry display
- **AddressBookForm**: Form for adding/editing addresses
- **AddressBookSearch**: Search input with clear button
- **AddressBookImportExport**: Import/export interface

### Storage Layer

- **AddressBookEntry**: Class representing a single address entry
- **AddressBook**: Singleton class managing all address book operations
- **addressBook**: Global instance for application-wide access

### Validation

- **validateAddressBookEntry**: Validates label, address, and notes
- **formatAddress**: Formats addresses for display (truncation)

## Testing

Comprehensive test coverage includes:

- **Unit Tests**: Storage layer and validation logic
- **Component Tests**: UI interactions and user flows
- **Integration Tests**: End-to-end address book workflows

Run tests with:
```bash
npm test addressBook
npm test addressBookValidation
npm test AddressBook
```

## Accessibility

- All form inputs have proper labels
- Buttons have descriptive text and ARIA labels
- Search clear button has aria-label
- Keyboard navigation supported throughout
- Focus management for form interactions

## Future Enhancements

Potential improvements for future versions:

- Address book categories/tags
- Bulk operations (delete multiple, export selected)
- Address verification against blockchain
- Sync across devices (requires backend)
- Address book sharing between users
- Recent addresses auto-save
- Address nicknames with emoji support
- Integration with Stacks Name Service (BNS)

## Troubleshooting

### Addresses not persisting
- Check if localStorage is enabled in browser
- Verify browser storage quota is not exceeded
- Check browser console for errors

### Import failing
- Ensure JSON format is valid
- Verify all required fields (label, address) are present
- Check that addresses are valid Stacks addresses

### Search not working
- Clear search and try again
- Check if addresses actually contain the search term
- Verify JavaScript is enabled

## Related Files

- `frontend/src/components/AddressBook.jsx`
- `frontend/src/components/AddressBookEntry.jsx`
- `frontend/src/components/AddressBookForm.jsx`
- `frontend/src/components/AddressBookSearch.jsx`
- `frontend/src/components/AddressBookImportExport.jsx`
- `frontend/src/lib/addressBook.js`
- `frontend/src/lib/addressValidation.js`
- `frontend/src/test/addressBook.test.js`
- `frontend/src/test/addressBookValidation.test.js`
- `frontend/src/test/AddressBook.test.jsx`
18 changes: 16 additions & 2 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import { useSessionSync } from './hooks/useSessionSync';
import { useDemoMode } from './context/DemoContext';
import {
ROUTE_SEND, ROUTE_BATCH, ROUTE_TOKEN_TIP, ROUTE_SCHEDULE, ROUTE_SCHEDULED_TIPS, ROUTE_FEED,
ROUTE_LEADERBOARD, ROUTE_ACTIVITY, ROUTE_PROFILE,
ROUTE_LEADERBOARD, ROUTE_ACTIVITY, ROUTE_PROFILE, ROUTE_ADDRESS_BOOK,
ROUTE_BLOCK, ROUTE_STATS, ROUTE_ADMIN, ROUTE_TELEMETRY,
DEFAULT_AUTHENTICATED_ROUTE, ROUTE_META,
} from './config/routes';
import { Zap, Radio, Trophy, User, BarChart3, Users, ShieldBan, Coins, UserCircle, Shield, Gauge, Calendar, Clock } from 'lucide-react';
import { Zap, Radio, Trophy, User, BarChart3, Users, ShieldBan, Coins, UserCircle, Shield, Gauge, Calendar, Clock, BookUser } from 'lucide-react';
import { activateDemo, deactivateDemo } from './lib/demo-utils';

const AnimatedHero = lazy(() => import('./components/ui/animated-hero').then(m => ({ default: m.AnimatedHero })));
Expand All @@ -36,6 +36,7 @@ const PlatformStats = lazy(() => import('./components/PlatformStats'));
const RecentTips = lazy(() => import('./components/RecentTips'));
const Leaderboard = lazy(() => import('./components/Leaderboard'));
const ProfileManager = lazy(() => import('./components/ProfileManager'));
const AddressBook = lazy(() => import('./components/AddressBook'));
const BlockManager = lazy(() => import('./components/BlockManager'));
const BatchTip = lazy(() => import('./components/BatchTip'));
const TokenTip = lazy(() => import('./components/TokenTip'));
Expand Down Expand Up @@ -165,6 +166,7 @@ function App() {
{ path: ROUTE_LEADERBOARD, label: 'Leaderboard', icon: Trophy },
{ path: ROUTE_ACTIVITY, label: 'My Activity', icon: User },
{ path: ROUTE_PROFILE, label: 'Profile', icon: UserCircle },
{ path: ROUTE_ADDRESS_BOOK, label: 'Address Book', icon: BookUser },
{ path: ROUTE_BLOCK, label: 'Block', icon: ShieldBan },
{ path: ROUTE_STATS, label: 'Stats', icon: BarChart3 },
];
Expand Down Expand Up @@ -368,6 +370,18 @@ function App() {
)
}
/>
<Route
path={ROUTE_ADDRESS_BOOK}
element={
userData || demoEnabled ? (
<AddressBook />
) : (
<RequireAuth onAuth={handleAuth} authLoading={authLoading} route={ROUTE_ADDRESS_BOOK}>
<AddressBook />
</RequireAuth>
)
}
/>

{/* Admin-only routes */}
<Route path={ROUTE_ADMIN} element={<RequireAdmin><AdminDashboard userAddress={userAddress} addToast={addToast} /></RequireAdmin>} />
Expand Down
Loading
Loading