This commit is contained in:
Ajay
2025-09-24 16:33:41 -04:00

View File

@@ -28,8 +28,8 @@ enum SegmentListTab {
Chapter Chapter
} }
interface segmentWithNesting extends SponsorTime { interface SegmentWithNesting extends SponsorTime {
innerChapters?: (segmentWithNesting|SponsorTime)[]; innerChapters?: (SegmentWithNesting|SponsorTime)[];
} }
export const SegmentListComponent = (props: SegmentListComponentProps) => { export const SegmentListComponent = (props: SegmentListComponentProps) => {
@@ -58,37 +58,43 @@ export const SegmentListComponent = (props: SegmentListComponentProps) => {
} }
}; };
const segmentsWithNesting: segmentWithNesting[] = []; const segmentsWithNesting = React.useMemo(() => {
let nbTrailingNonChapters = 0; const result: SegmentWithNesting[] = [];
function nestChapters(segments: segmentWithNesting[], seg: SponsorTime, topLevel?: boolean) { const chapterStack: SegmentWithNesting[] = [];
if (seg.actionType === ActionType.Chapter && segments.length) { for (let seg of props.segments) {
// trailing non-chapters can only exist at top level seg = {...seg};
const lastElement = segments[segments.length - (topLevel ? nbTrailingNonChapters + 1 : 1)] // non-chapter, do not nest
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 {
if (topLevel) {
nbTrailingNonChapters = 0;
}
segments.push(seg);
}
} else {
if (seg.actionType !== ActionType.Chapter) { if (seg.actionType !== ActionType.Chapter) {
nbTrailingNonChapters++; result.push(seg);
continue;
} }
// traverse the stack
while (chapterStack.length !== 0) {
// where's Array.prototype.at() :sob:
const lastChapter = chapterStack[chapterStack.length - 1];
// we know lastChapter.startTime <= seg.startTime, as content.ts sorts these
// so only compare endTime - if new ends before last, new is nested inside last
if (lastChapter.segment[1] >= seg.segment[1]) {
lastChapter.innerChapters ??= [];
lastChapter.innerChapters.push(seg);
chapterStack.push(seg);
break;
}
// last did not match, pop it off the stack
chapterStack.pop();
}
// chapter stack not empty = we found a place for the chapter
if (chapterStack.length !== 0) {
continue;
}
// push the chapter to the top-level list and to the stack
result.push(seg);
chapterStack.push(seg);
segments.push(seg);
} }
} return result;
props.segments.forEach((seg) => nestChapters(segmentsWithNesting, {...seg}, true)); }, [props.segments])
return ( return (
<div id="issueReporterContainer"> <div id="issueReporterContainer">
@@ -136,7 +142,7 @@ export const SegmentListComponent = (props: SegmentListComponentProps) => {
}; };
function SegmentListItem({ segment, videoID, currentTime, isVip, loopedChapter, tabFilter, sendMessage }: { function SegmentListItem({ segment, videoID, currentTime, isVip, loopedChapter, tabFilter, sendMessage }: {
segment: segmentWithNesting; segment: SegmentWithNesting;
videoID: VideoID; videoID: VideoID;
currentTime: number; currentTime: number;
isVip: boolean; isVip: boolean;
@@ -146,18 +152,32 @@ function SegmentListItem({ segment, videoID, currentTime, isVip, loopedChapter,
sendMessage: (request: Message) => Promise<MessageResponse>; sendMessage: (request: Message) => Promise<MessageResponse>;
}) { }) {
const [voteMessage, setVoteMessage] = React.useState<string | null>(null); const [voteMessage, setVoteMessage] = React.useState<string | null>(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); const [isLooped, setIsLooped] = React.useState(loopedChapter === segment.UUID);
let extraInfo = ""; // Update internal state if the hidden property of the segment changes
if (segment.hidden === SponsorHideType.Downvoted) { React.useEffect(() => {
// This one is downvoted setHidden(segment.hidden ?? SponsorHideType.Visible);
}, [segment.hidden])
let extraInfo: string;
switch (hidden) {
case SponsorHideType.Visible:
extraInfo = "";
break;
case SponsorHideType.Downvoted:
extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDownvote") + ")"; extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDownvote") + ")";
} else if (segment.hidden === SponsorHideType.MinimumDuration) { break;
// This one is too short case SponsorHideType.MinimumDuration:
extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDuration") + ")"; extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDuration") + ")";
} else if (segment.hidden === SponsorHideType.Hidden) { break;
case SponsorHideType.Hidden:
extraInfo = " (" + chrome.i18n.getMessage("manuallyHidden") + ")"; 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 ( return (
@@ -279,7 +299,7 @@ function SegmentListItem({ segment, videoID, currentTime, isVip, loopedChapter,
{ {
(segment.actionType === ActionType.Skip || segment.actionType === ActionType.Mute (segment.actionType === ActionType.Skip || segment.actionType === ActionType.Mute
|| segment.actionType === ActionType.Poi || segment.actionType === ActionType.Poi
&& [SponsorHideType.Visible, SponsorHideType.Hidden].includes(segment.hidden)) && && [SponsorHideType.Visible, SponsorHideType.Hidden].includes(hidden)) &&
<img <img
className="voteButton" className="voteButton"
title={chrome.i18n.getMessage("hideSegment")} title={chrome.i18n.getMessage("hideSegment")}
@@ -288,17 +308,11 @@ function SegmentListItem({ segment, videoID, currentTime, isVip, loopedChapter,
const stopAnimation = AnimationUtils.applyLoadingAnimation(e.currentTarget, 0.4); const stopAnimation = AnimationUtils.applyLoadingAnimation(e.currentTarget, 0.4);
stopAnimation(); stopAnimation();
if (segment.hidden === SponsorHideType.Hidden) { const newState = hidden === SponsorHideType.Hidden ? SponsorHideType.Visible : SponsorHideType.Hidden;
segment.hidden = SponsorHideType.Visible; setHidden(newState);
setHidden(SponsorHideType.Visible);
} else {
segment.hidden = SponsorHideType.Hidden;
setHidden(SponsorHideType.Hidden);
}
sendMessage({ sendMessage({
message: "hideSegment", message: "hideSegment",
type: segment.hidden, type: newState,
UUID: segment.UUID UUID: segment.UUID
}); });
}}/> }}/>
@@ -343,7 +357,7 @@ function SegmentListItem({ segment, videoID, currentTime, isVip, loopedChapter,
} }
function InnerChapterList({ chapters, videoID, currentTime, isVip, loopedChapter, tabFilter, sendMessage }: { function InnerChapterList({ chapters, videoID, currentTime, isVip, loopedChapter, tabFilter, sendMessage }: {
chapters: (segmentWithNesting)[]; chapters: (SegmentWithNesting)[];
videoID: VideoID; videoID: VideoID;
currentTime: number; currentTime: number;
isVip: boolean; isVip: boolean;