Add skip button to mute segment notices

Fixes #1075
This commit is contained in:
Ajay
2022-04-22 02:33:07 -04:00
parent f2172bb2ad
commit d7e67fb397
4 changed files with 70 additions and 39 deletions

View File

@@ -14,6 +14,12 @@ import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
import PencilSvg from "../svg-icons/pencil_svg"; import PencilSvg from "../svg-icons/pencil_svg";
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils"; import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
enum SkipButtonState {
Undo, // Unskip
Redo, // Reskip
Start // Skip
}
export interface SkipNoticeProps { export interface SkipNoticeProps {
segments: SponsorTime[]; segments: SponsorTime[];
@@ -39,8 +45,8 @@ export interface SkipNoticeState {
maxCountdownTime?: () => number; maxCountdownTime?: () => number;
countdownText?: string; countdownText?: string;
skipButtonText?: string; skipButtonState?: SkipButtonState;
skipButtonCallback?: (index: number) => void; skipButtonCallback?: (index: number, forceSeek: boolean) => void;
showSkipButton?: boolean; showSkipButton?: boolean;
editing?: boolean; editing?: boolean;
@@ -121,10 +127,10 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
countdownTime: Config.config.skipNoticeDuration, countdownTime: Config.config.skipNoticeDuration,
countdownText: null, countdownText: null,
skipButtonText: this.props.startReskip skipButtonState: this.props.startReskip
? this.getReskipText() : this.getUnskipText(), ? SkipButtonState.Redo : SkipButtonState.Undo,
skipButtonCallback: this.props.startReskip skipButtonCallback: this.props.startReskip
? (index) => this.reskip(index) : (index) => this.unskip(index), ? this.reskip.bind(this) : this.unskip.bind(this),
showSkipButton: true, showSkipButton: true,
editing: false, editing: false,
@@ -144,7 +150,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
if (!this.autoSkip) { if (!this.autoSkip) {
// Assume manual skip is only skipping 1 submission // Assume manual skip is only skipping 1 submission
Object.assign(this.state, this.getUnskippedModeInfo(0, this.getSkipText())); Object.assign(this.state, this.getUnskippedModeInfo(0, SkipButtonState.Start));
} }
} }
@@ -157,7 +163,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
// If it started out as smaller, always keep the // If it started out as smaller, always keep the
// skip button there // skip button there
const firstColumn = this.props.smaller ? ( const firstColumn = this.props.smaller || this.segments[0].actionType === ActionType.Mute ? (
this.getSkipButton() this.getSkipButton()
) : null; ) : null;
@@ -250,7 +256,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} }
{/* Unskip/Skip Button */} {/* Unskip/Skip Button */}
{!this.props.smaller ? this.getSkipButton() : null} {!this.props.smaller || this.segments[0].actionType === ActionType.Mute
? this.getSkipButton(this.segments[0].actionType === ActionType.Mute) : null}
{/* Never show button */} {/* Never show button */}
{!this.autoSkip || this.props.startReskip ? "" : {!this.autoSkip || this.props.startReskip ? "" :
@@ -327,14 +334,15 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
]; ];
} }
getSkipButton(): JSX.Element { getSkipButton(forceSeek = false): JSX.Element {
if (this.state.showSkipButton && (this.segments.length > 1 if (this.state.showSkipButton && (this.segments.length > 1
|| this.segments[0].actionType !== ActionType.Poi || this.segments[0].actionType !== ActionType.Poi
|| this.props.unskipTime)) { || this.props.unskipTime)) {
const style: React.CSSProperties = { const style: React.CSSProperties = {
marginLeft: "4px", marginLeft: "4px",
color: (this.state.actionState === SkipNoticeAction.Unskip) ? this.selectedColor : this.unselectedColor color: ([SkipNoticeAction.Unskip, SkipNoticeAction.UnskipForceSeek].includes(this.state.actionState))
? this.selectedColor : this.unselectedColor
}; };
if (this.contentContainer().onMobileYouTube) { if (this.contentContainer().onMobileYouTube) {
style.padding = "20px"; style.padding = "20px";
@@ -346,8 +354,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
<button id={"sponsorSkipUnskipButton" + this.idSuffix} <button id={"sponsorSkipUnskipButton" + this.idSuffix}
className="sponsorSkipObject sponsorSkipNoticeButton" className="sponsorSkipObject sponsorSkipNoticeButton"
style={style} style={style}
onClick={() => this.prepAction(SkipNoticeAction.Unskip)}> onClick={() => this.prepAction(forceSeek ? SkipNoticeAction.UnskipForceSeek : SkipNoticeAction.Unskip)}>
{this.state.skipButtonText + (this.state.showKeybindHint ? " (" + keybindToString(Config.config.skipKeybind) + ")" : "")} {this.getSkipButtonText(forceSeek ? ActionType.Skip : null) + (!forceSeek && this.state.showKeybindHint ? " (" + keybindToString(Config.config.skipKeybind) + ")" : "")}
</button> </button>
</span> </span>
); );
@@ -451,6 +459,9 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
case SkipNoticeAction.Unskip: case SkipNoticeAction.Unskip:
this.resetStateToStart(SkipNoticeAction.Unskip); this.resetStateToStart(SkipNoticeAction.Unskip);
break; break;
case SkipNoticeAction.UnskipForceSeek:
this.resetStateToStart(SkipNoticeAction.UnskipForceSeek);
break;
} }
} }
} }
@@ -478,7 +489,10 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
this.copyDownvote(index); this.copyDownvote(index);
break; break;
case SkipNoticeAction.Unskip: case SkipNoticeAction.Unskip:
this.unskipAction(index); this.unskipAction(index, false);
break;
case SkipNoticeAction.UnskipForceSeek:
this.unskipAction(index, true);
break; break;
default: default:
this.resetStateToStart(); this.resetStateToStart();
@@ -540,8 +554,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}); });
} }
unskipAction(index: number): void { unskipAction(index: number, forceSeek: boolean): void {
this.state.skipButtonCallback(index); this.state.skipButtonCallback(index, forceSeek);
} }
openEditingOptions(): void { openEditingOptions(): void {
@@ -568,17 +582,17 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
return this.props.contentContainer().lockedCategories.includes(category) ? "sponsorBlockLockedColor" : "" return this.props.contentContainer().lockedCategories.includes(category) ? "sponsorBlockLockedColor" : ""
} }
unskip(index: number): void { unskip(index: number, forceSeek: boolean): void {
this.contentContainer().unskipSponsorTime(this.segments[index], this.props.unskipTime); this.contentContainer().unskipSponsorTime(this.segments[index], this.props.unskipTime, forceSeek);
this.unskippedMode(index, this.getReskipText()); this.unskippedMode(index, SkipButtonState.Redo);
} }
reskip(index: number): void { reskip(index: number, forceSeek: boolean): void {
this.contentContainer().reskipSponsorTime(this.segments[index]); this.contentContainer().reskipSponsorTime(this.segments[index], forceSeek);
const newState: SkipNoticeState = { const newState: SkipNoticeState = {
skipButtonText: this.getUnskipText(), skipButtonState: SkipButtonState.Undo,
skipButtonCallback: this.unskip.bind(this), skipButtonCallback: this.unskip.bind(this),
maxCountdownTime: () => Config.config.skipNoticeDuration, maxCountdownTime: () => Config.config.skipNoticeDuration,
@@ -597,14 +611,14 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} }
/** Sets up notice to be not skipped yet */ /** Sets up notice to be not skipped yet */
unskippedMode(index: number, buttonText: string): void { unskippedMode(index: number, skipButtonState: SkipButtonState): void {
//setup new callback and reset countdown //setup new callback and reset countdown
this.setState(this.getUnskippedModeInfo(index, buttonText), () => { this.setState(this.getUnskippedModeInfo(index, skipButtonState), () => {
this.noticeRef.current.resetCountdown(); this.noticeRef.current.resetCountdown();
}); });
} }
getUnskippedModeInfo(index: number, buttonText: string): SkipNoticeState { getUnskippedModeInfo(index: number, skipButtonState: SkipButtonState): SkipNoticeState {
const changeCountdown = this.segments[index].actionType !== ActionType.Poi; const changeCountdown = this.segments[index].actionType !== ActionType.Poi;
const maxCountdownTime = changeCountdown ? () => { const maxCountdownTime = changeCountdown ? () => {
@@ -615,8 +629,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} : this.state.maxCountdownTime; } : this.state.maxCountdownTime;
return { return {
skipButtonText: buttonText, skipButtonState: skipButtonState,
skipButtonCallback: (index) => this.reskip(index), skipButtonCallback: this.reskip.bind(this),
// change max duration to however much of the sponsor is left // change max duration to however much of the sponsor is left
maxCountdownTime: maxCountdownTime, maxCountdownTime: maxCountdownTime,
countdownTime: maxCountdownTime() countdownTime: maxCountdownTime()
@@ -713,8 +727,20 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}); });
} }
private getUnskipText(): string { private getSkipButtonText(forceType?: ActionType): string {
switch (this.props.segments[0].actionType) { switch (this.state.skipButtonState) {
case SkipButtonState.Undo:
return this.getUndoText(forceType);
case SkipButtonState.Redo:
return this.getRedoText(forceType);
case SkipButtonState.Start:
return this.getStartText(forceType);
}
}
private getUndoText(forceType?: ActionType): string {
const actionType = forceType || this.segments[0].actionType;
switch (actionType) {
case ActionType.Mute: { case ActionType.Mute: {
return chrome.i18n.getMessage("unmute"); return chrome.i18n.getMessage("unmute");
} }
@@ -725,8 +751,9 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} }
} }
private getReskipText(): string { private getRedoText(forceType?: ActionType): string {
switch (this.props.segments[0].actionType) { const actionType = forceType || this.segments[0].actionType;
switch (actionType) {
case ActionType.Mute: { case ActionType.Mute: {
return chrome.i18n.getMessage("mute"); return chrome.i18n.getMessage("mute");
} }
@@ -737,8 +764,9 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} }
} }
private getSkipText(): string { private getStartText(forceType?: ActionType): string {
switch (this.props.segments[0].actionType) { const actionType = forceType || this.segments[0].actionType;
switch (actionType) {
case ActionType.Mute: { case ActionType.Mute: {
return chrome.i18n.getMessage("mute"); return chrome.i18n.getMessage("mute");
} }

View File

@@ -1385,19 +1385,21 @@ function createSkipNotice(skippingSegments: SponsorTime[], autoSkip: boolean, un
activeSkipKeybindElement = newSkipNotice; activeSkipKeybindElement = newSkipNotice;
} }
function unskipSponsorTime(segment: SponsorTime, unskipTime: number = null) { function unskipSponsorTime(segment: SponsorTime, unskipTime: number = null, forceSeek = false) {
if (segment.actionType === ActionType.Mute) { if (segment.actionType === ActionType.Mute) {
video.muted = false; video.muted = false;
videoMuted = false; videoMuted = false;
} else { }
if (forceSeek || segment.actionType === ActionType.Skip) {
//add a tiny bit of time to make sure it is not skipped again //add a tiny bit of time to make sure it is not skipped again
video.currentTime = unskipTime ?? segment.segment[0] + 0.001; video.currentTime = unskipTime ?? segment.segment[0] + 0.001;
} }
} }
function reskipSponsorTime(segment: SponsorTime) { function reskipSponsorTime(segment: SponsorTime, forceSeek = false) {
if (segment.actionType === ActionType.Mute) { if (segment.actionType === ActionType.Mute && !forceSeek) {
video.muted = true; video.muted = true;
videoMuted = true; videoMuted = true;
} else { } else {

View File

@@ -6,13 +6,13 @@ export interface ContentContainer {
(): { (): {
vote: (type: number, UUID: SegmentUUID, category?: Category, skipNotice?: SkipNoticeComponent) => void, vote: (type: number, UUID: SegmentUUID, category?: Category, skipNotice?: SkipNoticeComponent) => void,
dontShowNoticeAgain: () => void, dontShowNoticeAgain: () => void,
unskipSponsorTime: (segment: SponsorTime, unskipTime: number) => void, unskipSponsorTime: (segment: SponsorTime, unskipTime: number, forceSeek?: boolean) => void,
sponsorTimes: SponsorTime[], sponsorTimes: SponsorTime[],
sponsorTimesSubmitting: SponsorTime[], sponsorTimesSubmitting: SponsorTime[],
skipNotices: SkipNotice[], skipNotices: SkipNotice[],
v: HTMLVideoElement, v: HTMLVideoElement,
sponsorVideoID, sponsorVideoID,
reskipSponsorTime: (segment: SponsorTime) => void, reskipSponsorTime: (segment: SponsorTime, forceSeek?: boolean) => void,
updatePreviewBar: () => void, updatePreviewBar: () => void,
onMobileYouTube: boolean, onMobileYouTube: boolean,
sponsorSubmissionNotice: SubmissionNotice, sponsorSubmissionNotice: SubmissionNotice,

View File

@@ -7,7 +7,8 @@ export enum SkipNoticeAction {
Downvote, Downvote,
CategoryVote, CategoryVote,
CopyDownvote, CopyDownvote,
Unskip Unskip,
UnskipForceSeek
} }
export function downvoteButtonColor(segments: SponsorTime[], actionState: SkipNoticeAction, downvoteType: SkipNoticeAction): string { export function downvoteButtonColor(segments: SponsorTime[], actionState: SkipNoticeAction, downvoteType: SkipNoticeAction): string {