mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-07 03:57:09 +03:00
Add voting for chapters
This commit is contained in:
@@ -865,6 +865,13 @@
|
|||||||
"downvoteDescription": {
|
"downvoteDescription": {
|
||||||
"message": "Incorrect/Wrong Timing"
|
"message": "Incorrect/Wrong Timing"
|
||||||
},
|
},
|
||||||
|
"incorrectVote": {
|
||||||
|
"message": "Incorrect"
|
||||||
|
},
|
||||||
|
"harmfulVote": {
|
||||||
|
"message": "Harmful",
|
||||||
|
"description": "Used for chapter segments when the text is harmful/offensive to remove it faster"
|
||||||
|
},
|
||||||
"incorrectCategory": {
|
"incorrectCategory": {
|
||||||
"message": "Change Category"
|
"message": "Change Category"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -122,6 +122,16 @@ div:hover > .sponsorBlockChapterBar {
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Removes auto width from being a ytp-player-button */
|
||||||
|
.sbPlayerDownvote {
|
||||||
|
width: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adds back the padding */
|
||||||
|
.sbPlayerDownvote svg {
|
||||||
|
padding-right: 3.6px;
|
||||||
|
}
|
||||||
|
|
||||||
.autoHiding {
|
.autoHiding {
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
@@ -696,6 +706,11 @@ input::-webkit-inner-spin-button {
|
|||||||
border-color: rgba(28, 28, 28, 0.7) transparent transparent transparent;
|
border-color: rgba(28, 28, 28, 0.7) transparent transparent transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sponsorBlockTooltip.sbTriangle.centeredSBTriangle::after {
|
||||||
|
left: 50%;
|
||||||
|
right: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
.sponsorBlockLockedColor {
|
.sponsorBlockLockedColor {
|
||||||
color: #ffc83d;
|
color: #ffc83d;
|
||||||
}
|
}
|
||||||
|
|||||||
121
src/components/ChapterVoteComponent.tsx
Normal file
121
src/components/ChapterVoteComponent.tsx
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import Config from "../config";
|
||||||
|
import { Category, SegmentUUID, SponsorTime } from "../types";
|
||||||
|
|
||||||
|
import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
|
||||||
|
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
|
||||||
|
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
||||||
|
import { VoteResponse } from "../messageTypes";
|
||||||
|
import { AnimationUtils } from "../utils/animationUtils";
|
||||||
|
import { GenericUtils } from "../utils/genericUtils";
|
||||||
|
import { Tooltip } from "../render/Tooltip";
|
||||||
|
|
||||||
|
export interface ChapterVoteProps {
|
||||||
|
vote: (type: number, UUID: SegmentUUID, category?: Category) => Promise<VoteResponse>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChapterVoteState {
|
||||||
|
segment?: SponsorTime;
|
||||||
|
show: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChapterVoteComponent extends React.Component<ChapterVoteProps, ChapterVoteState> {
|
||||||
|
tooltip?: Tooltip;
|
||||||
|
|
||||||
|
constructor(props: ChapterVoteProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
segment: null,
|
||||||
|
show: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Upvote Button */}
|
||||||
|
<button id={"sponsorTimesDownvoteButtonsContainerUpvoteChapter"}
|
||||||
|
className={"playerButton sbPlayerUpvote ytp-button " + (!this.state.show ? "hidden" : "")}
|
||||||
|
draggable="false"
|
||||||
|
title={chrome.i18n.getMessage("upvoteButtonInfo")}
|
||||||
|
onClick={(e) => this.vote(e, 1)}>
|
||||||
|
<ThumbsUpSvg className="playerButtonImage"
|
||||||
|
fill={Config.config.colorPalette.white}
|
||||||
|
width={"inherit"} height={"inherit"} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Downvote Button */}
|
||||||
|
<button id={"sponsorTimesDownvoteButtonsContainerDownvoteChapter"}
|
||||||
|
className={"playerButton sbPlayerDownvote ytp-button " + (!this.state.show ? "hidden" : "")}
|
||||||
|
draggable="false"
|
||||||
|
title={chrome.i18n.getMessage("reportButtonInfo")}
|
||||||
|
onClick={(e) => {
|
||||||
|
const chapterNode = document.querySelector(".ytp-chapter-container") as HTMLElement;
|
||||||
|
|
||||||
|
if (this.tooltip) {
|
||||||
|
this.tooltip.close();
|
||||||
|
this.tooltip = null;
|
||||||
|
} else {
|
||||||
|
const referenceNode = chapterNode?.parentElement?.parentElement;
|
||||||
|
if (referenceNode) {
|
||||||
|
const outerBounding = referenceNode.getBoundingClientRect();
|
||||||
|
const buttonBounding = (e.target as HTMLElement)?.parentElement?.getBoundingClientRect();
|
||||||
|
|
||||||
|
this.tooltip = new Tooltip({
|
||||||
|
referenceNode: chapterNode?.parentElement?.parentElement,
|
||||||
|
prependElement: chapterNode?.parentElement,
|
||||||
|
showLogo: false,
|
||||||
|
showGotIt: false,
|
||||||
|
bottomOffset: `${outerBounding.height + 25}px`,
|
||||||
|
leftOffset: `${buttonBounding.x - outerBounding.x}px`,
|
||||||
|
extraClass: "centeredSBTriangle",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
name: chrome.i18n.getMessage("incorrectVote"),
|
||||||
|
listener: (event) => this.vote(event, 0, e.target as HTMLElement).then(() => {
|
||||||
|
this.tooltip?.close();
|
||||||
|
this.tooltip = null;
|
||||||
|
})
|
||||||
|
}, {
|
||||||
|
name: chrome.i18n.getMessage("harmfulVote"),
|
||||||
|
listener: (event) => this.vote(event, 30, e.target as HTMLElement).then(() => {
|
||||||
|
this.tooltip?.close();
|
||||||
|
this.tooltip = null;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<ThumbsDownSvg
|
||||||
|
className="playerButtonImage"
|
||||||
|
fill={downvoteButtonColor(this.state.segment ? [this.state.segment] : null, SkipNoticeAction.Downvote, SkipNoticeAction.Downvote)}
|
||||||
|
width={"inherit"}
|
||||||
|
height={"inherit"} />
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async vote(event: React.MouseEvent, type: number, element?: HTMLElement): Promise<void> {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (this.state.segment) {
|
||||||
|
const stopAnimation = AnimationUtils.applyLoadingAnimation(element ?? event.currentTarget as HTMLElement, 0.3);
|
||||||
|
|
||||||
|
const response = await this.props.vote(type, this.state.segment.UUID);
|
||||||
|
await stopAnimation();
|
||||||
|
|
||||||
|
if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) {
|
||||||
|
this.setState({
|
||||||
|
show: type === 1
|
||||||
|
});
|
||||||
|
} else if (response.statusCode !== 403) {
|
||||||
|
alert(GenericUtils.getErrorMessage(response.statusCode, response.responseText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChapterVoteComponent;
|
||||||
@@ -19,6 +19,7 @@ import { AnimationUtils } from "./utils/animationUtils";
|
|||||||
import { GenericUtils } from "./utils/genericUtils";
|
import { GenericUtils } from "./utils/genericUtils";
|
||||||
import { logDebug } from "./utils/logger";
|
import { logDebug } from "./utils/logger";
|
||||||
import { importTimes } from "./utils/exporter";
|
import { importTimes } from "./utils/exporter";
|
||||||
|
import { ChapterVote } from "./render/ChapterVote";
|
||||||
import { openWarningDialog } from "./utils/warnings";
|
import { openWarningDialog } from "./utils/warnings";
|
||||||
|
|
||||||
// Hack to get the CSS loaded on permission-based sites (Invidious)
|
// Hack to get the CSS loaded on permission-based sites (Invidious)
|
||||||
@@ -475,7 +476,8 @@ function createPreviewBar(): void {
|
|||||||
const el = option.isVisibleCheck ? findValidElement(allElements) : allElements[0];
|
const el = option.isVisibleCheck ? findValidElement(allElements) : allElements[0];
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
previewBar = new PreviewBar(el, onMobileYouTube, onInvidious);
|
const chapterVote = new ChapterVote(voteAsync);
|
||||||
|
previewBar = new PreviewBar(el, onMobileYouTube, onInvidious, chapterVote);
|
||||||
|
|
||||||
updatePreviewBar();
|
updatePreviewBar();
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ https://github.com/videosegments/videosegments/commits/f1e111bdfe231947800c6efdd
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
|
import { ChapterVote } from "../render/ChapterVote";
|
||||||
import { ActionType, Category, SegmentContainer, SponsorHideType, SponsorSourceType, SponsorTime } from "../types";
|
import { ActionType, Category, SegmentContainer, SponsorHideType, SponsorSourceType, SponsorTime } from "../types";
|
||||||
import { partition } from "../utils/arrayUtils";
|
import { partition } from "../utils/arrayUtils";
|
||||||
import { shortCategoryName } from "../utils/categoryUtils";
|
import { shortCategoryName } from "../utils/categoryUtils";
|
||||||
@@ -45,10 +46,12 @@ class PreviewBar {
|
|||||||
// For chapter bar
|
// For chapter bar
|
||||||
hoveredSection: HTMLElement;
|
hoveredSection: HTMLElement;
|
||||||
customChaptersBar: HTMLElement;
|
customChaptersBar: HTMLElement;
|
||||||
|
chaptersBarSegments: PreviewBarSegment[];
|
||||||
|
chapterVote: ChapterVote;
|
||||||
originalChapterBar: HTMLElement;
|
originalChapterBar: HTMLElement;
|
||||||
originalChapterBarBlocks: NodeListOf<HTMLElement>;
|
originalChapterBarBlocks: NodeListOf<HTMLElement>;
|
||||||
|
|
||||||
constructor(parent: HTMLElement, onMobileYouTube: boolean, onInvidious: boolean, test=false) {
|
constructor(parent: HTMLElement, onMobileYouTube: boolean, onInvidious: boolean, chapterVote: ChapterVote, test=false) {
|
||||||
if (test) return;
|
if (test) return;
|
||||||
this.container = document.createElement('ul');
|
this.container = document.createElement('ul');
|
||||||
this.container.id = 'previewbar';
|
this.container.id = 'previewbar';
|
||||||
@@ -56,6 +59,7 @@ class PreviewBar {
|
|||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.onMobileYouTube = onMobileYouTube;
|
this.onMobileYouTube = onMobileYouTube;
|
||||||
this.onInvidious = onInvidious;
|
this.onInvidious = onInvidious;
|
||||||
|
this.chapterVote = chapterVote;
|
||||||
|
|
||||||
this.createElement(parent);
|
this.createElement(parent);
|
||||||
this.createChapterMutationObservers();
|
this.createChapterMutationObservers();
|
||||||
@@ -233,7 +237,7 @@ class PreviewBar {
|
|||||||
|
|
||||||
this.createChaptersBar(this.segments.sort((a, b) => a.segment[0] - b.segment[0]));
|
this.createChaptersBar(this.segments.sort((a, b) => a.segment[0] - b.segment[0]));
|
||||||
|
|
||||||
const chapterChevron = document.querySelector(".ytp-chapter-title-chevron") as HTMLElement;
|
const chapterChevron = this.getChapterChevron();
|
||||||
if (this.segments.some((segment) => segment.actionType !== ActionType.Chapter
|
if (this.segments.some((segment) => segment.actionType !== ActionType.Chapter
|
||||||
&& segment.source === SponsorSourceType.YouTube)) {
|
&& segment.source === SponsorSourceType.YouTube)) {
|
||||||
chapterChevron.style.removeProperty("display");
|
chapterChevron.style.removeProperty("display");
|
||||||
@@ -655,6 +659,18 @@ class PreviewBar {
|
|||||||
|
|
||||||
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
|
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
|
||||||
chapterTitle.innerText = chosenSegment.description || shortCategoryName(chosenSegment.category);
|
chapterTitle.innerText = chosenSegment.description || shortCategoryName(chosenSegment.category);
|
||||||
|
|
||||||
|
const chapterVoteContainer = this.chapterVote.getContainer();
|
||||||
|
if (chosenSegment.source === SponsorSourceType.Server) {
|
||||||
|
if (!chapterButton.contains(chapterVoteContainer)) {
|
||||||
|
chapterButton.insertBefore(chapterVoteContainer, this.getChapterChevron());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chapterVote.setVisibility(true);
|
||||||
|
this.chapterVote.setSegment(chosenSegment);
|
||||||
|
} else {
|
||||||
|
this.chapterVote.setVisibility(false);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Hide chapters menu again
|
// Hide chapters menu again
|
||||||
chaptersContainer.style.display = "none";
|
chaptersContainer.style.display = "none";
|
||||||
@@ -759,6 +775,10 @@ class PreviewBar {
|
|||||||
|
|
||||||
return segment;
|
return segment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getChapterChevron(): HTMLElement {
|
||||||
|
return document.querySelector(".ytp-chapter-title-chevron");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PreviewBar;
|
export default PreviewBar;
|
||||||
|
|||||||
63
src/render/ChapterVote.tsx
Normal file
63
src/render/ChapterVote.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as ReactDOM from "react-dom";
|
||||||
|
import ChapterVoteComponent, { ChapterVoteState } from "../components/ChapterVoteComponent";
|
||||||
|
import { VoteResponse } from "../messageTypes";
|
||||||
|
import { Category, SegmentUUID, SponsorTime } from "../types";
|
||||||
|
|
||||||
|
export class ChapterVote {
|
||||||
|
container: HTMLElement;
|
||||||
|
ref: React.RefObject<ChapterVoteComponent>;
|
||||||
|
|
||||||
|
unsavedState: ChapterVoteState;
|
||||||
|
|
||||||
|
mutationObserver?: MutationObserver;
|
||||||
|
|
||||||
|
constructor(vote: (type: number, UUID: SegmentUUID, category?: Category) => Promise<VoteResponse>) {
|
||||||
|
this.ref = React.createRef();
|
||||||
|
|
||||||
|
this.container = document.createElement('span');
|
||||||
|
this.container.id = "chapterVote";
|
||||||
|
this.container.style.height = "100%";
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<ChapterVoteComponent ref={this.ref} vote={vote} />,
|
||||||
|
this.container
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getContainer(): HTMLElement {
|
||||||
|
return this.container;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
ReactDOM.unmountComponentAtNode(this.container);
|
||||||
|
this.container.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
setVisibility(show: boolean): void {
|
||||||
|
const newState = {
|
||||||
|
show,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.ref.current) {
|
||||||
|
this.ref.current?.setState(newState);
|
||||||
|
} else {
|
||||||
|
this.unsavedState = newState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setSegment(segment: SponsorTime): Promise<void> {
|
||||||
|
if (this.ref.current?.state?.segment !== segment) {
|
||||||
|
const newState = {
|
||||||
|
segment,
|
||||||
|
show: true
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.ref.current) {
|
||||||
|
this.ref.current?.setState(newState);
|
||||||
|
} else {
|
||||||
|
this.unsavedState = newState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,14 +5,9 @@ import NoticeComponent from "../components/NoticeComponent";
|
|||||||
import Utils from "../utils";
|
import Utils from "../utils";
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
|
|
||||||
import { ContentContainer } from "../types";
|
import { ButtonListener, ContentContainer } from "../types";
|
||||||
import NoticeTextSelectionComponent from "../components/NoticeTextSectionComponent";
|
import NoticeTextSelectionComponent from "../components/NoticeTextSectionComponent";
|
||||||
|
|
||||||
export interface ButtonListener {
|
|
||||||
name: string,
|
|
||||||
listener: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TextBox {
|
export interface TextBox {
|
||||||
icon: string,
|
icon: string,
|
||||||
text: string
|
text: string
|
||||||
|
|||||||
@@ -1,29 +1,37 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ReactDOM from "react-dom";
|
import * as ReactDOM from "react-dom";
|
||||||
|
import { ButtonListener } from "../types";
|
||||||
|
|
||||||
export interface TooltipProps {
|
export interface TooltipProps {
|
||||||
text: string,
|
text?: string;
|
||||||
link?: string,
|
link?: string;
|
||||||
referenceNode: HTMLElement,
|
referenceNode: HTMLElement;
|
||||||
prependElement?: HTMLElement, // Element to append before
|
prependElement?: HTMLElement; // Element to append before
|
||||||
bottomOffset?: string
|
bottomOffset?: string;
|
||||||
|
leftOffset?: string;
|
||||||
|
rightOffset?: string;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
opacity?: number;
|
opacity?: number;
|
||||||
displayTriangle?: boolean;
|
displayTriangle?: boolean;
|
||||||
|
extraClass?: string;
|
||||||
showLogo?: boolean;
|
showLogo?: boolean;
|
||||||
showGotIt?: boolean;
|
showGotIt?: boolean;
|
||||||
|
buttons?: ButtonListener[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Tooltip {
|
export class Tooltip {
|
||||||
text: string;
|
text?: string;
|
||||||
container: HTMLDivElement;
|
container: HTMLDivElement;
|
||||||
|
|
||||||
timer: NodeJS.Timeout;
|
timer: NodeJS.Timeout;
|
||||||
|
|
||||||
constructor(props: TooltipProps) {
|
constructor(props: TooltipProps) {
|
||||||
props.bottomOffset ??= "70px";
|
props.bottomOffset ??= "70px";
|
||||||
|
props.leftOffset ??= "inherit";
|
||||||
|
props.rightOffset ??= "inherit";
|
||||||
props.opacity ??= 0.7;
|
props.opacity ??= 0.7;
|
||||||
props.displayTriangle ??= true;
|
props.displayTriangle ??= true;
|
||||||
|
props.extraClass ??= "";
|
||||||
props.showLogo ??= true;
|
props.showLogo ??= true;
|
||||||
props.showGotIt ??= true;
|
props.showGotIt ??= true;
|
||||||
this.text = props.text;
|
this.text = props.text;
|
||||||
@@ -45,25 +53,29 @@ export class Tooltip {
|
|||||||
const backgroundColor = `rgba(28, 28, 28, ${props.opacity})`;
|
const backgroundColor = `rgba(28, 28, 28, ${props.opacity})`;
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<div style={{bottom: props.bottomOffset, backgroundColor}}
|
<div style={{bottom: props.bottomOffset, left: props.leftOffset, right: props.rightOffset, backgroundColor}}
|
||||||
className={"sponsorBlockTooltip" + (props.displayTriangle ? " sbTriangle" : "")} >
|
className={"sponsorBlockTooltip" + (props.displayTriangle ? " sbTriangle" : "") + ` ${props.extraClass}`}>
|
||||||
<div>
|
<div>
|
||||||
{props.showLogo ?
|
{props.showLogo ?
|
||||||
<img className="sponsorSkipLogo sponsorSkipObject"
|
<img className="sponsorSkipLogo sponsorSkipObject"
|
||||||
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}>
|
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}>
|
||||||
</img>
|
</img>
|
||||||
: null}
|
: null}
|
||||||
<span className="sponsorSkipObject">
|
{this.text ?
|
||||||
{this.text + (props.link ? ". " : "")}
|
<span className="sponsorSkipObject">
|
||||||
{props.link ?
|
{this.text + (props.link ? ". " : "")}
|
||||||
<a style={{textDecoration: "underline"}}
|
{props.link ?
|
||||||
target="_blank"
|
<a style={{textDecoration: "underline"}}
|
||||||
rel="noopener noreferrer"
|
target="_blank"
|
||||||
href={props.link}>
|
rel="noopener noreferrer"
|
||||||
{chrome.i18n.getMessage("LearnMore")}
|
href={props.link}>
|
||||||
</a>
|
{chrome.i18n.getMessage("LearnMore")}
|
||||||
: null}
|
</a>
|
||||||
</span>
|
: null}
|
||||||
|
</span>
|
||||||
|
: null}
|
||||||
|
|
||||||
|
{this.getButtons(props.buttons)}
|
||||||
</div>
|
</div>
|
||||||
{props.showGotIt ?
|
{props.showGotIt ?
|
||||||
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||||
@@ -78,6 +90,27 @@ export class Tooltip {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getButtons(buttons?: ButtonListener[]): JSX.Element[] {
|
||||||
|
if (buttons) {
|
||||||
|
const result: JSX.Element[] = [];
|
||||||
|
|
||||||
|
for (const button of buttons) {
|
||||||
|
result.push(
|
||||||
|
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
||||||
|
key={button.name}
|
||||||
|
onClick={(e) => button.listener(e)}>
|
||||||
|
|
||||||
|
{button.name}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
close(): void {
|
close(): void {
|
||||||
ReactDOM.unmountComponentAtNode(this.container);
|
ReactDOM.unmountComponentAtNode(this.container);
|
||||||
this.container.remove();
|
this.container.remove();
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
const thumbsDownSvg = ({
|
const thumbsDownSvg = ({
|
||||||
fill = "#ffffff"
|
fill = "#ffffff",
|
||||||
|
className = "",
|
||||||
|
width = "18",
|
||||||
|
height = "18"
|
||||||
}): JSX.Element => (
|
}): JSX.Element => (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="18"
|
width={width}
|
||||||
height="18"
|
height={height}
|
||||||
fill={fill}
|
fill={fill}
|
||||||
|
className={className}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
const thumbsUpSvg = ({
|
const thumbsUpSvg = ({
|
||||||
fill = "#ffffff"
|
fill = "#ffffff",
|
||||||
|
className = "",
|
||||||
|
width = "18",
|
||||||
|
height = "18"
|
||||||
}): JSX.Element => (
|
}): JSX.Element => (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
fill={fill}
|
fill={fill}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
className={className}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -237,4 +237,9 @@ export type Keybind = {
|
|||||||
ctrl?: boolean,
|
ctrl?: boolean,
|
||||||
alt?: boolean,
|
alt?: boolean,
|
||||||
shift?: boolean
|
shift?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ButtonListener {
|
||||||
|
name: string,
|
||||||
|
listener: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ import PreviewBar, { PreviewBarSegment } from "../src/js-components/previewBar";
|
|||||||
describe("createChapterRenderGroups", () => {
|
describe("createChapterRenderGroups", () => {
|
||||||
let previewBar: PreviewBar;
|
let previewBar: PreviewBar;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
previewBar = new PreviewBar(null, null, null, true);
|
previewBar = new PreviewBar(null, null, null, null, true);
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Two unrelated times", () => {
|
it("Two unrelated times", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user