mirror of
https://github.com/hoornet/vega.git
synced 2026-05-06 12:19:11 -07:00
Thread focus: auto-expand collapsed replies, debounced scroll, visible highlight; persist script filter
This commit is contained in:
@@ -55,7 +55,7 @@ export function NoteCard({ event, focused, onReplyInThread }: NoteCardProps) {
|
||||
<article
|
||||
ref={cardRef}
|
||||
data-note-id={event.id}
|
||||
className={`border-b border-border px-4 py-3 hover:bg-bg-hover transition-colors duration-100 cursor-pointer group/card${focused ? " ring-1 ring-inset ring-accent/30" : ""}`}
|
||||
className={`border-b border-border px-4 py-3 hover:bg-bg-hover transition-colors duration-100 cursor-pointer group/card${focused ? " bg-accent/10 border-l-2 border-l-accent" : ""}`}
|
||||
onClick={(e) => {
|
||||
// Don't navigate if clicking on interactive elements
|
||||
const target = e.target as HTMLElement;
|
||||
|
||||
@@ -108,8 +108,19 @@ function InlineThreadReply({ replyTo, rootEvent, onPublished }: {
|
||||
);
|
||||
}
|
||||
|
||||
/** Check if any descendant of a node has the given event ID. */
|
||||
function subtreeContains(node: ThreadNodeType, id: string): boolean {
|
||||
for (const child of node.children) {
|
||||
if (child.event.id === id) return true;
|
||||
if (subtreeContains(child, id)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function ThreadNodeComponent({ node, rootEvent, onReplyPublished, focusedId, mutedPubkeys, contentMatchesMutedKeyword }: ThreadNodeProps) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
// Auto-expand if the focused note is hidden inside a collapsed section
|
||||
const focusedInChildren = focusedId ? subtreeContains(node, focusedId) : false;
|
||||
const [expanded, setExpanded] = useState(focusedInChildren);
|
||||
const [showReplyBox, setShowReplyBox] = useState(false);
|
||||
|
||||
// Filter out muted children
|
||||
|
||||
@@ -108,16 +108,18 @@ export function ThreadView() {
|
||||
return () => { cancelled = true; };
|
||||
}, [focusedEvent.id, retryCount]);
|
||||
|
||||
// Scroll to focused note after tree renders (if not root)
|
||||
// Scroll to focused note after tree fully loads.
|
||||
// Use a short delay after each tree update; the last one wins.
|
||||
const scrollTimer = useRef<ReturnType<typeof setTimeout>>(undefined);
|
||||
useEffect(() => {
|
||||
if (!loading && rootEvent && focusedEvent.id !== rootEvent.id) {
|
||||
const timer = setTimeout(() => {
|
||||
const el = document.querySelector(`[data-note-id="${focusedEvent.id}"]`);
|
||||
el?.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}, 100);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [loading, rootEvent?.id, focusedEvent.id]);
|
||||
if (focusedEvent.id === rootEvent?.id) return;
|
||||
clearTimeout(scrollTimer.current);
|
||||
scrollTimer.current = setTimeout(() => {
|
||||
const el = document.querySelector(`[data-note-id="${focusedEvent.id}"]`);
|
||||
el?.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}, 400);
|
||||
return () => clearTimeout(scrollTimer.current);
|
||||
}, [tree, focusedEvent.id, rootEvent?.id]);
|
||||
|
||||
// Called when any inline reply box publishes a reply
|
||||
const handleReplyPublished = (reply: NDKEvent) => {
|
||||
|
||||
@@ -53,6 +53,7 @@ interface UIState {
|
||||
const SIDEBAR_KEY = "wrystr_sidebar_collapsed";
|
||||
const FONT_SIZE_KEY = "wrystr_font_size";
|
||||
const THEME_KEY = "wrystr_theme";
|
||||
const SCRIPT_FILTER_KEY = "wrystr_script_filter";
|
||||
|
||||
export const useUIStore = create<UIState>((set, _get) => ({
|
||||
currentView: "feed",
|
||||
@@ -69,7 +70,7 @@ export const useUIStore = create<UIState>((set, _get) => ({
|
||||
pendingHashtag: null,
|
||||
showHelp: false,
|
||||
showDebugPanel: false,
|
||||
feedLanguageFilter: null,
|
||||
feedLanguageFilter: localStorage.getItem(SCRIPT_FILTER_KEY) || null,
|
||||
followsTab: "followers",
|
||||
fontSize: parseInt(localStorage.getItem(FONT_SIZE_KEY) || "14", 10),
|
||||
themeId: localStorage.getItem(THEME_KEY) || "midnight",
|
||||
@@ -102,7 +103,14 @@ export const useUIStore = create<UIState>((set, _get) => ({
|
||||
}
|
||||
return { showHelp: false, currentView: "feed", selectedNote: null, viewStack: [] };
|
||||
}),
|
||||
setFeedLanguageFilter: (feedLanguageFilter) => set({ feedLanguageFilter }),
|
||||
setFeedLanguageFilter: (feedLanguageFilter) => {
|
||||
if (feedLanguageFilter) {
|
||||
localStorage.setItem(SCRIPT_FILTER_KEY, feedLanguageFilter);
|
||||
} else {
|
||||
localStorage.removeItem(SCRIPT_FILTER_KEY);
|
||||
}
|
||||
set({ feedLanguageFilter });
|
||||
},
|
||||
setFontSize: (fontSize) => {
|
||||
localStorage.setItem(FONT_SIZE_KEY, String(fontSize));
|
||||
set({ fontSize });
|
||||
|
||||
Reference in New Issue
Block a user