From 808c3718a8d4850dfc174bd3d049e2632694f894 Mon Sep 17 00:00:00 2001 From: mini-bomba <55105495+mini-bomba@users.noreply.github.com> Date: Fri, 19 Sep 2025 15:49:26 +0200 Subject: [PATCH 1/3] Fix segment hiding/unhiding --- src/popup/SegmentListComponent.tsx | 43 ++++++++++++++++-------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/popup/SegmentListComponent.tsx b/src/popup/SegmentListComponent.tsx index 4bf438b6e..3e7fee111 100644 --- a/src/popup/SegmentListComponent.tsx +++ b/src/popup/SegmentListComponent.tsx @@ -146,18 +146,27 @@ function SegmentListItem({ segment, videoID, currentTime, isVip, loopedChapter, sendMessage: (request: Message) => Promise; }) { const [voteMessage, setVoteMessage] = React.useState(null); - const [hidden, setHidden] = React.useState(segment.hidden || SponsorHideType.Visible); + const [hidden, setHidden] = React.useState(segment.hidden ?? SponsorHideType.Visible); // undefined ?? undefined lol const [isLooped, setIsLooped] = React.useState(loopedChapter === segment.UUID); - let extraInfo = ""; - if (segment.hidden === SponsorHideType.Downvoted) { - // This one is downvoted - extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDownvote") + ")"; - } else if (segment.hidden === SponsorHideType.MinimumDuration) { - // This one is too short - extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDuration") + ")"; - } else if (segment.hidden === SponsorHideType.Hidden) { - extraInfo = " (" + chrome.i18n.getMessage("manuallyHidden") + ")"; + let extraInfo: string; + switch (hidden) { + case SponsorHideType.Visible: + extraInfo = ""; + break; + case SponsorHideType.Downvoted: + extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDownvote") + ")"; + break; + case SponsorHideType.MinimumDuration: + extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDuration") + ")"; + break; + case SponsorHideType.Hidden: + extraInfo = " (" + chrome.i18n.getMessage("manuallyHidden") + ")"; + break; + default: + // hidden satisfies never; // need to upgrade TS + console.warn(`[SB] Unhandled variant of SponsorHideType in SegmentListItem: ${hidden}`); + extraInfo = ""; } return ( @@ -279,7 +288,7 @@ function SegmentListItem({ segment, videoID, currentTime, isVip, loopedChapter, { (segment.actionType === ActionType.Skip || segment.actionType === ActionType.Mute || segment.actionType === ActionType.Poi - && [SponsorHideType.Visible, SponsorHideType.Hidden].includes(segment.hidden)) && + && [SponsorHideType.Visible, SponsorHideType.Hidden].includes(hidden)) && From 5043a5fbe20bddfdbdd57237a82879b370f60625 Mon Sep 17 00:00:00 2001 From: mini-bomba <55105495+mini-bomba@users.noreply.github.com> Date: Fri, 19 Sep 2025 16:07:51 +0200 Subject: [PATCH 2/3] Sync internal hidden status with segment's value on change --- src/popup/SegmentListComponent.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/popup/SegmentListComponent.tsx b/src/popup/SegmentListComponent.tsx index 3e7fee111..c9c426c4c 100644 --- a/src/popup/SegmentListComponent.tsx +++ b/src/popup/SegmentListComponent.tsx @@ -149,6 +149,11 @@ function SegmentListItem({ segment, videoID, currentTime, isVip, loopedChapter, const [hidden, setHidden] = React.useState(segment.hidden ?? SponsorHideType.Visible); // undefined ?? undefined lol const [isLooped, setIsLooped] = React.useState(loopedChapter === segment.UUID); + // Update internal state if the hidden property of the segment changes + React.useEffect(() => { + setHidden(segment.hidden ?? SponsorHideType.Visible); + }, [segment.hidden]) + let extraInfo: string; switch (hidden) { case SponsorHideType.Visible: From 915fd70274bed0ebeba653bbe26437ea71a884af Mon Sep 17 00:00:00 2001 From: mini-bomba <55105495+mini-bomba@users.noreply.github.com> Date: Fri, 19 Sep 2025 16:17:30 +0200 Subject: [PATCH 3/3] Cache chapter nesting results --- src/popup/SegmentListComponent.tsx | 64 ++++++++++++++++-------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/popup/SegmentListComponent.tsx b/src/popup/SegmentListComponent.tsx index c9c426c4c..8ea207e38 100644 --- a/src/popup/SegmentListComponent.tsx +++ b/src/popup/SegmentListComponent.tsx @@ -28,8 +28,8 @@ enum SegmentListTab { Chapter } -interface segmentWithNesting extends SponsorTime { - innerChapters?: (segmentWithNesting|SponsorTime)[]; +interface SegmentWithNesting extends SponsorTime { + innerChapters?: (SegmentWithNesting|SponsorTime)[]; } export const SegmentListComponent = (props: SegmentListComponentProps) => { @@ -58,37 +58,41 @@ export const SegmentListComponent = (props: SegmentListComponentProps) => { } }; - const segmentsWithNesting: segmentWithNesting[] = []; - let nbTrailingNonChapters = 0; - function nestChapters(segments: segmentWithNesting[], seg: SponsorTime, topLevel?: boolean) { - if (seg.actionType === ActionType.Chapter && segments.length) { - // trailing non-chapters can only exist at top level - const lastElement = segments[segments.length - (topLevel ? nbTrailingNonChapters + 1 : 1)] - - if (lastElement.actionType === ActionType.Chapter - && lastElement.segment[0] <= seg.segment[0] - && lastElement.segment[1] >= seg.segment[1]) { - if (lastElement.innerChapters){ - nestChapters(lastElement.innerChapters, seg); + const segmentsWithNesting = React.useMemo(() => { + const result: SegmentWithNesting[] = []; + let nbTrailingNonChapters = 0; + function nestChapters(segments: SegmentWithNesting[], seg: SponsorTime, topLevel?: boolean) { + if (seg.actionType === ActionType.Chapter && segments.length) { + // trailing non-chapters can only exist at top level + const lastElement = segments[segments.length - (topLevel ? nbTrailingNonChapters + 1 : 1)] + + if (lastElement.actionType === ActionType.Chapter + && lastElement.segment[0] <= seg.segment[0] + && lastElement.segment[1] >= seg.segment[1]) { + if (lastElement.innerChapters){ + nestChapters(lastElement.innerChapters, seg); + } else { + lastElement.innerChapters = [seg]; + } } else { - lastElement.innerChapters = [seg]; - } - } else { - if (topLevel) { - nbTrailingNonChapters = 0; - } + if (topLevel) { + nbTrailingNonChapters = 0; + } - segments.push(seg); + segments.push(seg); + } + } else { + if (seg.actionType !== ActionType.Chapter) { + nbTrailingNonChapters++; } - } else { - if (seg.actionType !== ActionType.Chapter) { - nbTrailingNonChapters++; - } - segments.push(seg); + segments.push(seg); + } } - } - props.segments.forEach((seg) => nestChapters(segmentsWithNesting, {...seg}, true)); + props.segments.forEach((seg) => nestChapters(result, {...seg}, true)); + return result; + }, [props.segments]) + return (
@@ -136,7 +140,7 @@ export const SegmentListComponent = (props: SegmentListComponentProps) => { }; function SegmentListItem({ segment, videoID, currentTime, isVip, loopedChapter, tabFilter, sendMessage }: { - segment: segmentWithNesting; + segment: SegmentWithNesting; videoID: VideoID; currentTime: number; isVip: boolean; @@ -351,7 +355,7 @@ function SegmentListItem({ segment, videoID, currentTime, isVip, loopedChapter, } function InnerChapterList({ chapters, videoID, currentTime, isVip, loopedChapter, tabFilter, sendMessage }: { - chapters: (segmentWithNesting)[]; + chapters: (SegmentWithNesting)[]; videoID: VideoID; currentTime: number; isVip: boolean;