Browse Source
This commit represents a checkpoint in implementing a sophisticated event configuration system with people tag anchors. The implementation has grown complex and needs reimplementation in a smarter way. ## Major Changes: ### Event Configuration System Overhaul - Replaced simple allowed/disabled kinds with EventKindConfig objects - Each event kind now has individual limits and type-specific settings: - Kind 0 (profiles): Controls max profiles to fetch - Kind 3 (follow lists): Has depth setting and complex fetch logic - Kind 30040: Has nestedLevels setting - Created new EventTypeConfig component replacing EventKindFilter ### Follow List Fetching Logic - Kind 3 limit=1: Fetches only user's follow list - Kind 3 limit>1: Fetches user's + (limit-1) follow lists from follows - Added depth traversal (0=direct, 1=2 degrees, 2=3 degrees) - Attempted to implement "addFollowLists" toggle (later simplified) ### Profile Fetching Changes - Modified to be more selective about which profiles to fetch - Attempted to limit based on follow lists and event authors - Added progress indicators for profile loading ### People Tag Anchors Implementation - Complex logic to create "p" tag anchors from follow lists - Synthetic event creation to connect people to visualization - "Only show people with publications" checkbox - Attempted to connect people to: - Events they authored (pubkey match) - Events where they're tagged with "p" - Display limiting based on kind 0 limit ### UI/UX Changes - Tag anchors legend now scrollable when >20 items - Auto-disable functionality when >20 tag anchors - Added various UI controls that proved confusing - Multiple iterations on settings panel layout ## Problems with Current Implementation: 1. **Overly Complex Logic**: The synthetic event creation and connection logic for people tag anchors became convoluted 2. **Confusing UI**: Too many interdependent settings that aren't intuitive: - Limit inputs control different things for different event types - The relationship between kind 3 and kind 0 limits is unclear - "addFollowLists" checkbox functionality was confusing 3. **Performance Concerns**: Fetching all profiles then limiting display may not be optimal 4. **Unclear Requirements**: The exact behavior for people tag anchors connections needs clarification ## Next Steps: Need to revert and reimplement with: - Clearer separation of concerns - Simpler UI that's more intuitive - Better defined behavior for people tag anchors - More efficient profile fetching strategy ## Files Changed: - EventTypeConfig.svelte: New component for event configuration - visualizationConfig.ts: Major overhaul for EventKindConfig - profileCache.ts: Added selective fetching logic - visualize/+page.svelte: Complex follow list and profile fetching - EventNetwork components: People tag anchor implementation - settings_panel.org: Documentation of intended behavior 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>master
12 changed files with 1263 additions and 187 deletions
@ -0,0 +1,125 @@ |
|||||||
|
* Settings Panel Documentation |
||||||
|
|
||||||
|
** Overview |
||||||
|
The settings panel controls how events are fetched and displayed in the visualization. It has several sections that work together to create an efficient and user-friendly experience. |
||||||
|
|
||||||
|
** Event Types Configuration |
||||||
|
|
||||||
|
*** Purpose |
||||||
|
Controls which types of Nostr events are fetched and how many of each type. |
||||||
|
|
||||||
|
*** Key Event Types |
||||||
|
- *Kind 0* (Profiles/Metadata): User profile information (names, pictures, etc.) |
||||||
|
- *Kind 3* (Follow Lists): Who each user follows |
||||||
|
- *Kind 30040* (Index Events): Publication indices |
||||||
|
- *Kind 30041* (Content Events): Publication content |
||||||
|
- *Kind 30818* (Content Events): Alternative publication format |
||||||
|
|
||||||
|
*** How Limits Work |
||||||
|
Each event kind has a limit number that controls different things: |
||||||
|
|
||||||
|
**** For Kind 0 (Profiles) |
||||||
|
- Limit controls how many profiles to fetch from discovered pubkeys |
||||||
|
- These profiles are used for: |
||||||
|
- Displaying names instead of pubkeys |
||||||
|
- Showing profile pictures in tooltips |
||||||
|
- When "People" tag anchors are selected, this limit controls how many people anchors to display |
||||||
|
|
||||||
|
**** For Kind 3 (Follow Lists) |
||||||
|
- =limit = 1=: Only fetch the current user's follow list |
||||||
|
- =limit > 1=: Fetch the user's follow list PLUS (limit-1) follow lists from people they follow |
||||||
|
- The depth selector controls traversal: |
||||||
|
- =Direct= (0): Just the immediate follows |
||||||
|
- =2 degrees= (1): Follows of follows |
||||||
|
- =3 degrees= (2): Three levels deep |
||||||
|
|
||||||
|
**** For Kind 30040/30041/30818 |
||||||
|
- Limit controls maximum number of these events to fetch |
||||||
|
|
||||||
|
** Tag Anchors |
||||||
|
|
||||||
|
*** What Are Tag Anchors? |
||||||
|
Tag anchors are special nodes in the graph that act as gravity points for events sharing common attributes. They help organize the visualization by grouping related content. |
||||||
|
|
||||||
|
*** Tag Types Available |
||||||
|
- *Hashtags* (t): Groups events by hashtag |
||||||
|
- *Authors*: Groups events by author |
||||||
|
- *People* (p): Shows people from follow lists as anchor points |
||||||
|
- *Event References* (e): Groups events that reference each other |
||||||
|
- *Titles*: Groups events by title |
||||||
|
- *Summaries*: Groups events by summary |
||||||
|
|
||||||
|
*** How People Tag Anchors Work |
||||||
|
When "People" is selected as the tag type: |
||||||
|
|
||||||
|
1. The system looks at all loaded follow lists (kind 3 events) |
||||||
|
2. Extracts all pubkeys (people) from those follow lists |
||||||
|
3. Creates tag anchors for those people (up to the kind 0 limit) |
||||||
|
4. Connects each person anchor to: |
||||||
|
- Events they authored (where pubkey matches) |
||||||
|
- Events where they're mentioned in "p" tags |
||||||
|
|
||||||
|
*** Display Limiting and Auto-Disable |
||||||
|
- Tag anchors are created for ALL discovered tags |
||||||
|
- But only displayed up to the configured limit |
||||||
|
- When > 20 tag anchors exist, they're all auto-disabled |
||||||
|
- Users can selectively enable specific anchors |
||||||
|
- The legend becomes scrollable for many anchors |
||||||
|
|
||||||
|
*** "Only show people with publications" Checkbox |
||||||
|
When checked (default): |
||||||
|
- Only shows people who have events in the current visualization |
||||||
|
|
||||||
|
When unchecked: |
||||||
|
- Shows ALL people from follow lists, even if they have no events displayed |
||||||
|
- Useful for seeing your complete social graph |
||||||
|
|
||||||
|
** Display Limits Section |
||||||
|
|
||||||
|
*** Max Publication Indices (30040) |
||||||
|
Controls display filtering for publication indices after they're fetched. |
||||||
|
|
||||||
|
*** Max Events per Index |
||||||
|
Limits how many content events to show per publication index. |
||||||
|
|
||||||
|
*** Fetch if not found |
||||||
|
When enabled, automatically fetches missing referenced events. |
||||||
|
|
||||||
|
** Graph Traversal Section |
||||||
|
|
||||||
|
*** Search through already fetched |
||||||
|
When enabled, tag expansion only searches through events already loaded (more efficient). |
||||||
|
|
||||||
|
*** Append mode |
||||||
|
When enabled, new fetches add to the existing graph instead of replacing it. |
||||||
|
|
||||||
|
** Current Implementation Questions |
||||||
|
|
||||||
|
1. *Profile Fetching*: Should we fetch profiles for: |
||||||
|
- Only event authors? |
||||||
|
- All pubkeys in follow lists? |
||||||
|
- All pubkeys mentioned anywhere? |
||||||
|
|
||||||
|
2. *People Tag Anchors*: Should they connect to: |
||||||
|
- Only events where the person is tagged with "p"? |
||||||
|
- Events they authored? |
||||||
|
- Both? |
||||||
|
|
||||||
|
3. *Display Limits*: Should limits control: |
||||||
|
- How many to fetch from relays? |
||||||
|
- How many to display (fetch all, display subset)? |
||||||
|
- Both with separate controls? |
||||||
|
|
||||||
|
4. *Auto-disable Threshold*: Is 20 the right number for auto-disabling tag anchors? |
||||||
|
|
||||||
|
** Ideal User Flow |
||||||
|
|
||||||
|
1. User loads the visualization |
||||||
|
2. Their follow list is fetched (kind 3, limit 1) |
||||||
|
3. Profiles are fetched for people they follow (kind 0, respecting limit) |
||||||
|
4. Publications are fetched (kind 30040/30041/30818) |
||||||
|
5. User enables "People" tag anchors |
||||||
|
6. Sees their follows as anchor points |
||||||
|
7. Can see which follows have authored content |
||||||
|
8. Can selectively enable/disable specific people |
||||||
|
9. Can increase limits to see more content/people |
||||||
@ -0,0 +1,246 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import { visualizationConfig } from '$lib/stores/visualizationConfig'; |
||||||
|
import { Button, Input } from 'flowbite-svelte'; |
||||||
|
import { CloseCircleOutline } from 'flowbite-svelte-icons'; |
||||||
|
import { getEventKindName, getEventKindColor } from '$lib/utils/eventColors'; |
||||||
|
|
||||||
|
let { |
||||||
|
onReload = () => {}, |
||||||
|
eventCounts = {} |
||||||
|
} = $props<{ |
||||||
|
onReload?: () => void; |
||||||
|
eventCounts?: { [kind: number]: number }; |
||||||
|
}>(); |
||||||
|
|
||||||
|
let newKind = $state(''); |
||||||
|
let showAddInput = $state(false); |
||||||
|
let inputError = $state(''); |
||||||
|
|
||||||
|
function validateKind(value: string | number): number | null { |
||||||
|
// Convert to string for consistent handling |
||||||
|
const strValue = String(value); |
||||||
|
if (strValue === null || strValue === undefined || strValue.trim() === '') { |
||||||
|
inputError = ''; |
||||||
|
return null; |
||||||
|
} |
||||||
|
const kind = parseInt(strValue.trim()); |
||||||
|
if (isNaN(kind)) { |
||||||
|
inputError = 'Must be a number'; |
||||||
|
return null; |
||||||
|
} |
||||||
|
if (kind < 0) { |
||||||
|
inputError = 'Must be non-negative'; |
||||||
|
return null; |
||||||
|
} |
||||||
|
if ($visualizationConfig.eventConfigs.some(ec => ec.kind === kind)) { |
||||||
|
inputError = 'Already added'; |
||||||
|
return null; |
||||||
|
} |
||||||
|
inputError = ''; |
||||||
|
return kind; |
||||||
|
} |
||||||
|
|
||||||
|
function handleAddKind() { |
||||||
|
console.log('[EventTypeConfig] handleAddKind called with:', newKind); |
||||||
|
const kind = validateKind(newKind); |
||||||
|
console.log('[EventTypeConfig] Validation result:', kind); |
||||||
|
if (kind !== null) { |
||||||
|
console.log('[EventTypeConfig] Adding event kind:', kind); |
||||||
|
visualizationConfig.addEventKind(kind); |
||||||
|
newKind = ''; |
||||||
|
showAddInput = false; |
||||||
|
inputError = ''; |
||||||
|
} else { |
||||||
|
console.log('[EventTypeConfig] Validation failed:', inputError); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function handleKeydown(e: KeyboardEvent) { |
||||||
|
if (e.key === 'Enter') { |
||||||
|
handleAddKind(); |
||||||
|
} else if (e.key === 'Escape') { |
||||||
|
showAddInput = false; |
||||||
|
newKind = ''; |
||||||
|
inputError = ''; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function handleLimitChange(kind: number, value: string) { |
||||||
|
const limit = parseInt(value); |
||||||
|
if (!isNaN(limit) && limit > 0) { |
||||||
|
visualizationConfig.updateEventLimit(kind, limit); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function handleNestedLevelsChange(value: string) { |
||||||
|
const levels = parseInt(value); |
||||||
|
if (!isNaN(levels) && levels >= 0) { |
||||||
|
visualizationConfig.updateNestedLevels(levels); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function handleFollowDepthChange(value: string) { |
||||||
|
const depth = parseInt(value); |
||||||
|
if (!isNaN(depth) && depth >= 0 && depth <= 2) { |
||||||
|
visualizationConfig.updateFollowDepth(depth); |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<div class="space-y-3"> |
||||||
|
<span class="text-xs text-gray-600 dark:text-gray-400"> |
||||||
|
Showing {Object.values(eventCounts).reduce((a, b) => a + b, 0)} of {Object.values(eventCounts).reduce((a, b) => a + b, 0)} events |
||||||
|
</span> |
||||||
|
|
||||||
|
<!-- Event configurations --> |
||||||
|
<div class="space-y-2"> |
||||||
|
{#each $visualizationConfig.eventConfigs as config} |
||||||
|
{@const isLoaded = (eventCounts[config.kind] || 0) > 0} |
||||||
|
{@const isDisabled = $visualizationConfig.disabledKinds?.includes(config.kind) || false} |
||||||
|
{@const color = getEventKindColor(config.kind)} |
||||||
|
{@const borderColor = isLoaded ? 'border-green-500' : 'border-red-500'} |
||||||
|
<div class="flex items-center gap-2"> |
||||||
|
<!-- Kind badge with color indicator and load status border --> |
||||||
|
<button |
||||||
|
class="flex items-center gap-1 min-w-[140px] px-2 py-1 border-2 rounded {borderColor} {isDisabled ? 'opacity-50' : ''} hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors cursor-pointer" |
||||||
|
onclick={() => visualizationConfig.toggleKind(config.kind)} |
||||||
|
title={isDisabled ? `Click to enable ${getEventKindName(config.kind)}` : `Click to disable ${getEventKindName(config.kind)}`} |
||||||
|
> |
||||||
|
<span |
||||||
|
class="inline-block w-3 h-3 rounded-full flex-shrink-0" |
||||||
|
style="background-color: {color}" |
||||||
|
></span> |
||||||
|
<span class="text-sm font-medium dark:text-white"> |
||||||
|
{config.kind} |
||||||
|
</span> |
||||||
|
</button> |
||||||
|
<button |
||||||
|
onclick={() => visualizationConfig.removeEventKind(config.kind)} |
||||||
|
class="text-red-500 hover:text-red-700 transition-colors" |
||||||
|
title="Remove {getEventKindName(config.kind)}" |
||||||
|
> |
||||||
|
<CloseCircleOutline class="w-4 h-4" /> |
||||||
|
</button> |
||||||
|
|
||||||
|
<!-- Limit input for all kinds --> |
||||||
|
<input |
||||||
|
type="number" |
||||||
|
value={config.limit} |
||||||
|
min="1" |
||||||
|
max="1000" |
||||||
|
class="w-16 px-2 py-1 text-xs border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white" |
||||||
|
oninput={(e) => handleLimitChange(config.kind, e.currentTarget.value)} |
||||||
|
title="Max to display" |
||||||
|
/> |
||||||
|
|
||||||
|
<!-- Nested levels for 30040 --> |
||||||
|
{#if config.kind === 30040} |
||||||
|
<span class="text-xs text-gray-600 dark:text-gray-400">Nested Levels:</span> |
||||||
|
<input |
||||||
|
type="number" |
||||||
|
value={config.nestedLevels || 1} |
||||||
|
min="0" |
||||||
|
max="10" |
||||||
|
class="w-14 px-2 py-1 text-xs border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white" |
||||||
|
oninput={(e) => handleNestedLevelsChange(e.currentTarget.value)} |
||||||
|
title="Levels to traverse" |
||||||
|
/> |
||||||
|
{/if} |
||||||
|
|
||||||
|
<!-- Additional settings for kind 3 (follow lists) --> |
||||||
|
{#if config.kind === 3} |
||||||
|
<select |
||||||
|
value={config.depth || 0} |
||||||
|
onchange={(e) => handleFollowDepthChange(e.currentTarget.value)} |
||||||
|
class="px-2 py-1 text-xs border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white" |
||||||
|
title="How many degrees of separation to traverse" |
||||||
|
> |
||||||
|
<option value="0">Direct</option> |
||||||
|
<option value="1">2 degrees</option> |
||||||
|
<option value="2">3 degrees</option> |
||||||
|
</select> |
||||||
|
{/if} |
||||||
|
|
||||||
|
<!-- Load indicator --> |
||||||
|
{#if isLoaded} |
||||||
|
<span class="text-xs text-green-600 dark:text-green-400"> |
||||||
|
({eventCounts[config.kind]}) |
||||||
|
</span> |
||||||
|
{:else} |
||||||
|
<span class="text-xs text-red-600 dark:text-red-400"> |
||||||
|
(not loaded) |
||||||
|
</span> |
||||||
|
{/if} |
||||||
|
</div> |
||||||
|
{/each} |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- Add kind button/input --> |
||||||
|
{#if showAddInput} |
||||||
|
<div class="flex items-center gap-2"> |
||||||
|
<input |
||||||
|
bind:value={newKind} |
||||||
|
type="number" |
||||||
|
placeholder="Enter event kind number (e.g. 1)" |
||||||
|
class="flex-1 px-3 py-1 text-sm border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500" |
||||||
|
onkeydown={handleKeydown} |
||||||
|
oninput={(e) => validateKind(e.currentTarget.value)} |
||||||
|
/> |
||||||
|
<Button size="xs" onclick={handleAddKind} disabled={newKind === '' || !!inputError}> |
||||||
|
Add |
||||||
|
</Button> |
||||||
|
<Button |
||||||
|
size="xs" |
||||||
|
color="light" |
||||||
|
onclick={() => { |
||||||
|
showAddInput = false; |
||||||
|
newKind = ''; |
||||||
|
inputError = ''; |
||||||
|
}} |
||||||
|
> |
||||||
|
Cancel |
||||||
|
</Button> |
||||||
|
</div> |
||||||
|
{#if inputError} |
||||||
|
<p class="text-xs text-red-500 -mt-2"> |
||||||
|
{inputError} |
||||||
|
</p> |
||||||
|
{/if} |
||||||
|
{:else} |
||||||
|
<Button |
||||||
|
size="xs" |
||||||
|
color="light" |
||||||
|
onclick={() => showAddInput = true} |
||||||
|
class="gap-1" |
||||||
|
> |
||||||
|
<span>+</span> |
||||||
|
<span>Add Event Type</span> |
||||||
|
</Button> |
||||||
|
{/if} |
||||||
|
|
||||||
|
<!-- Reload button --> |
||||||
|
<Button |
||||||
|
size="xs" |
||||||
|
color="blue" |
||||||
|
onclick={onReload} |
||||||
|
class="gap-1" |
||||||
|
title="Reload graph with current settings" |
||||||
|
> |
||||||
|
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path> |
||||||
|
</svg> |
||||||
|
<span>Reload</span> |
||||||
|
</Button> |
||||||
|
|
||||||
|
<!-- Border legend --> |
||||||
|
<div class="text-xs text-gray-500 dark:text-gray-400 space-y-1 mt-2"> |
||||||
|
<p class="flex items-center gap-2"> |
||||||
|
<span class="inline-block w-3 h-3 border-2 border-green-500 rounded"></span> |
||||||
|
<span>Green = Events loaded</span> |
||||||
|
</p> |
||||||
|
<p class="flex items-center gap-2"> |
||||||
|
<span class="inline-block w-3 h-3 border-2 border-red-500 rounded"></span> |
||||||
|
<span>Red = Not loaded (click Reload)</span> |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
</div> |
||||||
@ -1,89 +1,234 @@ |
|||||||
import { writable, derived } from 'svelte/store'; |
import { writable, derived, get } from "svelte/store"; |
||||||
|
|
||||||
export interface VisualizationConfig { |
export interface EventKindConfig { |
||||||
// Event filtering
|
kind: number; |
||||||
allowedKinds: number[]; // Using array for ordered display
|
limit: number; |
||||||
disabledKinds: number[]; // Kinds that are temporarily disabled but not removed
|
nestedLevels?: number; // Only for kind 30040
|
||||||
allowFreeEvents: boolean; |
depth?: number; // Only for kind 3 (follow lists)
|
||||||
|
} |
||||||
|
|
||||||
// Display limits (moving from displayLimits store)
|
export interface VisualizationConfig { |
||||||
maxPublicationIndices: number; // -1 unlimited
|
// Event configurations with per-kind limits
|
||||||
maxEventsPerIndex: number; // -1 unlimited
|
eventConfigs: EventKindConfig[]; |
||||||
|
|
||||||
// Graph traversal
|
// Graph traversal
|
||||||
searchThroughFetched: boolean; |
searchThroughFetched: boolean; |
||||||
|
|
||||||
|
// Append mode - add new events to existing graph instead of replacing
|
||||||
|
appendMode?: boolean; |
||||||
|
|
||||||
|
// Legacy properties for backward compatibility
|
||||||
|
allowedKinds?: number[]; |
||||||
|
disabledKinds?: number[]; |
||||||
|
allowFreeEvents?: boolean; |
||||||
|
maxPublicationIndices?: number; |
||||||
|
maxEventsPerIndex?: number; |
||||||
} |
} |
||||||
|
|
||||||
|
// Default configurations for common event kinds
|
||||||
|
const DEFAULT_EVENT_CONFIGS: EventKindConfig[] = [ |
||||||
|
{ kind: 0, limit: 50 }, // Metadata events (profiles) - controls how many profiles to fetch
|
||||||
|
{ kind: 3, limit: 1, depth: 0 }, // Follow lists - limit 1 = just user's, higher = user's + from follows
|
||||||
|
{ kind: 30040, limit: 20, nestedLevels: 1 }, |
||||||
|
{ kind: 30041, limit: 20 }, |
||||||
|
{ kind: 30818, limit: 20 }, |
||||||
|
]; |
||||||
|
|
||||||
function createVisualizationConfig() { |
function createVisualizationConfig() { |
||||||
const { subscribe, set, update } = writable<VisualizationConfig>({ |
// Initialize with both new and legacy properties
|
||||||
allowedKinds: [30040, 30041, 30818], |
const initialConfig: VisualizationConfig = { |
||||||
disabledKinds: [30041, 30818], // 30041 and 30818 disabled by default
|
eventConfigs: DEFAULT_EVENT_CONFIGS, |
||||||
|
searchThroughFetched: true, |
||||||
|
appendMode: false, |
||||||
|
// Legacy properties
|
||||||
|
allowedKinds: DEFAULT_EVENT_CONFIGS.map(ec => ec.kind), |
||||||
|
disabledKinds: [30041, 30818], |
||||||
allowFreeEvents: false, |
allowFreeEvents: false, |
||||||
maxPublicationIndices: -1, |
maxPublicationIndices: -1, |
||||||
maxEventsPerIndex: -1, |
maxEventsPerIndex: -1, |
||||||
searchThroughFetched: true |
}; |
||||||
}); |
|
||||||
|
const { subscribe, set, update } = |
||||||
|
writable<VisualizationConfig>(initialConfig); |
||||||
|
|
||||||
|
// Helper to sync legacy properties with eventConfigs
|
||||||
|
const syncLegacyProperties = (config: VisualizationConfig) => { |
||||||
|
config.allowedKinds = config.eventConfigs.map((ec) => ec.kind); |
||||||
|
return config; |
||||||
|
}; |
||||||
|
|
||||||
return { |
return { |
||||||
subscribe, |
subscribe, |
||||||
update, |
update, |
||||||
reset: () => set({ |
reset: () => set(initialConfig), |
||||||
allowedKinds: [30040, 30041, 30818], |
|
||||||
disabledKinds: [30041, 30818], // 30041 and 30818 disabled by default
|
// Add a new event kind with default limit
|
||||||
allowFreeEvents: false, |
addEventKind: (kind: number, limit: number = 10) => |
||||||
maxPublicationIndices: -1, |
update((config) => { |
||||||
maxEventsPerIndex: -1, |
// Check if kind already exists
|
||||||
searchThroughFetched: true |
if (config.eventConfigs.some((ec) => ec.kind === kind)) { |
||||||
}), |
return config; |
||||||
addKind: (kind: number) => update(config => { |
} |
||||||
if (!config.allowedKinds.includes(kind)) { |
|
||||||
return { ...config, allowedKinds: [...config.allowedKinds, kind] }; |
const newConfig: EventKindConfig = { kind, limit }; |
||||||
} |
// Add nestedLevels for 30040
|
||||||
return config; |
if (kind === 30040) { |
||||||
}), |
newConfig.nestedLevels = 1; |
||||||
removeKind: (kind: number) => update(config => ({ |
} |
||||||
...config, |
// Add depth for kind 3
|
||||||
allowedKinds: config.allowedKinds.filter(k => k !== kind) |
if (kind === 3) { |
||||||
})), |
newConfig.depth = 0; |
||||||
toggleFreeEvents: () => update(config => ({ |
} |
||||||
...config, |
|
||||||
allowFreeEvents: !config.allowFreeEvents |
const updated = { |
||||||
})), |
...config, |
||||||
setMaxPublicationIndices: (max: number) => update(config => ({ |
eventConfigs: [...config.eventConfigs, newConfig], |
||||||
...config, |
}; |
||||||
maxPublicationIndices: max |
return syncLegacyProperties(updated); |
||||||
})), |
}), |
||||||
setMaxEventsPerIndex: (max: number) => update(config => ({ |
|
||||||
...config, |
// Legacy method for backward compatibility
|
||||||
maxEventsPerIndex: max |
addKind: (kind: number) => |
||||||
})), |
update((config) => { |
||||||
toggleSearchThroughFetched: () => update(config => ({ |
if (config.eventConfigs.some((ec) => ec.kind === kind)) { |
||||||
...config, |
return config; |
||||||
searchThroughFetched: !config.searchThroughFetched |
} |
||||||
})), |
const updated = { |
||||||
toggleKind: (kind: number) => update(config => { |
...config, |
||||||
const isDisabled = config.disabledKinds.includes(kind); |
eventConfigs: [...config.eventConfigs, { kind, limit: 10 }], |
||||||
if (isDisabled) { |
}; |
||||||
// Re-enable it
|
return syncLegacyProperties(updated); |
||||||
return { |
}), |
||||||
|
|
||||||
|
// Remove an event kind
|
||||||
|
removeEventKind: (kind: number) => |
||||||
|
update((config) => { |
||||||
|
const updated = { |
||||||
...config, |
...config, |
||||||
disabledKinds: config.disabledKinds.filter(k => k !== kind) |
eventConfigs: config.eventConfigs.filter((ec) => ec.kind !== kind), |
||||||
}; |
}; |
||||||
} else { |
return syncLegacyProperties(updated); |
||||||
// Disable it
|
}), |
||||||
return { |
|
||||||
|
// Legacy method for backward compatibility
|
||||||
|
removeKind: (kind: number) => |
||||||
|
update((config) => { |
||||||
|
const updated = { |
||||||
...config, |
...config, |
||||||
disabledKinds: [...config.disabledKinds, kind] |
eventConfigs: config.eventConfigs.filter((ec) => ec.kind !== kind), |
||||||
}; |
}; |
||||||
} |
return syncLegacyProperties(updated); |
||||||
}) |
}), |
||||||
|
|
||||||
|
// Update limit for a specific kind
|
||||||
|
updateEventLimit: (kind: number, limit: number) => |
||||||
|
update((config) => ({ |
||||||
|
...config, |
||||||
|
eventConfigs: config.eventConfigs.map((ec) => |
||||||
|
ec.kind === kind ? { ...ec, limit } : ec, |
||||||
|
), |
||||||
|
})), |
||||||
|
|
||||||
|
// Update nested levels for kind 30040
|
||||||
|
updateNestedLevels: (levels: number) => |
||||||
|
update((config) => ({ |
||||||
|
...config, |
||||||
|
eventConfigs: config.eventConfigs.map((ec) => |
||||||
|
ec.kind === 30040 ? { ...ec, nestedLevels: levels } : ec, |
||||||
|
), |
||||||
|
})), |
||||||
|
|
||||||
|
// Update depth for kind 3
|
||||||
|
updateFollowDepth: (depth: number) => |
||||||
|
update((config) => ({ |
||||||
|
...config, |
||||||
|
eventConfigs: config.eventConfigs.map((ec) => |
||||||
|
ec.kind === 3 ? { ...ec, depth: depth } : ec, |
||||||
|
), |
||||||
|
})), |
||||||
|
|
||||||
|
|
||||||
|
// Get config for a specific kind
|
||||||
|
getEventConfig: (kind: number) => { |
||||||
|
let config: EventKindConfig | undefined; |
||||||
|
subscribe((c) => { |
||||||
|
config = c.eventConfigs.find((ec) => ec.kind === kind); |
||||||
|
})(); |
||||||
|
return config; |
||||||
|
}, |
||||||
|
|
||||||
|
toggleSearchThroughFetched: () => |
||||||
|
update((config) => ({ |
||||||
|
...config, |
||||||
|
searchThroughFetched: !config.searchThroughFetched, |
||||||
|
})), |
||||||
|
|
||||||
|
toggleAppendMode: () => |
||||||
|
update((config) => ({ |
||||||
|
...config, |
||||||
|
appendMode: !config.appendMode, |
||||||
|
})), |
||||||
|
|
||||||
|
// Legacy methods for backward compatibility
|
||||||
|
toggleKind: (kind: number) => |
||||||
|
update((config) => { |
||||||
|
const isDisabled = config.disabledKinds?.includes(kind) || false; |
||||||
|
if (isDisabled) { |
||||||
|
// Re-enable it
|
||||||
|
return { |
||||||
|
...config, |
||||||
|
disabledKinds: |
||||||
|
config.disabledKinds?.filter((k) => k !== kind) || [], |
||||||
|
}; |
||||||
|
} else { |
||||||
|
// Disable it
|
||||||
|
return { |
||||||
|
...config, |
||||||
|
disabledKinds: [...(config.disabledKinds || []), kind], |
||||||
|
}; |
||||||
|
} |
||||||
|
}), |
||||||
|
|
||||||
|
toggleFreeEvents: () => |
||||||
|
update((config) => ({ |
||||||
|
...config, |
||||||
|
allowFreeEvents: !config.allowFreeEvents, |
||||||
|
})), |
||||||
|
|
||||||
|
setMaxPublicationIndices: (max: number) => |
||||||
|
update((config) => ({ |
||||||
|
...config, |
||||||
|
maxPublicationIndices: max, |
||||||
|
})), |
||||||
|
|
||||||
|
setMaxEventsPerIndex: (max: number) => |
||||||
|
update((config) => ({ |
||||||
|
...config, |
||||||
|
maxEventsPerIndex: max, |
||||||
|
})), |
||||||
}; |
}; |
||||||
} |
} |
||||||
|
|
||||||
export const visualizationConfig = createVisualizationConfig(); |
export const visualizationConfig = createVisualizationConfig(); |
||||||
|
|
||||||
// Helper to check if a kind is allowed and enabled
|
// Helper to get all enabled event kinds
|
||||||
|
export const enabledEventKinds = derived(visualizationConfig, ($config) => |
||||||
|
$config.eventConfigs.map((ec) => ec.kind), |
||||||
|
); |
||||||
|
|
||||||
|
// Helper to check if a kind is enabled
|
||||||
|
export const isKindEnabled = derived( |
||||||
|
visualizationConfig, |
||||||
|
($config) => (kind: number) => |
||||||
|
$config.eventConfigs.some((ec) => ec.kind === kind), |
||||||
|
); |
||||||
|
|
||||||
|
// Legacy helper for backward compatibility
|
||||||
export const isKindAllowed = derived( |
export const isKindAllowed = derived( |
||||||
visualizationConfig, |
visualizationConfig, |
||||||
$config => (kind: number) => $config.allowedKinds.includes(kind) && !$config.disabledKinds.includes(kind) |
($config) => (kind: number) => { |
||||||
|
const inEventConfigs = $config.eventConfigs.some((ec) => ec.kind === kind); |
||||||
|
const notDisabled = !($config.disabledKinds?.includes(kind) || false); |
||||||
|
return inEventConfigs && notDisabled; |
||||||
|
}, |
||||||
); |
); |
||||||
Loading…
Reference in new issue