mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-06-16 09:38:36 +02:00
fix: handle local spells and add comprehensive logging for spell tabs
- Parse command directly for local spells without event objects - Add proper relay resolution for each parameter type: - $pubkey: Use NIP-65 outbox selection with fallback - $event: Use relay hints from target event with fallback - $relay: Use target relay directly - Add extensive console logging throughout spell application pipeline - Update applySpellParameters to accept Pick<ParsedSpell> for flexibility - Display helpful error messages with console check hints
This commit is contained in:
@@ -26,6 +26,8 @@ import { useUserParameterizedSpells } from "@/hooks/useParameterizedSpells";
|
||||
import { EventFeed } from "./nostr/EventFeed";
|
||||
import { useReqTimelineEnhanced } from "@/hooks/useReqTimelineEnhanced";
|
||||
import { applySpellParameters, decodeSpell } from "@/lib/spell-conversion";
|
||||
import { parseReqCommand } from "@/lib/req-parser";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
|
||||
export interface EventDetailViewerProps {
|
||||
pointer: EventPointer | AddressPointer;
|
||||
@@ -51,39 +53,149 @@ function SpellTabContent({
|
||||
spellId,
|
||||
spell,
|
||||
targetEventId,
|
||||
}: SpellTabContentProps) {
|
||||
// Decode spell and apply parameters
|
||||
const { appliedFilter, relays } = useMemo(() => {
|
||||
if (!targetEventId || !spell.event) {
|
||||
return { appliedFilter: null, relays: [] };
|
||||
targetEvent,
|
||||
}: SpellTabContentProps & { targetEvent: any }) {
|
||||
// Parse spell and get filter - handle both published (with event) and local (command-only) spells
|
||||
const parsed = useMemo(() => {
|
||||
if (!targetEventId) {
|
||||
console.log(`[EventSpell:${spell.name || spellId}] No target event ID`);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = decodeSpell(spell.event);
|
||||
const applied = applySpellParameters(parsed, [targetEventId]);
|
||||
return {
|
||||
appliedFilter: applied,
|
||||
relays: parsed.relays || [],
|
||||
console.log(`[EventSpell:${spell.name || spellId}] Parsing spell:`, {
|
||||
hasEvent: !!spell.event,
|
||||
command: spell.command,
|
||||
parameterType: spell.parameterType,
|
||||
});
|
||||
|
||||
// If we have a published event, decode it
|
||||
if (spell.event) {
|
||||
const decoded = decodeSpell(spell.event);
|
||||
console.log(
|
||||
`[EventSpell:${spell.name || spellId}] Decoded from event:`,
|
||||
{
|
||||
filter: decoded.filter,
|
||||
relays: decoded.relays,
|
||||
parameter: decoded.parameter,
|
||||
},
|
||||
);
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// For local spells, parse the command directly
|
||||
console.log(
|
||||
`[EventSpell:${spell.name || spellId}] Parsing local spell command`,
|
||||
);
|
||||
const commandWithoutPrefix = spell.command
|
||||
.replace(/^\s*(req|count)\s+/i, "")
|
||||
.trim();
|
||||
const tokens = commandWithoutPrefix.split(/\s+/);
|
||||
const commandParsed = parseReqCommand(tokens);
|
||||
|
||||
// Create a ParsedSpell-like object for local spells
|
||||
const localParsed = {
|
||||
command: spell.command,
|
||||
filter: commandParsed.filter,
|
||||
relays: commandParsed.relays,
|
||||
closeOnEose: commandParsed.closeOnEose,
|
||||
parameter: spell.parameterType
|
||||
? {
|
||||
type: spell.parameterType,
|
||||
default: spell.parameterDefault,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
|
||||
console.log(`[EventSpell:${spell.name || spellId}] Parsed local spell:`, {
|
||||
filter: localParsed.filter,
|
||||
relays: localParsed.relays,
|
||||
parameter: localParsed.parameter,
|
||||
});
|
||||
|
||||
return localParsed;
|
||||
} catch (error) {
|
||||
console.error("Failed to apply spell parameters:", error);
|
||||
return { appliedFilter: null, relays: [] };
|
||||
console.error(
|
||||
`[EventSpell:${spell.name || spellId}] Failed to parse spell:`,
|
||||
error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}, [spell.event, targetEventId]);
|
||||
}, [spell, targetEventId, spellId]);
|
||||
|
||||
// Apply parameters to get final filter
|
||||
const appliedFilter = useMemo(() => {
|
||||
if (!parsed || !targetEventId) return null;
|
||||
|
||||
try {
|
||||
const applied = applySpellParameters(parsed, [targetEventId]);
|
||||
console.log(`[EventSpell:${spell.name || spellId}] Applied parameters:`, {
|
||||
input: targetEventId,
|
||||
result: applied,
|
||||
});
|
||||
return applied;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[EventSpell:${spell.name || spellId}] Failed to apply parameters:`,
|
||||
error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}, [parsed, targetEventId, spell.name, spellId]);
|
||||
|
||||
// Resolve relays - use explicit relays from spell, or use relay hints from target event
|
||||
const finalRelays = useMemo(() => {
|
||||
// Use explicit relays from spell if provided
|
||||
if (parsed?.relays && parsed.relays.length > 0) {
|
||||
console.log(
|
||||
`[EventSpell:${spell.name || spellId}] Using explicit relays:`,
|
||||
parsed.relays,
|
||||
);
|
||||
return parsed.relays;
|
||||
}
|
||||
|
||||
// Use relay hints from the target event
|
||||
if (targetEvent) {
|
||||
const seenRelaysSet = getSeenRelays(targetEvent);
|
||||
if (seenRelaysSet && seenRelaysSet.size > 0) {
|
||||
const eventRelays = Array.from(seenRelaysSet);
|
||||
console.log(
|
||||
`[EventSpell:${spell.name || spellId}] Using target event relays:`,
|
||||
eventRelays,
|
||||
);
|
||||
return eventRelays;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to aggregator relays
|
||||
console.log(
|
||||
`[EventSpell:${spell.name || spellId}] Using fallback AGGREGATOR_RELAYS`,
|
||||
);
|
||||
return AGGREGATOR_RELAYS;
|
||||
}, [parsed?.relays, targetEvent, spell.name, spellId]);
|
||||
|
||||
// Fetch events using the applied filter
|
||||
const { events, loading, eoseReceived } = appliedFilter
|
||||
? useReqTimelineEnhanced(
|
||||
`spell-${spellId}-${targetEventId}`,
|
||||
appliedFilter,
|
||||
relays,
|
||||
{ limit: appliedFilter.limit || 50, stream: true },
|
||||
)
|
||||
: {
|
||||
events: [],
|
||||
loading: false,
|
||||
eoseReceived: false,
|
||||
};
|
||||
const { events, loading, eoseReceived } =
|
||||
appliedFilter && finalRelays.length > 0
|
||||
? useReqTimelineEnhanced(
|
||||
`spell-${spellId}-${targetEventId}`,
|
||||
appliedFilter,
|
||||
finalRelays,
|
||||
{ limit: appliedFilter.limit || 50, stream: true },
|
||||
)
|
||||
: {
|
||||
events: [],
|
||||
loading: false,
|
||||
eoseReceived: false,
|
||||
};
|
||||
|
||||
console.log(`[EventSpell:${spell.name || spellId}] Render state:`, {
|
||||
hasFilter: !!appliedFilter,
|
||||
relayCount: finalRelays.length,
|
||||
eventCount: events.length,
|
||||
loading,
|
||||
eoseReceived,
|
||||
});
|
||||
|
||||
return (
|
||||
<TabsContent value={spellId} className="flex-1 overflow-hidden m-0">
|
||||
@@ -91,6 +203,7 @@ function SpellTabContent({
|
||||
<div className="flex items-center justify-center h-full p-8 text-center text-muted-foreground">
|
||||
<div>
|
||||
<p className="text-sm">Unable to apply spell to this event</p>
|
||||
<p className="text-xs mt-2">Check console for details</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
@@ -296,6 +409,7 @@ export function EventDetailViewer({ pointer }: EventDetailViewerProps) {
|
||||
spellId={spell.id}
|
||||
spell={spell}
|
||||
targetEventId={event.id}
|
||||
targetEvent={event}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
|
||||
@@ -39,6 +39,9 @@ import { useUserParameterizedSpells } from "@/hooks/useParameterizedSpells";
|
||||
import { EventFeed } from "./nostr/EventFeed";
|
||||
import { useReqTimelineEnhanced } from "@/hooks/useReqTimelineEnhanced";
|
||||
import { applySpellParameters, decodeSpell } from "@/lib/spell-conversion";
|
||||
import { parseReqCommand } from "@/lib/req-parser";
|
||||
import { useOutboxRelays } from "@/hooks/useOutboxRelays";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
|
||||
export interface ProfileViewerProps {
|
||||
pubkey: string;
|
||||
@@ -65,38 +68,178 @@ function SpellTabContent({
|
||||
spell,
|
||||
targetPubkey,
|
||||
}: SpellTabContentProps) {
|
||||
// Decode spell and apply parameters
|
||||
const { appliedFilter, relays } = useMemo(() => {
|
||||
if (!targetPubkey || !spell.event) {
|
||||
return { appliedFilter: null, relays: [] };
|
||||
const { state } = useGrimoire();
|
||||
|
||||
// Parse spell and get filter - handle both published (with event) and local (command-only) spells
|
||||
const parsed = useMemo(() => {
|
||||
if (!targetPubkey) {
|
||||
console.log(
|
||||
`[SpellTabContent:${spell.name || spellId}] No target pubkey`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = decodeSpell(spell.event);
|
||||
const applied = applySpellParameters(parsed, [targetPubkey]);
|
||||
return {
|
||||
appliedFilter: applied,
|
||||
relays: parsed.relays || [],
|
||||
console.log(`[SpellTabContent:${spell.name || spellId}] Parsing spell:`, {
|
||||
hasEvent: !!spell.event,
|
||||
command: spell.command,
|
||||
parameterType: spell.parameterType,
|
||||
});
|
||||
|
||||
// If we have a published event, decode it
|
||||
if (spell.event) {
|
||||
const decoded = decodeSpell(spell.event);
|
||||
console.log(
|
||||
`[SpellTabContent:${spell.name || spellId}] Decoded from event:`,
|
||||
{
|
||||
filter: decoded.filter,
|
||||
relays: decoded.relays,
|
||||
parameter: decoded.parameter,
|
||||
},
|
||||
);
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// For local spells, parse the command directly
|
||||
console.log(
|
||||
`[SpellTabContent:${spell.name || spellId}] Parsing local spell command`,
|
||||
);
|
||||
const commandWithoutPrefix = spell.command
|
||||
.replace(/^\s*(req|count)\s+/i, "")
|
||||
.trim();
|
||||
const tokens = commandWithoutPrefix.split(/\s+/);
|
||||
const commandParsed = parseReqCommand(tokens);
|
||||
|
||||
// Create a ParsedSpell-like object for local spells
|
||||
const localParsed = {
|
||||
command: spell.command,
|
||||
filter: commandParsed.filter,
|
||||
relays: commandParsed.relays,
|
||||
closeOnEose: commandParsed.closeOnEose,
|
||||
parameter: spell.parameterType
|
||||
? {
|
||||
type: spell.parameterType,
|
||||
default: spell.parameterDefault,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
|
||||
console.log(
|
||||
`[SpellTabContent:${spell.name || spellId}] Parsed local spell:`,
|
||||
{
|
||||
filter: localParsed.filter,
|
||||
relays: localParsed.relays,
|
||||
parameter: localParsed.parameter,
|
||||
},
|
||||
);
|
||||
|
||||
return localParsed;
|
||||
} catch (error) {
|
||||
console.error("Failed to apply spell parameters:", error);
|
||||
return { appliedFilter: null, relays: [] };
|
||||
console.error(
|
||||
`[SpellTabContent:${spell.name || spellId}] Failed to parse spell:`,
|
||||
error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}, [spell.event, targetPubkey]);
|
||||
}, [spell, targetPubkey, spellId]);
|
||||
|
||||
// Apply parameters to get final filter
|
||||
const appliedFilter = useMemo(() => {
|
||||
if (!parsed || !targetPubkey) return null;
|
||||
|
||||
try {
|
||||
const applied = applySpellParameters(parsed, [targetPubkey]);
|
||||
console.log(
|
||||
`[SpellTabContent:${spell.name || spellId}] Applied parameters:`,
|
||||
{
|
||||
input: targetPubkey,
|
||||
result: applied,
|
||||
},
|
||||
);
|
||||
return applied;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[SpellTabContent:${spell.name || spellId}] Failed to apply parameters:`,
|
||||
error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}, [parsed, targetPubkey, spell.name, spellId]);
|
||||
|
||||
// Resolve relays - use explicit relays from spell, or use NIP-65 outbox selection
|
||||
const fallbackRelays = useMemo(
|
||||
() =>
|
||||
state.activeAccount?.relays?.filter((r) => r.read).map((r) => r.url) ||
|
||||
AGGREGATOR_RELAYS,
|
||||
[state.activeAccount?.relays],
|
||||
);
|
||||
|
||||
const outboxOptions = useMemo(
|
||||
() => ({
|
||||
fallbackRelays,
|
||||
timeout: 1000,
|
||||
maxRelays: 42,
|
||||
}),
|
||||
[fallbackRelays],
|
||||
);
|
||||
|
||||
// Use outbox relay selection if no explicit relays provided in spell
|
||||
const { relays: selectedRelays, phase: relaySelectionPhase } =
|
||||
useOutboxRelays(appliedFilter || {}, outboxOptions);
|
||||
|
||||
const finalRelays = useMemo(() => {
|
||||
// Use explicit relays from spell if provided
|
||||
if (parsed?.relays && parsed.relays.length > 0) {
|
||||
console.log(
|
||||
`[SpellTabContent:${spell.name || spellId}] Using explicit relays:`,
|
||||
parsed.relays,
|
||||
);
|
||||
return parsed.relays;
|
||||
}
|
||||
|
||||
// Wait for outbox relay selection to complete
|
||||
if (relaySelectionPhase !== "ready") {
|
||||
console.log(
|
||||
`[SpellTabContent:${spell.name || spellId}] Waiting for relay selection (phase: ${relaySelectionPhase})`,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[SpellTabContent:${spell.name || spellId}] Using outbox-selected relays:`,
|
||||
selectedRelays,
|
||||
);
|
||||
return selectedRelays;
|
||||
}, [
|
||||
parsed?.relays,
|
||||
relaySelectionPhase,
|
||||
selectedRelays,
|
||||
spell.name,
|
||||
spellId,
|
||||
]);
|
||||
|
||||
// Fetch events using the applied filter
|
||||
const { events, loading, eoseReceived } = appliedFilter
|
||||
? useReqTimelineEnhanced(
|
||||
`spell-${spellId}-${targetPubkey}`,
|
||||
appliedFilter,
|
||||
relays,
|
||||
{ limit: appliedFilter.limit || 50, stream: true },
|
||||
)
|
||||
: {
|
||||
events: [],
|
||||
loading: false,
|
||||
eoseReceived: false,
|
||||
};
|
||||
const { events, loading, eoseReceived } =
|
||||
appliedFilter && finalRelays.length > 0
|
||||
? useReqTimelineEnhanced(
|
||||
`spell-${spellId}-${targetPubkey}`,
|
||||
appliedFilter,
|
||||
finalRelays,
|
||||
{ limit: appliedFilter.limit || 50, stream: true },
|
||||
)
|
||||
: {
|
||||
events: [],
|
||||
loading: false,
|
||||
eoseReceived: false,
|
||||
};
|
||||
|
||||
console.log(`[SpellTabContent:${spell.name || spellId}] Render state:`, {
|
||||
hasFilter: !!appliedFilter,
|
||||
relayCount: finalRelays.length,
|
||||
eventCount: events.length,
|
||||
loading,
|
||||
eoseReceived,
|
||||
});
|
||||
|
||||
return (
|
||||
<TabsContent value={spellId} className="flex-1 overflow-hidden m-0">
|
||||
@@ -104,6 +247,13 @@ function SpellTabContent({
|
||||
<div className="flex items-center justify-center h-full p-8 text-center text-muted-foreground">
|
||||
<div>
|
||||
<p className="text-sm">Unable to apply spell to this profile</p>
|
||||
<p className="text-xs mt-2">Check console for details</p>
|
||||
</div>
|
||||
</div>
|
||||
) : finalRelays.length === 0 ? (
|
||||
<div className="flex items-center justify-center h-full p-8 text-center text-muted-foreground">
|
||||
<div>
|
||||
<p className="text-sm">Selecting relays...</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -10,9 +10,8 @@ import { EventFeed } from "./nostr/EventFeed";
|
||||
import { useReqTimelineEnhanced } from "@/hooks/useReqTimelineEnhanced";
|
||||
import { applySpellParameters, decodeSpell } from "@/lib/spell-conversion";
|
||||
import { parseReqCommand } from "@/lib/req-parser";
|
||||
import { useMemo, useState } from "react";
|
||||
import { KindBadge } from "./KindBadge";
|
||||
import { CreateParameterizedSpellDialog } from "./CreateParameterizedSpellDialog";
|
||||
import { useMemo } from "react";
|
||||
import { NIPBadge } from "./NIPBadge";
|
||||
import { SpellHeader } from "./timeline/SpellHeader";
|
||||
|
||||
export interface RelayViewerProps {
|
||||
@@ -42,32 +41,119 @@ function SpellTabContent({
|
||||
}: SpellTabContentProps) {
|
||||
const { addWindow } = useGrimoire();
|
||||
|
||||
// Decode spell and apply parameters
|
||||
const { appliedFilter, relays } = useMemo(() => {
|
||||
if (!targetRelay || !spell.event) {
|
||||
return { appliedFilter: null, relays: [] };
|
||||
// Parse spell and get filter - handle both published (with event) and local (command-only) spells
|
||||
const parsed = useMemo(() => {
|
||||
if (!targetRelay) {
|
||||
console.log(`[RelaySpell:${spell.name || spellId}] No target relay`);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = decodeSpell(spell.event);
|
||||
const applied = applySpellParameters(parsed, [targetRelay]);
|
||||
return {
|
||||
appliedFilter: applied,
|
||||
relays: parsed.relays || [],
|
||||
console.log(`[RelaySpell:${spell.name || spellId}] Parsing spell:`, {
|
||||
hasEvent: !!spell.event,
|
||||
command: spell.command,
|
||||
parameterType: spell.parameterType,
|
||||
});
|
||||
|
||||
// If we have a published event, decode it
|
||||
if (spell.event) {
|
||||
const decoded = decodeSpell(spell.event);
|
||||
console.log(
|
||||
`[RelaySpell:${spell.name || spellId}] Decoded from event:`,
|
||||
{
|
||||
filter: decoded.filter,
|
||||
relays: decoded.relays,
|
||||
parameter: decoded.parameter,
|
||||
},
|
||||
);
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// For local spells, parse the command directly
|
||||
console.log(
|
||||
`[RelaySpell:${spell.name || spellId}] Parsing local spell command`,
|
||||
);
|
||||
const commandWithoutPrefix = spell.command
|
||||
.replace(/^\s*(req|count)\s+/i, "")
|
||||
.trim();
|
||||
const tokens = commandWithoutPrefix.split(/\s+/);
|
||||
const commandParsed = parseReqCommand(tokens);
|
||||
|
||||
// Create a ParsedSpell-like object for local spells
|
||||
const localParsed = {
|
||||
command: spell.command,
|
||||
filter: commandParsed.filter,
|
||||
relays: commandParsed.relays,
|
||||
closeOnEose: commandParsed.closeOnEose,
|
||||
parameter: spell.parameterType
|
||||
? {
|
||||
type: spell.parameterType,
|
||||
default: spell.parameterDefault,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
|
||||
console.log(`[RelaySpell:${spell.name || spellId}] Parsed local spell:`, {
|
||||
filter: localParsed.filter,
|
||||
relays: localParsed.relays,
|
||||
parameter: localParsed.parameter,
|
||||
});
|
||||
|
||||
return localParsed;
|
||||
} catch (error) {
|
||||
console.error("Failed to apply spell parameters:", error);
|
||||
return { appliedFilter: null, relays: [] };
|
||||
console.error(
|
||||
`[RelaySpell:${spell.name || spellId}] Failed to parse spell:`,
|
||||
error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}, [spell.event, targetRelay]);
|
||||
}, [spell, targetRelay, spellId]);
|
||||
|
||||
// Apply parameters to get final filter
|
||||
const appliedFilter = useMemo(() => {
|
||||
if (!parsed || !targetRelay) return null;
|
||||
|
||||
try {
|
||||
const applied = applySpellParameters(parsed, [targetRelay]);
|
||||
console.log(`[RelaySpell:${spell.name || spellId}] Applied parameters:`, {
|
||||
input: targetRelay,
|
||||
result: applied,
|
||||
});
|
||||
return applied;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[RelaySpell:${spell.name || spellId}] Failed to apply parameters:`,
|
||||
error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}, [parsed, targetRelay, spell.name, spellId]);
|
||||
|
||||
// Resolve relays - for $relay spells, we query FROM the target relay itself
|
||||
const finalRelays = useMemo(() => {
|
||||
// Use explicit relays from spell if provided
|
||||
if (parsed?.relays && parsed.relays.length > 0) {
|
||||
console.log(
|
||||
`[RelaySpell:${spell.name || spellId}] Using explicit relays:`,
|
||||
parsed.relays,
|
||||
);
|
||||
return parsed.relays;
|
||||
}
|
||||
|
||||
// For $relay spells, query FROM the target relay
|
||||
console.log(`[RelaySpell:${spell.name || spellId}] Using target relay:`, [
|
||||
targetRelay,
|
||||
]);
|
||||
return [targetRelay];
|
||||
}, [parsed?.relays, targetRelay, spell.name, spellId]);
|
||||
|
||||
// Fetch events using the applied filter
|
||||
const { events, loading, eoseReceived, relayStates, overallState } =
|
||||
appliedFilter
|
||||
appliedFilter && finalRelays.length > 0
|
||||
? useReqTimelineEnhanced(
|
||||
`spell-${spellId}-${targetRelay}`,
|
||||
appliedFilter,
|
||||
relays,
|
||||
finalRelays,
|
||||
{ limit: appliedFilter.limit || 50, stream: true },
|
||||
)
|
||||
: {
|
||||
@@ -90,6 +176,14 @@ function SpellTabContent({
|
||||
return map;
|
||||
}, [relayStates]);
|
||||
|
||||
console.log(`[RelaySpell:${spell.name || spellId}] Render state:`, {
|
||||
hasFilter: !!appliedFilter,
|
||||
relayCount: finalRelays.length,
|
||||
eventCount: events.length,
|
||||
loading,
|
||||
eoseReceived,
|
||||
});
|
||||
|
||||
return (
|
||||
<TabsContent
|
||||
value={spellId}
|
||||
@@ -99,6 +193,7 @@ function SpellTabContent({
|
||||
<div className="flex items-center justify-center h-full p-8 text-center text-muted-foreground">
|
||||
<div>
|
||||
<p className="text-sm">Unable to apply spell to this relay</p>
|
||||
<p className="text-xs mt-2">Check console for details</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
@@ -109,7 +204,7 @@ function SpellTabContent({
|
||||
loading={loading}
|
||||
overallState={overallState}
|
||||
events={events}
|
||||
relays={relays}
|
||||
relays={finalRelays}
|
||||
filter={appliedFilter}
|
||||
spellEvent={spell.event}
|
||||
reqRelayStates={reqRelayStatesMap}
|
||||
|
||||
@@ -105,8 +105,22 @@ export function useParameterizedSpells(
|
||||
// Include spells with $me or $contacts that could be parameterized
|
||||
if (!spell.parameterType) {
|
||||
const cmd = spell.command.toLowerCase();
|
||||
// Check if command uses $me or $contacts (single arg that can become $pubkey)
|
||||
return cmd.includes("$me") || cmd.includes("$contacts");
|
||||
// Check if command uses ONLY $me or ONLY $contacts (not both, not mixed with other pubkeys)
|
||||
const hasMeOrContacts =
|
||||
cmd.includes("$me") || cmd.includes("$contacts");
|
||||
if (!hasMeOrContacts) return false;
|
||||
|
||||
// Make sure it doesn't use multiple different variables
|
||||
const hasBothMeAndContacts =
|
||||
cmd.includes("$me") && cmd.includes("$contacts");
|
||||
if (hasBothMeAndContacts) return false; // Can't have both
|
||||
|
||||
// Check if it has hex pubkeys mixed with $me/$contacts
|
||||
// This is a rough check - looking for hex strings in -a or #p tags
|
||||
const hasHexPubkey = /(?:-a|#p)\s+[a-f0-9]{64}/.test(cmd);
|
||||
if (hasHexPubkey) return false; // Mixed pubkeys, not eligible
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -518,7 +518,7 @@ export function reconstructCommand(
|
||||
* @returns Filter with parameters applied
|
||||
*/
|
||||
export function applySpellParameters(
|
||||
parsed: ParsedSpell,
|
||||
parsed: Pick<ParsedSpell, "filter" | "parameter">,
|
||||
args: string[] = [],
|
||||
): NostrFilter {
|
||||
if (!parsed.parameter) {
|
||||
|
||||
Reference in New Issue
Block a user