Skip to content

Commit

Permalink
Added a welcome page shown on install (#248)
Browse files Browse the repository at this point in the history
  • Loading branch information
NikkelM authored Dec 2, 2023
1 parent eebdeba commit acc577b
Show file tree
Hide file tree
Showing 17 changed files with 402 additions and 200 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# Changelog

## v2.4.0 (Unreleased)
## v3.0.0 (Unreleased)

<!--Releasenotes start-->
- Shorts pages are now supported! Shuffle buttons can now be found on all shorts pages.
- Added a new option to shuffle only from shorts.
- Added a 'welcome' page that will be shown when you first install the extension and guide you through the first steps.
- The popup now also works as a standalone options page.
- When watching shorts, the extension will now update the last visited channel in the popup.
- Reduced the time it takes to shuffle, as the extension now uses a more sophisticated way to decide whether or not to check if a video has been deleted or not.
- Reduced the time it takes to shuffle, as the extension now uses a new way to decide whether or not to check if a video has been deleted or not.
- The playlist that is opened when shuffling (if the option is enabled) now has a better title and tooltip.
- The shuffle button now feels more responsive when clicked, and will give more information if it takes a bit longer to shuffle.
- The popup now feels smoother in some places, and will make it clearer when some inputs are invalid.
- Fixed bugs that would prevent some initialization logic to run after the extension is updated.
<!--Releasenotes end-->
Expand Down
12 changes: 8 additions & 4 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import { configSync, setSyncStorageValue } from "./chromeStorage.js";
// Check whether a new version was installed
async function initExtension() {
const manifestData = chrome.runtime.getManifest();
if (configSync.previousVersion < manifestData.version) {
if (configSync.previousVersion === null) {
console.log(`Extension was installed for the first time (v${manifestData.version})`);
await setSyncStorageValue("previousVersion", manifestData.version);
await chrome.tabs.create({ url: "html/welcome.html" });
} else if (configSync.previousVersion < manifestData.version) {
await handleExtensionUpdate(manifestData, configSync.previousVersion);
}

Expand Down Expand Up @@ -67,9 +71,9 @@ async function handleExtensionUpdate(manifestData, previousVersion) {
}

async function handleVersionSpecificUpdates(previousVersion) {
// v2.4.0 changed the data type for the shuffleIgnoreShortsOption from boolean to number
if (previousVersion < "2.4.0") {
console.log("Updating sync storage to v2.4.0 format...");
// v3.0.0 changed the data type for the shuffleIgnoreShortsOption from boolean to number
if (previousVersion < "3.0.0") {
console.log("Updating sync storage to v3.0.0 format...");
const syncStorageContents = await chrome.storage.sync.get();
if (syncStorageContents["shuffleIgnoreShortsOption"] == true) {
await setSyncStorageValue("shuffleIgnoreShortsOption", 2);
Expand Down
6 changes: 2 additions & 4 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,13 @@ export const configSyncDefaults = {
// For april fools: Will be the number of the year in which the user was last rickrolled (we only want to rickroll the user once per year)
"wasLastRickRolledInYear": "1970",
// Used when updating the extension
/* c8 ignore next */
"previousVersion": typeof chrome !== "undefined" ? chrome.runtime.getManifest().version : "0",
"previousVersion": null,
};

export const shufflingHints = [
// General extension hints
"The extension adds a 'Shuffle' button to all channel, video and shorts pages on YouTube, this button has the same behaviour as shuffling from the popup!",
"The extension adds a 'Shuffle' button to all channel, video and shorts pages on YouTube. This button has the same behaviour as shuffling from the popup!",
"If you are the first person to shuffle from a channel, the video ID's of that channel are saved both locally and in a remote database for other users to use!",
"Try using the extension on April 1st - maybe something unexpected will happen!",
"The extension does not collect any personal information, it only stores video ID's of channels you shuffle from, without linking them back to you!",
"You only have a certain number of requests to the YouTube API per day. You can remove this limit by providing your own API key in the popup!",
"The extension popup shows you how many videos you have shuffled so far!",
Expand Down
12 changes: 10 additions & 2 deletions src/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,18 @@ async function shuffleVideos() {

// Only use this text if the button is the large shuffle button, the small one only has space for an icon
if (shuffleButtonTextElement.id.includes("large-shuffle-button")) {
setDOMTextWithDelay(shuffleButtonTextElement, "\xa0Shuffling...", 1000, () => { return ((shuffleButtonTextElement.innerText === "\xa0Shuffle" || shuffleButtonTextElement.innerText === "\xa0Fetching: 100%") && !hasBeenShuffled); });
shuffleButtonTextElement.innerText = "\xa0Shuffling...";
setDOMTextWithDelay(shuffleButtonTextElement, "\xa0Still on it...", 5000, () => { return ((shuffleButtonTextElement.innerText === "\xa0Shuffling..." || shuffleButtonTextElement.innerText === "\xa0Fetching: 100%") && !hasBeenShuffled); });
if (configSync.shuffleIgnoreShortsOption != "1") {
setDOMTextWithDelay(shuffleButtonTextElement, "\xa0Sorting shorts...", 8000, () => { return ((shuffleButtonTextElement.innerText === "\xa0Still on it..." || shuffleButtonTextElement.innerText === "\xa0Fetching: 100%") && !hasBeenShuffled); });
setDOMTextWithDelay(shuffleButtonTextElement, "\xa0Sorting shorts...", 10000, () => { return ((shuffleButtonTextElement.innerText === "\xa0Still on it..." || shuffleButtonTextElement.innerText === "\xa0Fetching: 100%") && !hasBeenShuffled); });
if (configSync.shuffleIgnoreShortsOption == "2") {
setDOMTextWithDelay(shuffleButtonTextElement, "\xa0Lots of shorts...", 20000, () => { return ((shuffleButtonTextElement.innerText === "\xa0Sorting shorts..." || shuffleButtonTextElement.innerText === "\xa0Fetching: 100%") && !hasBeenShuffled); });
} else if (configSync.shuffleIgnoreShortsOption == "0") {
setDOMTextWithDelay(shuffleButtonTextElement, "\xa0Not many shorts...", 20000, () => { return ((shuffleButtonTextElement.innerText === "\xa0Sorting shorts..." || shuffleButtonTextElement.innerText === "\xa0Fetching: 100%") && !hasBeenShuffled); });
}
setDOMTextWithDelay(shuffleButtonTextElement, "\xa0Still sorting...", 35000, () => { return ((shuffleButtonTextElement.innerText === "\xa0Lots of shorts..." || shuffleButtonTextElement.innerText === "\xa0Not many shorts..." || shuffleButtonTextElement.innerText === "\xa0Fetching: 100%") && !hasBeenShuffled); });
} else {
setDOMTextWithDelay(shuffleButtonTextElement, "\xa0Still shuffling...", 20000, () => { return ((shuffleButtonTextElement.innerText === "\xa0Still on it..." || shuffleButtonTextElement.innerText === "\xa0Fetching: 100%") && !hasBeenShuffled); });
}
} else {
let iterationsWaited = 0;
Expand Down
1 change: 1 addition & 0 deletions src/domManipulation.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export function buildShuffleButton(pageType, channelId, clickHandler) {
switch (pageType) {
case "channel":
buttonDivID = "youtube-random-video-large-shuffle-button-channel";
buttonDivExtraStyle = "margin-left: 14px;";
buttonDivOwner = [document.getElementById("channel-header").querySelector("#inner-header-container").children.namedItem("buttons")];
break;
case "video":
Expand Down
16 changes: 2 additions & 14 deletions src/html/changelog.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Contains logic for the "Changelog" page
import { delay } from "../utils.js";
import { displayShufflingHint } from "./htmlUtils.js";
import { buildShufflingHints } from "./htmlUtils.js";

// ----- Setup -----
const domElements = getDomElements();
await buildShufflingHints();
await buildShufflingHints(domElements);

// --- Set headers ---
const currentVersion = chrome.runtime.getManifest().version;
Expand Down Expand Up @@ -63,8 +63,6 @@ async function fetchChangelog(forVersion = `v${currentVersion}`) {
// Get all relevant DOM elements
function getDomElements() {
return {
// The div containing all other elements
randomYoutubeVideo: document.getElementById("randomYoutubeVideo"),
// The document heading with the current version
updateHeading: document.getElementById("updateHeading"),
// Text that is shown if there is no changelog for the currently installed version
Expand All @@ -86,7 +84,6 @@ function getDomElements() {
}
}


// ----- Changelog -----
async function updateChangelog(forVersion = `v${currentVersion}`) {
if (changelogText === null) {
Expand Down Expand Up @@ -131,13 +128,4 @@ async function displayErrorAfterWaiting(ms = 2000) {
if (domElements.belowHeadingDiv.classList.contains("hidden")) {
domElements.genericErrorDiv.classList.remove("hidden");
}
}

// ----- Shuffling Hints -----
async function buildShufflingHints() {
let currentHint = await displayShufflingHint(domElements.shufflingHintP);
// Add click listener to the "New hint" button
domElements.nextHintButton.addEventListener("click", async function () {
currentHint = await displayShufflingHint(domElements.shufflingHintP, currentHint);
});
}
10 changes: 9 additions & 1 deletion src/html/htmlUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ import { shufflingHints } from "../config.js";

// ---------- Public ----------
// ----- Shuffling Hints -----
export async function displayShufflingHint(displayElement, currentHintIndex = null) {
export async function buildShufflingHints(domElements) {
let currentHint = await displayShufflingHint(domElements.shufflingHintP);
// Add click listener to the "New hint" button
domElements.nextHintButton.addEventListener("click", async function () {
currentHint = await displayShufflingHint(domElements.shufflingHintP, currentHint);
});
}

async function displayShufflingHint(displayElement, currentHintIndex = null) {
// Choose a (new) random hint from the JSON file and display it
let randomHintIndex = currentHintIndex;
while (randomHintIndex === currentHintIndex) {
Expand Down
38 changes: 29 additions & 9 deletions src/html/popup/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@ import { configSync, setSyncStorageValue, removeSyncStorageValue } from "../../c
import { manageDependents, manageDbOptOutOption, validateApiKey, setChannelSetting, removeChannelSetting, updateFYIDiv } from "./popupUtils.js";
import { tryFocusingTab } from "../htmlUtils.js";

// ----- Setup -----
const isPopup = chrome.extension.getViews({ type: "popup" }).length > 0;
if (isPopup) {
const tabs = await chrome.tabs.query({});
const activeTab = tabs.filter(tab => tab.active);
const popupURL = chrome.runtime.getURL("html/popup.html");

// If the options page is focused, do not open the popup
if (activeTab[0].url === popupURL) {
window.close();
} else {
// Else, close the options page in the background
for (const tab of tabs) {
if (tab.url === popupURL) {
chrome.tabs.remove(tab.id);
break;
}
}
}
}

const domElements = getPopupDomElements();
await setPopupDomElementValuesFromConfig(domElements);
await setPopupDomElemenEventListeners(domElements);
Expand Down Expand Up @@ -351,24 +372,24 @@ async function setPopupDomElemenEventListeners(domElements) {

// Popup shuffle button
domElements.popupShuffleButton.addEventListener("click", async function () {
const tabUrl = chrome.runtime.getURL("html/shufflingPage.html");
const shufflingPage = chrome.runtime.getURL("html/shufflingPage.html");

// Get the status of the shufflingPage, if it exists
const shufflingPageIsShuffling = await chrome.runtime.sendMessage({ command: "getShufflingPageShuffleStatus" });
// If the page is not shuffling, close it if it exists, as that means an error was encountered
if (!shufflingPageIsShuffling) {
const tabs = await chrome.tabs.query({});
for (const tab of tabs) {
if (tab.url === tabUrl) {
if (tab.url === shufflingPage) {
chrome.tabs.remove(tab.id);
break;
}
}
}

let mustOpenTab = await tryFocusingTab(tabUrl);
let mustOpenTab = await tryFocusingTab(shufflingPage);
if (mustOpenTab) {
window.open(tabUrl, "_blank");
await chrome.tabs.create({ url: shufflingPage });
}

// Close the popup
Expand All @@ -379,14 +400,13 @@ async function setPopupDomElemenEventListeners(domElements) {
domElements.viewChangelogButton.addEventListener("click", async function () {
await setSyncStorageValue("lastViewedChangelogVersion", chrome.runtime.getManifest().version);

const tabUrl = chrome.runtime.getURL("html/changelog.html");
let mustOpenTab = await tryFocusingTab(tabUrl);
const changelogPage = chrome.runtime.getURL("html/changelog.html");
let mustOpenTab = await tryFocusingTab(changelogPage);
if (mustOpenTab) {
window.open(tabUrl, "_blank");
await chrome.tabs.create({ url: changelogPage });
}

// Close the popup
window.close();
domElements.viewChangelogButton.classList.remove("highlight-green");
});
}

Expand Down
56 changes: 29 additions & 27 deletions src/html/shufflingPage.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Contains logic for the "shufflingPage" that is opened when the user clicks the "Shuffle" button from the popup
import { delay, setDOMTextWithDelay } from "../utils.js";
import { configSync, setSyncStorageValue } from "../chromeStorage.js";
import { displayShufflingHint, tryFocusingTab } from "./htmlUtils.js";
import { buildShufflingHints, tryFocusingTab } from "./htmlUtils.js";
import { chooseRandomVideo } from "../shuffleVideo.js";

// ----- Setup -----
Expand All @@ -20,6 +20,19 @@ try {
const port = chrome.runtime.connect({ name: "shufflingPage" });

const domElements = getDomElements();
await setDomElemenEventListeners(domElements);

// If this page is open, it means the user has clicked the shuffle button
shuffleButtonClicked();

// If the current extension version is newer than configSync.lastViewedChangelogVersion, highlight the changelog button
if (configSync.lastViewedChangelogVersion !== chrome.runtime.getManifest().version) {
domElements.viewChangelogButton.classList.add("highlight-green");
}

await buildShufflingHints(domElements);
// Only show the contents of the page after a short delay, so that the user doesn't see the page at all for short loading times
waitUntilShowingDivContents();

// Get all relevant DOM elements
function getDomElements() {
Expand All @@ -45,18 +58,8 @@ function getDomElements() {
}
}

// If the current extension version is newer than configSync.lastViewedChangelogVersion, highlight the changelog button
if (configSync.lastViewedChangelogVersion !== chrome.runtime.getManifest().version) {
domElements.viewChangelogButton.classList.add("highlight-green");
}

// Set event listeners for DOM elements
async function setDomElemenEventListeners(domElements) {
// Add click listener to the "New hint" button
domElements.nextHintButton.addEventListener("click", async function () {
currentHint = await displayShufflingHint(domElements.shufflingHintP, currentHint);
});

// View changelog button
domElements.viewChangelogButton.addEventListener("click", async function () {
await setSyncStorageValue("lastViewedChangelogVersion", chrome.runtime.getManifest().version);
Expand All @@ -69,29 +72,28 @@ async function setDomElemenEventListeners(domElements) {
});
}

await setDomElemenEventListeners(domElements);

// ----- Main -----
// If this page is open, it means the user has clicked the shuffle button
shuffleButtonClicked();

// Only show the contents of the page after a short delay, so that the user doesn't see the page at all for short loading times
waitUntilShowingDivContents();

// ----- Shuffling Hints -----
let currentHint = await displayShufflingHint(domElements.shufflingHintP);

// Called when the randomize-button from the popup is clicked
async function shuffleButtonClicked() {
try {
domElements.shufflingFromChannelHeading.innerText = configSync.currentChannelName;
const shuffleButtonTextElement = domElements.fetchPercentageNotice;

setDOMTextWithDelay(domElements.fetchPercentageNotice, "Applying filters...", 2000, () => { console.log(domElements.fetchPercentageNotice.innerText); return (domElements.fetchPercentageNotice.innerText === "Please wait..." || domElements.fetchPercentageNotice.innerText === "\xa0Fetching: 100%"); });
setDOMTextWithDelay(domElements.fetchPercentageNotice, "Should be done soon...", 5000, () => { return (domElements.fetchPercentageNotice.innerText === "Applying filters..." || domElements.fetchPercentageNotice.innerText === "\xa0Fetching: 100%"); });
setDOMTextWithDelay(shuffleButtonTextElement, "Applying filters...", 4000, () => { return ((shuffleButtonTextElement.innerText === "Please wait..." || shuffleButtonTextElement.innerText === "Fetching: 100%")); });
setDOMTextWithDelay(shuffleButtonTextElement, "Should be done soon...", 8000, () => { return (shuffleButtonTextElement.innerText === "Applying filters..." || shuffleButtonTextElement.innerText === "Fetching: 100%"); });

if (configSync.shuffleIgnoreShortsOption != "1") {
setDOMTextWithDelay(domElements.fetchPercentageNotice, "Sorting shorts...", 9000, () => { return (domElements.fetchPercentageNotice.innerText === "Should be done soon..." || domElements.fetchPercentageNotice.innerText === "\xa0Fetching: 100%"); });
setDOMTextWithDelay(shuffleButtonTextElement, "Sorting shorts...", 12000, () => { return ((shuffleButtonTextElement.innerText === "Should be done soon..." || shuffleButtonTextElement.innerText === "Fetching: 100%")); });
if (configSync.shuffleIgnoreShortsOption == "2") {
setDOMTextWithDelay(shuffleButtonTextElement, "Lots of shorts...", 20000, () => { return ((shuffleButtonTextElement.innerText === "Sorting shorts..." || shuffleButtonTextElement.innerText === "Fetching: 100%")); });
} else if (configSync.shuffleIgnoreShortsOption == "0") {
setDOMTextWithDelay(shuffleButtonTextElement, "Not many shorts...", 20000, () => { return ((shuffleButtonTextElement.innerText === "Sorting shorts..." || shuffleButtonTextElement.innerText === "Fetching: 100%")); });
}
setDOMTextWithDelay(shuffleButtonTextElement, "Still sorting...", 35000, () => { return ((shuffleButtonTextElement.innerText === "Lots of shorts..." || shuffleButtonTextElement.innerText === "Not many shorts..." || shuffleButtonTextElement.innerText === "Fetching: 100%")); });
} else {
setDOMTextWithDelay(shuffleButtonTextElement, "Still shuffling...", 20000, () => { return ((shuffleButtonTextElement.innerText === "Should be done soon..." || shuffleButtonTextElement.innerText === "Fetching: 100%")); });
}
await chooseRandomVideo(configSync.currentChannelId, true, domElements.fetchPercentageNotice);

await chooseRandomVideo(configSync.currentChannelId, true, shuffleButtonTextElement);

// Focus this tab when the shuffle completes
chrome.tabs.query({ url: chrome.runtime.getURL('html/shufflingPage.html') }, function (tabs) {
Expand Down
Loading

0 comments on commit acc577b

Please sign in to comment.