feat(panels): drag UX improvements and collapsible live panels (#2217)#2500
feat(panels): drag UX improvements and collapsible live panels (#2217)#2500rayanhabib07 wants to merge 4 commits intokoala73:mainfrom
Conversation
- Add collapsible option to Panel base class with collapse/expand button - Enable collapsible on LiveNewsPanel and LiveWebcamsPanel - Add CSS to shrink panel to header height when collapsed - Clean up debug console.logs from undo handler Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Sync aria-label on collapse button toggle (was only updating btn.title) - Use optional chaining on isContentEditable to match null-safety of tagName guard - Add onCollapse hook to Panel base; LiveNewsPanel pauses/resumes player on collapse; LiveWebcamsPanel nulls/restores iframe srcs to stop stream bandwidth Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@rayanhabib07 is attempting to deploy a commit to the Elie Team on Vercel. A member of the Team first needs to authorize it. |
Greptile SummaryThis PR adds a collapsible header button to The approach is architecturally sound but
Confidence Score: 4/5Two P1 defects in LiveWebcamsPanel.onCollapse should be resolved before merging: incorrect iframe clearing and the restore-collapsed race condition. The template-method design and LiveNewsPanel implementation are solid. However, the two confirmed bugs in LiveWebcamsPanel — wrong src/components/LiveWebcamsPanel.ts requires attention for both the Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant CollapseBtn
participant Panel
participant LiveWebcamsPanel
participant LiveNewsPanel
User->>CollapseBtn: click
CollapseBtn->>Panel: _applyCollapsed(btn, !collapsed)
Panel->>Panel: content.style.display = 'none'
Panel->>Panel: update aria-label / aria-expanded
Panel->>LiveWebcamsPanel: onCollapse(true)
LiveWebcamsPanel->>LiveWebcamsPanel: save iframe.src → dataset.src
LiveWebcamsPanel->>LiveWebcamsPanel: iframe.src = '' ⚠️ (should be 'about:blank')
Panel->>LiveNewsPanel: onCollapse(true)
LiveNewsPanel->>LiveNewsPanel: player?.pauseVideo()
LiveNewsPanel->>LiveNewsPanel: nativeVideoElement?.pause()
Panel->>Panel: savePanelCollapsed(panelId, true)
User->>CollapseBtn: click again
CollapseBtn->>Panel: _applyCollapsed(btn, false)
Panel->>Panel: content.style.display = ''
Panel->>LiveWebcamsPanel: onCollapse(false)
LiveWebcamsPanel->>LiveWebcamsPanel: iframe.src = dataset.src (restore)
Panel->>LiveNewsPanel: onCollapse(false)
LiveNewsPanel->>LiveNewsPanel: player?.playVideo()
LiveNewsPanel->>LiveNewsPanel: nativeVideoElement?.play()
Reviews (1): Last reviewed commit: "fix: apply greptile feedback on collapse..." | Re-trigger Greptile |
| for (const iframe of this.iframes) { | ||
| if (collapsed) { | ||
| iframe.dataset.src = iframe.src; | ||
| iframe.src = ''; |
There was a problem hiding this comment.
iframe.src = '' navigates to the current page, not a blank frame
Setting iframe.src = '' resolves the empty string relative to the document's base URL, which makes the browser navigate the iframe to the current application page — not clear it. This means each webcam iframe will begin loading the WorldMonitor SPA itself when collapsed, wasting bandwidth and CPU.
Every other cleanup path in this file explicitly uses 'about:blank' (see retryIframe at line 383 and destroyIframes at lines 595 and 601). This should follow the same pattern:
| iframe.src = ''; | |
| iframe.src = 'about:blank'; |
| protected override onCollapse(collapsed: boolean): void { | ||
| for (const iframe of this.iframes) { | ||
| if (collapsed) { | ||
| iframe.dataset.src = iframe.src; | ||
| iframe.src = ''; | ||
| } else if (iframe.dataset.src) { | ||
| iframe.src = iframe.dataset.src; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Streams not paused when panel is restored as collapsed from storage
Panel._applyCollapsed(btn, true) is called in the Panel base-class constructor (line 320–321 of Panel.ts) to restore the previously-saved collapsed state. At that point this.iframes is still the freshly-initialised empty array [], so the for loop in onCollapse is a no-op.
LiveWebcamsPanel.render() is only called after super() returns (constructor line 146), so all iframes are created — with live stream src values — after onCollapse(true) already fired. The panel is visually hidden but every YouTube embed continues loading in the background, consuming bandwidth and connections.
A straightforward fix is to check this._collapsed when creating each iframe and skip setting src if the panel is currently collapsed:
// in createIframe (or where the iframe is pushed to this.iframes)
const src = this._collapsed ? 'about:blank' : this.buildEmbedUrl(feed.fallbackVideoId);
iframe.src = src;
if (this._collapsed) iframe.dataset.src = this.buildEmbedUrl(feed.fallbackVideoId);Alternatively, expose get collapsed(): boolean from Panel and read it here.
| } else if (iframe.dataset.src) { | ||
| iframe.src = iframe.dataset.src; | ||
| } |
There was a problem hiding this comment.
dataset.src is never cleared after restoring the iframe
After expanding, iframe.src is correctly restored from iframe.dataset.src, but dataset.src is never deleted. The stale attribute persists on the element for the rest of its lifetime. While not a runtime bug, it leaves confusing hidden state on the DOM node and could mislead future code that inspects dataset.src to determine whether the iframe is "paused".
| } else if (iframe.dataset.src) { | |
| iframe.src = iframe.dataset.src; | |
| } | |
| iframe.src = iframe.dataset.src; | |
| delete iframe.dataset.src; |
|
Same, you sent this yesterday #2444 You should check Github to see your PRs |
Summary
Notes
Test plan