mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-11 22:17:21 +03:00
Merge pull request #988 from FlorianZahn/copySegment
Copy segments into your unsubmitted and SkipNotice changes
This commit is contained in:
@@ -12,5 +12,17 @@
|
|||||||
"preview": ["skip"],
|
"preview": ["skip"],
|
||||||
"music_offtopic": ["skip"],
|
"music_offtopic": ["skip"],
|
||||||
"poi_highlight": ["skip"]
|
"poi_highlight": ["skip"]
|
||||||
|
},
|
||||||
|
"wikiLinks": {
|
||||||
|
"sponsor": "https://wiki.sponsor.ajay.app/w/Sponsor",
|
||||||
|
"selfpromo": "https://wiki.sponsor.ajay.app/w/Unpaid/Self_Promotion",
|
||||||
|
"interaction": "https://wiki.sponsor.ajay.app/w/Interaction_Reminder_(Subscribe)",
|
||||||
|
"intro": "https://wiki.sponsor.ajay.app/w/Intermission/Intro_Animation",
|
||||||
|
"outro": "https://wiki.sponsor.ajay.app/w/Endcards/Credits",
|
||||||
|
"preview": "https://wiki.sponsor.ajay.app/w/Preview/Recap",
|
||||||
|
"music_offtopic": "https://wiki.sponsor.ajay.app/w/Music:_Non-Music_Section",
|
||||||
|
"poi_highlight": "https://wiki.sponsor.ajay.app/w/Highlight",
|
||||||
|
"guidelines": "https://wiki.sponsor.ajay.app/w/Guidelines",
|
||||||
|
"mute": "https://wiki.sponsor.ajay.app/w/Mute_Segment"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"icons/upvote.png",
|
"icons/upvote.png",
|
||||||
"icons/downvote.png",
|
"icons/downvote.png",
|
||||||
"icons/thumbs_down.svg",
|
"icons/thumbs_down.svg",
|
||||||
|
"icons/thumbs_down_locked.svg",
|
||||||
"icons/thumbs_up.svg",
|
"icons/thumbs_up.svg",
|
||||||
"icons/help.svg",
|
"icons/help.svg",
|
||||||
"icons/report.png",
|
"icons/report.png",
|
||||||
|
|||||||
@@ -700,7 +700,7 @@
|
|||||||
"message": "Incorrect/Wrong Timing"
|
"message": "Incorrect/Wrong Timing"
|
||||||
},
|
},
|
||||||
"incorrectCategory": {
|
"incorrectCategory": {
|
||||||
"message": "Wrong Category"
|
"message": "Change Category"
|
||||||
},
|
},
|
||||||
"nonMusicCategoryOnMusic": {
|
"nonMusicCategoryOnMusic": {
|
||||||
"message": "This video is categorized as music. Are you sure this has a sponsor? If this is actually a \"Non-Music segment\", open up the extension options and enable this category. Then, you can submit this segment as \"Non-Music\" instead of sponsor. Please read the guidelines if you are confused."
|
"message": "This video is categorized as music. Are you sure this has a sponsor? If this is actually a \"Non-Music segment\", open up the extension options and enable this category. Then, you can submit this segment as \"Non-Music\" instead of sponsor. Please read the guidelines if you are confused."
|
||||||
@@ -810,6 +810,21 @@
|
|||||||
},
|
},
|
||||||
"LearnMore": {
|
"LearnMore": {
|
||||||
"message": "Learn More"
|
"message": "Learn More"
|
||||||
|
},
|
||||||
|
"CopyDownvoteButtonInfo": {
|
||||||
|
"message": "Downvotes and creates a local copy for you to resubmit"
|
||||||
|
},
|
||||||
|
"OpenCategoryWikiPage": {
|
||||||
|
"message": "Open this category's wiki page."
|
||||||
|
},
|
||||||
|
"CopyAndDownvote": {
|
||||||
|
"message": "Copy and downvote"
|
||||||
|
},
|
||||||
|
"ContinueVoting": {
|
||||||
|
"message": "Continue Voting"
|
||||||
|
},
|
||||||
|
"ChangeCategoryTooltip": {
|
||||||
|
"message": "This will instantly apply to your segments"
|
||||||
},
|
},
|
||||||
"SponsorTimeEditScrollNewFeature": {
|
"SponsorTimeEditScrollNewFeature": {
|
||||||
"message": "Use your mousewheel while hovering over the edit box to quickly adjust the time. Combinations of the ctrl or shift key can be used to fine tune the changes."
|
"message": "Use your mousewheel while hovering over the edit box to quickly adjust the time. Combinations of the ctrl or shift key can be used to fine tune the changes."
|
||||||
|
|||||||
@@ -217,7 +217,7 @@
|
|||||||
|
|
||||||
/* if two are very close to eachother */
|
/* if two are very close to eachother */
|
||||||
.secondSkipNotice {
|
.secondSkipNotice {
|
||||||
bottom: 250px;
|
bottom: 290px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.noticeLeftIcon {
|
.noticeLeftIcon {
|
||||||
@@ -254,12 +254,16 @@
|
|||||||
|
|
||||||
.sponsorTimesVoteButtonsContainer {
|
.sponsorTimesVoteButtonsContainer {
|
||||||
float: left;
|
float: left;
|
||||||
|
vertical-align:middle;
|
||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
|
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sponsorTimesVoteButtonsContainer div{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
.sponsorSkipNoticeRightSection {
|
.sponsorSkipNoticeRightSection {
|
||||||
right: 0;
|
right: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -330,7 +334,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.voteButton {
|
.voteButton {
|
||||||
height: 17px;
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.voteButton:hover {
|
.voteButton:hover {
|
||||||
@@ -556,6 +561,10 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sponsorBlockLockedColor {
|
||||||
|
color: #ffc83d;
|
||||||
|
}
|
||||||
|
|
||||||
.sponsorBlockRectangleTooltip {
|
.sponsorBlockRectangleTooltip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@@ -565,3 +574,4 @@ input::-webkit-inner-spin-button {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
58
public/icons/thumbs_down_locked.svg
Normal file
58
public/icons/thumbs_down_locked.svg
Normal 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="thumbs_down.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="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"
|
||||||
|
id="path4"
|
||||||
|
style="fill:#ffc83d" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -4,14 +4,22 @@ import Config from "../config"
|
|||||||
import { Category, ContentContainer, CategoryActionType, SponsorHideType, SponsorTime, NoticeVisbilityMode, ActionType } from "../types";
|
import { Category, ContentContainer, CategoryActionType, SponsorHideType, SponsorTime, NoticeVisbilityMode, ActionType } from "../types";
|
||||||
import NoticeComponent from "./NoticeComponent";
|
import NoticeComponent from "./NoticeComponent";
|
||||||
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
||||||
|
import SubmissionNotice from "../render/SubmissionNotice";
|
||||||
|
import Utils from "../utils";
|
||||||
|
const utils = new Utils();
|
||||||
|
|
||||||
import { getCategoryActionType, getSkippingText } from "../utils/categoryUtils";
|
import { getCategoryActionType, getSkippingText } from "../utils/categoryUtils";
|
||||||
|
|
||||||
|
import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
|
||||||
|
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
|
||||||
|
import PencilSvg from "../svg-icons/pencil_svg";
|
||||||
|
|
||||||
export enum SkipNoticeAction {
|
export enum SkipNoticeAction {
|
||||||
None,
|
None,
|
||||||
Upvote,
|
Upvote,
|
||||||
Downvote,
|
Downvote,
|
||||||
CategoryVote,
|
CategoryVote,
|
||||||
|
CopyDownvote,
|
||||||
Unskip
|
Unskip
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +51,7 @@ export interface SkipNoticeState {
|
|||||||
skipButtonCallback?: (index: number) => void;
|
skipButtonCallback?: (index: number) => void;
|
||||||
showSkipButton?: boolean;
|
showSkipButton?: boolean;
|
||||||
|
|
||||||
downvoting?: boolean;
|
editing?: boolean;
|
||||||
choosingCategory?: boolean;
|
choosingCategory?: boolean;
|
||||||
thanksForVotingText?: string; //null until the voting buttons should be hidden
|
thanksForVotingText?: string; //null until the voting buttons should be hidden
|
||||||
|
|
||||||
@@ -52,6 +60,10 @@ export interface SkipNoticeState {
|
|||||||
showKeybindHint?: boolean;
|
showKeybindHint?: boolean;
|
||||||
|
|
||||||
smaller?: boolean;
|
smaller?: boolean;
|
||||||
|
|
||||||
|
voted?: SkipNoticeAction[];
|
||||||
|
copied?: SkipNoticeAction[];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> {
|
class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> {
|
||||||
@@ -69,6 +81,10 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
noticeRef: React.MutableRefObject<NoticeComponent>;
|
noticeRef: React.MutableRefObject<NoticeComponent>;
|
||||||
categoryOptionRef: React.RefObject<HTMLSelectElement>;
|
categoryOptionRef: React.RefObject<HTMLSelectElement>;
|
||||||
|
|
||||||
|
selectedColor: string;
|
||||||
|
unselectedColor: string;
|
||||||
|
lockedColor: string;
|
||||||
|
|
||||||
// Used to update on config change
|
// Used to update on config change
|
||||||
configListener: () => void;
|
configListener: () => void;
|
||||||
|
|
||||||
@@ -94,12 +110,16 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
this.segments.sort((a, b) => a.segment[0] - b.segment[0]);
|
this.segments.sort((a, b) => a.segment[0] - b.segment[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//this is the suffix added at the end of every id
|
// This is the suffix added at the end of every id
|
||||||
for (const segment of this.segments) {
|
for (const segment of this.segments) {
|
||||||
this.idSuffix += segment.UUID;
|
this.idSuffix += segment.UUID;
|
||||||
}
|
}
|
||||||
this.idSuffix += this.amountOfPreviousNotices;
|
this.idSuffix += this.amountOfPreviousNotices;
|
||||||
|
|
||||||
|
this.selectedColor = Config.config.colorPalette.red;
|
||||||
|
this.unselectedColor = Config.config.colorPalette.white;
|
||||||
|
this.lockedColor = Config.config.colorPalette.locked;
|
||||||
|
|
||||||
// Setup state
|
// Setup state
|
||||||
this.state = {
|
this.state = {
|
||||||
noticeTitle,
|
noticeTitle,
|
||||||
@@ -115,7 +135,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
skipButtonCallback: (index) => this.unskip(index),
|
skipButtonCallback: (index) => this.unskip(index),
|
||||||
showSkipButton: true,
|
showSkipButton: true,
|
||||||
|
|
||||||
downvoting: false,
|
editing: false,
|
||||||
choosingCategory: false,
|
choosingCategory: false,
|
||||||
thanksForVotingText: null,
|
thanksForVotingText: null,
|
||||||
|
|
||||||
@@ -123,7 +143,11 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
|
|
||||||
showKeybindHint: this.props.showKeybindHint ?? true,
|
showKeybindHint: this.props.showKeybindHint ?? true,
|
||||||
|
|
||||||
smaller: this.props.smaller ?? false
|
smaller: this.props.smaller ?? false,
|
||||||
|
|
||||||
|
// Keep track of what segment the user interacted with.
|
||||||
|
voted: new Array(this.props.segments.length).fill(SkipNoticeAction.None),
|
||||||
|
copied: new Array(this.props.segments.length).fill(SkipNoticeAction.None),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.autoSkip) {
|
if (!this.autoSkip) {
|
||||||
@@ -191,24 +215,33 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
className="sponsorTimesVoteButtonsContainer">
|
className="sponsorTimesVoteButtonsContainer">
|
||||||
|
|
||||||
{/* Upvote Button */}
|
{/* Upvote Button */}
|
||||||
<img id={"sponsorTimesDownvoteButtonsContainer" + this.idSuffix}
|
<div id={"sponsorTimesDownvoteButtonsContainerUpvote" + this.idSuffix}
|
||||||
className="sponsorSkipObject voteButton"
|
className="voteButton"
|
||||||
style={{marginRight: "10px"}}
|
style={{marginRight: "5px"}}
|
||||||
src={chrome.extension.getURL("icons/thumbs_up.svg")}
|
title={chrome.i18n.getMessage("upvoteButtonInfo")}
|
||||||
title={chrome.i18n.getMessage("upvoteButtonInfo")}
|
onClick={() => this.prepAction(SkipNoticeAction.Upvote)}>
|
||||||
onClick={() => this.prepAction(SkipNoticeAction.Upvote)}>
|
<ThumbsUpSvg fill={(this.state.actionState === SkipNoticeAction.Upvote) ? this.selectedColor : this.unselectedColor} />
|
||||||
|
</div>
|
||||||
</img>
|
|
||||||
|
|
||||||
{/* Report Button */}
|
{/* Report Button */}
|
||||||
<img id={"sponsorTimesDownvoteButtonsContainer" + this.idSuffix}
|
<div id={"sponsorTimesDownvoteButtonsContainerDownvote" + this.idSuffix}
|
||||||
className="sponsorSkipObject voteButton"
|
className="voteButton"
|
||||||
src={chrome.extension.getURL("icons/thumbs_down.svg")}
|
style={{marginRight: "5px", marginLeft: "5px"}}
|
||||||
title={chrome.i18n.getMessage("reportButtonInfo")}
|
title={chrome.i18n.getMessage("reportButtonInfo")}
|
||||||
onClick={() => this.adjustDownvotingState(true)}>
|
onClick={() => this.prepAction(SkipNoticeAction.Downvote)}>
|
||||||
|
<ThumbsDownSvg fill={this.downvoteButtonColor(SkipNoticeAction.Downvote)} />
|
||||||
</img>
|
</div>
|
||||||
|
|
||||||
|
{/* Copy and Downvote Button */}
|
||||||
|
<div id={"sponsorTimesDownvoteButtonsContainerCopyDownvote" + this.idSuffix}
|
||||||
|
className="voteButton"
|
||||||
|
style={{marginLeft: "5px"}}
|
||||||
|
onClick={() => this.openEditingOptions()}>
|
||||||
|
<PencilSvg fill={this.state.editing === true
|
||||||
|
|| this.state.actionState === SkipNoticeAction.CopyDownvote
|
||||||
|
|| this.state.choosingCategory === true
|
||||||
|
? this.selectedColor : this.unselectedColor} />
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
:
|
:
|
||||||
@@ -216,7 +249,22 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
<td id={"sponsorTimesVoteButtonInfoMessage" + this.idSuffix}
|
<td id={"sponsorTimesVoteButtonInfoMessage" + this.idSuffix}
|
||||||
className="sponsorTimesInfoMessage sponsorTimesVoteButtonMessage"
|
className="sponsorTimesInfoMessage sponsorTimesVoteButtonMessage"
|
||||||
style={{marginRight: "10px"}}>
|
style={{marginRight: "10px"}}>
|
||||||
{this.state.thanksForVotingText}
|
|
||||||
|
{/* Submitted string */}
|
||||||
|
<span style={{marginRight: "10px"}}>
|
||||||
|
{this.state.thanksForVotingText}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{/* Continue Voting Button */}
|
||||||
|
<button id={"sponsorTimesContinueVotingContainer" + this.idSuffix}
|
||||||
|
className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||||
|
title={"Continue Voting"}
|
||||||
|
onClick={() => this.setState({
|
||||||
|
thanksForVotingText: null,
|
||||||
|
messages: []
|
||||||
|
})}>
|
||||||
|
{chrome.i18n.getMessage("ContinueVoting")}
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,45 +277,46 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
key={1}>
|
key={1}>
|
||||||
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
||||||
onClick={this.contentContainer().dontShowNoticeAgain}>
|
onClick={this.contentContainer().dontShowNoticeAgain}>
|
||||||
|
|
||||||
{chrome.i18n.getMessage("Hide")}
|
{chrome.i18n.getMessage("Hide")}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
</tr>),
|
</tr>),
|
||||||
|
|
||||||
/* Downvote Options Row */
|
/* Edit Segments Row */
|
||||||
(this.state.downvoting &&
|
(this.state.editing && !this.state.thanksForVotingText && !(this.state.choosingCategory || this.state.actionState === SkipNoticeAction.CopyDownvote) &&
|
||||||
<tr id={"sponsorSkipNoticeDownvoteOptionsRow" + this.idSuffix}
|
<tr id={"sponsorSkipNoticeEditSegmentsRow" + this.idSuffix}
|
||||||
key={2}>
|
key={2}>
|
||||||
<td id={"sponsorTimesDownvoteOptionsContainer" + this.idSuffix}>
|
<td id={"sponsorTimesEditSegmentsContainer" + this.idSuffix}>
|
||||||
|
|
||||||
{/* Normal downvote */}
|
{/* Copy Segment */}
|
||||||
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||||
onClick={() => this.prepAction(SkipNoticeAction.Downvote)}>
|
title={chrome.i18n.getMessage("CopyDownvoteButtonInfo")}
|
||||||
{chrome.i18n.getMessage("downvoteDescription")}
|
style={{color: this.downvoteButtonColor(SkipNoticeAction.Downvote)}}
|
||||||
|
onClick={() => this.prepAction(SkipNoticeAction.CopyDownvote)}>
|
||||||
|
{chrome.i18n.getMessage("CopyAndDownvote")}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Category vote */}
|
{/* Category vote */}
|
||||||
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||||
onClick={() => this.openCategoryChooser()}>
|
title={chrome.i18n.getMessage("ChangeCategoryTooltip")}
|
||||||
|
style={{color: (this.state.actionState === SkipNoticeAction.CategoryVote && this.state.editing == true) ? this.selectedColor : this.unselectedColor}}
|
||||||
|
onClick={() => this.resetStateToStart(SkipNoticeAction.CategoryVote, true, true)}>
|
||||||
{chrome.i18n.getMessage("incorrectCategory")}
|
{chrome.i18n.getMessage("incorrectCategory")}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
),
|
),
|
||||||
|
|
||||||
/* Category Chooser Row */
|
/* Category Chooser Row */
|
||||||
(this.state.choosingCategory &&
|
(this.state.choosingCategory && !this.state.thanksForVotingText &&
|
||||||
<tr id={"sponsorSkipNoticeCategoryChooserRow" + this.idSuffix}
|
<tr id={"sponsorSkipNoticeCategoryChooserRow" + this.idSuffix}
|
||||||
key={3}>
|
key={3}>
|
||||||
<td>
|
<td>
|
||||||
{/* Category Selector */}
|
{/* Category Selector */}
|
||||||
<select id={"sponsorTimeCategories" + this.idSuffix}
|
<select id={"sponsorTimeCategories" + this.idSuffix}
|
||||||
className="sponsorTimeCategories sponsorTimeEditSelector"
|
className="sponsorTimeCategories sponsorTimeEditSelector"
|
||||||
defaultValue={this.segments[0].category} //Just default to the first segment, as we don't know which they'll choose
|
defaultValue={this.segments[0].category}
|
||||||
ref={this.categoryOptionRef}>
|
ref={this.categoryOptionRef}>
|
||||||
|
|
||||||
{this.getCategoryOptions()}
|
{this.getCategoryOptions()}
|
||||||
@@ -281,13 +330,12 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
{chrome.i18n.getMessage("submit")}
|
{chrome.i18n.getMessage("submit")}
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
),
|
),
|
||||||
|
|
||||||
/* Segment Chooser Row */
|
/* Segment Chooser Row */
|
||||||
(this.state.actionState !== SkipNoticeAction.None &&
|
(this.state.actionState !== SkipNoticeAction.None && this.segments.length > 1 && !this.state.thanksForVotingText &&
|
||||||
<tr id={"sponsorSkipNoticeSubmissionOptionsRow" + this.idSuffix}
|
<tr id={"sponsorSkipNoticeSubmissionOptionsRow" + this.idSuffix}
|
||||||
key={4}>
|
key={4}>
|
||||||
<td id={"sponsorTimesSubmissionOptionsContainer" + this.idSuffix}>
|
<td id={"sponsorTimesSubmissionOptionsContainer" + this.idSuffix}>
|
||||||
@@ -305,10 +353,11 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
return (
|
return (
|
||||||
<span className="sponsorSkipNoticeUnskipSection">
|
<span className="sponsorSkipNoticeUnskipSection">
|
||||||
<button id={"sponsorSkipUnskipButton" + this.idSuffix}
|
<button id={"sponsorSkipUnskipButton" + this.idSuffix}
|
||||||
className="sponsorSkipObject sponsorSkipNoticeButton"
|
className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||||
style={{marginLeft: "4px"}}
|
style={{marginLeft: "4px",
|
||||||
onClick={() => this.prepAction(SkipNoticeAction.Unskip)}>
|
color: (this.state.actionState === SkipNoticeAction.Unskip) ? this.selectedColor : this.unselectedColor
|
||||||
|
}}
|
||||||
|
onClick={() => this.prepAction(SkipNoticeAction.Unskip)}>
|
||||||
{this.state.skipButtonText + (this.state.showKeybindHint ? " (" + Config.config.skipKeybind + ")" : "")}
|
{this.state.skipButtonText + (this.state.showKeybindHint ? " (" + Config.config.skipKeybind + ")" : "")}
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
@@ -318,20 +367,40 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
|
|
||||||
getSubmissionChooser(): JSX.Element[] {
|
getSubmissionChooser(): JSX.Element[] {
|
||||||
const elements: JSX.Element[] = [];
|
const elements: JSX.Element[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < this.segments.length; i++) {
|
for (let i = 0; i < this.segments.length; i++) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||||
|
style={{opacity: this.getSubmissionChooserOpacity(i),
|
||||||
|
color: this.getSubmissionChooserColor(i)}}
|
||||||
onClick={() => this.performAction(i)}
|
onClick={() => this.performAction(i)}
|
||||||
key={"submission" + i + this.segments[i].category + this.idSuffix}>
|
key={"submission" + i + this.segments[i].category + this.idSuffix}>
|
||||||
{(i + 1) + ". " + chrome.i18n.getMessage("category_" + this.segments[i].category)}
|
{(i + 1) + ". " + chrome.i18n.getMessage("category_" + this.segments[i].category)}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSubmissionChooserOpacity(index: number): number {
|
||||||
|
const isUpvote = this.state.actionState === SkipNoticeAction.Upvote;
|
||||||
|
const isDownvote = this.state.actionState == SkipNoticeAction.Downvote;
|
||||||
|
const isCopyDownvote = this.state.actionState == SkipNoticeAction.CopyDownvote;
|
||||||
|
const shouldBeGray: boolean = (isUpvote && this.state.voted[index] == SkipNoticeAction.Upvote) ||
|
||||||
|
(isDownvote && this.state.voted[index] == SkipNoticeAction.Downvote) ||
|
||||||
|
(isCopyDownvote && this.state.copied[index] == SkipNoticeAction.CopyDownvote);
|
||||||
|
|
||||||
|
return shouldBeGray ? 0.35 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSubmissionChooserColor(index: number): string {
|
||||||
|
const isDownvote = this.state.actionState == SkipNoticeAction.Downvote;
|
||||||
|
const isCopyDownvote = this.state.actionState == SkipNoticeAction.CopyDownvote;
|
||||||
|
const shouldWarnUser = Config.config.isVip && (isDownvote || isCopyDownvote)
|
||||||
|
&& this.segments[index].locked === 1;
|
||||||
|
|
||||||
|
return shouldWarnUser ? this.lockedColor : this.unselectedColor;
|
||||||
|
}
|
||||||
|
|
||||||
onMouseEnter(): void {
|
onMouseEnter(): void {
|
||||||
if (this.state.smaller) {
|
if (this.state.smaller) {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -340,16 +409,6 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prepAction(action: SkipNoticeAction): void {
|
|
||||||
if (this.segments.length === 1) {
|
|
||||||
this.performAction(0, action);
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
actionState: action
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getMessageBoxes(): JSX.Element[] {
|
getMessageBoxes(): JSX.Element[] {
|
||||||
if (this.state.messages.length === 0) {
|
if (this.state.messages.length === 0) {
|
||||||
// Add a spacer if there is no text
|
// Add a spacer if there is no text
|
||||||
@@ -365,8 +424,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
|
|
||||||
for (let i = 0; i < this.state.messages.length; i++) {
|
for (let i = 0; i < this.state.messages.length; i++) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<tr>
|
<tr key={i + "_messageBox"}>
|
||||||
<td>
|
<td key={i + "_messageBox"}>
|
||||||
<NoticeTextSelectionComponent idSuffix={this.idSuffix}
|
<NoticeTextSelectionComponent idSuffix={this.idSuffix}
|
||||||
text={this.state.messages[i]}
|
text={this.state.messages[i]}
|
||||||
onClick={this.state.messageOnClick}
|
onClick={this.state.messageOnClick}
|
||||||
@@ -380,6 +439,33 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepAction(action: SkipNoticeAction): void {
|
||||||
|
if (this.segments.length === 1) {
|
||||||
|
this.performAction(0, action);
|
||||||
|
} else {
|
||||||
|
switch (action ?? this.state.actionState) {
|
||||||
|
case SkipNoticeAction.None:
|
||||||
|
this.resetStateToStart();
|
||||||
|
break;
|
||||||
|
case SkipNoticeAction.Upvote:
|
||||||
|
this.resetStateToStart(SkipNoticeAction.Upvote);
|
||||||
|
break;
|
||||||
|
case SkipNoticeAction.Downvote:
|
||||||
|
this.resetStateToStart(SkipNoticeAction.Downvote);
|
||||||
|
break;
|
||||||
|
case SkipNoticeAction.CategoryVote:
|
||||||
|
this.resetStateToStart(SkipNoticeAction.CategoryVote, true, true);
|
||||||
|
break;
|
||||||
|
case SkipNoticeAction.CopyDownvote:
|
||||||
|
this.resetStateToStart(SkipNoticeAction.CopyDownvote, true);
|
||||||
|
break;
|
||||||
|
case SkipNoticeAction.Unskip:
|
||||||
|
this.resetStateToStart(SkipNoticeAction.Unskip);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the action from the current state
|
* Performs the action from the current state
|
||||||
*
|
*
|
||||||
@@ -388,74 +474,110 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
performAction(index: number, action?: SkipNoticeAction): void {
|
performAction(index: number, action?: SkipNoticeAction): void {
|
||||||
switch (action ?? this.state.actionState) {
|
switch (action ?? this.state.actionState) {
|
||||||
case SkipNoticeAction.None:
|
case SkipNoticeAction.None:
|
||||||
|
this.noAction(index);
|
||||||
break;
|
break;
|
||||||
case SkipNoticeAction.Upvote:
|
case SkipNoticeAction.Upvote:
|
||||||
this.contentContainer().vote(1, this.segments[index].UUID, undefined, this);
|
this.upvote(index);
|
||||||
break;
|
break;
|
||||||
case SkipNoticeAction.Downvote:
|
case SkipNoticeAction.Downvote:
|
||||||
this.contentContainer().vote(0, this.segments[index].UUID, undefined, this);
|
this.downvote(index);
|
||||||
break;
|
break;
|
||||||
case SkipNoticeAction.CategoryVote:
|
case SkipNoticeAction.CategoryVote:
|
||||||
this.contentContainer().vote(undefined, this.segments[index].UUID, this.categoryOptionRef.current.value as Category, this)
|
this.categoryVote(index);
|
||||||
|
break;
|
||||||
|
case SkipNoticeAction.CopyDownvote:
|
||||||
|
this.copyDownvote(index);
|
||||||
break;
|
break;
|
||||||
case SkipNoticeAction.Unskip:
|
case SkipNoticeAction.Unskip:
|
||||||
this.state.skipButtonCallback(index);
|
this.unskipAction(index);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.resetStateToStart();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
noAction(index: number): void {
|
||||||
|
const voted = this.state.voted;
|
||||||
|
voted[index] = SkipNoticeAction.None;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
actionState: SkipNoticeAction.None
|
voted
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
adjustDownvotingState(value: boolean): void {
|
upvote(index: number): void {
|
||||||
if (!value) this.clearConfigListener();
|
if (this.segments.length === 1) this.resetStateToStart();
|
||||||
|
this.contentContainer().vote(1, this.segments[index].UUID, undefined, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
downvote(index: number): void {
|
||||||
|
if (this.segments.length === 1) this.resetStateToStart();
|
||||||
|
|
||||||
|
this.contentContainer().vote(0, this.segments[index].UUID, undefined, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryVote(index: number): void {
|
||||||
|
this.contentContainer().vote(undefined, this.segments[index].UUID, this.categoryOptionRef.current.value as Category, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
copyDownvote(index: number): void {
|
||||||
|
const sponsorVideoID = this.props.contentContainer().sponsorVideoID;
|
||||||
|
const sponsorTimesSubmitting : SponsorTime = {
|
||||||
|
segment: this.segments[index].segment,
|
||||||
|
UUID: null,
|
||||||
|
category: this.segments[index].category,
|
||||||
|
actionType: this.segments[index].actionType,
|
||||||
|
source: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
const segmentTimes = Config.config.segmentTimes.get(sponsorVideoID) || [];
|
||||||
|
segmentTimes.push(sponsorTimesSubmitting);
|
||||||
|
Config.config.segmentTimes.set(sponsorVideoID, segmentTimes);
|
||||||
|
|
||||||
|
this.props.contentContainer().sponsorTimesSubmitting.push(sponsorTimesSubmitting);
|
||||||
|
this.props.contentContainer().updatePreviewBar();
|
||||||
|
this.props.contentContainer().resetSponsorSubmissionNotice();
|
||||||
|
this.props.contentContainer().updateEditButtonsOnPlayer();
|
||||||
|
|
||||||
|
this.contentContainer().vote(0, this.segments[index].UUID, undefined, this);
|
||||||
|
|
||||||
|
const copied = this.state.copied;
|
||||||
|
copied[index] = SkipNoticeAction.CopyDownvote;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
downvoting: value,
|
copied
|
||||||
choosingCategory: false
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
clearConfigListener(): void {
|
unskipAction(index: number): void {
|
||||||
if (this.configListener) {
|
this.state.skipButtonCallback(index);
|
||||||
Config.configListeners.splice(Config.configListeners.indexOf(this.configListener), 1);
|
|
||||||
this.configListener = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openCategoryChooser(): void {
|
openEditingOptions(): void {
|
||||||
// Add as a config listener
|
this.resetStateToStart(undefined, true);
|
||||||
this.configListener = () => this.forceUpdate();
|
|
||||||
Config.configListeners.push(this.configListener);
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
choosingCategory: true,
|
|
||||||
downvoting: false
|
|
||||||
}, () => {
|
|
||||||
if (this.segments.length > 1) {
|
|
||||||
// Use the action selectors as a submit button
|
|
||||||
this.prepAction(SkipNoticeAction.CategoryVote);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCategoryOptions(): React.ReactElement[] {
|
getCategoryOptions(): React.ReactElement[] {
|
||||||
const elements = [];
|
const elements = [];
|
||||||
|
|
||||||
const categories = CompileConfig.categoryList.filter((cat => getCategoryActionType(cat as Category) === CategoryActionType.Skippable));
|
const categories = (CompileConfig.categoryList.filter((cat => getCategoryActionType(cat as Category) === CategoryActionType.Skippable))) as Category[];
|
||||||
for (const category of categories) {
|
for (const category of categories) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<option value={category}
|
<option value={category}
|
||||||
key={category}>
|
key={category}
|
||||||
|
className={this.getCategoryNameClass(category)}>
|
||||||
{chrome.i18n.getMessage("category_" + category)}
|
{chrome.i18n.getMessage("category_" + category)}
|
||||||
</option>
|
</option>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCategoryNameClass(category: string): string {
|
||||||
|
return this.props.contentContainer().lockedCategories.includes(category) ? "sponsorBlockLockedColor" : ""
|
||||||
|
}
|
||||||
|
|
||||||
unskip(index: number): void {
|
unskip(index: number): void {
|
||||||
this.contentContainer().unskipSponsorTime(this.segments[index], this.props.unskipTime);
|
this.contentContainer().unskipSponsorTime(this.segments[index], this.props.unskipTime);
|
||||||
|
|
||||||
@@ -512,19 +634,40 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
afterVote(segment: SponsorTime, type: number, category: Category): void {
|
afterVote(segment: SponsorTime, type: number, category: Category): void {
|
||||||
this.addVoteButtonInfo(chrome.i18n.getMessage("voted"));
|
const index = utils.getSponsorIndexFromUUID(this.segments, segment.UUID);
|
||||||
|
const wikiLinkText = CompileConfig.wikiLinks[segment.category];
|
||||||
|
|
||||||
if (type === 0) {
|
const voted = this.state.voted;
|
||||||
this.setNoticeInfoMessage(chrome.i18n.getMessage("hitGoBack"));
|
switch (type) {
|
||||||
this.adjustDownvotingState(false);
|
case 0:
|
||||||
|
this.clearConfigListener();
|
||||||
|
this.setNoticeInfoMessageWithOnClick(() => window.open(wikiLinkText), chrome.i18n.getMessage("OpenCategoryWikiPage"));
|
||||||
|
|
||||||
|
voted[index] = SkipNoticeAction.Downvote;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
voted[index] = SkipNoticeAction.Upvote;
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
voted[index] = SkipNoticeAction.None;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
voted
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addVoteButtonInfo(chrome.i18n.getMessage("voted"));
|
||||||
|
|
||||||
// Change the sponsor locally
|
// Change the sponsor locally
|
||||||
if (segment) {
|
if (segment) {
|
||||||
if (type === 0) {
|
if (type === 0) {
|
||||||
segment.hidden = SponsorHideType.Downvoted;
|
segment.hidden = SponsorHideType.Downvoted;
|
||||||
} else if (category) {
|
} else if (category) {
|
||||||
segment.category = category;
|
segment.category = category; // This is the actual segment on the video page
|
||||||
|
this.segments[index].category = category; //this is the segment inside the skip notice.
|
||||||
|
} else if (type === 1) {
|
||||||
|
segment.hidden = SponsorHideType.Visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.contentContainer().updatePreviewBar();
|
this.contentContainer().updatePreviewBar();
|
||||||
@@ -562,6 +705,13 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
this.props.closeListener();
|
this.props.closeListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearConfigListener(): void {
|
||||||
|
if (this.configListener) {
|
||||||
|
Config.configListeners.splice(Config.configListeners.indexOf(this.configListener), 1);
|
||||||
|
this.configListener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unmutedListener(): void {
|
unmutedListener(): void {
|
||||||
if (this.props.segments.length === 1
|
if (this.props.segments.length === 1
|
||||||
&& this.props.segments[0].actionType === ActionType.Mute
|
&& this.props.segments[0].actionType === ActionType.Mute
|
||||||
@@ -572,6 +722,26 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetStateToStart(actionState: SkipNoticeAction = SkipNoticeAction.None, editing = false, choosingCategory = false): void {
|
||||||
|
this.setState({
|
||||||
|
actionState: actionState,
|
||||||
|
editing: editing,
|
||||||
|
choosingCategory: choosingCategory,
|
||||||
|
thanksForVotingText: null,
|
||||||
|
messages: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
downvoteButtonColor(downvoteType: SkipNoticeAction): string {
|
||||||
|
// Also used for "Copy and Downvote"
|
||||||
|
if (this.segments.length > 1) {
|
||||||
|
return (this.state.actionState === downvoteType) ? this.selectedColor : this.unselectedColor;
|
||||||
|
} else {
|
||||||
|
// You dont have segment selectors so the lockbutton needs to be colored and cannot be selected.
|
||||||
|
return Config.config.isVip && this.segments[0].locked === 1 ? this.lockedColor : this.unselectedColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private getUnskipText(): string {
|
private getUnskipText(): string {
|
||||||
switch (this.props.segments[0].actionType) {
|
switch (this.props.segments[0].actionType) {
|
||||||
case ActionType.Mute: {
|
case ActionType.Mute: {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export interface SponsorTimeEditProps {
|
|||||||
export interface SponsorTimeEditState {
|
export interface SponsorTimeEditState {
|
||||||
editing: boolean;
|
editing: boolean;
|
||||||
sponsorTimeEdits: [string, string];
|
sponsorTimeEdits: [string, string];
|
||||||
|
selectedCategory: Category;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_CATEGORY = "chooseACategory";
|
const DEFAULT_CATEGORY = "chooseACategory";
|
||||||
@@ -47,7 +48,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
editing: false,
|
editing: false,
|
||||||
sponsorTimeEdits: [null, null]
|
sponsorTimeEdits: [null, null],
|
||||||
|
selectedCategory: DEFAULT_CATEGORY as Category
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +308,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||||||
for (const category of (this.props.categoryList ?? CompileConfig.categoryList)) {
|
for (const category of (this.props.categoryList ?? CompileConfig.categoryList)) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<option value={category}
|
<option value={category}
|
||||||
key={category}>
|
key={category}
|
||||||
|
className={this.getCategoryLockedClass(category)}>
|
||||||
{chrome.i18n.getMessage("category_" + category)}
|
{chrome.i18n.getMessage("category_" + category)}
|
||||||
</option>
|
</option>
|
||||||
);
|
);
|
||||||
@@ -315,6 +318,10 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCategoryLockedClass(category: string): string {
|
||||||
|
return this.props.contentContainer().lockedCategories.includes(category) ? "sponsorBlockLockedColor" : "";
|
||||||
|
}
|
||||||
|
|
||||||
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>): void {
|
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||||
// See if show more categories was pressed
|
// See if show more categories was pressed
|
||||||
if (event.target.value !== DEFAULT_CATEGORY && !Config.config.categorySelections.some((category) => category.name === event.target.value)) {
|
if (event.target.value !== DEFAULT_CATEGORY && !Config.config.categorySelections.some((category) => category.name === event.target.value)) {
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, P
|
|||||||
|
|
||||||
interface SBConfig {
|
interface SBConfig {
|
||||||
userID: string,
|
userID: string,
|
||||||
/** Contains unsubmitted segments that the user has created. */
|
isVip: boolean,
|
||||||
|
lastIsVipUpdate: number,
|
||||||
|
/* Contains unsubmitted segments that the user has created. */
|
||||||
segmentTimes: SBMap<string, SponsorTime[]>,
|
segmentTimes: SBMap<string, SponsorTime[]>,
|
||||||
defaultCategory: Category,
|
defaultCategory: Category,
|
||||||
whitelistedChannels: string[],
|
whitelistedChannels: string[],
|
||||||
@@ -44,7 +46,12 @@ interface SBConfig {
|
|||||||
autoHideInfoButton: boolean,
|
autoHideInfoButton: boolean,
|
||||||
autoSkipOnMusicVideos: boolean,
|
autoSkipOnMusicVideos: boolean,
|
||||||
highlightCategoryUpdate: boolean,
|
highlightCategoryUpdate: boolean,
|
||||||
scrollToEditTimeUpdate: boolean
|
colorPalette: {
|
||||||
|
red: string,
|
||||||
|
white: string,
|
||||||
|
locked: string
|
||||||
|
},
|
||||||
|
scrollToEditTimeUpdate: boolean,
|
||||||
|
|
||||||
// What categories should be skipped
|
// What categories should be skipped
|
||||||
categorySelections: CategorySelection[],
|
categorySelections: CategorySelection[],
|
||||||
@@ -152,6 +159,8 @@ const Config: SBObject = {
|
|||||||
configListeners: [],
|
configListeners: [],
|
||||||
defaults: {
|
defaults: {
|
||||||
userID: null,
|
userID: null,
|
||||||
|
isVip: false,
|
||||||
|
lastIsVipUpdate: 0,
|
||||||
segmentTimes: new SBMap("segmentTimes"),
|
segmentTimes: new SBMap("segmentTimes"),
|
||||||
defaultCategory: "chooseACategory" as Category,
|
defaultCategory: "chooseACategory" as Category,
|
||||||
whitelistedChannels: [],
|
whitelistedChannels: [],
|
||||||
@@ -199,6 +208,12 @@ const Config: SBObject = {
|
|||||||
option: CategorySkipOption.AutoSkip
|
option: CategorySkipOption.AutoSkip
|
||||||
}],
|
}],
|
||||||
|
|
||||||
|
colorPalette: {
|
||||||
|
red: "#780303",
|
||||||
|
white: "#ffffff",
|
||||||
|
locked: "#ffc83d"
|
||||||
|
},
|
||||||
|
|
||||||
// Preview bar
|
// Preview bar
|
||||||
barTypes: {
|
barTypes: {
|
||||||
"preview-chooseACategory": {
|
"preview-chooseACategory": {
|
||||||
|
|||||||
@@ -34,8 +34,10 @@ let lastPOISkip = 0;
|
|||||||
|
|
||||||
// JSON video info
|
// JSON video info
|
||||||
let videoInfo: VideoInfo = null;
|
let videoInfo: VideoInfo = null;
|
||||||
//the channel this video is about
|
// The channel this video is about
|
||||||
let channelIDInfo: ChannelIDInfo;
|
let channelIDInfo: ChannelIDInfo;
|
||||||
|
// Locked Categories in this tab, like: ["sponsor","intro","outro"]
|
||||||
|
let lockedCategories: Category[] = [];
|
||||||
|
|
||||||
// Skips are scheduled to ensure precision.
|
// Skips are scheduled to ensure precision.
|
||||||
// Skips are rescheduled every seeking event.
|
// Skips are rescheduled every seeking event.
|
||||||
@@ -121,7 +123,8 @@ const skipNoticeContentContainer: ContentContainer = () => ({
|
|||||||
updateEditButtonsOnPlayer,
|
updateEditButtonsOnPlayer,
|
||||||
previewTime,
|
previewTime,
|
||||||
videoInfo,
|
videoInfo,
|
||||||
getRealCurrentTime: getRealCurrentTime
|
getRealCurrentTime: getRealCurrentTime,
|
||||||
|
lockedCategories
|
||||||
});
|
});
|
||||||
|
|
||||||
// value determining when to count segment as skipped and send telemetry to server (percent based)
|
// value determining when to count segment as skipped and send telemetry to server (percent based)
|
||||||
@@ -231,6 +234,7 @@ function resetValues() {
|
|||||||
status: ChannelIDStatus.Fetching,
|
status: ChannelIDStatus.Fetching,
|
||||||
id: null
|
id: null
|
||||||
};
|
};
|
||||||
|
lockedCategories = [];
|
||||||
|
|
||||||
//empty the preview bar
|
//empty the preview bar
|
||||||
if (previewBar !== null) {
|
if (previewBar !== null) {
|
||||||
@@ -752,6 +756,55 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) {
|
|||||||
|
|
||||||
sponsorLookupRetries++;
|
sponsorLookupRetries++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lookupVipInformation(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookupVipInformation(id: string): void {
|
||||||
|
updateVipInfo().then((isVip) => {
|
||||||
|
if (isVip) {
|
||||||
|
lockedCategoriesLookup(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateVipInfo(): Promise<boolean> {
|
||||||
|
const currentTime = Date.now();
|
||||||
|
const lastUpdate = Config.config.lastIsVipUpdate;
|
||||||
|
if (currentTime - lastUpdate > 1000 * 60 * 60 * 72) { // 72 hours
|
||||||
|
Config.config.lastIsVipUpdate = currentTime;
|
||||||
|
|
||||||
|
const response = await utils.asyncRequestToServer("GET", "/api/isUserVIP", { userID: Config.config.userID});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
let isVip = false;
|
||||||
|
try {
|
||||||
|
const vipResponse = JSON.parse(response.responseText)?.vip;
|
||||||
|
if (typeof(vipResponse) === "boolean") {
|
||||||
|
isVip = vipResponse;
|
||||||
|
}
|
||||||
|
} catch (e) { } //eslint-disable-line no-empty
|
||||||
|
|
||||||
|
Config.config.isVip = isVip;
|
||||||
|
return isVip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Config.config.isVip;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function lockedCategoriesLookup(id: string): Promise<void> {
|
||||||
|
const hashPrefix = (await utils.getHash(id, 1)).substr(0, 4);
|
||||||
|
const response = await utils.asyncRequestToServer("GET", "/api/lockCategories/" + hashPrefix);
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
try {
|
||||||
|
const categoriesResponse = JSON.parse(response.responseText).filter((lockInfo) => lockInfo.videoID === id)[0]?.categories;
|
||||||
|
if (Array.isArray(categoriesResponse)) {
|
||||||
|
lockedCategories = categoriesResponse;
|
||||||
|
}
|
||||||
|
} catch (e) { } //eslint-disable-line no-empty
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function retryFetch(): void {
|
function retryFetch(): void {
|
||||||
@@ -1683,7 +1736,11 @@ function resetSponsorSubmissionNotice() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function submitSponsorTimes() {
|
function submitSponsorTimes() {
|
||||||
if (submissionNotice !== null) return;
|
if (submissionNotice !== null){
|
||||||
|
submissionNotice.close();
|
||||||
|
submissionNotice = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (sponsorTimesSubmitting !== undefined && sponsorTimesSubmitting.length > 0) {
|
if (sponsorTimesSubmitting !== undefined && sponsorTimesSubmitting.length > 0) {
|
||||||
submissionNotice = new SubmissionNotice(skipNoticeContentContainer, sendSubmitMessage);
|
submissionNotice = new SubmissionNotice(skipNoticeContentContainer, sendSubmitMessage);
|
||||||
|
|||||||
@@ -379,8 +379,10 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
container.removeChild(container.firstChild);
|
container.removeChild(container.firstChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isVip = Config.config.isVip;
|
||||||
for (let i = 0; i < segmentTimes.length; i++) {
|
for (let i = 0; i < segmentTimes.length; i++) {
|
||||||
const UUID = segmentTimes[i].UUID;
|
const UUID = segmentTimes[i].UUID;
|
||||||
|
const locked = segmentTimes[i].locked;
|
||||||
|
|
||||||
const sponsorTimeButton = document.createElement("button");
|
const sponsorTimeButton = document.createElement("button");
|
||||||
sponsorTimeButton.className = "segmentTimeButton popupElement";
|
sponsorTimeButton.className = "segmentTimeButton popupElement";
|
||||||
@@ -430,7 +432,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
const downvoteButton = document.createElement("img");
|
const downvoteButton = document.createElement("img");
|
||||||
downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + UUID;
|
downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + UUID;
|
||||||
downvoteButton.className = "voteButton";
|
downvoteButton.className = "voteButton";
|
||||||
downvoteButton.src = chrome.runtime.getURL("icons/thumbs_down.svg");
|
downvoteButton.src = locked && isVip ? chrome.runtime.getURL("icons/thumbs_down_locked.svg") : chrome.runtime.getURL("icons/thumbs_down.svg");
|
||||||
downvoteButton.addEventListener("click", () => vote(0, UUID));
|
downvoteButton.addEventListener("click", () => vote(0, UUID));
|
||||||
|
|
||||||
//uuid button
|
//uuid button
|
||||||
|
|||||||
18
src/svg-icons/pencil_svg.tsx
Normal file
18
src/svg-icons/pencil_svg.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
const pencilSvg = ({
|
||||||
|
fill = "#ffffff"
|
||||||
|
}): JSX.Element => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill={fill}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M14.1 7.1l2.9 2.9L6.1 20.7l-3.6.7.7-3.6L14.1 7.1zm0-2.8L1.4 16.9 0 24l7.1-1.4L19.8 9.9l-5.7-5.7zm7.1 4.3L24 5.7 18.3 0l-2.8 2.8 5.7 5.7z"></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default pencilSvg;
|
||||||
23
src/svg-icons/thumbs_down_svg.tsx
Normal file
23
src/svg-icons/thumbs_down_svg.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
const thumbsDownSvg = ({
|
||||||
|
fill = "#ffffff"
|
||||||
|
}): JSX.Element => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
fill={fill}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="none"
|
||||||
|
d="M0 0h24v24H0z">
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default thumbsDownSvg;
|
||||||
22
src/svg-icons/thumbs_up_svg.tsx
Normal file
22
src/svg-icons/thumbs_up_svg.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
const thumbsUpSvg = ({
|
||||||
|
fill = "#ffffff"
|
||||||
|
}): JSX.Element => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
fill={fill}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="none"
|
||||||
|
d="M0 0h24v24H0V0z"></path>
|
||||||
|
<path
|
||||||
|
d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default thumbsUpSvg;
|
||||||
@@ -20,7 +20,8 @@ export interface ContentContainer {
|
|||||||
updateEditButtonsOnPlayer: () => void,
|
updateEditButtonsOnPlayer: () => void,
|
||||||
previewTime: (time: number, unpause?: boolean) => void,
|
previewTime: (time: number, unpause?: boolean) => void,
|
||||||
videoInfo: VideoInfo,
|
videoInfo: VideoInfo,
|
||||||
getRealCurrentTime: () => number
|
getRealCurrentTime: () => number,
|
||||||
|
lockedCategories: string[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +75,7 @@ export enum SponsorSourceType {
|
|||||||
export interface SponsorTime {
|
export interface SponsorTime {
|
||||||
segment: [number] | [number, number];
|
segment: [number] | [number, number];
|
||||||
UUID: SegmentUUID;
|
UUID: SegmentUUID;
|
||||||
|
locked?: number;
|
||||||
|
|
||||||
category: Category;
|
category: Category;
|
||||||
actionType: ActionType;
|
actionType: ActionType;
|
||||||
|
|||||||
@@ -539,5 +539,4 @@ export default class Utils {
|
|||||||
|
|
||||||
return hashHex;
|
return hashHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user