aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAjay Ramachandran <[email protected]>2021-05-16 20:48:22 -0400
committerGitHub <[email protected]>2021-05-16 20:48:22 -0400
commit592af4e20f08549b2afb63c3e94cadb791d27b93 (patch)
treee85de1f939db9a254466d60e8da936135fd768da
parentdaa7a653c9f21f8e83d4043654985493a2705b6f (diff)
parentecfcb0b846b07ae2fa5b5a4c1aa5e863f04efb5c (diff)
downloadSponsorBlock-592af4e20f08549b2afb63c3e94cadb791d27b93.tar.gz
SponsorBlock-592af4e20f08549b2afb63c3e94cadb791d27b93.zip
Merge pull request #569 from opl-/cleanup/segment-creation
Clean up segment creation code
-rw-r--r--public/_locales/en/messages.json3
-rw-r--r--public/_locales/pl/messages.json3
-rw-r--r--public/popup.html2
-rw-r--r--src/components/SponsorTimeEditComponent.tsx4
-rw-r--r--src/config.ts1
-rw-r--r--src/content.ts329
-rw-r--r--src/messageTypes.ts11
-rw-r--r--src/popup.ts120
-rw-r--r--src/types.ts8
-rw-r--r--src/utils.ts4
10 files changed, 222 insertions, 263 deletions
diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json
index 035578bd..f58eefb4 100644
--- a/public/_locales/en/messages.json
+++ b/public/_locales/en/messages.json
@@ -79,6 +79,9 @@
"sponsorEnd": {
"message": "Segment Ends Now"
},
+ "sponsorCancel": {
+ "message": "Cancel Creating Segment"
+ },
"noVideoID": {
"message": "No YouTube video found.\nIf this is incorrect, refresh the tab."
},
diff --git a/public/_locales/pl/messages.json b/public/_locales/pl/messages.json
index 5544f8ba..faf25472 100644
--- a/public/_locales/pl/messages.json
+++ b/public/_locales/pl/messages.json
@@ -79,6 +79,9 @@
"sponsorEnd": {
"message": "Koniec segmentu"
},
+ "sponsorCancel": {
+ "message": "Anuluj tworzenie segmentu"
+ },
"noVideoID": {
"message": "Nie znaleziono filmu YouTube.\nJeżeli to błąd, odśwież stronę."
},
diff --git a/public/popup.html b/public/popup.html
index 435007f7..4f3669eb 100644
--- a/public/popup.html
+++ b/public/popup.html
@@ -71,7 +71,7 @@
</div>
<div id="submissionSection" style="display: none">
<b style="display: block; margin-top: 12px;">__MSG_submissionEditHint__</b>
- <div id="submitTimesContainer" style="display: none; margin-top: 12px;">
+ <div id="submitTimesContainer" style="margin-top: 12px;">
<button id="submitTimes" class="mediumButton">__MSG_submitTimesButton__</button>
</div>
</div>
diff --git a/src/components/SponsorTimeEditComponent.tsx b/src/components/SponsorTimeEditComponent.tsx
index e00813ca..aa2ff326 100644
--- a/src/components/SponsorTimeEditComponent.tsx
+++ b/src/components/SponsorTimeEditComponent.tsx
@@ -344,7 +344,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
//if it is not a complete sponsor time
if (sponsorTimes[index].segment.length < 2) {
//update video player
- this.props.contentContainer().changeStartSponsorButton(true, false);
+ this.props.contentContainer().updateEditButtonsOnPlayer();
}
sponsorTimes.splice(index, 1);
@@ -359,7 +359,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
this.props.submissionNotice.cancel();
//update video player
- this.props.contentContainer().changeStartSponsorButton(true, false);
+ this.props.contentContainer().updateEditButtonsOnPlayer();
} else {
//update display
this.props.submissionNotice.forceUpdate();
diff --git a/src/config.ts b/src/config.ts
index 3afb029c..a385e581 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -6,6 +6,7 @@ const utils = new Utils();
interface SBConfig {
userID: string,
+ /** Contains unsubmitted segments that the user has created. */
segmentTimes: SBMap<string, SponsorTime[]>,
defaultCategory: string,
whitelistedChannels: string[],
diff --git a/src/content.ts b/src/content.ts
index 619f783b..22ab7c3e 100644
--- a/src/content.ts
+++ b/src/content.ts
@@ -1,6 +1,6 @@
import Config from "./config";
-import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, FetchResponse, VideoInfo, StorageChangesObject } from "./types";
+import { SponsorTime, IncompleteSponsorTime, CategorySkipOption, VideoID, SponsorHideType, FetchResponse, VideoInfo, StorageChangesObject } from "./types";
import { ContentContainer } from "./types";
import Utils from "./utils";
@@ -71,8 +71,11 @@ let channelWhitelisted = false;
// create preview bar
let previewBar: PreviewBar = null;
-//the player controls on the YouTube player
-let controls = null;
+/** Element containing the player controls on the YouTube player. */
+let controls: HTMLElement | null = null;
+
+/** Contains buttons created by `createButton()`. */
+const playerButtons: Record<string, {button: HTMLButtonElement, image: HTMLImageElement}> = {};
// Direct Links after the config is loaded
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document.URL)));
@@ -81,10 +84,10 @@ utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYo
//this only happens if there is an error
let sponsorLookupRetries = 0;
-//if showing the start sponsor button or the end sponsor button on the player
-let showingStartSponsor = true;
+/** Currently timed segment, which will be added to the unsubmitted segments when ready. */
+let currentlyTimedSegment: IncompleteSponsorTime | null = null;
-//the sponsor times being prepared to be submitted
+/** Segments created by the user which have not yet been submitted. */
let sponsorTimesSubmitting: SponsorTime[] = [];
//becomes true when isInfoFound is called
@@ -111,7 +114,7 @@ const skipNoticeContentContainer: ContentContainer = () => ({
onMobileYouTube,
sponsorSubmissionNotice: submissionNotice,
resetSponsorSubmissionNotice,
- changeStartSponsorButton,
+ updateEditButtonsOnPlayer,
previewTime,
videoInfo,
getRealCurrentTime: getRealCurrentTime
@@ -127,11 +130,11 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
videoIDChange(getYouTubeVideoID(document.URL));
break;
case "sponsorStart":
- sponsorMessageStarted(sendResponse);
+ startOrEndTimingNewSegment()
- break;
- case "sponsorDataChanged":
- updateSponsorTimesSubmitting();
+ sendResponse({
+ creatingSegment: currentlyTimedSegment !== null,
+ });
break;
case "isInfoFound":
@@ -150,7 +153,8 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
break;
case "getVideoID":
sendResponse({
- videoID: sponsorVideoID
+ videoID: sponsorVideoID,
+ creatingSegment: currentlyTimedSegment !== null,
});
break;
@@ -171,10 +175,6 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
sponsorsLookup(sponsorVideoID);
break;
- case "changeStartSponsorButton":
- changeStartSponsorButton(request.showStartSponsor, request.uploadButtonVisible);
-
- break;
case "submitTimes":
submitSponsorTimes();
break;
@@ -298,33 +298,21 @@ async function videoIDChange(id) {
//close popup
closeInfoMenu();
-
+
sponsorsLookup(id);
- //make sure everything is properly added
- updateVisibilityOfPlayerControlsButton().then(() => {
- //see if the onvideo control image needs to be changed
- const segments = Config.config.segmentTimes.get(sponsorVideoID);
- if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length >= 2) {
- changeStartSponsorButton(true, true);
- } else if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length < 2) {
- changeStartSponsorButton(false, true);
- } else {
- changeStartSponsorButton(true, false);
- }
- });
+ // Make sure all player buttons are properly added
+ updateVisibilityOfPlayerControlsButton();
- //reset sponsor times submitting
+ // Clear unsubmitted segments from the previous video
sponsorTimesSubmitting = [];
+ currentlyTimedSegment = null;
updateSponsorTimesSubmitting();
-
- //see if video controls buttons should be added
- if (!onInvidious) {
- updateVisibilityOfPlayerControlsButton();
- }
}
function handleMobileControlsMutations(): void {
+ updateVisibilityOfPlayerControlsButton();
+
if (previewBar !== null) {
if (document.body.contains(previewBar.container)) {
const progressBarBackground = document.querySelector<HTMLElement>(".progress-bar-background");
@@ -1043,8 +1031,9 @@ function reskipSponsorTime(segment: SponsorTime) {
startSponsorSchedule(true, segment.segment[1], false);
}
-function createButton(baseID, title, callback, imageName, isDraggable=false): boolean {
- if (document.getElementById(baseID + "Button") != null) return false;
+function createButton(baseID: string, title: string, callback: () => void, imageName: string, isDraggable = false): HTMLElement {
+ const existingElement = document.getElementById(baseID + "Button");
+ if (existingElement !== null) return existingElement;
// Button HTML
const newButton = document.createElement("button");
@@ -1068,9 +1057,15 @@ function createButton(baseID, title, callback, imageName, isDraggable=false): bo
newButton.appendChild(newButtonImage);
// Add the button to player
- controls.prepend(newButton);
+ if (controls) controls.prepend(newButton);
- return true;
+ // Store the elements to prevent unnecessary querying
+ playerButtons[baseID] = {
+ button: newButton,
+ image: newButtonImage,
+ };
+
+ return newButton;
}
function getControls(): HTMLElement | false {
@@ -1080,8 +1075,8 @@ function getControls(): HTMLElement | false {
// Mobile YouTube
".player-controls-top",
// Invidious/videojs video element's controls element
- ".vjs-control-bar"
- ]
+ ".vjs-control-bar",
+ ];
for (const controlsSelector of controlsSelectors) {
const controls = document.querySelectorAll(controlsSelector);
@@ -1094,53 +1089,75 @@ function getControls(): HTMLElement | false {
return false;
}
-//adds all the player controls buttons
-async function createButtons(): Promise<boolean> {
+/** Creates any missing buttons on the YouTube player if possible. */
+async function createButtons(): Promise<void> {
if (onMobileYouTube) return;
- const result = await utils.wait(getControls).catch();
-
- //set global controls variable
- controls = result;
-
- let createdButton = false;
+ controls = await utils.wait(getControls).catch();
// Add button if does not already exist in html
- createdButton = createButton("startSponsor", "sponsorStart", startSponsorClicked, "PlayerStartIconSponsorBlocker256px.png") || createdButton;
- createdButton = createButton("info", "openPopup", openInfoMenu, "PlayerInfoIconSponsorBlocker256px.png") || createdButton;
- createdButton = createButton("delete", "clearTimes", clearSponsorTimes, "PlayerDeleteIconSponsorBlocker256px.png") || createdButton;
- createdButton = createButton("submit", "SubmitTimes", submitSponsorTimes, "PlayerUploadIconSponsorBlocker256px.png") || createdButton;
-
- return createdButton;
+ createButton("startSponsor", "sponsorStart", () => closeInfoMenuAnd(() => startOrEndTimingNewSegment()), "PlayerStartIconSponsorBlocker256px.png");
+ createButton("cancelSponsor", "sponsorCancel", () => closeInfoMenuAnd(() => cancelCreatingSegment()), "PlayerUploadFailedIconSponsorBlocker256px.png");
+ createButton("info", "openPopup", openInfoMenu, "PlayerInfoIconSponsorBlocker256px.png");
+ createButton("delete", "clearTimes", () => closeInfoMenuAnd(() => clearSponsorTimes()), "PlayerDeleteIconSponsorBlocker256px.png");
+ createButton("submit", "SubmitTimes", submitSponsorTimes, "PlayerUploadIconSponsorBlocker256px.png");
}
-//adds or removes the player controls button to what it should be
-async function updateVisibilityOfPlayerControlsButton(): Promise<boolean> {
- //not on a proper video yet
- if (!sponsorVideoID) return false;
+/** Creates any missing buttons on the player and updates their visiblity. */
+async function updateVisibilityOfPlayerControlsButton(): Promise<void> {
+ // Not on a proper video yet
+ if (!sponsorVideoID) return;
- const createdButtons = await createButtons();
- if (!createdButtons) return;
+ await createButtons();
- if (Config.config.hideVideoPlayerControls || onInvidious) {
- document.getElementById("startSponsorButton").style.display = "none";
- document.getElementById("submitButton").style.display = "none";
- } else {
- document.getElementById("startSponsorButton").style.removeProperty("display");
- }
+ updateEditButtonsOnPlayer();
- //don't show the info button on embeds
+ // Don't show the info button on embeds
if (Config.config.hideInfoButtonPlayerControls || document.URL.includes("/embed/") || onInvidious) {
- document.getElementById("infoButton").style.display = "none";
+ playerButtons.info.button.style.display = "none";
} else {
- document.getElementById("infoButton").style.removeProperty("display");
+ playerButtons.info.button.style.removeProperty("display");
}
-
- if (Config.config.hideDeleteButtonPlayerControls || onInvidious) {
- document.getElementById("deleteButton").style.display = "none";
+}
+
+/** Updates the visibility of buttons on the player related to creating segments. */
+function updateEditButtonsOnPlayer(): void {
+ // Don't try to update the buttons if we aren't on a YouTube video page
+ if (!sponsorVideoID) return;
+
+ const buttonsEnabled = !Config.config.hideVideoPlayerControls && !onInvidious;
+
+ let creatingSegment = false;
+ let submitButtonVisible = false;
+ let deleteButtonVisible = false;
+
+ // Only check if buttons should be visible if they're enabled
+ if (buttonsEnabled) {
+ creatingSegment = currentlyTimedSegment !== null;
+
+ // Show only if there are any segments to submit
+ submitButtonVisible = sponsorTimesSubmitting.length > 0;
+
+ // Show only if there are any segments to delete
+ deleteButtonVisible = sponsorTimesSubmitting.length > 0;
}
- return createdButtons;
+ // Update the elements
+ playerButtons.startSponsor.button.style.display = buttonsEnabled ? "unset" : "none";
+ playerButtons.cancelSponsor.button.style.display = buttonsEnabled && creatingSegment ? "unset" : "none";
+
+ if (buttonsEnabled) {
+ if (creatingSegment) {
+ playerButtons.startSponsor.image.src = chrome.extension.getURL("icons/PlayerStopIconSponsorBlocker256px.png");
+ playerButtons.startSponsor.button.setAttribute("title", chrome.i18n.getMessage("sponsorEnd"));
+ } else {
+ playerButtons.startSponsor.image.src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker256px.png");
+ playerButtons.startSponsor.button.setAttribute("title", chrome.i18n.getMessage("sponsorStart"));
+ }
+ }
+
+ playerButtons.submit.button.style.display = submitButtonVisible && !Config.config.hideUploadButtonPlayerControls ? "unset" : "none";
+ playerButtons.delete.button.style.display = deleteButtonVisible && !Config.config.hideDeleteButtonPlayerControls ? "unset" : "none";
}
/**
@@ -1161,30 +1178,40 @@ function getRealCurrentTime(): number {
}
}
-function startSponsorClicked() {
- //it can't update to this info yet
- closeInfoMenu();
-
- toggleStartSponsorButton();
-
- //add to sponsorTimes
- if (sponsorTimesSubmitting.length > 0 && sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment.length < 2) {
- //it is an end time
- sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment[1] = getRealCurrentTime();
- sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment.sort((a, b) => a > b ? 1 : (a < b ? -1 : 0));
- } else {
- //it is a start time
- sponsorTimesSubmitting.push({
+function startOrEndTimingNewSegment() {
+ if (!currentlyTimedSegment) {
+ // Start a new segment
+ currentlyTimedSegment = {
segment: [getRealCurrentTime()],
UUID: null,
- category: Config.config.defaultCategory
+ category: Config.config.defaultCategory,
+ };
+ } else {
+ // Finish creating the new segment
+ const existingTime = currentlyTimedSegment.segment[0];
+ const currentTime = getRealCurrentTime();
+
+ sponsorTimesSubmitting.push({
+ ...currentlyTimedSegment,
+ // Swap timestamps if the user put the segment end before the start
+ segment: [Math.min(existingTime, currentTime), Math.max(existingTime, currentTime)],
});
+
+ currentlyTimedSegment = null;
+
+ // Save the newly created segment
+ Config.config.segmentTimes.set(sponsorVideoID, sponsorTimesSubmitting);
}
- //save this info
- Config.config.segmentTimes.set(sponsorVideoID, sponsorTimesSubmitting);
+ updateEditButtonsOnPlayer();
- updateSponsorTimesSubmitting(false)
+ updateSponsorTimesSubmitting(false);
+}
+
+function cancelCreatingSegment() {
+ currentlyTimedSegment = null;
+
+ updateEditButtonsOnPlayer();
}
function updateSponsorTimesSubmitting(getFromConfig = true) {
@@ -1213,38 +1240,6 @@ function updateSponsorTimesSubmitting(getFromConfig = true) {
}
}
-async function changeStartSponsorButton(showStartSponsor: boolean, uploadButtonVisible: boolean): Promise<boolean> {
- if(!sponsorVideoID || onMobileYouTube) return false;
-
- //if it isn't visible, there is no data
- const shouldHide = (uploadButtonVisible && !(Config.config.hideDeleteButtonPlayerControls || onInvidious)) ? "unset" : "none"
- document.getElementById("deleteButton").style.display = shouldHide;
-
- if (showStartSponsor) {
- showingStartSponsor = true;
- (<HTMLImageElement> document.getElementById("startSponsorImage")).src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker256px.png");
- document.getElementById("startSponsorButton").setAttribute("title", chrome.i18n.getMessage("sponsorStart"));
-
- if (document.getElementById("startSponsorImage").style.display != "none" && uploadButtonVisible && !Config.config.hideUploadButtonPlayerControls && !onInvidious) {
- document.getElementById("submitButton").style.display = "unset";
- } else if (!uploadButtonVisible || onInvidious) {
- //disable submit button
- document.getElementById("submitButton").style.display = "none";
- }
- } else {
- showingStartSponsor = false;
- (<HTMLImageElement> document.getElementById("startSponsorImage")).src = chrome.extension.getURL("icons/PlayerStopIconSponsorBlocker256px.png");
- document.getElementById("startSponsorButton").setAttribute("title", chrome.i18n.getMessage("sponsorEND"));
-
- //disable submit button
- document.getElementById("submitButton").style.display = "none";
- }
-}
-
-function toggleStartSponsorButton() {
- changeStartSponsorButton(!showingStartSponsor, true);
-}
-
function openInfoMenu() {
if (document.getElementById("sponsorBlockPopupContainer") != null) {
//it's already added
@@ -1254,7 +1249,7 @@ function openInfoMenu() {
popupInitialised = false;
//hide info button
- document.getElementById("infoButton").style.display = "none";
+ if (playerButtons.info) playerButtons.info.button.style.display = "none";
sendRequestToCustomServer('GET', chrome.extension.getURL("popup.html"), function(xmlhttp) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
@@ -1316,20 +1311,28 @@ function openInfoMenu() {
function closeInfoMenu() {
const popup = document.getElementById("sponsorBlockPopupContainer");
- if (popup != null) {
- popup.remove();
+ if (popup === null) return;
- //show info button if it's not an embed
- if (!document.URL.includes("/embed/")) {
- document.getElementById("infoButton").style.display = "unset";
- }
+ popup.remove();
+
+ // Show info button if it's not an embed
+ if (!document.URL.includes("/embed/") && playerButtons.info) {
+ playerButtons.info.button.style.display = "unset";
}
}
-function clearSponsorTimes() {
- //it can't update to this info yet
+/**
+ * The content script currently has no way to notify the info menu of changes. As a workaround we close it, thus making it query the new information when reopened.
+ *
+ * This function and all its uses should be removed when this issue is fixed.
+ * */
+function closeInfoMenuAnd<T>(func: () => T): T {
closeInfoMenu();
+ return func();
+}
+
+function clearSponsorTimes() {
const currentVideoID = sponsorVideoID;
const sponsorTimes = Config.config.segmentTimes.get(currentVideoID);
@@ -1347,8 +1350,7 @@ function clearSponsorTimes() {
updatePreviewBar();
- //set buttons to be correct
- changeStartSponsorButton(true, false);
+ updateEditButtonsOnPlayer();
}
}
@@ -1414,18 +1416,6 @@ function dontShowNoticeAgain() {
closeAllSkipNotices();
}
-function sponsorMessageStarted(callback: (response: MessageResponse) => void) {
- video = document.querySelector('video');
-
- //send back current time
- callback({
- time: video.currentTime
- })
-
- //update button
- toggleStartSponsorButton();
-}
-
/**
* Helper method for the submission notice to clear itself when it closes
*/
@@ -1436,9 +1426,6 @@ function resetSponsorSubmissionNotice() {
function submitSponsorTimes() {
if (submissionNotice !== null) return;
- //it can't update to this info yet
- closeInfoMenu();
-
if (sponsorTimesSubmitting !== undefined && sponsorTimesSubmitting.length > 0) {
submissionNotice = new SubmissionNotice(skipNoticeContentContainer, sendSubmitMessage);
}
@@ -1447,10 +1434,10 @@ function submitSponsorTimes() {
//send the message to the background js
//called after all the checks have been made that it's okay to do so
-async function sendSubmitMessage(): Promise<void> {
- //add loading animation
- (<HTMLImageElement> document.getElementById("submitImage")).src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png");
- document.getElementById("submitButton").style.animation = "rotate 1s 0s infinite";
+async function sendSubmitMessage() {
+ // Add loading animation
+ playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png");
+ playerButtons.submit.button.style.animation = "rotate 1s 0s infinite";
//check if a sponsor exceeds the duration of the video
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
@@ -1477,17 +1464,19 @@ async function sendSubmitMessage(): Promise<void> {
const response = await utils.asyncRequestToServer("POST", "/api/skipSegments", {
videoID: sponsorVideoID,
userID: Config.config.userID,
- segments: sponsorTimesSubmitting
+ segments: sponsorTimesSubmitting,
});
if (response.status === 200) {
- //hide loading message
- const submitButton = document.getElementById("submitButton");
+ // Handle submission success
+ const submitButton = playerButtons.submit.button;
+
+ // Make the animation finite
submitButton.style.animation = "rotate 1s";
- //finish this animation
- //when the animation is over, hide the button
- const animationEndListener = function() {
- changeStartSponsorButton(true, false);
+
+ // When the animation is over, hide the button
+ const animationEndListener = () => {
+ updateEditButtonsOnPlayer();
submitButton.style.animation = "none";
@@ -1496,13 +1485,12 @@ async function sendSubmitMessage(): Promise<void> {
submitButton.addEventListener("animationend", animationEndListener);
- //clear the sponsor times
+ // Remove segments from storage since they've already been submitted
Config.config.segmentTimes.delete(sponsorVideoID);
- //add submissions to current sponsors list
- if (sponsorTimes === null) sponsorTimes = [];
-
- sponsorTimes = sponsorTimes.concat(sponsorTimesSubmitting);
+ // Add submissions to current sponsors list
+ // FIXME: segments from sponsorTimesSubmitting do not contain UUIDs .-.
+ sponsorTimes = (sponsorTimes || []).concat(sponsorTimesSubmitting);
// Increase contribution count
Config.config.sponsorTimesContributed = Config.config.sponsorTimesContributed + sponsorTimesSubmitting.length;
@@ -1512,13 +1500,14 @@ async function sendSubmitMessage(): Promise<void> {
Config.config.submissionCountSinceCategories = Config.config.submissionCountSinceCategories + 1;
// Empty the submitting times
+ currentlyTimedSegment = null;
sponsorTimesSubmitting = [];
updatePreviewBar();
} else {
- //show that the upload failed
- document.getElementById("submitButton").style.animation = "unset";
- (<HTMLImageElement> document.getElementById("submitImage")).src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker256px.png");
+ // Show that the upload failed
+ playerButtons.submit.button.style.animation = "unset";
+ playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker256px.png");
alert(utils.getErrorMessage(response.status, response.responseText));
}
@@ -1575,7 +1564,7 @@ function hotkeyListener(e: KeyboardEvent): void {
}
break;
case startSponsorKey:
- startSponsorClicked();
+ startOrEndTimingNewSegment();
break;
case submitKey:
submitSponsorTimes();
diff --git a/src/messageTypes.ts b/src/messageTypes.ts
index cd1914a8..1c69388a 100644
--- a/src/messageTypes.ts
+++ b/src/messageTypes.ts
@@ -12,7 +12,6 @@ interface DefaultMessage {
message:
"update"
| "sponsorStart"
- | "sponsorDataChanged"
| "isInfoFound"
| "getVideoID"
| "getChannelID"
@@ -25,13 +24,7 @@ interface BoolValueMessage {
value: boolean;
}
-interface ChangeStartSponsorButtonMessage {
- message: "changeStartSponsorButton";
- showStartSponsor: boolean;
- uploadButtonVisible: boolean;
-}
-
-export type Message = BaseMessage & (DefaultMessage | BoolValueMessage | ChangeStartSponsorButtonMessage);
+export type Message = BaseMessage & (DefaultMessage | BoolValueMessage);
interface IsInfoFoundMessageResponse {
found: boolean;
@@ -47,7 +40,7 @@ interface GetChannelIDResponse {
}
interface SponsorStartResponse {
- time: number;
+ creatingSegment: boolean;
}
interface IsChannelWhitelistedResponse {
diff --git a/src/popup.ts b/src/popup.ts
index c37d3d90..511b2d3e 100644
--- a/src/popup.ts
+++ b/src/popup.ts
@@ -126,8 +126,8 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
PageElements.optionsButton.addEventListener("click", openOptions);
PageElements.helpButton.addEventListener("click", openHelp);
- //if true, the button now selects the end time
- let startTimeChosen = false;
+ /** If true, the content script is in the process of creating a new segment. */
+ let creatingSegment = false;
//the start and end time pairs (2d)
let sponsorTimes: SponsorTime[] = [];
@@ -233,11 +233,13 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
function onTabs(tabs) {
messageHandler.sendMessage(tabs[0].id, {message: 'getVideoID'}, function(result) {
- if (result != undefined && result.videoID) {
+ if (result !== undefined && result.videoID) {
currentVideoID = result.videoID;
+ creatingSegment = result.creatingSegment;
+
loadTabData(tabs);
- } else if (result == undefined && chrome.runtime.lastError) {
- // this isn't a YouTube video then, or at least the content script is not loaded
+ } else if (result === undefined && chrome.runtime.lastError) {
+ //this isn't a YouTube video then, or at least the content script is not loaded
displayNoVideo();
}
});
@@ -253,19 +255,11 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
//load video times for this video
const sponsorTimesStorage = Config.config.segmentTimes.get(currentVideoID);
if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) {
- if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].segment.length < 2) {
- startTimeChosen = true;
- PageElements.sponsorStart.innerHTML = chrome.i18n.getMessage("sponsorEnd");
- }
-
sponsorTimes = sponsorTimesStorage;
-
- //show submission section
- PageElements.submissionSection.style.display = "unset";
-
- showSubmitTimesIfNecessary();
}
+ updateSegmentEditingUI();
+
//check if this video's sponsors are known
messageHandler.sendMessage(
tabs[0].id,
@@ -321,51 +315,44 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
//the content script will get the message if a YouTube page is open
messageHandler.query({
active: true,
- currentWindow: true
- }, tabs => {
+ currentWindow: true,
+ }, (tabs) => {
messageHandler.sendMessage(
tabs[0].id,
{from: 'popup', message: 'sponsorStart'},
- startSponsorCallback
- );
- });
- }
-
- function startSponsorCallback(response) {
- const sponsorTimesIndex = sponsorTimes.length - (startTimeChosen ? 1 : 0);
+ async (response) => {
+ startSponsorCallback(response);
- if (sponsorTimes[sponsorTimesIndex] == undefined) {
- sponsorTimes[sponsorTimesIndex] = {
- segment: [],
- category: Config.config.defaultCategory,
- UUID: null
- };
- }
+ // Perform a second update after the config changes take effect as a workaround for a race condition
+ const removeListener = (listener: typeof lateUpdate) => {
+ const index = Config.configListeners.indexOf(listener);
+ if (index !== -1) Config.configListeners.splice(index, 1);
+ };
- sponsorTimes[sponsorTimesIndex].segment[startTimeChosen ? 1 : 0] = response.time;
+ const lateUpdate = () => {
+ startSponsorCallback(response);
+ removeListener(lateUpdate);
+ };
- const localStartTimeChosen = startTimeChosen;
- Config.config.segmentTimes.set(currentVideoID, sponsorTimes);
+ Config.configListeners.push(lateUpdate);
- //send a message to the client script
- if (localStartTimeChosen) {
- messageHandler.query({
- active: true,
- currentWindow: true
- }, tabs => {
- messageHandler.sendMessage(
- tabs[0].id,
- {message: "sponsorDataChanged"}
- );
- });
- }
+ // Remove the listener after 200ms in case the changes were propagated by the time we got the response
+ setTimeout(() => removeListener(lateUpdate), 200);
+ },
+ );
+ });
+ }
- updateStartTimeChosen();
+ function startSponsorCallback(response: {creatingSegment: boolean}) {
+ creatingSegment = response.creatingSegment;
- //show submission section
- PageElements.submissionSection.style.display = "unset";
+ // Only update the segments after a segment was created
+ if (!creatingSegment) {
+ sponsorTimes = Config.config.segmentTimes.get(currentVideoID) || [];
+ }
- showSubmitTimesIfNecessary();
+ // Update the UI
+ updateSegmentEditingUI();
}
//display the video times from the array at the top, in a different section
@@ -484,34 +471,13 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
PageElements.showNoticeAgain.style.display = "none";
}
- function updateStartTimeChosen() {
- //update startTimeChosen letiable
- if (!startTimeChosen) {
- startTimeChosen = true;
- PageElements.sponsorStart.innerHTML = chrome.i18n.getMessage("sponsorEnd");
- } else {
- resetStartTimeChosen();
- }
- }
-
- //set it to false
- function resetStartTimeChosen() {
- startTimeChosen = false;
- PageElements.sponsorStart.innerHTML = chrome.i18n.getMessage("sponsorStart");
- }
-
- //hides and shows the submit times button when needed
- function showSubmitTimesIfNecessary() {
- //check if an end time has been specified for the latest sponsor time
- if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].segment.length > 1) {
- //show submit times button
- document.getElementById("submitTimesContainer").style.display = "flex";
- } else {
- //hide submit times button
- document.getElementById("submitTimesContainer").style.display = "none";
- }
+ /** Updates any UI related to segment editing and submission according to the current state. */
+ function updateSegmentEditingUI() {
+ PageElements.sponsorStart.innerText = chrome.i18n.getMessage(creatingSegment ? "sponsorEnd" : "sponsorStart");
+
+ PageElements.submissionSection.style.display = sponsorTimes && sponsorTimes.length > 0 ? "unset" : "none";
}
-
+
//make the options div visible
function openOptions() {
chrome.runtime.sendMessage({"message": "openConfig"});
diff --git a/src/types.ts b/src/types.ts
index dd870f37..c5659d3a 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -17,7 +17,7 @@ export interface ContentContainer {
onMobileYouTube: boolean,
sponsorSubmissionNotice: SubmissionNotice,
resetSponsorSubmissionNotice: () => void,
- changeStartSponsorButton: (showStartSponsor: boolean, uploadButtonVisible: boolean) => Promise<boolean>,
+ updateEditButtonsOnPlayer: () => void,
previewTime: (time: number, unpause?: boolean) => void,
videoInfo: VideoInfo,
getRealCurrentTime: () => number
@@ -60,6 +60,10 @@ export interface SponsorTime {
hidden?: SponsorHideType;
}
+export type IncompleteSponsorTime = Omit<SponsorTime, 'segment'> & {
+ segment: [number];
+};
+
export interface PreviewBarOption {
color: string,
opacity: string
@@ -160,4 +164,4 @@ export type VideoID = string;
export type StorageChangesObject = { [key: string]: chrome.storage.StorageChange };
-export type UnEncodedSegmentTimes = [string, SponsorTime[]][]; \ No newline at end of file
+export type UnEncodedSegmentTimes = [string, SponsorTime[]][];
diff --git a/src/utils.ts b/src/utils.ts
index bb42afc5..fd5684f4 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -23,8 +23,8 @@ export default class Utils {
this.backgroundScriptContainer = backgroundScriptContainer;
}
- // Function that can be used to wait for a condition before returning
- async wait(condition: () => HTMLElement | boolean, timeout = 5000, check = 100): Promise<HTMLElement | boolean> {
+ /** Function that can be used to wait for a condition before returning. */
+ async wait<T>(condition: () => T | false, timeout = 5000, check = 100): Promise<T> {
return await new Promise((resolve, reject) => {
setTimeout(() => reject("TIMEOUT"), timeout);