# @nomyx/gun-sync

A lightweight, powerful library for synchronizing application state in real-time using Gun.js with React hooks and TypeScript support.

## Features

- 🔄 **Real-time state synchronization** using Gun.js peer-to-peer database
- ⚛️ **React hooks** for seamless integration with React applications
- 🔐 **User authentication** with built-in auth lifecycle management
- 👥 **Presence tracking** to monitor active users
- 📝 **Collection management** with CRUD operations
- 🎯 **TypeScript support** with full type definitions
- 🌐 **Context provider** for easy Gun instance sharing
- ⚡ **Lightweight** with minimal dependencies

## Installation

```bash
npm install @nomyx/gun-sync gun
```

## Quick Start

### 1. Setup with Context Provider

```tsx
import React from 'react';
import { GunProvider } from '@nomyx/gun-sync';
import App from './App';

function Root() {
  return (
    <GunProvider peers={['https://gun-manhattan.herokuapp.com/gun']}>
      <App />
    </GunProvider>
  );
}
```

### 2. Use Hooks in Components

```tsx
import React from 'react';
import { useGunState, useGunUser } from '@nomyx/gun-sync';

function MyComponent() {
  // Synchronized state
  const [message, setMessage] = useGunState('chat', 'currentMessage', '');
  
  // User authentication
  const [userState, userActions] = useGunUser();
  
  return (
    <div>
      <input 
        value={message} 
        onChange={(e) => setMessage(e.target.value)}
        placeholder="Type a message..."
      />
      
      {userState.isLoggedIn ? (
        <button onClick={userActions.signOut}>Sign Out</button>
      ) : (
        <button onClick={() => userActions.signIn('username', 'password')}>
          Sign In
        </button>
      )}
    </div>
  );
}
```

## API Reference

### Core Functions

#### `createSync(config: SyncConfig): IGunInstance`

Creates and initializes a Gun instance with the specified peer configuration.

```typescript
import { createSync } from '@nomyx/gun-sync';

const gun = createSync({
  peers: ['https://gun-manhattan.herokuapp.com/gun']
});
```

### Context

#### `<GunProvider peers={string[]}>`

Provides a Gun instance to all descendant components.

```tsx
<GunProvider peers={['wss://gun-us.herokuapp.com/gun']}>
  <App />
</GunProvider>
```

#### `useGun(): IGunInstance`

Accesses the Gun instance from context.

```typescript
const gun = useGun();
```

### State Management Hooks

#### `useGunState<T>(scope: string, key: string, initialValue: T, gunInstance?: IGunInstance)`

Synchronizes a piece of state with Gun.js, similar to `useState`.

```typescript
const [count, setCount] = useGunState('app', 'counter', 0);

// Update state
setCount(count + 1);

// Or with function
setCount(prev => prev + 1);
```

#### `useGunValue<T>(scope: string, key: string, initialValue: T, gunInstance?: IGunInstance)`

Read-only access to synchronized values.

```typescript
const currentUser = useGunValue('app', 'currentUser', null);
```

#### `useGunSet<T>(scope: string, gunInstance?: IGunInstance)`

Manages a synchronized collection of items.

```typescript
const [items, { addItem, updateItem, removeItem }] = useGunSet<Todo>('todos');

// Add new item (returns generated ID)
const id = addItem({ text: 'Learn Gun.js', completed: false });

// Update item
updateItem(id, { completed: true });

// Remove item
removeItem(id);
```

### Authentication

#### `useGunUser(gunInstance?: IGunInstance)`

Manages user authentication lifecycle.

```typescript
const [userState, userActions] = useGunUser();

// User state
userState.isLoggedIn;   // boolean
userState.isLoading;    // boolean
userState.user;         // user object or null
userState.error;        // error message or undefined

// User actions
await userActions.signUp('alice', 'password123');
await userActions.signIn('alice', 'password123');
userActions.signOut();
```

### Presence Tracking

#### `useGunPresence(scope: string, gunInstance?: IGunInstance)`

Tracks active users within a scope.

```typescript
const activeUsers = useGunPresence('chat-room-1');

// Returns array of PresenceInfo objects
activeUsers.forEach(user => {
  console.log(`User ${user.id} last seen: ${new Date(user.lastSeen)}`);
});
```

## Advanced Usage

### Without Context Provider

You can use hooks with a specific Gun instance:

```typescript
import { createSync, useGunState } from '@nomyx/gun-sync';

function MyComponent() {
  const gun = createSync({ peers: ['https://gun-manhattan.herokuapp.com/gun'] });
  const [data, setData] = useGunState('app', 'data', null, gun);
  
  return <div>{data}</div>;
}
```

### Type Safety

All hooks support TypeScript generics for type safety:

```typescript
interface User {
  id: string;
  name: string;
  email: string;
}

const [users, userActions] = useGunSet<User>('users');
const [currentUser, setCurrentUser] = useGunState<User | null>('app', 'currentUser', null);
```

## Examples

### Real-time Chat

```tsx
import React, { useState } from 'react';
import { useGunSet, useGunUser } from '@nomyx/gun-sync';

interface Message {
  text: string;
  author: string;
  timestamp: number;
}

function Chat() {
  const [messages, { addItem }] = useGunSet<Message>('chat-messages');
  const [userState] = useGunUser();
  const [newMessage, setNewMessage] = useState('');

  const sendMessage = () => {
    if (newMessage.trim() && userState.user) {
      addItem({
        text: newMessage,
        author: userState.user.alias,
        timestamp: Date.now()
      });
      setNewMessage('');
    }
  };

  return (
    <div>
      <div>
        {messages
          .sort((a, b) => a.timestamp - b.timestamp)
          .map(msg => (
            <div key={msg.id}>
              <strong>{msg.author}:</strong> {msg.text}
            </div>
          ))
        }
      </div>
      
      {userState.isLoggedIn && (
        <div>
          <input
            value={newMessage}
            onChange={(e) => setNewMessage(e.target.value)}
            onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
          />
          <button onClick={sendMessage}>Send</button>
        </div>
      )}
    </div>
  );
}
```

### Collaborative Todo List

```tsx
import React from 'react';
import { useGunSet } from '@nomyx/gun-sync';

interface Todo {
  text: string;
  completed: boolean;
  createdAt: number;
}

function TodoList() {
  const [todos, { addItem, updateItem, removeItem }] = useGunSet<Todo>('todos');

  const addTodo = (text: string) => {
    addItem({
      text,
      completed: false,
      createdAt: Date.now()
    });
  };

  const toggleTodo = (id: string) => {
    const todo = todos.find(t => t.id === id);
    if (todo) {
      updateItem(id, { completed: !todo.completed });
    }
  };

  return (
    <div>
      <input
        placeholder="Add todo..."
        onKeyPress={(e) => {
          if (e.key === 'Enter') {
            addTodo(e.currentTarget.value);
            e.currentTarget.value = '';
          }
        }}
      />
      
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => toggleTodo(todo.id)}
            />
            <span style={{ 
              textDecoration: todo.completed ? 'line-through' : 'none' 
            }}>
              {todo.text}
            </span>
            <button onClick={() => removeItem(todo.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}
```

## Peer Dependencies

- React >= 16.8.0
- Gun.js ^0.2020.1239

## License

ISC

## Contributing

Issues and pull requests are welcome! Please check out the [GitHub repository](https://github.com/nomyx/gun-sync).

## Support

For questions and support, please visit the [Gun.js community chat](http://chat.gun.eco).