diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json index 48b25279..788f9a93 100644 --- a/public/_locales/en/messages.json +++ b/public/_locales/en/messages.json @@ -425,5 +425,23 @@ }, "mobileUpdateInfo": { "message": "m.youtube.com is now supported" + }, + "confirmNoticeTitle" : { + "message": "Submit Segment" + }, + "submit": { + "message": "Submit" + }, + "cancel": { + "message": "Cancel" + }, + "delete": { + "message": "Delete" + }, + "preview": { + "message": "Preview" + }, + "edit": { + "message": "Edit" } } diff --git a/public/content.css b/public/content.css index c76484ad..04385c44 100644 --- a/public/content.css +++ b/public/content.css @@ -80,13 +80,15 @@ border-radius: 5px; - animation: fadeIn 0.5s; - border-spacing: 5px 10px; padding-left: 5px; padding-right: 5px; } +.sponsorSkipNoticeFadeIn { + animation: fadeIn 0.5s; +} + .sponsorSkipNoticeFadeOut { animation: fadeOut 3s cubic-bezier(0.55, 0.055, 0.675, 0.19); } diff --git a/src/components/NoticeComponent.tsx b/src/components/NoticeComponent.tsx index 9a81ce4e..91eedbeb 100644 --- a/src/components/NoticeComponent.tsx +++ b/src/components/NoticeComponent.tsx @@ -6,12 +6,16 @@ export interface NoticeProps { maxCountdownTime?: () => number, amountOfPreviousNotices?: number, timed?: boolean, - idSuffix: string + idSuffix?: string, + + fadeIn?: boolean } export interface NoticeState { noticeTitle: string, + maxCountdownTime?: () => number, + countdownTime: number, countdownText: string, } @@ -25,21 +29,23 @@ class NoticeComponent extends React.Component { constructor(props: NoticeProps) { super(props); - if (props.maxCountdownTime === undefined) props.maxCountdownTime = () => 4; + let maxCountdownTime = props.maxCountdownTime || (() => 4); //the id for the setInterval running the countdown this.countdownInterval = null; this.amountOfPreviousNotices = props.amountOfPreviousNotices || 0; - this.idSuffix = props.idSuffix; + this.idSuffix = props.idSuffix || ""; // Setup state this.state = { noticeTitle: props.noticeTitle, + maxCountdownTime, + //the countdown until this notice closes - countdownTime: props.maxCountdownTime(), + countdownTime: maxCountdownTime(), countdownText: null, } } @@ -55,7 +61,8 @@ class NoticeComponent extends React.Component { return ( @@ -141,7 +148,7 @@ class NoticeComponent extends React.Component { //reset countdown and inform the user this.setState({ - countdownTime: this.props.maxCountdownTime(), + countdownTime: this.state.maxCountdownTime(), countdownText: chrome.i18n.getMessage("paused") }); @@ -158,7 +165,7 @@ class NoticeComponent extends React.Component { if (this.countdownInterval !== null) return; this.setState({ - countdownTime: this.props.maxCountdownTime(), + countdownTime: this.state.maxCountdownTime(), countdownText: null }); @@ -169,7 +176,7 @@ class NoticeComponent extends React.Component { if (!this.props.timed) return; this.setState({ - countdownTime: this.props.maxCountdownTime(), + countdownTime: this.state.maxCountdownTime(), countdownText: null }); } diff --git a/src/components/NoticeTextSectionComponent.tsx b/src/components/NoticeTextSectionComponent.tsx index 3b64f106..839d7448 100644 --- a/src/components/NoticeTextSectionComponent.tsx +++ b/src/components/NoticeTextSectionComponent.tsx @@ -10,10 +10,6 @@ export interface NoticeTextSelectionState { } class NoticeTextSelectionComponent extends React.Component { - countdownInterval: NodeJS.Timeout; - idSuffix: any; - - amountOfPreviousNotices: number; constructor(props: NoticeTextSelectionProps) { super(props); diff --git a/src/components/SkipNoticeComponent.tsx b/src/components/SkipNoticeComponent.tsx index 85f95893..e15407c1 100644 --- a/src/components/SkipNoticeComponent.tsx +++ b/src/components/SkipNoticeComponent.tsx @@ -91,6 +91,7 @@ class SkipNoticeComponent extends React.Component @@ -167,7 +168,8 @@ class SkipNoticeComponent extends React.Component + text={this.state.messages[i]} + key={i}> ) } diff --git a/src/components/SponsorTimeEditComponent.tsx b/src/components/SponsorTimeEditComponent.tsx new file mode 100644 index 00000000..9a52896f --- /dev/null +++ b/src/components/SponsorTimeEditComponent.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; + +export interface SponsorTimeEditProps { + index: number, + + idSuffix: string, + // Contains functions and variables from the content script needed by the skip notice + contentContainer: () => any; +} + +export interface SponsorTimeEditState { + +} + +class SponsorTimeEditComponent extends React.Component { + + constructor(props: SponsorTimeEditProps) { + super(props); + } + + render() { + return ( + <> +
+ {this.props.contentContainer().sponsorTimesSubmitting[this.props.index][0] + + " to " + this.props.contentContainer().sponsorTimesSubmitting[this.props.index][1]} +
+ + + {chrome.i18n.getMessage("delete")} + + + + {chrome.i18n.getMessage("preview")} + + + + {chrome.i18n.getMessage("edit")} + + + ); + } +} + +export default SponsorTimeEditComponent; \ No newline at end of file diff --git a/src/components/SubmissionNoticeComponent.tsx b/src/components/SubmissionNoticeComponent.tsx new file mode 100644 index 00000000..69c57c0f --- /dev/null +++ b/src/components/SubmissionNoticeComponent.tsx @@ -0,0 +1,141 @@ +import * as React from "react"; +import Config from "../config" + +import NoticeComponent from "./NoticeComponent"; +import NoticeTextSelectionComponent from "./NoticeTextSectionComponent"; +import SponsorTimeEditComponent from "./SponsorTimeEditComponent"; + +export interface SkipNoticeProps { + // Contains functions and variables from the content script needed by the skip notice + contentContainer: () => any; + + callback: () => any; +} + +export interface SkipNoticeState { + noticeTitle: string, + messages: string[], + idSuffix: string; +} + +class SkipNoticeComponent extends React.Component { + // Contains functions and variables from the content script needed by the skip notice + contentContainer: () => any; + + callback: () => any; + + noticeRef: React.MutableRefObject; + + constructor(props: SkipNoticeProps) { + super(props); + this.noticeRef = React.createRef(); + + this.contentContainer = props.contentContainer; + this.callback = props.callback; + + let noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle"); + + // Setup state + this.state = { + noticeTitle, + messages: [], + idSuffix: "SubmissionNotice" + } + } + + render() { + let noticeStyle: React.CSSProperties = {}; + if (this.contentContainer().onMobileYouTube) { + noticeStyle.bottom = "4em"; + noticeStyle.transform = "scale(0.8) translate(10%, 10%)"; + } + + return ( + + + {/* Text Boxes */} + {this.getMessageBoxes()} + + {/* Sponsor Time List */} +
+ + + + {/* Last Row */} + + + + + + + ); + } + + getSponsorTimeMessages(): JSX.Element[] | JSX.Element { + let elements: JSX.Element[] = []; + + let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting; + + for (let i = 0; i < sponsorTimes.length; i++) { + elements.push( + + + ) + } + + return elements; + } + + getMessageBoxes(): JSX.Element[] | JSX.Element { + let elements: JSX.Element[] = []; + + for (let i = 0; i < this.state.messages.length; i++) { + elements.push( + + + ) + } + + return elements; + } + + cancel() { + this.noticeRef.current.close(); + + this.contentContainer().resetSponsorSubmissionNotice(); + } + + submit() { + this.props.callback(); + + this.cancel(); + } + +} + +export default SkipNoticeComponent; \ No newline at end of file diff --git a/src/content.ts b/src/content.ts index e071f033..75bcb9d0 100644 --- a/src/content.ts +++ b/src/content.ts @@ -8,6 +8,7 @@ import runThePopup from "./popup"; import PreviewBar from "./js-components/previewBar"; import SkipNotice from "./render/SkipNotice"; import SkipNoticeComponent from "./components/SkipNoticeComponent"; +import SubmissionNotice from "./render/SubmissionNotice"; // Hack to get the CSS loaded on permission-based sites (Invidious) utils.wait(() => Config.config !== null, 5000, 10).then(addCSS); @@ -87,18 +88,23 @@ var sponsorTimesSubmitting = []; //this is used to close the popup on YouTube when the other popup opens var popupInitialised = false; +var submissionNotice: SubmissionNotice = null; + // Contains all of the functions and variables needed by the skip notice var skipNoticeContentContainer = () => ({ vote, dontShowNoticeAgain, unskipSponsorTime, sponsorTimes, + sponsorTimesSubmitting, UUIDs, v: video, reskipSponsorTime, hiddenSponsorTimes, updatePreviewBar, - onMobileYouTube + onMobileYouTube, + sponsorSubmissionNotice: submissionNotice, + resetSponsorSubmissionNotice }); //get messages from the background script and the popup @@ -1205,12 +1211,21 @@ function sponsorMessageStarted(callback) { toggleStartSponsorButton(); } +/** + * Helper method for the submission notice to clear itself when it closes + */ +function resetSponsorSubmissionNotice() { + submissionNotice = null; +} + function submitSponsorTimes() { if (document.getElementById("submitButton").style.display == "none") { //don't submit, not ready return; } + if (submissionNotice !== null) return; + //it can't update to this info yet closeInfoMenu(); @@ -1242,11 +1257,7 @@ function submitSponsorTimes() { } } - let confirmMessage = chrome.i18n.getMessage("submitCheck") + "\n\n" + getSponsorTimesMessage(sponsorTimes) - + "\n\n" + chrome.i18n.getMessage("confirmMSG") + "\n\n" + chrome.i18n.getMessage("guildlinesSummary"); - if(!confirm(confirmMessage)) return; - - sendSubmitMessage(); + submissionNotice = new SubmissionNotice(skipNoticeContentContainer, sendSubmitMessage); } } @@ -1314,7 +1325,7 @@ function getSponsorTimesMessage(sponsorTimes) { for (let i = 0; i < sponsorTimes.length; i++) { for (let s = 0; s < sponsorTimes[i].length; s++) { - let timeMessage = getFormattedTime(sponsorTimes[i][s]); + let timeMessage = utils.getFormattedTime(sponsorTimes[i][s]); //if this is an end time if (s == 1) { timeMessage = " to " + timeMessage; @@ -1377,22 +1388,6 @@ function addCSS() { } } -//converts time in seconds to minutes:seconds -function getFormattedTime(seconds) { - let minutes = Math.floor(seconds / 60); - let secondsNum: number = Math.round(seconds - minutes * 60); - let secondsDisplay: string = String(secondsNum); - - if (secondsNum < 10) { - //add a zero - secondsDisplay = "0" + secondsNum; - } - - let formatted = minutes + ":" + secondsDisplay; - - return formatted; -} - function sendRequestToCustomServer(type, fullAddress, callback) { let xmlhttp = new XMLHttpRequest(); diff --git a/src/render/SubmissionNotice.tsx b/src/render/SubmissionNotice.tsx new file mode 100644 index 00000000..0c961fcd --- /dev/null +++ b/src/render/SubmissionNotice.tsx @@ -0,0 +1,47 @@ +import * as React from "react"; +import * as ReactDOM from "react-dom"; + +import SubmissionNoticeComponent from "../components/SubmissionNoticeComponent"; + +class SubmissionNotice { + // Contains functions and variables from the content script needed by the skip notice + contentContainer: () => any; + + callback: () => any; + + constructor(contentContainer: () => any, callback: () => any) { + this.contentContainer = contentContainer; + this.callback = callback; + + //get reference node + let referenceNode = document.getElementById("player-container-id") + || document.getElementById("movie_player") || document.querySelector("#player-container .video-js"); + if (referenceNode == null) { + //for embeds + let player = document.getElementById("player"); + referenceNode = player.firstChild as HTMLElement; + let index = 1; + + //find the child that is the video player (sometimes it is not the first) + while (!referenceNode.classList.contains("html5-video-player") || !referenceNode.classList.contains("ytp-embed")) { + referenceNode = player.children[index] as HTMLElement; + + index++; + } + } + + let noticeElement = document.createElement("div"); + noticeElement.id = "submissionNoticeContainer"; + + referenceNode.prepend(noticeElement); + + ReactDOM.render( + , + noticeElement + ); + } +} + +export default SubmissionNotice; \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 2134c6d7..4c5e4a0e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -256,6 +256,21 @@ class Utils { xmlhttp.send(); } + getFormattedTime(seconds) { + let minutes = Math.floor(seconds / 60); + let secondsNum: number = Math.round(seconds - minutes * 60); + let secondsDisplay: string = String(secondsNum); + + if (secondsNum < 10) { + //add a zero + secondsDisplay = "0" + secondsNum; + } + + let formatted = minutes + ":" + secondsDisplay; + + return formatted; + } + /** * Is this Firefox (web-extensions) */
+ {this.getSponsorTimeMessages()} +
+ + {/* Cancel Button */} + + + {/* Submit Button */} + +