mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-11 14:07:13 +03:00
add upcoming notices
This commit is contained in:
@@ -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">
|
||||||
|
|||||||
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;
|
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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
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 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;
|
||||||
|
|||||||
@@ -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";
|
||||||
Reference in New Issue
Block a user