mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-08 12:37:05 +03:00
Add a section in options for unsubmitted segments
This commit is contained in:
@@ -1153,5 +1153,37 @@
|
|||||||
},
|
},
|
||||||
"chaptersPage1": {
|
"chaptersPage1": {
|
||||||
"message": "SponsorBlock crowd-sourced chapters feature is only available to people who purchase a license, or for people who are granted access for free due their past contributions"
|
"message": "SponsorBlock crowd-sourced chapters feature is only available to people who purchase a license, or for people who are granted access for free due their past contributions"
|
||||||
|
},
|
||||||
|
"unsubmittedSegmentCounts": {
|
||||||
|
"message": "You currently have {0} unsubmitted segments on {1} videos",
|
||||||
|
"description": "Example: You currently have 12 unsubmitted segments on 5 videos"
|
||||||
|
},
|
||||||
|
"clearUnsubmittedSegments": {
|
||||||
|
"message": "Clear all segments",
|
||||||
|
"description": "Label for a button in settings"
|
||||||
|
},
|
||||||
|
"clearUnsubmittedSegmentsConfirm": {
|
||||||
|
"message": "Are you sure you want to clear all your unsubmitted segments?",
|
||||||
|
"description": "Confirmation message for the Clear unsubmitted segments button"
|
||||||
|
},
|
||||||
|
"showUnsubmittedSegments": {
|
||||||
|
"message": "Show segments",
|
||||||
|
"description": "Show/hide button for the unsubmitted segments list"
|
||||||
|
},
|
||||||
|
"hideUnsubmittedSegments": {
|
||||||
|
"message": "Hide segments",
|
||||||
|
"description": "Show/hide button for the unsubmitted segments list"
|
||||||
|
},
|
||||||
|
"videoID": {
|
||||||
|
"message": "Video ID",
|
||||||
|
"description": "Header of the unsubmitted segments list"
|
||||||
|
},
|
||||||
|
"segmentCount": {
|
||||||
|
"message": "Segment Count",
|
||||||
|
"description": "Header of the unsubmitted segments list"
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"message": "Actions",
|
||||||
|
"description": "Header of the unsubmitted segments list"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -491,6 +491,10 @@
|
|||||||
<div class="small-description">__MSG_copyDebugInformationOptions__</div>
|
<div class="small-description">__MSG_copyDebugInformationOptions__</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div data-type="react-UnsubmittedVideosComponent">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<div data-type="toggle" data-sync="testingServer" data-confirm-message="testingServerWarning" data-no-safari="true">
|
<div data-type="toggle" data-sync="testingServer" data-confirm-message="testingServerWarning" data-no-safari="true">
|
||||||
<div class="switch-container">
|
<div class="switch-container">
|
||||||
<label class="switch">
|
<label class="switch">
|
||||||
|
|||||||
71
src/components/UnsubmittedVideoListComponent.tsx
Normal file
71
src/components/UnsubmittedVideoListComponent.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import Config from "../config";
|
||||||
|
import UnsubmittedVideoListItem from "./UnsubmittedVideoListItem";
|
||||||
|
|
||||||
|
export interface UnsubmittedVideoListProps {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UnsubmittedVideoListState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnsubmittedVideoListComponent extends React.Component<UnsubmittedVideoListProps, UnsubmittedVideoListState> {
|
||||||
|
|
||||||
|
constructor(props: UnsubmittedVideoListProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
// Setup state
|
||||||
|
this.state = {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): React.ReactElement {
|
||||||
|
// Render nothing if there are no unsubmitted segments
|
||||||
|
if (Object.keys(Config.config.unsubmittedSegments).length == 0)
|
||||||
|
return <></>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<table id="unsubmittedVideosList"
|
||||||
|
className="categoryChooserTable">
|
||||||
|
<tbody>
|
||||||
|
{/* Headers */}
|
||||||
|
<tr id="UnsubmittedVideosListHeader"
|
||||||
|
className="categoryTableElement categoryTableHeader">
|
||||||
|
<th id="UnsubmittedVideoID">
|
||||||
|
{chrome.i18n.getMessage("videoID")}
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th id="UnsubmittedSegmentCount">
|
||||||
|
{chrome.i18n.getMessage("segmentCount")}
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th id="UnsubmittedVideoActions">
|
||||||
|
{chrome.i18n.getMessage("actions")}
|
||||||
|
</th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{this.getUnsubmittedVideos()}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnsubmittedVideos(): JSX.Element[] {
|
||||||
|
const elements: JSX.Element[] = [];
|
||||||
|
|
||||||
|
for (const videoID of Object.keys(Config.config.unsubmittedSegments)) {
|
||||||
|
elements.push(
|
||||||
|
<UnsubmittedVideoListItem videoID={videoID} key={videoID}>
|
||||||
|
</UnsubmittedVideoListItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UnsubmittedVideoListComponent;
|
||||||
64
src/components/UnsubmittedVideoListItem.tsx
Normal file
64
src/components/UnsubmittedVideoListItem.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import Config from "../config"
|
||||||
|
|
||||||
|
export interface UnsubmittedVideosListItemProps {
|
||||||
|
videoID: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UnsubmittedVideosListItemState {
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnsubmittedVideoListItem extends React.Component<UnsubmittedVideosListItemProps, UnsubmittedVideosListItemState> {
|
||||||
|
|
||||||
|
constructor(props: UnsubmittedVideosListItemProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
// Setup state
|
||||||
|
this.state = {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): React.ReactElement {
|
||||||
|
const segmentCount = Config.config.unsubmittedSegments[this.props.videoID]?.length ?? 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<tr id={this.props.videoID + "UnsubmittedSegmentsRow"}
|
||||||
|
className="categoryTableElement">
|
||||||
|
<td id={this.props.videoID + "UnsubmittedVideoID"}
|
||||||
|
className="categoryTableLabel">
|
||||||
|
<a href={`https://youtu.be/${this.props.videoID}`}
|
||||||
|
target="_blank" rel="noreferrer">
|
||||||
|
{this.props.videoID}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td id={this.props.videoID + "UnsubmittedSegmentCount"}>
|
||||||
|
{segmentCount}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td id={this.props.videoID + "UnsubmittedVideoActions"}>
|
||||||
|
<div id={this.props.videoID + "ClearSegmentsAction"}
|
||||||
|
className="option-button inline low-profile"
|
||||||
|
onClick={this.clearSegments.bind(this)}>
|
||||||
|
{chrome.i18n.getMessage("clearTimes")}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSegments(): void {
|
||||||
|
if (confirm(chrome.i18n.getMessage("clearThis")))
|
||||||
|
delete Config.config.unsubmittedSegments[this.props.videoID]
|
||||||
|
|
||||||
|
Config.forceSyncUpdate("unsubmittedSegments")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UnsubmittedVideoListItem;
|
||||||
51
src/components/UnsubmittedVideosComponent.tsx
Normal file
51
src/components/UnsubmittedVideosComponent.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import Config from "../config";
|
||||||
|
import UnsubmittedVideoListComponent from "./UnsubmittedVideoListComponent";
|
||||||
|
|
||||||
|
export interface UnsubmittedVideosProps {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UnsubmittedVideosState {
|
||||||
|
tableVisible: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnsubmittedVideosComponent extends React.Component<UnsubmittedVideosProps, UnsubmittedVideosState> {
|
||||||
|
|
||||||
|
constructor(props: UnsubmittedVideosProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
// Setup state
|
||||||
|
this.state = {
|
||||||
|
tableVisible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): React.ReactElement {
|
||||||
|
const videoCount = Object.keys(Config.config.unsubmittedSegments).length;
|
||||||
|
const segmentCount = Object.values(Config.config.unsubmittedSegments).reduce((acc: number, vid: Array<unknown>) => acc+vid.length, 0);
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<div>
|
||||||
|
{chrome.i18n.getMessage("unsubmittedSegmentCounts").replace("{0}", segmentCount.toString()).replace("{1}", videoCount.toString())}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{videoCount > 0 && <div className="option-button inline" onClick={() => this.setState({tableVisible: !this.state.tableVisible})}>
|
||||||
|
{chrome.i18n.getMessage(this.state.tableVisible ? "hideUnsubmittedSegments" : "showUnsubmittedSegments")}
|
||||||
|
</div>}
|
||||||
|
|
||||||
|
<div className="option-button inline" onClick={this.clearAllSegments}>
|
||||||
|
{chrome.i18n.getMessage("clearUnsubmittedSegments")}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{this.state.tableVisible && <UnsubmittedVideoListComponent/>}
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearAllSegments(): void {
|
||||||
|
if (confirm(chrome.i18n.getMessage("clearUnsubmittedSegmentsConfirm")))
|
||||||
|
Config.config.unsubmittedSegments = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UnsubmittedVideosComponent;
|
||||||
@@ -10,6 +10,7 @@ window.SB = Config;
|
|||||||
|
|
||||||
import Utils from "./utils";
|
import Utils from "./utils";
|
||||||
import CategoryChooser from "./render/CategoryChooser";
|
import CategoryChooser from "./render/CategoryChooser";
|
||||||
|
import UnsubmittedVideos from "./render/UnsubmittedVideos";
|
||||||
import KeybindComponent from "./components/options/KeybindComponent";
|
import KeybindComponent from "./components/options/KeybindComponent";
|
||||||
import { showDonationLink } from "./utils/configUtils";
|
import { showDonationLink } from "./utils/configUtils";
|
||||||
import { localizeHtmlPage } from "./utils/pageUtils";
|
import { localizeHtmlPage } from "./utils/pageUtils";
|
||||||
@@ -292,6 +293,9 @@ async function init() {
|
|||||||
case "react-CategoryChooserComponent":
|
case "react-CategoryChooserComponent":
|
||||||
new CategoryChooser(optionsElements[i]);
|
new CategoryChooser(optionsElements[i]);
|
||||||
break;
|
break;
|
||||||
|
case "react-UnsubmittedVideosComponent":
|
||||||
|
new UnsubmittedVideos(optionsElements[i])
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,6 +363,10 @@ function optionsConfigUpdateListener() {
|
|||||||
switch (optionsElements[i].getAttribute("data-type")) {
|
switch (optionsElements[i].getAttribute("data-type")) {
|
||||||
case "display":
|
case "display":
|
||||||
updateDisplayElement(<HTMLElement> optionsElements[i])
|
updateDisplayElement(<HTMLElement> optionsElements[i])
|
||||||
|
break;
|
||||||
|
case "react-UnsubmittedVideosComponent":
|
||||||
|
new UnsubmittedVideos(optionsElements[i])
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/render/UnsubmittedVideos.tsx
Normal file
15
src/render/UnsubmittedVideos.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as ReactDOM from "react-dom";
|
||||||
|
import UnsubmittedVideosComponent from "../components/UnsubmittedVideosComponent";
|
||||||
|
|
||||||
|
class UnsubmittedVideos {
|
||||||
|
|
||||||
|
constructor(element: Element) {
|
||||||
|
ReactDOM.render(
|
||||||
|
<UnsubmittedVideosComponent/>,
|
||||||
|
element
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UnsubmittedVideos;
|
||||||
Reference in New Issue
Block a user