diff --git a/manifest/manifest.json b/manifest/manifest.json
index 2b3e0b56..aab3bba8 100644
--- a/manifest/manifest.json
+++ b/manifest/manifest.json
@@ -63,6 +63,8 @@
"icons/bolt.svg",
"icons/stopwatch.svg",
"icons/music-note.svg",
+ "icons/import.svg",
+ "icons/export.svg",
"icons/PlayerInfoIconSponsorBlocker.svg",
"icons/PlayerDeleteIconSponsorBlocker.svg",
"popup.html",
diff --git a/public/icons/export.svg b/public/icons/export.svg
new file mode 100644
index 00000000..337823ab
--- /dev/null
+++ b/public/icons/export.svg
@@ -0,0 +1,106 @@
+
+
diff --git a/public/icons/import.svg b/public/icons/import.svg
new file mode 100644
index 00000000..b1692f37
--- /dev/null
+++ b/public/icons/import.svg
@@ -0,0 +1,91 @@
+
+
diff --git a/public/popup.css b/public/popup.css
index ac55cbeb..c6c78064 100644
--- a/public/popup.css
+++ b/public/popup.css
@@ -148,22 +148,34 @@
margin: 8px;
}
-/*
- * Refresh segments button
- */
#refreshSegmentsButton {
display: flex;
align-items: center;
+ padding: 5px;
+ margin: 5px auto;
+}
+
+#refreshSegmentsButton, #issueReporterImportExport button {
background: transparent;
border-radius: 50%;
- margin: 5px auto;
border: none;
- padding: 5px;
}
-#refreshSegmentsButton:hover {
+
+#refreshSegmentsButton:hover, #issueReporterImportExport button:hover {
background-color: var(--sb-grey-bg-color);
}
+#issueReporterImportExport button {
+ padding: 5px;
+ margin-right: 15px;
+ margin-left: 15px;
+}
+
+#issueReporterImportExport img {
+ width: 24px;
+ display: block;
+}
+
/*
* wrapper around each segment
*/
diff --git a/public/popup.html b/public/popup.html
index 6a47da8a..f8f076ad 100644
--- a/public/popup.html
+++ b/public/popup.html
@@ -43,6 +43,14 @@
+
+
+
+
diff --git a/src/popup.ts b/src/popup.ts
index 48a724d5..7a598281 100644
--- a/src/popup.ts
+++ b/src/popup.ts
@@ -8,6 +8,7 @@ import { AnimationUtils } from "./utils/animationUtils";
import { GenericUtils } from "./utils/genericUtils";
import { shortCategoryName } from "./utils/categoryUtils";
import { localizeHtmlPage } from "./utils/pageUtils";
+import { exportTimes } from "./utils/exporter";
const utils = new Utils();
interface MessageListener {
@@ -69,6 +70,7 @@ async function runThePopup(messageListener?: MessageListener): Promise {
//the start and end time pairs (2d)
let sponsorTimes: SponsorTime[] = [];
+ let downloadedTimes: SponsorTime[] = [];
//current video ID of this tab
let currentVideoID = null;
@@ -137,7 +139,10 @@ async function runThePopup(messageListener?: MessageListener): Promise {
"sbConsiderDonateLink",
"sbCloseDonate",
"sbBetaServerWarning",
- "sbCloseButton"
+ "sbCloseButton",
+ "issueReporterImportExport",
+ "importSegmentsButton",
+ "exportSegmentsButton"
].forEach(id => PageElements[id] = document.getElementById(id));
getSegmentsFromContentScript(false);
@@ -167,7 +172,8 @@ async function runThePopup(messageListener?: MessageListener): Promise {
});
}
- //setup click listeners
+ PageElements.exportSegmentsButton.addEventListener("click", exportSegments);
+
PageElements.sponsorStart.addEventListener("click", sendSponsorStartMessage);
PageElements.whitelistToggle.addEventListener("change", function () {
if (this.checked) {
@@ -441,7 +447,7 @@ async function runThePopup(messageListener?: MessageListener): Promise {
}
// Sort list by start time
- const segmentTimes = request.sponsorTimes
+ downloadedTimes = request.sponsorTimes
.filter((segment) => {
if (currentSegmentTab === SegmentTab.Segments) {
return segment.actionType !== ActionType.Chapter;
@@ -461,12 +467,18 @@ async function runThePopup(messageListener?: MessageListener): Promise {
container.removeChild(container.firstChild);
}
+ if (downloadedTimes.length > 0) {
+ PageElements.issueReporterImportExport.classList.remove("hidden");
+ } else {
+ PageElements.issueReporterImportExport.classList.add("hidden");
+ }
+
const isVip = Config.config.isVip;
- for (let i = 0; i < segmentTimes.length; i++) {
- const UUID = segmentTimes[i].UUID;
- const locked = segmentTimes[i].locked;
- const category = segmentTimes[i].category;
- const actionType = segmentTimes[i].actionType;
+ for (let i = 0; i < downloadedTimes.length; i++) {
+ const UUID = downloadedTimes[i].UUID;
+ const locked = downloadedTimes[i].locked;
+ const category = downloadedTimes[i].category;
+ const actionType = downloadedTimes[i].actionType;
const segmentSummary = document.createElement("summary");
segmentSummary.className = "segmentSummary";
@@ -478,25 +490,25 @@ async function runThePopup(messageListener?: MessageListener): Promise {
categoryColorCircle.classList.add("sponsorTimesCategoryColorCircle");
let extraInfo = "";
- if (segmentTimes[i].hidden === SponsorHideType.Downvoted) {
+ if (downloadedTimes[i].hidden === SponsorHideType.Downvoted) {
//this one is downvoted
extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDownvote") + ")";
- } else if (segmentTimes[i].hidden === SponsorHideType.MinimumDuration) {
+ } else if (downloadedTimes[i].hidden === SponsorHideType.MinimumDuration) {
//this one is too short
extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDuration") + ")";
- } else if (segmentTimes[i].hidden === SponsorHideType.Hidden) {
+ } else if (downloadedTimes[i].hidden === SponsorHideType.Hidden) {
extraInfo = " (" + chrome.i18n.getMessage("manuallyHidden") + ")";
}
- const name = segmentTimes[i].description || shortCategoryName(category);
+ const name = downloadedTimes[i].description || shortCategoryName(category);
const textNode = document.createTextNode(name + extraInfo);
const segmentTimeFromToNode = document.createElement("div");
- if (segmentTimes[i].actionType === ActionType.Full) {
+ if (downloadedTimes[i].actionType === ActionType.Full) {
segmentTimeFromToNode.innerText = chrome.i18n.getMessage("full");
} else {
- segmentTimeFromToNode.innerText = GenericUtils.getFormattedTime(segmentTimes[i].segment[0], true) +
+ segmentTimeFromToNode.innerText = GenericUtils.getFormattedTime(downloadedTimes[i].segment[0], true) +
(actionType !== ActionType.Poi
- ? " " + chrome.i18n.getMessage("to") + " " + GenericUtils.getFormattedTime(segmentTimes[i].segment[1], true)
+ ? " " + chrome.i18n.getMessage("to") + " " + GenericUtils.getFormattedTime(downloadedTimes[i].segment[1], true)
: "");
}
@@ -551,7 +563,7 @@ async function runThePopup(messageListener?: MessageListener): Promise {
hideButton.id = "sponsorTimesCopyUUIDButtonContainer" + UUID;
hideButton.className = "voteButton";
hideButton.title = chrome.i18n.getMessage("hideSegment");
- if (segmentTimes[i].hidden === SponsorHideType.Hidden) {
+ if (downloadedTimes[i].hidden === SponsorHideType.Hidden) {
hideButton.src = chrome.runtime.getURL("icons/not_visible.svg");
} else {
hideButton.src = chrome.runtime.getURL("icons/visible.svg");
@@ -560,12 +572,12 @@ async function runThePopup(messageListener?: MessageListener): Promise {
const stopAnimation = AnimationUtils.applyLoadingAnimation(hideButton, 0.4);
stopAnimation();
- if (segmentTimes[i].hidden === SponsorHideType.Hidden) {
+ if (downloadedTimes[i].hidden === SponsorHideType.Hidden) {
hideButton.src = chrome.runtime.getURL("icons/visible.svg");
- segmentTimes[i].hidden = SponsorHideType.Visible;
+ downloadedTimes[i].hidden = SponsorHideType.Visible;
} else {
hideButton.src = chrome.runtime.getURL("icons/not_visible.svg");
- segmentTimes[i].hidden = SponsorHideType.Hidden;
+ downloadedTimes[i].hidden = SponsorHideType.Hidden;
}
messageHandler.query({
@@ -576,7 +588,7 @@ async function runThePopup(messageListener?: MessageListener): Promise {
tabs[0].id,
{
message: "hideSegment",
- type: segmentTimes[i].hidden,
+ type: downloadedTimes[i].hidden,
UUID: UUID
}
);
@@ -594,8 +606,8 @@ async function runThePopup(messageListener?: MessageListener): Promise {
voteButtonsContainer.appendChild(upvoteButton);
voteButtonsContainer.appendChild(downvoteButton);
voteButtonsContainer.appendChild(uuidButton);
- if (segmentTimes[i].actionType === ActionType.Skip
- && [SponsorHideType.Visible, SponsorHideType.Hidden].includes(segmentTimes[i].hidden)) {
+ if (downloadedTimes[i].actionType === ActionType.Skip
+ && [SponsorHideType.Visible, SponsorHideType.Hidden].includes(downloadedTimes[i].hidden)) {
voteButtonsContainer.appendChild(hideButton);
}
voteButtonsContainer.appendChild(skipButton);
@@ -949,6 +961,13 @@ async function runThePopup(messageListener?: MessageListener): Promise {
}
}
+ function exportSegments() {
+ copyToClipboard(exportTimes(downloadedTimes));
+
+ const stopAnimation = AnimationUtils.applyLoadingAnimation(PageElements.exportSegmentsButton, 0.3);
+ stopAnimation();
+ }
+
/**
* Converts time in minutes to 2d 5h 25.1
* If less than 1 hour, just returns minutes