mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-11 22:17:21 +03:00
add upcoming notices
This commit is contained in:
@@ -209,6 +209,18 @@
|
||||
<div class="small-description">__MSG_skipNoticeDurationDescription__</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 class="switch-container">
|
||||
<label class="switch">
|
||||
|
||||
99
src/components/UpcomingNoticeComponent.tsx
Normal file
99
src/components/UpcomingNoticeComponent.tsx
Normal 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;
|
||||
@@ -31,6 +31,7 @@ interface SBConfig {
|
||||
trackDownvotes: boolean;
|
||||
trackDownvotesInPrivate: boolean;
|
||||
dontShowNotice: boolean;
|
||||
dontShowUpcomingNotice: boolean;
|
||||
noticeVisibilityMode: NoticeVisbilityMode;
|
||||
hideVideoPlayerControls: boolean;
|
||||
hideInfoButtonPlayerControls: boolean;
|
||||
@@ -293,6 +294,7 @@ const syncDefaults = {
|
||||
trackDownvotes: true,
|
||||
trackDownvotesInPrivate: false,
|
||||
dontShowNotice: false,
|
||||
dontShowUpcomingNotice: false,
|
||||
noticeVisibilityMode: NoticeVisbilityMode.FadedForAutoSkip,
|
||||
hideVideoPlayerControls: false,
|
||||
hideInfoButtonPlayerControls: false,
|
||||
|
||||
@@ -20,6 +20,7 @@ import Utils from "./utils";
|
||||
import PreviewBar, { PreviewBarSegment } from "./js-components/previewBar";
|
||||
import SkipNotice from "./render/SkipNotice";
|
||||
import SkipNoticeComponent from "./components/SkipNoticeComponent";
|
||||
import UpcomingNotice from "./render/UpcomingNotice";
|
||||
import SubmissionNotice from "./render/SubmissionNotice";
|
||||
import { Message, MessageResponse, VoteResponse } from "./messageTypes";
|
||||
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
|
||||
@@ -77,6 +78,7 @@ let importingChaptersWaiting = false;
|
||||
let triedImportingChapters = false;
|
||||
// List of open skip notices
|
||||
const skipNotices: SkipNotice[] = [];
|
||||
const upcomingNotices: UpcomingNotice[] = [];
|
||||
let activeSkipKeybindElement: ToggleSkippable = null;
|
||||
let retryFetchTimeout: NodeJS.Timeout = null;
|
||||
let shownSegmentFailedToFetchWarning = false;
|
||||
@@ -107,6 +109,7 @@ const lastNextChapterKeybind = {
|
||||
let currentSkipSchedule: NodeJS.Timeout = null;
|
||||
let currentSkipInterval: NodeJS.Timeout = null;
|
||||
let currentVirtualTimeInterval: NodeJS.Timeout = null;
|
||||
let currentUpcomingSchedule: NodeJS.Timeout = null;
|
||||
|
||||
/** Has the sponsor been skipped */
|
||||
let sponsorSkipped: boolean[] = [];
|
||||
@@ -177,6 +180,7 @@ const skipNoticeContentContainer: ContentContainer = () => ({
|
||||
sponsorTimes,
|
||||
sponsorTimesSubmitting,
|
||||
skipNotices,
|
||||
upcomingNotices,
|
||||
sponsorVideoID: getVideoID(),
|
||||
reskipSponsorTime,
|
||||
updatePreviewBar,
|
||||
@@ -417,6 +421,10 @@ function resetValues() {
|
||||
skipNotices.pop()?.close();
|
||||
}
|
||||
|
||||
for (let i = 0; i < upcomingNotices.length; i++) {
|
||||
upcomingNotices.pop()?.close();
|
||||
}
|
||||
|
||||
hideDeArrowPromotion();
|
||||
}
|
||||
|
||||
@@ -601,6 +609,11 @@ function cancelSponsorSchedule(): void {
|
||||
clearInterval(currentSkipInterval);
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (segment.actionType === ActionType.Mute) {
|
||||
getVideo().muted = false;
|
||||
@@ -2561,6 +2597,7 @@ function hotkeyListener(e: KeyboardEvent): void {
|
||||
} else if (keybindEquals(key, closeSkipNoticeKey)) {
|
||||
for (let i = 0; i < skipNotices.length; i++) {
|
||||
skipNotices.pop().close();
|
||||
upcomingNotices.pop().close();
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
54
src/render/UpcomingNotice.tsx
Normal file
54
src/render/UpcomingNotice.tsx
Normal 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;
|
||||
@@ -1,6 +1,7 @@
|
||||
import SubmissionNotice from "./render/SubmissionNotice";
|
||||
import SkipNoticeComponent from "./components/SkipNoticeComponent";
|
||||
import SkipNotice from "./render/SkipNotice";
|
||||
import UpcomingNotice from "./render/UpcomingNotice";
|
||||
|
||||
export interface ContentContainer {
|
||||
(): {
|
||||
@@ -10,6 +11,7 @@ export interface ContentContainer {
|
||||
sponsorTimes: SponsorTime[];
|
||||
sponsorTimesSubmitting: SponsorTime[];
|
||||
skipNotices: SkipNotice[];
|
||||
upcomingNotices: UpcomingNotice[];
|
||||
sponsorVideoID;
|
||||
reskipSponsorTime: (segment: SponsorTime, forceSeek?: boolean) => void;
|
||||
updatePreviewBar: () => void;
|
||||
|
||||
@@ -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 {
|
||||
if (category.startsWith("poi_")) {
|
||||
return "_POI";
|
||||
@@ -51,5 +60,4 @@ export function getCategorySuffix(category: Category): string {
|
||||
export function shortCategoryName(categoryName: string): string {
|
||||
return chrome.i18n.getMessage("category_" + categoryName + "_short") || chrome.i18n.getMessage("category_" + categoryName);
|
||||
}
|
||||
|
||||
export const DEFAULT_CATEGORY = "chooseACategory";
|
||||
Reference in New Issue
Block a user