Merge pull request #363 from ajayyy/react

Category Improvements
This commit is contained in:
Ajay Ramachandran 2020-06-07 11:30:33 -04:00 committed by GitHub
commit 9ad67c1a03
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 438 additions and 104 deletions

View file

@ -1,7 +1,7 @@
{
"name": "__MSG_fullName__",
"short_name": "__MSG_Name__",
"version": "1.2.30",
"version": "1.2.31",
"default_locale": "en",
"description": "__MSG_Description__",
"content_scripts": [{
@ -32,6 +32,7 @@
"icons/downvote.png",
"icons/thumbs_down.svg",
"icons/thumbs_up.svg",
"icons/help.svg",
"icons/report.png",
"icons/close.png",
"icons/beep.ogg",

View file

@ -483,24 +483,54 @@
"category_sponsor": {
"message": "Sponsor"
},
"category_sponsor_description": {
"message": "Paid promotion, paid referrals and direct advertisements. Not for self-promotion or free shoutouts to causes/creators/websites/products they like."
},
"category_intro": {
"message": "Intro Animation"
},
"category_intro_description": {
"message": "Intro animations that are recurring in the series or provide no direct value. This should not be used on music videos."
},
"category_intro_short": {
"message": "Intro"
},
"category_outro": {
"message": "Endcards/Credits"
},
"category_outro_description": {
"message": "Credits or when the YouTube endcards appear. Not for spoken conclusions. This should not include useful content. This should not be used on music videos."
},
"category_interaction": {
"message": "Interaction Reminder (Subscribe)"
},
"category_interaction_description": {
"message": "When there is a short reminder to like, subscribe or follow them in the middle of content. If it is long or about something specific, it should be under self promotion instead."
},
"category_interaction_short": {
"message": "Interaction Reminder"
},
"category_selfpromo": {
"message": "Unpaid/Self Promotion"
},
"category_selfpromo_description": {
"message": "Similar to \"sponsor\" except for unpaid or self promotion. This includes sections about merchandise, donations, or information about who they collaborated with."
},
"category_music_offtopic": {
"message": "Music: Non-Music Section"
},
"category_music_offtopic_description": {
"message": "Only for use in music videos. This includes introductions or outros in music videos."
},
"category_music_offtopic_short": {
"message": "Non-Music"
},
"category_livestream_messages": {
"message": "Livestream: Donation/Message Readings"
},
"category_livestream_messages_short": {
"message": "Message Reading"
},
"disable": {
"message": "Disable"
},
@ -510,6 +540,23 @@
"showOverlay": {
"message": "Show In Seek Bar"
},
"colorFormatIncorrect": {
"message": "Your color is formatted incorrectly. It should be a 3 or 6 digit hex code with a number sign at the beginning."
},
"previewColor": {
"message": "Preview Color",
"description": "Referring to submissions that have not been sent to the server yet."
},
"seekBarColor": {
"message": "Seek Bar Color"
},
"category": {
"message": "Category"
},
"skipOption": {
"message": "Skip Option",
"description": "Used on the options page to describe the ways to skip the segment (auto skip, manual, etc.)"
},
"enableTestingServer": {
"message": "Enable Beta Testing Server"
},
@ -563,5 +610,24 @@
},
"multipleSegments": {
"message": "Multiple Segments"
},
"guidelines": {
"message": "Guidelines"
},
"readTheGuidelines": {
"message": "Read The Guidelines!!",
"description": "Show the first time they submit or if they are \"high risk\""
},
"categoryUpdate1": {
"message": "Categories are here!"
},
"categoryUpdate2": {
"message": "Open the options to skip intros, outros, merch, etc."
},
"unsubmittedWarning": {
"message": "Unsubmitted Segments Notification"
},
"unsubmittedWarningDescription": {
"message": "Send a notification when you leave a video with segments that are not uploaded"
}
}

View file

@ -395,3 +395,21 @@ input::-webkit-inner-spin-button {
border-width: 3px;
padding: 3px;
}
.helpButton {
}
.helpButton {
height: 25px;
cursor: pointer;
padding: 5px;
margin: auto;
top: 0;
bottom: 0;
position: absolute;
}
.helpButton:hover {
filter: brightness(80%);
}

View file

@ -30,15 +30,15 @@
Come contribute, make some suggestions and help out in the Discord: <a href="https://discord.gg/QnmVMpU">https://discord.gg/QnmVMpU</a>
</p>
<div class="center">
<a class="bigText" href="/options/options.html">Enable optional features</a>
</div>
<p style="margin-bottom: 0" class="bigText center">Please review the options below</p>
<p>
Some features, such as support for non third-party YouTube sites, are disabled by default and can be enabled in the options. These can be enabled or disabled at any time.
Many features are disabled by default. If you want to skip Intros, outros, use Invidious, please enable the specific options. These can be enabled or disabled at any time.
You can also hide/show all UI elements added to the YouTube page.
</p>
<iframe src="../options/options.html#embed" width="100%" height="500px" style="border: none"></iframe>
<h1>How skipping works</h1>
<p class="projectPreview">

58
public/icons/help.svg Normal file
View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg6"
sodipodi:docname="help.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="730"
inkscape:window-height="480"
id="namedview8"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="12"
inkscape:cy="12"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg6" />
<path
d="M0 0h24v24H0z"
fill="none"
id="path2" />
<path
d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"
id="path4"
style="fill:#ffffff" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -347,3 +347,7 @@ svg {
padding: 5px;
border-radius: 5px;
}
.categoryColorTextBox {
width: 60px;
}

View file

@ -62,12 +62,14 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
//this allows the callback to be called later
return true;
case "alertPrevious":
if (Config.config.unsubmittedWarning) {
chrome.notifications.create("stillThere" + Math.random(), {
type: "basic",
title: chrome.i18n.getMessage("wantToSubmit") + " " + request.previousVideoID + "?",
message: chrome.i18n.getMessage("leftTimes"),
iconUrl: "./icons/LogoSponsorBlocker256px.png"
});
}
case "registerContentScript":
registerFirefoxContentScript(request);
return false;

View file

@ -28,6 +28,26 @@ class CategoryChooserComponent extends React.Component<CategoryChooserProps, Cat
<table id="categoryChooserTable"
className="categoryChooserTable">
<tbody>
{/* Headers */}
<tr id={"CategoryOptionsRow"}
className="categoryTableElement categoryTableHeader">
<td id={"CategoryOptionName"}>
{chrome.i18n.getMessage("category")}
</td>
<td id={"CategorySkipOption"}>
{chrome.i18n.getMessage("skipOption")}
</td>
<td id={"CategoryColorOption"}>
{chrome.i18n.getMessage("seekBarColor")}
</td>
<td id={"CategoryPreviewColorOption"}>
{chrome.i18n.getMessage("previewColor")}
</td>
</tr>
{this.getCategorySkipOptions()}
</tbody>
</table>
@ -40,7 +60,6 @@ class CategoryChooserComponent extends React.Component<CategoryChooserProps, Cat
for (const category of CompileConfig.categoryList) {
elements.push(
<CategorySkipOptionsComponent category={category}
defaultColor={"00d400"}
key={category}>
</CategorySkipOptionsComponent>
);

View file

@ -2,14 +2,19 @@ import * as React from "react";
import Config from "../config"
import { CategorySkipOption } from "../types";
import Utils from "../utils";
const utils = new Utils();
export interface CategorySkipOptionsProps {
category: string;
defaultColor: string;
defaultColor?: string;
defaultPreviewColor?: string;
}
export interface CategorySkipOptionsState {
color: string;
previewColor: string;
}
class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsProps, CategorySkipOptionsState> {
@ -19,7 +24,8 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
// Setup state
this.state = {
color: props.defaultColor
color: props.defaultColor || Config.config.barTypes[this.props.category].color,
previewColor: props.defaultPreviewColor || Config.config.barTypes["preview-" + this.props.category].color,
}
}
@ -39,10 +45,13 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
defaultOption = "autoSkip";
break;
}
break;
}
}
return (
<>
<tr id={this.props.category + "OptionsRow"}
className="categoryTableElement">
<td id={this.props.category + "OptionName"}
@ -59,8 +68,42 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
</select>
</td>
{/* TODO: Add colour chooser */}
<td id={this.props.category + "ColorOption"}>
<input
className="categoryColorTextBox option-text-box"
type="text"
onChange={(event) => this.setColorState(event, false)}
value={this.state.color} />
</td>
<td id={this.props.category + "PreviewColorOption"}>
<input
className="categoryColorTextBox option-text-box"
type="text"
onChange={(event) => this.setColorState(event, true)}
value={this.state.previewColor} />
</td>
<td id={this.props.category + "SaveButton"}>
<div
className="option-button trigger-button"
onClick={() => this.save()}>
{chrome.i18n.getMessage("save")}
</div>
</td>
</tr>
<tr id={this.props.category + "DescriptionRow"}
className="small-description">
<td
colSpan={2}>
{chrome.i18n.getMessage("category_" + this.props.category + "_description")}
</td>
</tr>
</>
);
}
@ -112,7 +155,7 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
getCategorySkipOptions(): JSX.Element[] {
let elements: JSX.Element[] = [];
""
let optionNames = ["disable", "showOverlay", "manualSkip", "autoSkip"];
for (const optionName of optionNames) {
@ -125,6 +168,36 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
return elements;
}
setColorState(event: React.ChangeEvent<HTMLInputElement>, preview: boolean) {
if (preview) {
this.setState({
previewColor: event.target.value
});
} else {
this.setState({
color: event.target.value
});
}
}
// Save text box data
save() {
// Validate colors
let checkVar = [this.state.color, this.state.previewColor]
for (const color of checkVar) {
if (color[0] !== "#" || (color.length !== 7 && color.length !== 4) || !utils.isHex(color.slice(1))) {
alert(chrome.i18n.getMessage("colorFormatIncorrect") + " " + color.slice(1) + " " + utils.isHex(color.slice(1)) + " " + utils.isHex("abcd123"));
return;
}
}
// Save colors
Config.config.barTypes[this.props.category].color = this.state.color;
Config.config.barTypes["preview-" + this.props.category].color = this.state.previewColor;
// Make listener get called
Config.config.barTypes = Config.config.barTypes;
}
}
export default CategorySkipOptionsComponent;

View file

@ -2,7 +2,8 @@ import * as React from "react";
export interface NoticeTextSelectionProps {
text: string,
idSuffix: string
idSuffix: string,
onClick?: (event: React.MouseEvent) => any
}
export interface NoticeTextSelectionState {
@ -16,8 +17,16 @@ class NoticeTextSelectionComponent extends React.Component<NoticeTextSelectionPr
}
render() {
let style: React.CSSProperties = {};
if (this.props.onClick) {
style.cursor = "pointer";
style.textDecoration = "underline"
}
return (
<p id={"sponsorTimesInfoMessage" + this.props.idSuffix}
onClick={this.props.onClick}
style={style}
className="sponsorTimesInfoMessage">
{this.props.text}
</p>

View file

@ -31,6 +31,7 @@ export interface SkipNoticeState {
noticeTitle: string;
messages: string[];
messageOnClick: (event: React.MouseEvent) => any;
countdownTime: number;
maxCountdownTime: () => number;
@ -73,7 +74,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
this.contentContainer = props.contentContainer;
this.audio = null;
let categoryName = chrome.i18n.getMessage(this.segments.length > 1 ? "multipleSegments" : "category_" + this.segments[0].category);
let categoryName = chrome.i18n.getMessage(this.segments.length > 1 ? "multipleSegments"
: "category_" + this.segments[0].category + "_short") || chrome.i18n.getMessage("category_" + this.segments[0].category);
let noticeTitle = categoryName + " " + chrome.i18n.getMessage("skipped");
if (!this.autoSkip) {
noticeTitle = chrome.i18n.getMessage("skip") + " " + categoryName + "?";
@ -104,6 +106,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
this.state = {
noticeTitle,
messages: [],
messageOnClick: null,
//the countdown until this notice closes
maxCountdownTime: () => 4,
@ -131,6 +134,13 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
this.audio.volume = this.contentContainer().v.volume * 0.1;
this.audio.play();
}
if (Config.config.categoryUpdateShowCount < 3 && Config.config.categorySelections.length <= 1) {
this.setNoticeInfoMessageWithOnClick(() => chrome.runtime.sendMessage({"message": "openConfig"})
, chrome.i18n.getMessage("categoryUpdate1"), chrome.i18n.getMessage("categoryUpdate2"));
Config.config.categoryUpdateShowCount = Config.config.categoryUpdateShowCount + 1
}
}
render() {
@ -324,6 +334,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
elements.push(
<NoticeTextSelectionComponent idSuffix={this.idSuffix}
text={this.state.messages[i]}
onClick={this.state.messageOnClick}
key={i}>
</NoticeTextSelectionComponent>
)
@ -504,6 +515,13 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
}
setNoticeInfoMessageWithOnClick(onClick: (event: React.MouseEvent) => any, ...messages: string[]) {
this.setState({
messages,
messageOnClick: (event) => onClick(event)
});
}
setNoticeInfoMessage(...messages: string[]) {
this.setState({
messages

View file

@ -182,7 +182,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
{timeDisplay}
{/* Category */}
<div style={{position: "relative"}}>
<select id={"sponsorTimeCategories" + this.idSuffix}
className="sponsorTimeCategories"
defaultValue={sponsorTime.category}
@ -191,6 +191,15 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
{this.getCategoryOptions()}
</select>
<img id={"sponsorTimeCategoriesHelpButton" + this.idSuffix}
className="helpButton"
src={chrome.extension.getURL("icons/help.svg")}
title={chrome.i18n.getMessage("categoryGuidelines")}
onClick={() => chrome.runtime.sendMessage({"message": "openConfig"})}>
</img>
</div>
<br/>
{/* Editing Tools */}

View file

@ -93,6 +93,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
<td className="sponsorSkipNoticeRightSection"
style={{position: "relative"}}>
{/* Guidelines button */}
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
onClick={() => window.open("https://github.com/ajayyy/SponsorBlock/wiki/Guidelines")}>
{chrome.i18n.getMessage(Config.config.submissionCountSinceCategories > 3 ? "guidelines" : "readTheGuidelines")}
</button>
{/* Submit Button */}
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
onClick={this.submit.bind(this)}>

View file

@ -1,5 +1,5 @@
import * as CompileConfig from "../config.json";
import { CategorySelection, CategorySkipOption } from "./types";
import { CategorySelection, CategorySkipOption, PreviewBarOption } from "./types";
import Utils from "./utils";
const utils = new Utils();
@ -14,6 +14,8 @@ interface SBConfig {
minutesSaved: number,
skipCount: number,
sponsorTimesContributed: number,
submissionCountSinceCategories: number, // New count used to show the "Read The Guidelines!!" message
unsubmittedWarning: boolean,
disableSkipping: boolean,
trackViewCount: boolean,
dontShowNotice: boolean,
@ -29,11 +31,28 @@ interface SBConfig {
minDuration: number,
audioNotificationOnSkip,
checkForUnlistedVideos: boolean,
mobileUpdateShowCount: number,
testingServer: boolean,
categoryUpdateShowCount: number,
// What categories should be skipped
categorySelections: CategorySelection[]
categorySelections: CategorySelection[],
// Preview bar
barTypes: {
"sponsor": PreviewBarOption,
"preview-sponsor": PreviewBarOption,
"intro": PreviewBarOption,
"preview-intro": PreviewBarOption,
"outro": PreviewBarOption,
"preview-outro": PreviewBarOption,
"interaction": PreviewBarOption,
"preview-interaction": PreviewBarOption,
"selfpromo": PreviewBarOption,
"preview-selfpromo": PreviewBarOption,
"music_offtopic": PreviewBarOption,
"preview-music_offtopic": PreviewBarOption
}
}
interface SBObject {
@ -113,6 +132,8 @@ var Config: SBObject = {
minutesSaved: 0,
skipCount: 0,
sponsorTimesContributed: 0,
submissionCountSinceCategories: 0,
unsubmittedWarning: true,
disableSkipping: false,
trackViewCount: true,
dontShowNotice: false,
@ -128,13 +149,66 @@ var Config: SBObject = {
minDuration: 0,
audioNotificationOnSkip: false,
checkForUnlistedVideos: false,
mobileUpdateShowCount: 0,
testingServer: false,
categoryUpdateShowCount: 0,
categorySelections: [{
name: "sponsor",
option: CategorySkipOption.AutoSkip
}]
}],
// Preview bar
barTypes: {
"sponsor": {
color: "#00d400",
opacity: "0.7"
},
"preview-sponsor": {
color: "#007800",
opacity: "0.7"
},
"intro": {
color: "#00ffff",
opacity: "0.7"
},
"preview-intro": {
color: "#008080",
opacity: "0.7"
},
"outro": {
color: "#0202ed",
opacity: "0.7"
},
"preview-outro": {
color: "#000070",
opacity: "0.7"
},
"interaction": {
color: "#cc00ff",
opacity: "0.7"
},
"preview-interaction": {
color: "#6c0087",
opacity: "0.7"
},
"selfpromo": {
color: "#ffff00",
opacity: "0.7"
},
"preview-selfpromo": {
color: "#bfbf35",
opacity: "0.7"
},
"music_offtopic": {
color: "#ff9900",
opacity: "0.7"
},
"preview-music_offtopic": {
color: "#a6634a",
opacity: "0.7"
}
}
},
localConfig: null,
config: null,
@ -255,6 +329,11 @@ async function migrateOldFormats() {
chrome.storage.sync.remove("autoUpvote");
}
// mobileUpdateShowCount removal
if (Config.config["mobileUpdateShowCount"] !== undefined) {
chrome.storage.sync.remove("mobileUpdateShowCount");
}
// Channel URLS
if (Config.config.whitelistedChannels.length > 0 &&
(Config.config.whitelistedChannels[0] == null || Config.config.whitelistedChannels[0].includes("/"))) {

View file

@ -973,7 +973,7 @@ function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: S
if (openNotice) {
//send out the message saying that a sponsor message was skipped
if (!Config.config.dontShowNotice || !autoSkip) {
let skipNotice = new SkipNotice(skippingSegments, autoSkip, skipNoticeContentContainer);
new SkipNotice(skippingSegments, autoSkip, skipNoticeContentContainer);
}
}
@ -1494,6 +1494,10 @@ async function sendSubmitMessage(){
// Increase contribution count
Config.config.sponsorTimesContributed = Config.config.sponsorTimesContributed + sponsorTimesSubmitting.length;
// New count just used to see if a warning "Read The Guidelines!!" message needs to be shown
// One per time submitting
Config.config.submissionCountSinceCategories = Config.config.submissionCountSinceCategories + 1;
// Empty the submitting times
sponsorTimesSubmitting = [];

View file

@ -5,60 +5,7 @@
'use strict';
let barTypes = {
"undefined": {
color: "#00d400",
opacity: "0.7"
},
"sponsor": {
color: "#00d400",
opacity: "0.7"
},
"preview-sponsor": {
color: "#007800",
opacity: "0.7"
},
"intro": {
color: "#00ffff",
opacity: "0.7"
},
"preview-intro": {
color: "#008080",
opacity: "0.7"
},
"outro": {
color: "#0202ed",
opacity: "0.7"
},
"preview-outro": {
color: "#000070",
opacity: "0.7"
},
"interaction": {
color: "#cc00ff",
opacity: "0.7"
},
"preview-interaction": {
color: "#6c0087",
opacity: "0.7"
},
"selfpromo": {
color: "#ffff00",
opacity: "0.7"
},
"preview-selfpromo": {
color: "#bfbf35",
opacity: "0.7"
},
"music_offtopic": {
color: "#ff9900",
opacity: "0.7"
},
"preview-music_offtopic": {
color: "#a6634a",
opacity: "0.7"
}
};
import Config from "../config";
class PreviewBar {
container: HTMLUListElement;
@ -208,8 +155,8 @@ class PreviewBar {
let bar = this.createBar();
bar.setAttribute('data-vs-segment-type', types[i]);
bar.style.backgroundColor = barTypes[types[i]].color;
if (!this.onMobileYouTube) bar.style.opacity = barTypes[types[i]].opacity;
bar.style.backgroundColor = Config.config.barTypes[types[i]].color;
if (!this.onMobileYouTube) bar.style.opacity = Config.config.barTypes[types[i]].opacity;
bar.style.width = width + '%';
bar.style.left = (timestamps[i][0] / duration * 100) + "%";
bar.style.position = "absolute"

View file

@ -13,6 +13,13 @@ window.addEventListener('DOMContentLoaded', init);
async function init() {
utils.localizeHtmlPage();
// Remove header if needed
if (window.location.hash === "#embed") {
for (const element of document.getElementsByClassName("titleBar")) {
element.classList.add("hidden");
}
}
if (!Config.configListeners.includes(optionsConfigUpdateListener)) {
Config.configListeners.push(optionsConfigUpdateListener);
}

View file

@ -12,6 +12,8 @@ class SkipNotice {
noticeElement: HTMLDivElement;
skipNoticeRef: React.MutableRefObject<SkipNoticeComponent>;
constructor(segments: SponsorTime[], autoSkip: boolean = false, contentContainer) {
this.segments = segments;
this.autoSkip = autoSkip;
@ -51,6 +53,7 @@ class SkipNotice {
<SkipNoticeComponent segments={segments}
autoSkip={autoSkip}
contentContainer={contentContainer}
ref={this.skipNoticeRef}
closeListener={() => this.close()} />,
this.noticeElement
);

View file

@ -58,6 +58,11 @@ interface SponsorTime {
hidden?: SponsorHideType;
}
interface PreviewBarOption {
color: string,
opacity: string
}
type VideoID = string;
export {
@ -68,5 +73,6 @@ export {
CategorySkipOption,
SponsorTime,
VideoID,
SponsorHideType
SponsorHideType,
PreviewBarOption
};

View file

@ -358,6 +358,10 @@ class Utils {
return window.location.protocol === "http:" || window.location.protocol === "https:";
}
isHex(num: string): boolean {
return Boolean(num.match(/^[0-9a-f]+$/i));
}
/**
* Is this Firefox (web-extensions)
*/