@lumina-study/user-settings-redux
Version:
Redux store for Lumina Study user settings with unstorage persistence
388 lines (268 loc) • 8.79 kB
Markdown
# @lumina-study/user-settings-redux
Redux store for Lumina Study user settings with unstorage persistence.
This package provides a ready-to-use Redux store that manages user settings (language, degree ID) with automatic persistence using [unstorage](https://unstorage.unjs.io/). Changes to the store are automatically synced to your chosen storage backend (localStorage, IndexedDB, file system, etc.).
## Features
- Redux Toolkit-based store for user settings
- Automatic persistence with unstorage
- TypeScript support with full type safety
- React hooks for easy integration
- Debounced storage writes for performance
- Support for any unstorage driver
## Installation
```bash
pnpm add @lumina-study/user-settings-redux
# or
npm install @lumina-study/user-settings-redux
```
## Quick Start
### 1. Create the Store
```typescript
import { createStorage } from 'unstorage';
import localStorageDriver from 'unstorage/drivers/localstorage';
import { createUserSettingsStore } from '@lumina-study/user-settings-redux';
// Create an unstorage instance with your preferred driver
const storage = createStorage({
driver: localStorageDriver({ base: 'lumina:' })
});
// Create the Redux store (async - loads persisted settings)
const store = await createUserSettingsStore({ storage });
```
### 2. Integrate with React
```typescript
import { Provider } from 'react-redux';
import { createStorage } from 'unstorage';
import localStorageDriver from 'unstorage/drivers/localstorage';
import { createUserSettingsStore } from '@lumina-study/user-settings-redux';
// Create storage and store
const storage = createStorage({
driver: localStorageDriver({ base: 'lumina:' })
});
const store = await createUserSettingsStore({ storage });
// Wrap your app with the Provider
function App() {
return (
<Provider store={store}>
<YourApp />
</Provider>
);
}
```
### 3. Use in Components
```typescript
import {
useUserSettings,
useLanguage,
useDegreeId,
} from '@lumina-study/user-settings-redux';
function UserSettingsComponent() {
// Get all settings
const settings = useUserSettings();
// Or use specific hooks
const [language, setLanguage] = useLanguage();
const [degreeId, setDegreeId] = useDegreeId();
return (
<div>
<p>Current Language: {language}</p>
<button onClick={() => setLanguage('he')}>עברית</button>
<button onClick={() => setLanguage('en')}>English</button>
<p>Degree ID: {degreeId || 'Not set'}</p>
<button onClick={() => setDegreeId('degree-123')}>
Set Degree
</button>
</div>
);
}
```
## Storage Drivers
You can use any [unstorage driver](https://unstorage.unjs.io/drivers). Here are some examples:
### Browser localStorage
```typescript
import { createStorage } from 'unstorage';
import localStorageDriver from 'unstorage/drivers/localstorage';
const storage = createStorage({
driver: localStorageDriver({ base: 'lumina:' })
});
```
### Browser IndexedDB
```typescript
import { createStorage } from 'unstorage';
import indexedDbDriver from 'unstorage/drivers/indexedb';
const storage = createStorage({
driver: indexedDbDriver({ base: 'lumina:' })
});
```
### Node.js File System
```typescript
import { createStorage } from 'unstorage';
import fsDriver from 'unstorage/drivers/fs';
const storage = createStorage({
driver: fsDriver({ base: './.data' })
});
```
### Memory (for testing)
```typescript
import { createStorage } from 'unstorage';
const storage = createStorage(); // Uses memory driver by default
```
## API Reference
### Store Creation
#### `createUserSettingsStore(options)`
Creates and initializes the Redux store with persistence.
**Parameters:**
- `options.storage` (required): The unstorage instance to use for persistence
- `options.devTools` (optional): Enable Redux DevTools (default: `true`)
**Returns:** Promise<Store> - The configured Redux store
### Actions
#### `setUserSettings(settings: UserSettings)`
Set the complete user settings state.
```typescript
import { setUserSettings } from '@lumina-study/user-settings-redux';
dispatch(setUserSettings({ language: 'he', degreeId: 'degree-123' }));
```
#### `setLanguage(language: 'he' | 'en')`
Update the user interface language.
```typescript
import { setLanguage } from '@lumina-study/user-settings-redux';
dispatch(setLanguage('he'));
```
#### `setDegreeId(degreeId: string | null)`
Update the user's degree identifier.
```typescript
import { setDegreeId } from '@lumina-study/user-settings-redux';
dispatch(setDegreeId('degree-123'));
```
#### `resetUserSettings()`
Reset settings to default values.
```typescript
import { resetUserSettings } from '@lumina-study/user-settings-redux';
dispatch(resetUserSettings());
```
### Hooks
#### `useUserSettings()`
Access the complete user settings state.
```typescript
const settings = useUserSettings();
// { language: 'en', degreeId: null }
```
#### `useLanguage()`
Access and update the language setting.
```typescript
const [language, setLanguage] = useLanguage();
setLanguage('he');
```
#### `useDegreeId()`
Access and update the degree ID setting.
```typescript
const [degreeId, setDegreeId] = useDegreeId();
setDegreeId('degree-123');
```
#### `useAppDispatch()`
Typed dispatch hook.
```typescript
const dispatch = useAppDispatch();
dispatch(setLanguage('en'));
```
#### `useAppSelector(selector)`
Typed selector hook.
```typescript
const language = useAppSelector((state) => state.userSettings.language);
```
### Utilities
#### `loadPersistedSettings(storage)`
Manually load settings from storage.
```typescript
import { loadPersistedSettings } from '@lumina-study/user-settings-redux';
const settings = await loadPersistedSettings(storage);
```
#### `clearPersistedSettings(storage)`
Clear persisted settings from storage.
```typescript
import { clearPersistedSettings } from '@lumina-study/user-settings-redux';
await clearPersistedSettings(storage);
```
## TypeScript
The package is fully typed and exports all necessary types:
```typescript
import type {
UserSettings,
RootState,
AppDispatch,
UserSettingsStore,
CreateStoreOptions,
} from '@lumina-study/user-settings-redux';
```
## How Persistence Works
1. **Initialization**: When you create the store, it automatically loads any previously saved settings from the storage driver
2. **Auto-save**: Any action that modifies the user settings state triggers an automatic save to storage (debounced by 300ms)
3. **Persistence Key**: Settings are stored under the key `'user-settings'` in your storage backend
## Advanced Usage
### Direct Store Usage (without React)
```typescript
import { createUserSettingsStore, setLanguage } from '@lumina-study/user-settings-redux';
const store = await createUserSettingsStore({ storage });
// Subscribe to changes
store.subscribe(() => {
console.log('Settings updated:', store.getState().userSettings);
});
// Dispatch actions
store.dispatch(setLanguage('he'));
```
### Custom Middleware
```typescript
import { configureStore } from '@reduxjs/toolkit';
import { userSettingsReducer, createPersistenceMiddleware } from '@lumina-study/user-settings-redux';
const store = configureStore({
reducer: {
userSettings: userSettingsReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware()
.concat(createPersistenceMiddleware(storage))
.concat(yourCustomMiddleware),
});
```
## Development
### Building
```bash
pnpm install
pnpm run build
```
This will compile TypeScript files to the `dist` directory.
### Publishing
#### Automated Publishing (Recommended)
The package is automatically published to npm when pushing to the `main` branch via GitHub Actions.
**Setup:**
1. Create an npm access token:
- Go to https://www.npmjs.com/settings/YOUR_USERNAME/tokens
- Click "Generate New Token" → "Automation"
- Copy the token
2. Add the token to GitHub:
- Go to your repository settings → Secrets and variables → Actions
- Click "New repository secret"
- Name: `NPM_TOKEN`
- Value: Paste your npm token
3. Update the version in `package.json`
4. Push to main:
```bash
git add package.json CHANGELOG.md
git commit -m "chore: bump version to x.x.x"
git push origin main
```
The CI/CD workflow will:
- Run tests
- Build the package
- Publish to npm (if version changed)
- Create a git tag
- Create a GitHub release
#### Manual Publishing
You can also publish manually using [release-it](https://github.com/release-it/release-it):
```bash
# Dry run
pnpm run release:dry
# Create a release
pnpm run release
```
## License
MIT
## Related Packages
- [@lumina-study/user-settings](https://github.com/luminastudy/user-settings) - JSON schema and TypeScript types for user settings