This document describes the state management stores used in the Crypto Twitter Alpha Stream frontend. All stores are built using Svelte 5's Runes system for optimal reactivity and performance.
Location: src/lib/stores/events.svelte.ts
Manages the collection of Twitter events received from the backend.
class EventStore {
events: TwitterEvent[] // Array of all events
eventMap: Map<string, TwitterEvent> // Map for quick lookups by ID
}filteredEvents: TwitterEvent[] // Events filtered by current filter criteriaAdds a new event to the store. If an event with the same primaryId already exists, it updates the existing event instead.
Parameters:
event: The Twitter event to add
Example:
import { eventsStore } from '$lib/stores/events.svelte';
eventsStore.addEvent({
primaryId: '123',
type: 'post_created',
timestamp: Date.now(),
user: { username: 'elonmusk', ... },
data: { tweet: { ... } }
});Updates an existing event in the store.
Parameters:
event: The updated event data
Example:
eventsStore.updateEvent({
primaryId: '123',
type: 'post_updated',
// ... updated fields
});Removes all events from the store.
Example:
eventsStore.clear();<script lang="ts">
import { eventsStore } from '$lib/stores/events.svelte';
// Access reactive state
const events = $derived(eventsStore.filteredEvents);
</script>
{#each events as event}
<EventCard {event} />
{/each}Location: src/lib/stores/socket.svelte.ts
Manages the Socket.IO connection to the backend server.
class SocketStore {
socket: Socket | null // Socket.IO instance
connectionStatus: ConnectionStatus // 'connected' | 'disconnected' | 'reconnecting'
endpoint: string // Current endpoint URL
}Establishes a Socket.IO connection to the backend.
Parameters:
endpoint(optional): The backend URL to connect to. Defaults to current host.
Example:
import { socketStore } from '$lib/stores/socket.svelte';
socketStore.connect('http://localhost:3000');Events Handled:
connect: Updates status to 'connected'disconnect: Updates status to 'disconnected'event: Adds received event to events storestate: Syncs initial state from backend
Closes the Socket.IO connection.
Example:
socketStore.disconnect();Switches to a different backend endpoint.
Parameters:
endpoint: The new backend URL
Example:
socketStore.changeEndpoint('http://localhost:4000');<script lang="ts">
import { socketStore } from '$lib/stores/socket.svelte';
import { onMount } from 'svelte';
onMount(() => {
socketStore.connect();
return () => {
socketStore.disconnect();
};
});
const status = $derived(socketStore.connectionStatus);
</script>
<div class="status-{status}">
{status === 'connected' ? 'Connected' : 'Disconnected'}
</div>Location: src/lib/stores/filters.svelte.ts
Manages filtering criteria for events.
class FilterStore {
keywords: string[] // Keywords to filter by
eventTypes: string[] // Event types to display
users: string[] // Usernames to filter by
}hasActiveFilters: boolean // True if any filters are activeSets the keyword filter.
Parameters:
keywords: Array of keywords to filter by
Example:
import { filtersStore } from '$lib/stores/filters.svelte';
filtersStore.setKeywords(['bitcoin', 'ethereum']);Toggles an event type filter on/off.
Parameters:
type: The event type to toggle
Example:
filtersStore.toggleEventType('post_created');Toggles a user filter on/off.
Parameters:
username: The username to toggle
Example:
filtersStore.toggleUser('elonmusk');Determines if an event should be displayed based on current filters.
Parameters:
event: The event to check
Returns:
trueif the event matches all filter criteria
Example:
const shouldShow = filtersStore.shouldDisplayEvent(event);Removes all active filters.
Example:
filtersStore.clearAll();<script lang="ts">
import { filtersStore } from '$lib/stores/filters.svelte';
const hasFilters = $derived(filtersStore.hasActiveFilters);
function handleUserClick(username: string) {
filtersStore.toggleUser(username);
}
</script>
<button onclick={() => handleUserClick('elonmusk')}>
Filter by @elonmusk
</button>
{#if hasFilters}
<button onclick={() => filtersStore.clearAll()}>
Clear Filters
</button>
{/if}Location: src/lib/stores/stats.svelte.ts
Tracks statistics about the event stream.
class StatsStore {
total: number // Total events received
delivered: number // Events delivered (not deduped)
deduped: number // Events deduplicated
startTime: number // Timestamp when tracking started
}eventsPerMin: string // Events per minute rate (formatted)Increments the total and delivered counters.
Example:
import { statsStore } from '$lib/stores/stats.svelte';
statsStore.incrementTotal();Increments the total and deduped counters.
Example:
statsStore.incrementDeduped();Updates stats from backend state sync.
Parameters:
state: State object from backend
Example:
statsStore.updateFromState({
total: 100,
delivered: 95,
deduped: 5
});Resets all counters to zero.
Example:
statsStore.reset();<script lang="ts">
import { statsStore } from '$lib/stores/stats.svelte';
const eventsPerMin = $derived(statsStore.eventsPerMin);
const total = $derived(statsStore.total);
</script>
<div class="stats">
<div>Events/min: {eventsPerMin}</div>
<div>Total: {total}</div>
</div>Location: src/lib/stores/search.svelte.ts
Manages search functionality and history.
class SearchStore {
query: string // Current search query
history: string[] // Search history
isActive: boolean // Whether search is active
}hasResults: boolean // True if search has resultsSets the current search query.
Parameters:
query: The search string
Example:
import { searchStore } from '$lib/stores/search.svelte';
searchStore.setQuery('bitcoin');Adds a query to search history.
Parameters:
query: The search string to save
Example:
searchStore.addToHistory('ethereum');Clears all search history.
Example:
searchStore.clearHistory();Activates search mode.
Example:
searchStore.activate();Deactivates search mode and clears query.
Example:
searchStore.deactivate();<script lang="ts">
import { searchStore } from '$lib/stores/search.svelte';
let searchInput = $state('');
function handleSearch() {
searchStore.setQuery(searchInput);
searchStore.addToHistory(searchInput);
}
</script>
<input
bind:value={searchInput}
onfocus={() => searchStore.activate()}
placeholder="Search events..."
/>Location: src/lib/stores/toast.svelte.ts
Manages toast notifications.
class ToastStore {
toasts: Toast[] // Array of active toasts
}interface Toast {
id: string
message: string
type: 'info' | 'success' | 'warning' | 'error'
duration: number
}Displays a toast notification.
Parameters:
message: The message to displaytype(optional): Toast type. Default: 'info'duration(optional): Duration in ms. Default: 3000
Example:
import { toastStore } from '$lib/stores/toast.svelte';
toastStore.show('Connected to server', 'success');
toastStore.show('Connection lost', 'error', 5000);Dismisses a specific toast.
Parameters:
id: The toast ID to dismiss
Example:
toastStore.dismiss('toast-123');Dismisses all toasts.
Example:
toastStore.clear();<script lang="ts">
import { toastStore } from '$lib/stores/toast.svelte';
function handleAction() {
try {
// ... some action
toastStore.show('Action completed', 'success');
} catch (error) {
toastStore.show('Action failed', 'error');
}
}
</script>Always use $derived to access store state in components:
<script lang="ts">
import { eventsStore } from '$lib/stores/events.svelte';
// ✅ Good - reactive
const events = $derived(eventsStore.filteredEvents);
// ❌ Bad - not reactive
const events = eventsStore.filteredEvents;
</script>Call store methods directly, don't try to mutate state:
// ✅ Good
eventsStore.addEvent(newEvent);
// ❌ Bad
eventsStore.events.push(newEvent);Always disconnect sockets and clear subscriptions:
<script lang="ts">
import { onMount } from 'svelte';
import { socketStore } from '$lib/stores/socket.svelte';
onMount(() => {
socketStore.connect();
return () => {
socketStore.disconnect();
};
});
</script>Use derived state for computed values instead of recalculating in components:
// ✅ Good - computed once in store
const filteredEvents = $derived(eventsStore.filteredEvents);
// ❌ Bad - recalculated on every render
const filteredEvents = events.filter(e => filtersStore.shouldDisplayEvent(e));Always use TypeScript types for store data:
import type { TwitterEvent } from '$lib/types';
function addEvent(event: TwitterEvent) {
eventsStore.addEvent(event);
}All stores include comprehensive unit tests. To run store tests:
npm test -- storesExample test:
import { describe, it, expect, beforeEach } from 'vitest';
import { eventsStore } from '$lib/stores/events.svelte';
describe('EventStore', () => {
beforeEach(() => {
eventsStore.clear();
});
it('should add new event', () => {
const event = { primaryId: '123', type: 'post_created', ... };
eventsStore.addEvent(event);
expect(eventsStore.events).toHaveLength(1);
});
});