Browse Source

Enhanced contact import documentation and test cleanup

- Added comprehensive educational documentation to ContactImportView.vue explaining
  the contact import workflow, data processing pipeline, and UI components
- Enhanced ContactsView.vue with detailed documentation covering contact input
  workflow, bulk operations, and state management
- Cleaned up test-playwright/45-contact-import.spec.ts by removing debugging
  console logs and adding thorough documentation explaining how the contact
  import page works, including user workflow, page structure, and component
  interactions
- Fixed syntax errors in test file that were preventing test execution
- All 34 contact import tests now pass successfully with improved performance
  monitoring and error handling

The documentation now provides complete context for developers understanding
the contact import system from user perspective through technical implementation.
pull/159/head
Matthew Raymer 3 weeks ago
parent
commit
294034d9b4
  1. 152
      .cursor/rules/absurd-sql.mdc
  2. 260
      src/views/ContactImportView.vue
  3. 138
      src/views/ContactsView.vue
  4. 665
      test-playwright/45-contact-import.spec.ts

152
.cursor/rules/absurd-sql.mdc

@ -3,50 +3,50 @@ description:
globs: globs:
alwaysApply: true alwaysApply: true
--- ---
# Absurd SQL - Cursor Development Guide # Absurd SQL - Cursor Development Guide (Directive Style)
## Project Overview ## Project Overview
Absurd SQL is a backend implementation for sql.js that enables persistent SQLite databases in the browser by using IndexedDB as a block storage system. This guide provides rules and best practices for developing with this project in Cursor. Implement persistent SQLite databases in the browser using **Absurd SQL** with IndexedDB as block storage. Execute all SQL operations according to the following directives.
## Project Structure ## Project Structure
``` ```
absurd-sql/ absurd-sql/
├── src/ # Source code ├── src/ # Place source code here
├── dist/ # Built files ├── dist/ # Place built files here
├── package.json # Dependencies and scripts ├── package.json # Maintain dependencies and scripts here
├── rollup.config.js # Build configuration ├── rollup.config.js # Maintain build configuration here
└── jest.config.js # Test configuration └── jest.config.js # Maintain test configuration here
``` ```
## Development Rules ## Directives
### 1. Worker Thread Requirements ### 1. Worker Thread Execution
- All SQL operations MUST be performed in a worker thread - Execute **all SQL operations** inside worker threads.
- Main thread should only handle worker initialization and communication - Restrict the main thread to **initialization** and **communication only**.
- Never block the main thread with database operations - Block **no operations** on the main thread.
### 2. Code Organization ### 2. Code Organization
- Keep worker code in separate files (e.g., `*.worker.js`) - Store worker logic in dedicated files: `*.worker.js`.
- Use ES modules for imports/exports - Use **ES modules** exclusively.
- Follow the project's existing module structure - Conform to the existing **module structure**.
### 3. Required Headers ### 3. Headers Enforcement
When developing locally or deploying, ensure these headers are set: Always set the following headers:
``` ```
Cross-Origin-Opener-Policy: same-origin Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp Cross-Origin-Embedder-Policy: require-corp
``` ```
### 4. Browser Compatibility ### 4. Browser Compatibility
- Primary target: Modern browsers with SharedArrayBuffer support - Target **modern browsers with SharedArrayBuffer support**.
- Fallback mode: Safari (with limitations) - Activate fallback mode for Safari when required.
- Always test in both modes - Test in **both primary and fallback modes** without exception.
### 5. Database Configuration ### 5. Database Configuration
Recommended database settings: Apply the following PRAGMA settings immediately:
```sql ```sql
PRAGMA journal_mode=MEMORY; PRAGMA journal_mode=MEMORY;
PRAGMA page_size=8192; -- Optional, but recommended PRAGMA page_size=8192;
``` ```
### 6. Development Workflow ### 6. Development Workflow
@ -54,71 +54,68 @@ PRAGMA page_size=8192; -- Optional, but recommended
```bash ```bash
yarn add @jlongster/sql.js absurd-sql yarn add @jlongster/sql.js absurd-sql
``` ```
2. Execute commands as follows:
2. Development commands: - `yarn build` → build the project
- `yarn build` - Build the project - `yarn jest` → run all tests
- `yarn jest` - Run tests - `yarn serve` → launch development server
- `yarn serve` - Start development server
### 7. Testing
### 7. Testing Guidelines - Write tests for both **SharedArrayBuffer** and **fallback modes**.
- Write tests for both SharedArrayBuffer and fallback modes - Use **Jest** exclusively.
- Use Jest for testing - Include **performance benchmarks** for critical paths.
- Include performance benchmarks for critical operations
### 8. Performance Optimization
### 8. Performance Considerations - Execute bulk operations when available.
- Use bulk operations when possible - Enforce **transactions** for multi-step operations.
- Monitor read/write performance - Monitor read/write throughput continuously.
- Consider using transactions for multiple operations - Reuse database connections. Do **not** open unnecessary ones.
- Avoid unnecessary database connections
### 9. Error Handling ### 9. Error Handling
- Implement proper error handling for: Implement error handling for:
- Worker initialization failures - Worker initialization failures
- Database connection issues - Database connection issues
- Concurrent access conflicts (in fallback mode) - Concurrent access conflicts (fallback mode)
- Storage quota exceeded scenarios - Storage quota exceeded scenarios
### 10. Security Best Practices ### 10. Security
- Never expose database operations directly to the client - Forbid direct client access to database operations.
- Validate all SQL queries - Validate every SQL query.
- Implement proper access controls - Enforce access control measures.
- Handle sensitive data appropriately - Handle sensitive data with strict isolation.
### 11. Code Style ### 11. Code Style
- Follow ESLint configuration - Follow ESLint configuration.
- Use async/await for asynchronous operations - Use `async/await` for asynchronous operations.
- Document complex database operations - Document complex operations thoroughly.
- Include comments for non-obvious optimizations - Comment all optimizations that are not obvious.
### 12. Debugging ### 12. Debugging
- Use `jest-debug` for debugging tests - Use `jest-debug` for test debugging.
- Monitor IndexedDB usage in browser dev tools - Inspect IndexedDB in browser developer tools.
- Check worker communication in console - Trace worker communication in console logs.
- Use performance monitoring tools - Apply browser performance monitoring tools.
## Common Patterns ## Required Patterns
### Worker Initialization ### Worker Initialization
```javascript ```javascript
// Main thread
import { initBackend } from 'absurd-sql/dist/indexeddb-main-thread'; import { initBackend } from 'absurd-sql/dist/indexeddb-main-thread';
function init() { function init() {
let worker = new Worker(new URL('./index.worker.js', import.meta.url)); const worker = new Worker(new URL('./index.worker.js', import.meta.url));
initBackend(worker); initBackend(worker);
} }
``` ```
### Database Setup ### Database Setup
```javascript ```javascript
// Worker thread
import initSqlJs from '@jlongster/sql.js'; import initSqlJs from '@jlongster/sql.js';
import { SQLiteFS } from 'absurd-sql'; import { SQLiteFS } from 'absurd-sql';
import IndexedDBBackend from 'absurd-sql/dist/indexeddb-backend'; import IndexedDBBackend from 'absurd-sql/dist/indexeddb-backend';
async function setupDatabase() { async function setupDatabase() {
let SQL = await initSqlJs({ locateFile: file => file }); const SQL = await initSqlJs({ locateFile: f => f });
let sqlFS = new SQLiteFS(SQL.FS, new IndexedDBBackend()); const sqlFS = new SQLiteFS(SQL.FS, new IndexedDBBackend());
SQL.register_for_idb(sqlFS); SQL.register_for_idb(sqlFS);
SQL.FS.mkdir('/sql'); SQL.FS.mkdir('/sql');
@ -128,25 +125,24 @@ async function setupDatabase() {
} }
``` ```
## Troubleshooting ## Troubleshooting Directives
### Common Issues ### If SharedArrayBuffer is unavailable:
1. SharedArrayBuffer not available - Verify COOP/COEP headers.
- Check COOP/COEP headers - Check browser support.
- Verify browser support - Activate fallback mode.
- Test fallback mode
2. Worker initialization failures ### If worker initialization fails:
- Check file paths - Verify file paths.
- Verify module imports - Confirm module imports.
- Check browser console for errors - Inspect browser console for errors.
3. Performance issues ### If performance degrades:
- Monitor IndexedDB usage - Inspect IndexedDB usage.
- Check for unnecessary operations - Eliminate redundant operations.
- Verify transaction usage - Confirm transaction enforcement.
## Resources ## Reference Materials
- [Project Demo](https://priceless-keller-d097e5.netlify.app/) - [Project Demo](https://priceless-keller-d097e5.netlify.app/)
- [Example Project](https://github.com/jlongster/absurd-example-project) - [Example Project](https://github.com/jlongster/absurd-example-project)
- [Blog Post](https://jlongster.com/future-sql-web) - [Blog Post](https://jlongster.com/future-sql-web)

260
src/views/ContactImportView.vue

@ -123,74 +123,222 @@
<script lang="ts"> <script lang="ts">
/** /**
* @file Contact Import View Component * ContactImportView - Contact Import and Batch Processing Page
* @author Matthew Raymer *
* This component handles the batch import of contacts with comprehensive
* validation, duplicate detection, and field comparison capabilities.
* It provides users with detailed information about each contact before
* importing, allowing them to make informed decisions about their contact list.
*
* ## How the Contact Import Page Works
*
* ### Page Entry and Data Processing
*
* **Entry Points**:
* - **URL Parameters**: Direct navigation with contact data in URL
* - **Contact Input Form**: Redirected from ContactsView with parsed data
* - **Manual Entry**: Users can input contact data directly
*
* **Data Processing Pipeline**:
* 1. **Input Validation**: Parse and validate contact data format
* 2. **Contact Analysis**: Check each contact against existing database
* 3. **Duplicate Detection**: Identify existing contacts and compare fields
* 4. **UI Preparation**: Prepare contact list with status indicators
*
* ### Contact Analysis and Display
*
* **Contact Status Classification**:
* - **New Contacts** (Green): Contacts not in database
* - **Existing Contacts** (Orange): Contacts already in database
* - **Identical Contacts**: Existing contacts with no field differences
*
* **Field Comparison System**:
* - **Automatic Detection**: Compare all contact fields
* - **Difference Display**: Show old vs new values in table format
* - **User Decision**: Allow users to see what will be updated
*
* **Contact List Structure**:
* ```typescript
* interface ContactImportItem {
* did: string; // Decentralized identifier
* name?: string; // Display name
* publicKey?: string; // Public key
* publicKeyBase64?: string; // Base64 encoded key
* status: 'new' | 'existing'; // Import status
* differences?: FieldDifferences; // Field comparison results
* }
* ```
*
* ### User Interface Components
*
* **Header Section**:
* - **Back Navigation**: Return to previous page
* - **Page Title**: "Contact Import" heading
* - **Loading State**: Spinner during data processing
*
* **Visibility Settings**:
* - **Activity Visibility Checkbox**: Control activity sharing with imported contacts
* - **Global Setting**: Applies to all contacts being imported
* *
* This component handles the import of contacts into the TimeSafari app. * **Contact List Display**:
* It supports multiple import methods and handles duplicate detection, * - **Contact Cards**: Individual contact information with:
* contact validation, and visibility settings. * - Selection checkbox for import control
* - Contact name and DID display
* - Status indicator (New/Existing)
* - Field comparison table for existing contacts
* *
* Import Methods: * **Field Comparison Table**:
* 1. Direct URL Query Parameters: * - **Three-Column Layout**: Field name, old value, new value
* Example: /contact-import?contacts=[{"did":"did:example:123","name":"Alice"}] * - **Difference Highlighting**: Clear visual indication of changes
* - **Comprehensive Coverage**: All contact fields are compared
* *
* 2. JWT in URL Path: * **Import Controls**:
* Example: /contact-import/eyJhbGciOiJFUzI1NksifQ... * - **Select All/None**: Bulk selection controls
* - Supports both single and bulk imports * - **Individual Selection**: Per-contact import control
* - JWT payload can be either: * - **Import Button**: Execute the selected imports
* a) Array format: { contacts: [{did: "...", name: "..."}, ...] }
* b) Single contact: { own: true, did: "...", name: "..." }
* *
* 3. Manual JWT Input: * ### Data Processing Logic
* - Accepts pasted JWT strings
* - Validates format and content before processing
* *
* URL Examples: * **Contact Validation**:
* ```typescript
* // Validate DID format
* const isValidDid = (did: string): boolean => {
* return did.startsWith('did:') && did.length > 10;
* };
*
* // Check for existing contact
* const existingContact = await $getContact(did);
* const isExisting = existingContact !== null;
* ``` * ```
* # Bulk import via query params
* /contact-import?contacts=[
* {"did":"did:example:123","name":"Alice"},
* {"did":"did:example:456","name":"Bob"}
* ]
* *
* # Single contact via JWT * **Field Comparison Algorithm**:
* /contact-import/eyJhbGciOiJFUzI1NksifQ.eyJvd24iOnRydWUsImRpZCI6ImRpZDpleGFtcGxlOjEyMyJ9... * ```typescript
* // Compare contact fields
* const compareFields = (existing: Contact, importing: Contact) => {
* const differences: FieldDifferences = {};
* *
* # Bulk import via JWT * for (const field of ['name', 'publicKey', 'publicKeyBase64']) {
* /contact-import/eyJhbGciOiJFUzI1NksifQ.eyJjb250YWN0cyI6W3siZGlkIjoiZGlkOmV4YW1wbGU6MTIzIn1dfQ... * if (existing[field] !== importing[field]) {
* differences[field] = {
* old: existing[field] || '',
* new: importing[field] || ''
* };
* }
* }
* *
* # Redirect to contacts page (single contact) * return differences;
* /contacts?contactJwt=eyJhbGciOiJFUzI1NksifQ... * };
* ``` * ```
* *
* Features: * **Import Decision Logic**:
* - Automatic duplicate detection * - **New Contact**: Add to database with all provided fields
* - Field-by-field comparison for existing contacts * - **Existing Contact with Differences**: Update with new field values
* - Batch visibility settings * - **Existing Contact without Differences**: Skip import (already identical)
* - Auto-import for single new contacts * - **Invalid Contact**: Skip import and show error
* - Error handling and validation *
* * ### Batch Import Process
* State Management: *
* - Tracks existing contacts * **Pre-Import Validation**:
* - Maintains selection state for bulk imports * - Verify all selected contacts are valid
* - Records differences for duplicate contacts * - Check database constraints
* - Manages visibility settings * - Validate visibility settings
*
* Security Considerations:
* - JWT validation for imported contacts
* - Visibility control per contact
* - Error handling for malformed data
*
* @example
* // Component usage in router
* {
* path: "/contact-import/:jwt?",
* name: "contact-import",
* component: ContactImportView
* }
* *
* @see {@link Contact} for contact data structure * **Database Transaction**:
* @see {@link setVisibilityUtil} for visibility management * ```typescript
* // Execute batch import
* const importContacts = async () => {
* const selectedContacts = contactsImporting.filter((_, index) =>
* contactsSelected[index]
* );
*
* await $beginTransaction();
*
* try {
* for (const contact of selectedContacts) {
* if (contactsExisting[contact.did]) {
* await $updateContact(contact.did, contact);
* } else {
* await $addContact(contact);
* }
* }
*
* await $commitTransaction();
* notify.success('Contacts imported successfully');
* } catch (error) {
* await $rollbackTransaction();
* notify.error('Import failed: ' + error.message);
* }
* };
* ```
*
* **Post-Import Actions**:
* - Update contact list in parent component
* - Apply visibility settings if enabled
* - Navigate back to contacts list
* - Display success/error notifications
*
* ### Error Handling and Edge Cases
*
* **Input Validation Errors**:
* - Malformed JSON data
* - Invalid DID format
* - Missing required fields
* - Empty contact arrays
*
* **Database Errors**:
* - Constraint violations
* - Storage quota exceeded
* - Concurrent access conflicts
* - Transaction failures
*
* **UI Error Recovery**:
* - Graceful handling of network failures
* - Retry mechanisms for failed operations
* - Clear error messages for users
* - Fallback options for unsupported features
*
* ### Performance Optimizations
*
* **Efficient Processing**:
* - Batch database operations
* - Optimized field comparison algorithms
* - Lazy loading of contact details
* - Debounced UI updates
*
* **Memory Management**:
* - Cleanup of temporary data structures
* - Proper disposal of event listeners
* - Efficient state management
* - Garbage collection optimization
*
* **UI Responsiveness**:
* - Asynchronous data processing
* - Progressive loading of contact data
* - Non-blocking UI updates
* - Optimized rendering for large lists
*
* ### Integration Points
*
* **Database Integration**:
* - PlatformServiceMixin for database operations
* - Transaction-based data integrity
* - Optimized queries for contact retrieval
* - Proper error handling and rollback
*
* **Navigation Integration**:
* - Route-based data passing
* - Deep linking support
* - Back navigation handling
* - Modal dialog management
*
* **Notification System**:
* - Success/error message display
* - Progress indication during import
* - User feedback for all operations
* - Accessibility-compliant notifications
*
* @author Matthew Raymer
* @date 2025-08-04
*/ */
import * as R from "ramda"; import * as R from "ramda";

138
src/views/ContactsView.vue

@ -123,6 +123,144 @@
</template> </template>
<script lang="ts"> <script lang="ts">
/**
* ContactsView - Main Contacts Management Page
*
* This component serves as the central hub for contact management in Time Safari.
* It provides a comprehensive interface for viewing, adding, importing, and managing
* contacts with various input methods and bulk operations.
*
* ## How the Contacts Page Works
*
* ### Contact Input and Import Workflow
*
* **ContactInputForm Component**:
* - **Input Field**: Accepts contact data in multiple formats:
* - Individual contact: `"did:ethr:0x..., Alice, publicKey"`
* - JSON array: `"Paste this: [{"did":"did:ethr:0x...","name":"Alice"}]"`
* - URL with contact data: `"https://example.com/contact-data"`
* - **Add Button**: Triggers contact processing and validation
* - **QR Scanner**: Alternative input method for mobile devices
* - **Real-time Validation**: Checks DID format and required fields
*
* **Contact Processing Logic**:
* 1. **Input Parsing**: The system parses the input to determine format
* 2. **Data Validation**: Validates DID format and required fields
* 3. **Duplicate Detection**: Checks if contact already exists
* 4. **Import Decision**:
* - Single contact: Direct addition to database
* - Multiple contacts: Redirect to ContactImportView for batch processing
* - Invalid data: Display error message
*
* **Import Workflow**:
* - **Single Contact**: Added directly with success notification
* - **Multiple Contacts**: Redirected to ContactImportView for:
* - Contact comparison and selection
* - Field difference display
* - Batch import execution
* - Visibility settings configuration
*
* ### Contact List Management
*
* **ContactListItem Components**:
* - **Contact Display**: Name, DID, and identicon
* - **Selection Checkboxes**: For bulk operations
* - **Action Buttons**: Gift, offer, and contact management
* - **Status Indicators**: Online/offline status, activity visibility
*
* **Bulk Operations**:
* - **Select All**: Toggle selection of all contacts
* - **Copy Selected**: Export selected contacts as JSON/CSV
* - **Bulk Actions**: Gift amounts, visibility settings
*
* **Contact Actions**:
* - **Gift Dialog**: Record gifts given to/received from contact
* - **Offer Dialog**: Create and manage offers
* - **Contact Edit**: Modify contact information
* - **Large Identicon**: View full-size contact identicon
*
* ### Data Flow and State Management
*
* **Contact Data Structure**:
* ```typescript
* interface Contact {
* did: string; // Decentralized identifier
* name?: string; // Display name (optional)
* publicKey?: string; // Public key for verification
* publicKeyBase64?: string; // Base64 encoded public key
* visibility?: boolean; // Activity visibility setting
* }
* ```
*
* **State Management**:
* - **Contact List**: Reactive list of all user contacts
* - **Selection State**: Track selected contacts for bulk operations
* - **UI State**: Toggle visibility of give totals, actions, etc.
* - **Modal State**: Manage dialog visibility and data
*
* **Database Operations**:
* - **Contact Addition**: Add new contacts with validation
* - **Contact Updates**: Modify existing contact information
* - **Contact Deletion**: Remove contacts (with confirmation)
* - **Bulk Operations**: Process multiple contacts efficiently
*
* ### Error Handling and User Feedback
*
* **Input Validation Errors**:
* - Invalid DID format
* - Missing required fields
* - Malformed JSON data
* - Network errors for URL-based imports
*
* **User Notifications**:
* - Success messages for successful operations
* - Error messages with specific details
* - Warning messages for potential issues
* - Confirmation dialogs for destructive actions
*
* **Error Recovery**:
* - Graceful handling of network failures
* - Retry mechanisms for failed operations
* - Fallback options for unsupported features
*
* ### Performance Optimizations
*
* **Contact List Rendering**:
* - Virtual scrolling for large contact lists
* - Efficient filtering and sorting
* - Lazy loading of contact details
*
* **Database Operations**:
* - Batch processing for multiple contacts
* - Transaction-based updates for data integrity
* - Optimized queries for contact retrieval
*
* **UI Responsiveness**:
* - Debounced input validation
* - Asynchronous contact processing
* - Progressive loading of contact data
*
* ### Integration Points
*
* **Platform Services**:
* - Database operations via PlatformServiceMixin
* - QR code scanning via platform-specific implementations
* - File system access for contact export
*
* **External Services**:
* - Endorser.ch for contact verification
* - JWT token processing for secure imports
* - URL-based contact data retrieval
*
* **Navigation Integration**:
* - Deep linking to contact import
* - Route-based contact filtering
* - Modal dialog management
*
* @author Matthew Raymer
* @date 2025-08-04
*/
import { AxiosError } from "axios"; import { AxiosError } from "axios";
import { Buffer } from "buffer/"; import { Buffer } from "buffer/";
import { IndexableType } from "dexie"; import { IndexableType } from "dexie";

665
test-playwright/45-contact-import.spec.ts

File diff suppressed because it is too large
Loading…
Cancel
Save