diff --git a/src/background.ts b/src/background.ts index 704bc7c..c7af465 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,3 +1,4 @@ +import { consoleLog } from "./log" import { Message, Messages } from "./types/messages" import { Options, Theme } from "./types/option-types" @@ -38,10 +39,7 @@ const handlePageLoaded = (tabUrl: string): void => { try { openTabs.set(tab.id, tab) } catch (e) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - // eslint-disable-next-line no-console - if (DEBUG) console.log(`error adding tab: ${tab.url}`) + consoleLog(`error adding tab: ${tab.url}`) } }) }) @@ -78,7 +76,7 @@ const handleUpdateOptions = (options: Options): void => { {}, () => { if (chrome.runtime.lastError) { - // console.log("update options error", chrome.runtime.lastError, "for tab", tab) + // consoleLog("update options error", chrome.runtime.lastError, "for tab", tab) // if unable to send message, assume closed, stop tracking openTabs.delete(tab.id) } @@ -104,7 +102,7 @@ const handleRumbleThemeChanged = (theme: Theme): void => { {}, () => { if (chrome.runtime.lastError) { - // console.log("update theme error", chrome.runtime.lastError, "for tab", tab) + // consoleLog("update theme error", chrome.runtime.lastError, "for tab", tab) // if unable to send message, assume closed, stop tracking openTabs.delete(tab.id) } diff --git a/src/components/events/send-action.ts b/src/components/events/send-action.ts index 3e73df5..fe22827 100644 --- a/src/components/events/send-action.ts +++ b/src/components/events/send-action.ts @@ -18,7 +18,7 @@ export const sendAction = ( } as Message, () => { if (chrome.runtime.lastError) { - // console.log("action error", chrome.runtime.lastError) + // consoleLog("action error", chrome.runtime.lastError) } }, ) diff --git a/src/components/open-chat/open-chat.scss b/src/components/open-chat/open-chat.scss index f0059f4..9743762 100644 --- a/src/components/open-chat/open-chat.scss +++ b/src/components/open-chat/open-chat.scss @@ -53,16 +53,32 @@ &.theme-light { .chat-history--row { + &.chat-history--subscribed { + background-image: linear-gradient(to right, var(--light-subscribed), var(--light-accent)); + } + &:nth-child(odd) { - background: var(--light-accent2); + background-color: var(--light-accent2); + + &.chat-history--subscribed { + background-image: linear-gradient(to right, var(--light-subscribed), var(--light-accent2)); + } } } } &.theme-dark { .chat-history--row { + &.chat-history--subscribed { + background-image: linear-gradient(to right, var(--dark-subscribed), var(--dark-accent)); + } + &:nth-child(odd) { - background: var(--dark-accent2); + background-color: var(--dark-accent2); + + &.chat-history--subscribed { + background-image: linear-gradient(to right, var(--dark-subscribed), var(--dark-accent2)); + } } } } diff --git a/src/components/rants/handler-init.ts b/src/components/rants/handler-init.ts index efa3d11..0bc73c8 100644 --- a/src/components/rants/handler-init.ts +++ b/src/components/rants/handler-init.ts @@ -1,4 +1,5 @@ import { saveBadges } from "../../cache" +import { consoleError, consoleLog } from "../../log" import { CacheBadge } from "../../types/cache" import { RantsConfig, RumbleBadge, RumbleConfig, RumbleEventInit, RumbleEventType } from "../../types/rumble-types" @@ -55,10 +56,7 @@ const parseBadgeDefinitions = (badges: { [key: string]: RumbleBadge }): void => const parseRants = (rants: RantsConfig): void => { const { levels } = rants if (levels === null) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - // eslint-disable-next-line no-console - if (DEBUG) console.log("Invalid rants config, no levels", levels) + consoleLog("Invalid rants config, no levels", levels) return } parseLevels(levels) @@ -74,10 +72,7 @@ const parseConfig = (config: RumbleConfig): void => { parseBadgeDefinitions(badges) if (rants === null) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - // eslint-disable-next-line no-console - if (DEBUG) console.log("Invalid Rumble config, no rants", config) + consoleLog("Invalid Rumble config, no rants", config) return } parseRants(rants) @@ -91,9 +86,7 @@ const parseConfig = (config: RumbleConfig): void => { export const initEventHandler = (eventData: RumbleEventInit, videoId: string): void => { const { type } = eventData if (type !== RumbleEventType.init) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if (DEBUG) console.error(`Invalid event type passed to init event handler: ${type}`) + consoleError(`Invalid event type passed to init event handler: ${type}`) return } diff --git a/src/components/rants/handler-message.ts b/src/components/rants/handler-message.ts index 3deafa2..d0c36ff 100644 --- a/src/components/rants/handler-message.ts +++ b/src/components/rants/handler-message.ts @@ -1,3 +1,4 @@ +import { consoleError } from "../../log" import { RumbleEventMessages, RumbleEventType } from "../../types/rumble-types" import { parseMessages } from "./messages" @@ -11,9 +12,7 @@ import { parseUsers } from "./users" export const messagesEventHandler = (eventData: RumbleEventMessages, videoId: string): void => { const { type } = eventData if (type !== RumbleEventType.messages) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if (DEBUG) console.error(`Invalid event type passed to messages event handler: ${type}`) + consoleError(`Invalid event type passed to messages event handler: ${type}`) return } diff --git a/src/components/rants/levels.ts b/src/components/rants/levels.ts index c16a610..d5e875c 100644 --- a/src/components/rants/levels.ts +++ b/src/components/rants/levels.ts @@ -3,6 +3,89 @@ import { RumbleRantLevel } from "../../types/rumble-types" import { setRantLevelValues } from "./rant" +const fallbackLevels: Array = [ + { + price_dollars: 1, + duration: 120, + colors: { fg: "white", main: "#4a90e2", bg2: "#4382cb" }, + ids: ["rant1", "rant1qa", "rant1dev"], + }, + { + price_dollars: 2, + duration: 180, + colors: { fg: "black", main: "#b8e986", bg2: "#a6d279" }, + ids: ["rant2", "rant2qa", "rant2dev"], + }, + { + price_dollars: 5, + duration: 300, + colors: { fg: "black", main: "#f8e71c", bg2: "#dfd019" }, + ids: ["rant5", "rant5qa", "rant5dev"], + }, + { + price_dollars: 10, + duration: 600, + colors: { fg: "black", main: "#f5a623", bg2: "#dd9520" }, + ids: ["rant10", "rant10qa", "rant10dev"], + }, + { + price_dollars: 20, + duration: 1200, + colors: { fg: "white", main: "#bd10e0", bg2: "#aa0eca" }, + ids: ["rant20", "rant20qa", "rant20dev"], + }, + { + price_dollars: 50, + duration: 2400, + colors: { fg: "white", main: "#9013fe", bg2: "#8211e5" }, + ids: ["rant50", "rant50qa", "rant50dev"], + }, + { + price_dollars: 100, + duration: 3600, + colors: { fg: "white", main: "#d0021b", bg2: "#bb0218" }, + ids: ["rant100", "rant100qa", "rant100dev"], + }, + { + price_dollars: 200, + duration: 7200, + colors: { fg: "white", main: "#d0021b", bg2: "#bb0218" }, + ids: ["rant200", "rant200qa", "rant200dev"], + }, + { + price_dollars: 300, + duration: 10800, + colors: { fg: "white", main: "#d0021b", bg2: "#bb0218" }, + ids: ["rant300", "rant300qa", "rant300dev"], + }, + { + price_dollars: 400, + duration: 14400, + colors: { fg: "white", main: "#d0021b", bg2: "#bb0218" }, + ids: ["rant400", "rant400qa", "rant400dev"], + }, + { + price_dollars: 500, + duration: 18000, + colors: { fg: "white", main: "#d0021b", bg2: "#bb0218" }, + ids: ["rant500", "rant500qa", "rant500dev"], + }, +] + +/** + * Generate CSS style data for Rumble Rants based on the level data received + * @param levelInfo The received Rant level data + * @returns The CSS style lines + */ +const generateStyleLines = (levelInfo: RumbleRantLevel): Array => { + const externalChat = `.external-chat[data-level="${levelInfo.price_dollars}"]` + return [ + `${externalChat} { background: ${levelInfo.colors.bg2} !important; }`, + `${externalChat} * { color: ${levelInfo.colors.fg} !important; }`, + `${externalChat} .rant-amount { background: ${levelInfo.colors.main} !important; }`, + ] +} + /** * Parse level data from received message * @param levels received level data @@ -13,71 +96,24 @@ export const parseLevels = (levels: Array, fallback: boolean = return } - const styleLines = [] + const styleLines: Array = [] + let levelList: Array if (fallback || levels.length === 0) { - setRantLevelValues([1, 2, 5, 10, 20, 50, 100, 200, 300, 400, 500]) - styleLines.push( - ...[ - '.external-chat[data-level="1"] { background: #4382CB !important; }', - '.external-chat[data-level="1"] * {color: white !important; }', - '.external-chat[data-level="1"] .rant-amount { background: #4A90E2 !important; }', - '.external-chat[data-level="2"] { background: #A6D279 !important; }', - '.external-chat[data-level="2"] * {color: black !important; }', - '.external-chat[data-level="2"] .rant-amount { background: #B8E986 !important; }', - '.external-chat[data-level="5"] { background: #DFD019 !important; }', - '.external-chat[data-level="5"] * {color: black !important; }', - '.external-chat[data-level="5"] .rant-amount { background: #F8E71C !important; }', - '.external-chat[data-level="10"] { background: #DD9520 !important; }', - '.external-chat[data-level="10"] * {color: black !important; }', - '.external-chat[data-level="10"] .rant-amount { background: #F5A623 !important; }', - '.external-chat[data-level="20"] { background: #AA0ECA !important; }', - '.external-chat[data-level="20"] * {color: white !important; }', - '.external-chat[data-level="20"] .rant-amount { background: #BD10E0 !important; }', - '.external-chat[data-level="50"] { background: #8211E5 !important; }', - '.external-chat[data-level="50"] * {color: white !important; }', - '.external-chat[data-level="50"] .rant-amount { background: #9013FE !important; }', - '.external-chat[data-level="100"] { background: #BB0218 !important; }', - '.external-chat[data-level="100"] * {color: white !important; }', - '.external-chat[data-level="100"] .rant-amount { background: #D0021B !important; }', - '.external-chat[data-level="200"] { background: #BB0218 !important; }', - '.external-chat[data-level="200"] * {color: white !important; }', - '.external-chat[data-level="200"] .rant-amount { background: #D0021B !important; }', - '.external-chat[data-level="300"] { background: #BB0218 !important; }', - '.external-chat[data-level="300"] * {color: white !important; }', - '.external-chat[data-level="300"] .rant-amount { background: #D0021B !important; }', - '.external-chat[data-level="400"] { background: #BB0218 !important; }', - '.external-chat[data-level="400"] * {color: white !important; }', - '.external-chat[data-level="400"] .rant-amount { background: #D0021B !important; }', - '.external-chat[data-level="500"] { background: #BB0218 !important; }', - '.external-chat[data-level="500"] * {color: white !important; }', - '.external-chat[data-level="500"] .rant-amount { background: #D0021B !important; }', - ], - ) + levelList = fallbackLevels } else { - const rantLevelValues = [] - levels.forEach((rantLevel: RumbleRantLevel) => { - rantLevelValues.push(rantLevel.price_dollars) - const externalChat = `.external-chat[data-level="${rantLevel.price_dollars}"]` - styleLines.push( - ...[ - `${externalChat} {`, - `background: ${rantLevel.colors.bg2} !important;`, - `}`, - `${externalChat} * {`, - `color: ${rantLevel.colors.fg} !important;`, - `}`, - `${externalChat} .rant-amount {`, - `background: ${rantLevel.colors.main} !important;`, - `}`, - ], - ) - }) - - rantLevelValues.sort() - setRantLevelValues(rantLevelValues) + levelList = levels } + const rantLevelValues: Array = [] + levelList.forEach((rantLevel) => { + rantLevelValues.push(rantLevel.price_dollars) + styleLines.push(...generateStyleLines(rantLevel)) + }) + + rantLevelValues.sort() + setRantLevelValues(rantLevelValues) + const levelStyle = document.createElement("style") as HTMLStyleElement levelStyle.id = CONSTS.LEVEL_STYLE_ID levelStyle.appendChild(document.createTextNode(styleLines.join(" "))) diff --git a/src/components/rants/rant.scss b/src/components/rants/rant.scss index 78e9b2e..c0bcf1d 100644 --- a/src/components/rants/rant.scss +++ b/src/components/rants/rant.scss @@ -105,7 +105,7 @@ } .notification-badge { - height: 5rem; + height: 3rem; width: auto; margin-left: auto; } diff --git a/src/components/rants/rants.ts b/src/components/rants/rants.ts index b2952f2..794f0e5 100644 --- a/src/components/rants/rants.ts +++ b/src/components/rants/rants.ts @@ -1,4 +1,5 @@ import { getLastWidth, getSortOrder, getStream, setLastWidth } from "../../cache" +import { consoleLog } from "../../log" import { triggerOpenOptionsPage } from "../events/events" import { updateTheme } from "../../theme" import { CONSTS } from "../../types/consts" @@ -78,10 +79,7 @@ export const addRantStatsSidebar = async ( popup: boolean = false, cache: boolean = false, ): Promise => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - // eslint-disable-next-line no-console - if (DEBUG) console.log(`open rant stats sidebar for video ${videoId}`) + consoleLog(`open rant stats sidebar for video ${videoId}`) const existingSidebar = document.getElementById(CONSTS.SIDEBAR_ID) if (existingSidebar !== null && !popup) { @@ -107,7 +105,7 @@ export const addRantStatsSidebar = async (

Rant Stats

${refreshIcon.outerHTML} ` + + document.title = `${title} | ${DEFAULT_TITLE}` } } /** * Populate the list of all cached streams + * @returns A void promise */ -const populateStreamList = (): void => { +const populateStreamList = async (): Promise => { streamSelect.innerHTML = "" const defaultOption = document.createElement("option") as HTMLOptionElement defaultOption.value = "-1" defaultOption.text = "None" streamSelect.appendChild(defaultOption) - getAllStreams().then((cachedStreams) => { + return getAllStreams().then((cachedStreams) => { cachedStreams.sort((a, b) => { return parseInt(a.videoId, 10) - parseInt(b.videoId, 10) }) @@ -117,7 +133,7 @@ const populateStreamList = (): void => { const optGroups: Map> = new Map>() cachedStreams.forEach((cachedStream) => { - const option = document.createElement("option") as HTMLOptionElement + const option = document.createElement("option") option.value = cachedStream.videoId const time = new Date(cachedStream.time) option.text = `${time.toLocaleDateString()} - ${cachedStream.title}` @@ -149,7 +165,7 @@ const populateStreamList = (): void => { const refresh = (): void => { const videoId = getVideoIdFromDiv() clearRants() - populateStreamList() + populateStreamList().then() if (videoId === null) { return } @@ -173,7 +189,8 @@ const deleteStream = (): void => { if (doDelete) { removeStream(videoId).then(() => { clearRants() - populateStreamList() + populateStreamList().then() + setVideoParam("") }) } }) @@ -200,21 +217,36 @@ cleanHistory().then() * Initialize the page */ const populateView = async (): Promise => { - registerThemeWatcher() - updateTheme().then() - setLastSortOrder(await getSortOrder()) + registerSystemColorSchemeWatcher() + await updateTheme() + + getOptions().then((options: Options) => { + setLastSortOrder(options.sortOrder as SortOrder) + }) - populateStreamList() + await populateStreamList() parseLevels([], true) } -populateView().then() +populateView().then(async () => { + const params = new URLSearchParams(window.location.search) + const defaultVideoId = params.get("video") + if (defaultVideoId !== undefined && defaultVideoId !== "-1") { + const matches = Array.from(streamSelect.options).filter((option) => option.value === defaultVideoId) + if (matches.length > 0) { + streamSelect.value = defaultVideoId + await loadStream(defaultVideoId) + } + } +}) document.addEventListener("DOMContentLoaded", () => { streamSelect.addEventListener("change", (event) => { const target = event.target as HTMLSelectElement - loadStream(target.value).then() + loadStream(target.value).then(() => { + setVideoParam(target.value) + }) }) }) document.addEventListener("click", async (event) => { diff --git a/src/styles.scss b/src/styles.scss index 48bd69a..644db5a 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -10,6 +10,9 @@ --dark-accent: rgb(6, 23, 38); --dark-accent2: rgb(26, 42, 55); + --light-subscribed: #f7c0c5; + --dark-subscribed: #481d2d; + --heading-font: "sans-serif"; --data-font: "sans-serif"; } diff --git a/src/theme.ts b/src/theme.ts index 12c975d..5d03f79 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -82,7 +82,7 @@ export const updateThemeStyle = (themePreference: Theme): void => { * * Will update theme of data to match OS preference. */ -export const registerThemeWatcher = (): void => { +export const registerSystemColorSchemeWatcher = (): void => { window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => { const newTheme = event.matches ? Theme.Dark : Theme.Light updateThemeStyle(newTheme) @@ -139,8 +139,10 @@ const headObserverCallback = (mutations: Array): void => { }) } -// setup observer to watch for changes to Rumble.com . Used to catch theme changes -if (window.location.host === "rumble.com") { +/** + * Register observer for detecting the Rumble theme + */ +export const registerRumbleThemeObserver = (): void => { const headObserver = new MutationObserver(headObserverCallback) headObserver.observe(document.documentElement, { childList: false, attributes: true, subtree: false }) } diff --git a/src/types/consts.ts b/src/types/consts.ts index 07894e0..9e079b4 100644 --- a/src/types/consts.ts +++ b/src/types/consts.ts @@ -2,8 +2,6 @@ * Define strings constants used throughout extension */ export enum CONSTS { - // BYTES_PERCENTAGE_ID = 'bytes-percentage', - // BYTES_TOTAL_ID = 'bytes-total', BYTES_USE_ID = "bytes-use", CACHE_MESSAGE_COUNT_ID = "cache-message-count", CACHE_MESSAGE_ID = "cache-message", diff --git a/src/types/option-types.ts b/src/types/option-types.ts index 91e7dc2..91104ac 100644 --- a/src/types/option-types.ts +++ b/src/types/option-types.ts @@ -51,8 +51,6 @@ export type Options = { * Saved as string but should be cast to {@link SortOrder} */ sortOrder: string // SortOrder - // cache: boolean - // sync: boolean /** * The number of days to keep stream in cache */ @@ -73,8 +71,6 @@ export type Options = { */ export const defaultOptions: Options = { sortOrder: SortOrder.NewToOld.toString(), - // cache: true, - // sync: false, historyDays: 30, theme: Theme.Rumble.toString(), asPopup: false, diff --git a/webpack.config.js b/webpack.config.js index b693726..137fc6e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -53,10 +53,6 @@ module.exports = (env, argv) => { from: "src/pages/rants/rants.html", to: "pages/rants", }, - { - from: "src/components/chat-watcher/muteWords.txt", - to: "components/chat-watcher/", - }, ], }), ],