add upcoming notices

This commit is contained in:
Acors24
2024-09-03 23:37:42 +02:00
parent 3738b180dd
commit ae4f850ff4
7 changed files with 216 additions and 2 deletions

View File

@@ -209,6 +209,18 @@
<div class="small-description">__MSG_skipNoticeDurationDescription__</div> <div class="small-description">__MSG_skipNoticeDurationDescription__</div>
</div> </div>
<div data-type="toggle" data-toggle-type="reverse" data-sync="dontShowUpcomingNotice">
<div class="switch-container">
<label class="switch">
<input id="dontShowUpcomingNotice" type="checkbox" checked>
<span class="slider round"></span>
</label>
<label class="switch-label" for="dontShowUpcomingNotice">
__MSG_showUpcomingNotice__
</label>
</div>
</div>
<div data-type="toggle" data-toggle-type="reverse" data-sync="dontShowNotice"> <div data-type="toggle" data-toggle-type="reverse" data-sync="dontShowNotice">
<div class="switch-container"> <div class="switch-container">
<label class="switch"> <label class="switch">

View File

@@ -0,0 +1,99 @@
import * as React from "react";
import { ContentContainer, NoticeVisbilityMode, SponsorTime } from "../types";
import NoticeComponent from "./NoticeComponent";
import Config from "../config";
import { getUpcomingText } from "../utils/categoryUtils";
export interface UpcomingNoticeProps {
segments: SponsorTime[];
autoSkip: boolean;
timeUntilSegment: number;
contentContainer: ContentContainer;
closeListener: () => void;
showKeybindHint?: boolean;
}
class UpcomingNoticeComponent extends React.Component<UpcomingNoticeProps> {
noticeTitle: string;
segments: SponsorTime[];
autoSkip: boolean;
contentContainer: ContentContainer;
amountOfPreviousNotices: number;
timeUntilSegment: number;
idSuffix: string;
noticeRef: React.MutableRefObject<NoticeComponent>;
configListener: () => void;
constructor(props: UpcomingNoticeProps) {
super(props);
this.noticeRef = React.createRef();
this.segments = props.segments;
this.autoSkip = props.autoSkip;
this.contentContainer = props.contentContainer;
this.timeUntilSegment = props.timeUntilSegment;
const previousUpcomingNotices = document.getElementsByClassName("sponsorSkipNoticeParent");
this.amountOfPreviousNotices = previousUpcomingNotices.length;
if (this.segments.length > 1) {
this.segments.sort((a, b) => a.segment[0] - b.segment[0]);
}
// This is the suffix added at the end of every id
for (const segment of this.segments) {
this.idSuffix += segment.UUID;
}
this.idSuffix += this.amountOfPreviousNotices;
this.noticeTitle = getUpcomingText(this.segments);
}
render(): React.ReactElement {
const noticeStyle: React.CSSProperties = { };
if (this.contentContainer().onMobileYouTube) {
noticeStyle.bottom = "4em";
noticeStyle.transform = "scale(0.8) translate(10%, 10%)";
}
return (
<NoticeComponent
noticeTitle={this.noticeTitle}
amountOfPreviousNotices={this.amountOfPreviousNotices}
idSuffix={this.idSuffix}
fadeIn
startFaded={Config.config.noticeVisibilityMode >= NoticeVisbilityMode.FadedForAll
|| (Config.config.noticeVisibilityMode >= NoticeVisbilityMode.FadedForAutoSkip && this.autoSkip)}
timed
maxCountdownTime={() => Math.round(this.timeUntilSegment / 1000)}
style={noticeStyle}
biggerCloseButton={this.contentContainer().onMobileYouTube}
ref={this.noticeRef}
closeListener={() => this.closeListener()}
logoFill={Config.config.barTypes[this.segments[0].category].color}
limitWidth
dontPauseCountdown />
)
}
closeListener(): void {
this.clearConfigListener();
this.props.closeListener();
}
clearConfigListener(): void {
if (this.configListener) {
Config.configSyncListeners.splice(Config.configSyncListeners.indexOf(this.configListener), 1);
this.configListener = null;
}
}
}
export default UpcomingNoticeComponent;

View File

@@ -31,6 +31,7 @@ interface SBConfig {
trackDownvotes: boolean; trackDownvotes: boolean;
trackDownvotesInPrivate: boolean; trackDownvotesInPrivate: boolean;
dontShowNotice: boolean; dontShowNotice: boolean;
dontShowUpcomingNotice: boolean;
noticeVisibilityMode: NoticeVisbilityMode; noticeVisibilityMode: NoticeVisbilityMode;
hideVideoPlayerControls: boolean; hideVideoPlayerControls: boolean;
hideInfoButtonPlayerControls: boolean; hideInfoButtonPlayerControls: boolean;
@@ -293,6 +294,7 @@ const syncDefaults = {
trackDownvotes: true, trackDownvotes: true,
trackDownvotesInPrivate: false, trackDownvotesInPrivate: false,
dontShowNotice: false, dontShowNotice: false,
dontShowUpcomingNotice: false,
noticeVisibilityMode: NoticeVisbilityMode.FadedForAutoSkip, noticeVisibilityMode: NoticeVisbilityMode.FadedForAutoSkip,
hideVideoPlayerControls: false, hideVideoPlayerControls: false,
hideInfoButtonPlayerControls: false, hideInfoButtonPlayerControls: false,

View File

@@ -20,6 +20,7 @@ import Utils from "./utils";
import PreviewBar, { PreviewBarSegment } from "./js-components/previewBar"; import PreviewBar, { PreviewBarSegment } from "./js-components/previewBar";
import SkipNotice from "./render/SkipNotice"; import SkipNotice from "./render/SkipNotice";
import SkipNoticeComponent from "./components/SkipNoticeComponent"; import SkipNoticeComponent from "./components/SkipNoticeComponent";
import UpcomingNotice from "./render/UpcomingNotice";
import SubmissionNotice from "./render/SubmissionNotice"; import SubmissionNotice from "./render/SubmissionNotice";
import { Message, MessageResponse, VoteResponse } from "./messageTypes"; import { Message, MessageResponse, VoteResponse } from "./messageTypes";
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar"; import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
@@ -77,6 +78,7 @@ let importingChaptersWaiting = false;
let triedImportingChapters = false; let triedImportingChapters = false;
// List of open skip notices // List of open skip notices
const skipNotices: SkipNotice[] = []; const skipNotices: SkipNotice[] = [];
const upcomingNotices: UpcomingNotice[] = [];
let activeSkipKeybindElement: ToggleSkippable = null; let activeSkipKeybindElement: ToggleSkippable = null;
let retryFetchTimeout: NodeJS.Timeout = null; let retryFetchTimeout: NodeJS.Timeout = null;
let shownSegmentFailedToFetchWarning = false; let shownSegmentFailedToFetchWarning = false;
@@ -107,6 +109,7 @@ const lastNextChapterKeybind = {
let currentSkipSchedule: NodeJS.Timeout = null; let currentSkipSchedule: NodeJS.Timeout = null;
let currentSkipInterval: NodeJS.Timeout = null; let currentSkipInterval: NodeJS.Timeout = null;
let currentVirtualTimeInterval: NodeJS.Timeout = null; let currentVirtualTimeInterval: NodeJS.Timeout = null;
let currentUpcomingSchedule: NodeJS.Timeout = null;
/** Has the sponsor been skipped */ /** Has the sponsor been skipped */
let sponsorSkipped: boolean[] = []; let sponsorSkipped: boolean[] = [];
@@ -177,6 +180,7 @@ const skipNoticeContentContainer: ContentContainer = () => ({
sponsorTimes, sponsorTimes,
sponsorTimesSubmitting, sponsorTimesSubmitting,
skipNotices, skipNotices,
upcomingNotices,
sponsorVideoID: getVideoID(), sponsorVideoID: getVideoID(),
reskipSponsorTime, reskipSponsorTime,
updatePreviewBar, updatePreviewBar,
@@ -417,6 +421,10 @@ function resetValues() {
skipNotices.pop()?.close(); skipNotices.pop()?.close();
} }
for (let i = 0; i < upcomingNotices.length; i++) {
upcomingNotices.pop()?.close();
}
hideDeArrowPromotion(); hideDeArrowPromotion();
} }
@@ -601,6 +609,11 @@ function cancelSponsorSchedule(): void {
clearInterval(currentSkipInterval); clearInterval(currentSkipInterval);
currentSkipInterval = null; currentSkipInterval = null;
} }
if (currentUpcomingSchedule !== null) {
clearTimeout(currentUpcomingSchedule);
currentUpcomingSchedule = null;
}
} }
/** /**
@@ -782,7 +795,17 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
const offset = (isFirefoxOrSafari() && !isSafari() ? 600 : 150); const offset = (isFirefoxOrSafari() && !isSafari() ? 600 : 150);
// Schedule for right before to be more precise than normal timeout // Schedule for right before to be more precise than normal timeout
currentSkipSchedule = setTimeout(skippingFunction, Math.max(0, delayTime - offset)); const offsetDelayTime = Math.max(0, delayTime - offset);
currentSkipSchedule = setTimeout(skippingFunction, offsetDelayTime);
// Show the notice only if the segment hasn't already started
if (!Config.config.dontShowUpcomingNotice && getCurrentTime() < skippingSegments[0].segment[0]) {
const maxPopupTime = 3000;
const timeUntilPopup = Math.max(0, offsetDelayTime - maxPopupTime);
const popupTime = offsetDelayTime - timeUntilPopup;
const autoSkip = shouldAutoSkip(skippingSegments[0])
currentUpcomingSchedule = setTimeout(createUpcomingNotice, timeUntilPopup, skippingSegments, popupTime, autoSkip);
}
} }
} }
} }
@@ -1784,6 +1807,19 @@ function createSkipNotice(skippingSegments: SponsorTime[], autoSkip: boolean, un
activeSkipKeybindElement = newSkipNotice; activeSkipKeybindElement = newSkipNotice;
} }
function createUpcomingNotice(skippingSegments: SponsorTime[], timeLeft: number, autoSkip: boolean) {
for (const upcomingNotice of upcomingNotices) {
if (skippingSegments.length === upcomingNotice.segments.length
&& skippingSegments.every((segment) => upcomingNotice.segments.some((s) => s.UUID === segment.UUID))) {
// Upcoming notice already exists
return;
}
}
const newUpcomingNotice = new UpcomingNotice(skippingSegments, skipNoticeContentContainer, timeLeft, autoSkip);
upcomingNotices.push(newUpcomingNotice);
}
function unskipSponsorTime(segment: SponsorTime, unskipTime: number = null, forceSeek = false) { function unskipSponsorTime(segment: SponsorTime, unskipTime: number = null, forceSeek = false) {
if (segment.actionType === ActionType.Mute) { if (segment.actionType === ActionType.Mute) {
getVideo().muted = false; getVideo().muted = false;
@@ -2561,6 +2597,7 @@ function hotkeyListener(e: KeyboardEvent): void {
} else if (keybindEquals(key, closeSkipNoticeKey)) { } else if (keybindEquals(key, closeSkipNoticeKey)) {
for (let i = 0; i < skipNotices.length; i++) { for (let i = 0; i < skipNotices.length; i++) {
skipNotices.pop().close(); skipNotices.pop().close();
upcomingNotices.pop().close();
} }
return; return;

View File

@@ -0,0 +1,54 @@
import * as React from "react";
import { createRoot, Root } from "react-dom/client";
import { ContentContainer, SponsorTime } from "../types";
import UpcomingNoticeComponent from "../components/UpcomingNoticeComponent";
import Utils from "../utils";
const utils = new Utils();
class UpcomingNotice {
segments: SponsorTime[];
// Contains functions and variables from the content script needed by the skip notice
contentContainer: ContentContainer;
noticeElement: HTMLDivElement;
upcomingNoticeRef: React.MutableRefObject<UpcomingNoticeComponent>;
root: Root;
constructor(segments: SponsorTime[], contentContainer: ContentContainer, timeLeft: number, autoSkip: boolean) {
this.upcomingNoticeRef = React.createRef();
this.segments = segments;
this.contentContainer = contentContainer;
const referenceNode = utils.findReferenceNode();
this.noticeElement = document.createElement("div");
this.noticeElement.className = "sponsorSkipNoticeContainer";
referenceNode.prepend(this.noticeElement);
this.root = createRoot(this.noticeElement);
this.root.render(
<UpcomingNoticeComponent
segments={segments}
autoSkip={autoSkip}
contentContainer={contentContainer}
timeUntilSegment={timeLeft}
ref={this.upcomingNoticeRef}
closeListener={() => this.close()} />
);
}
close(): void {
this.root.unmount();
this.noticeElement.remove();
const upcomingNotices = this.contentContainer().upcomingNotices;
upcomingNotices.splice(upcomingNotices.indexOf(this), 1);
}
}
export default UpcomingNotice;

View File

@@ -1,6 +1,7 @@
import SubmissionNotice from "./render/SubmissionNotice"; import SubmissionNotice from "./render/SubmissionNotice";
import SkipNoticeComponent from "./components/SkipNoticeComponent"; import SkipNoticeComponent from "./components/SkipNoticeComponent";
import SkipNotice from "./render/SkipNotice"; import SkipNotice from "./render/SkipNotice";
import UpcomingNotice from "./render/UpcomingNotice";
export interface ContentContainer { export interface ContentContainer {
(): { (): {
@@ -10,6 +11,7 @@ export interface ContentContainer {
sponsorTimes: SponsorTime[]; sponsorTimes: SponsorTime[];
sponsorTimesSubmitting: SponsorTime[]; sponsorTimesSubmitting: SponsorTime[];
skipNotices: SkipNotice[]; skipNotices: SkipNotice[];
upcomingNotices: UpcomingNotice[];
sponsorVideoID; sponsorVideoID;
reskipSponsorTime: (segment: SponsorTime, forceSeek?: boolean) => void; reskipSponsorTime: (segment: SponsorTime, forceSeek?: boolean) => void;
updatePreviewBar: () => void; updatePreviewBar: () => void;

View File

@@ -36,6 +36,15 @@ export function getSkippingText(segments: SponsorTime[], autoSkip: boolean): str
} }
} }
export function getUpcomingText(segments: SponsorTime[]): string {
const categoryName = chrome.i18n.getMessage(segments.length > 1 ? "multipleSegments"
: "category_" + segments[0].category + "_short") || chrome.i18n.getMessage("category_" + segments[0].category);
const messageId = "upcoming";
return chrome.i18n.getMessage(messageId).replace("{0}", categoryName);
}
export function getCategorySuffix(category: Category): string { export function getCategorySuffix(category: Category): string {
if (category.startsWith("poi_")) { if (category.startsWith("poi_")) {
return "_POI"; return "_POI";
@@ -51,5 +60,4 @@ export function getCategorySuffix(category: Category): string {
export function shortCategoryName(categoryName: string): string { export function shortCategoryName(categoryName: string): string {
return chrome.i18n.getMessage("category_" + categoryName + "_short") || chrome.i18n.getMessage("category_" + categoryName); return chrome.i18n.getMessage("category_" + categoryName + "_short") || chrome.i18n.getMessage("category_" + categoryName);
} }
export const DEFAULT_CATEGORY = "chooseACategory"; export const DEFAULT_CATEGORY = "chooseACategory";