diff options
author | Ajay <[email protected]> | 2022-01-05 02:35:58 -0500 |
---|---|---|
committer | Ajay <[email protected]> | 2022-01-05 02:35:58 -0500 |
commit | 2883a50f2796628960775c7d241f44f58ac52ef5 (patch) | |
tree | cec0ec5f602bec59807149080a03dff9ea48e4c3 | |
parent | d36b4a54f3154fdcfbd2e5f059c401bdd6ceeee3 (diff) | |
download | SponsorBlock-2883a50f2796628960775c7d241f44f58ac52ef5.tar.gz SponsorBlock-2883a50f2796628960775c7d241f44f58ac52ef5.zip |
Add pill beside title for full video reports
-rw-r--r-- | public/content.css | 12 | ||||
-rw-r--r-- | src/components/CategoryPillComponent.tsx | 47 | ||||
-rw-r--r-- | src/content.ts | 22 | ||||
-rw-r--r-- | src/render/CategoryPill.tsx | 71 | ||||
-rw-r--r-- | src/utils.ts | 22 | ||||
-rw-r--r-- | src/utils/genericUtils.ts | 26 |
6 files changed, 178 insertions, 22 deletions
diff --git a/public/content.css b/public/content.css index b9f19080..98a6b4f2 100644 --- a/public/content.css +++ b/public/content.css @@ -613,3 +613,15 @@ input::-webkit-inner-spin-button { line-height: 1.5em; } +.sponsorBlockCategoryPill { + border-radius: 25px; + padding-left: 8px; + padding-right: 8px; + margin-right: 3px; + color: white; +} + +.sponsorBlockCategoryPillTitleSection { + display: flex; + align-items: center; +}
\ No newline at end of file diff --git a/src/components/CategoryPillComponent.tsx b/src/components/CategoryPillComponent.tsx new file mode 100644 index 00000000..0f20c82f --- /dev/null +++ b/src/components/CategoryPillComponent.tsx @@ -0,0 +1,47 @@ +import * as React from "react"; +import Config from "../config"; +import { SponsorTime } from "../types"; + +export interface CategoryPillProps { + +} + +export interface CategoryPillState { + segment?: SponsorTime; + show: boolean; +} + +class CategoryPillComponent extends React.Component<CategoryPillProps, CategoryPillState> { + + constructor(props: CategoryPillProps) { + super(props); + + this.state = { + segment: null, + show: false + }; + } + + render(): React.ReactElement { + const style: React.CSSProperties = { + backgroundColor: Config.config.barTypes["preview-" + this.state.segment?.category]?.color, + display: this.state.show ? "flex" : "none" + } + + return ( + <span style={style} + className="sponsorBlockCategoryPill" > + <span className="sponsorBlockCategoryPillTitleSection"> + <img className="sponsorSkipLogo sponsorSkipObject" + src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}> + </img> + <span className="sponsorBlockCategoryPillTitle"> + {chrome.i18n.getMessage("category_" + this.state.segment?.category)} + </span> + </span> + </span> + ); + } +} + +export default CategoryPillComponent; diff --git a/src/content.ts b/src/content.ts index 681ef10c..f3d589cc 100644 --- a/src/content.ts +++ b/src/content.ts @@ -18,6 +18,7 @@ import { SkipButtonControlBar } from "./js-components/skipButtonControlBar"; import { Tooltip } from "./render/Tooltip"; import { getStartTimeFromUrl } from "./utils/urlParser"; import { getControls } from "./utils/pageUtils"; +import { CategoryPill } from "./render/CategoryPill"; // Hack to get the CSS loaded on permission-based sites (Invidious) utils.wait(() => Config.config !== null, 5000, 10).then(addCSS); @@ -75,9 +76,11 @@ let lastCheckVideoTime = -1; //is this channel whitelised from getting sponsors skipped let channelWhitelisted = false; -// create preview bar let previewBar: PreviewBar = null; +// Skip to highlight button let skipButtonControlBar: SkipButtonControlBar = null; +// For full video sponsors/selfpromo +let categoryPill: CategoryPill = null; /** Element containing the player controls on the YouTube player. */ let controls: HTMLElement | null = null; @@ -263,6 +266,7 @@ function resetValues() { } skipButtonControlBar?.disable(); + categoryPill?.setVisibility(false); } async function videoIDChange(id) { @@ -549,6 +553,7 @@ function refreshVideoAttachments() { setupVideoListeners(); setupSkipButtonControlBar(); + setupCategoryPill(); } } } @@ -637,6 +642,14 @@ function setupSkipButtonControlBar() { skipButtonControlBar.attachToPage(); } +function setupCategoryPill() { + if (!categoryPill) { + categoryPill = new CategoryPill(); + } + + categoryPill.attachToPage(); +} + async function sponsorsLookup(id: string, keepOldSubmissions = true) { if (!video) refreshVideoAttachments(); //there is still no video here @@ -672,7 +685,7 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) { const hashPrefix = (await utils.getHash(id, 1)).substr(0, 4); const response = await utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, { categories, - actionTypes: Config.config.muteSegments ? [ActionType.Skip, ActionType.Mute] : [ActionType.Skip], + actionTypes: Config.config.muteSegments ? [ActionType.Skip, ActionType.Mute, ActionType.Full] : [ActionType.Skip, ActionType.Full], userAgent: `${chrome.runtime.id}`, ...extraRequestData }); @@ -864,6 +877,11 @@ function startSkipScheduleCheckingForStartSponsors() { } } + const fullVideoSegment = sponsorTimes.filter((time) => time.actionType === ActionType.Full)[0]; + if (fullVideoSegment) { + categoryPill?.setSegment(fullVideoSegment); + } + if (startingSegmentTime !== -1) { startSponsorSchedule(undefined, startingSegmentTime); } else { diff --git a/src/render/CategoryPill.tsx b/src/render/CategoryPill.tsx new file mode 100644 index 00000000..6c6695e2 --- /dev/null +++ b/src/render/CategoryPill.tsx @@ -0,0 +1,71 @@ +import * as React from "react"; +import * as ReactDOM from "react-dom"; +import CategoryPillComponent, { CategoryPillState } from "../components/CategoryPillComponent"; +import { SponsorTime } from "../types"; +import { GenericUtils } from "../utils/genericUtils"; + +export class CategoryPill { + container: HTMLElement; + ref: React.RefObject<CategoryPillComponent>; + + unsavedState: CategoryPillState; + + constructor() { + this.ref = React.createRef(); + } + + async attachToPage(): Promise<void> { + // TODO: Mobile and invidious + const referenceNode = await GenericUtils.wait(() => document.querySelector(".ytd-video-primary-info-renderer.title") as HTMLElement); + + if (referenceNode && !referenceNode.contains(this.container)) { + this.container = document.createElement('span'); + this.container.id = "categoryPill"; + this.container.style.display = "relative"; + + referenceNode.prepend(this.container); + referenceNode.style.display = "flex"; + + ReactDOM.render( + <CategoryPillComponent ref={this.ref} />, + this.container + ); + + if (this.unsavedState) { + this.ref.current?.setState(this.unsavedState); + this.unsavedState = null; + } + } + } + + close(): void { + ReactDOM.unmountComponentAtNode(this.container); + this.container.remove(); + } + + setVisibility(show: boolean): void { + const newState = { + show + }; + + if (this.ref.current) { + this.ref.current?.setState(newState); + } else { + this.unsavedState = newState; + } + } + + setSegment(segment: SponsorTime): void { + const newState = { + segment, + show: true + }; + + if (this.ref.current) { + this.ref.current?.setState(newState); + } else { + this.unsavedState = newState; + } + + } +}
\ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 661ca0bb..7cffa45a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,6 +2,7 @@ import Config from "./config"; import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContainer, Registration } from "./types"; import * as CompileConfig from "../config.json"; +import { GenericUtils } from "./utils/genericUtils"; export default class Utils { @@ -23,27 +24,8 @@ export default class Utils { this.backgroundScriptContainer = backgroundScriptContainer; } - /** 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(() => { - clearInterval(interval); - reject("TIMEOUT"); - }, timeout); - - const intervalCheck = () => { - const result = condition(); - if (result !== false) { - resolve(result); - clearInterval(interval); - } - }; - - const interval = setInterval(intervalCheck, check); - - //run the check once first, this speeds it up a lot - intervalCheck(); - }); + return GenericUtils.wait(condition, timeout, check); } containsPermission(permissions: chrome.permissions.Permissions): Promise<boolean> { diff --git a/src/utils/genericUtils.ts b/src/utils/genericUtils.ts new file mode 100644 index 00000000..32cf83f5 --- /dev/null +++ b/src/utils/genericUtils.ts @@ -0,0 +1,26 @@ +/** Function that can be used to wait for a condition before returning. */ +async function wait<T>(condition: () => T | false, timeout = 5000, check = 100): Promise<T> { + return await new Promise((resolve, reject) => { + setTimeout(() => { + clearInterval(interval); + reject("TIMEOUT"); + }, timeout); + + const intervalCheck = () => { + const result = condition(); + if (result) { + resolve(result); + clearInterval(interval); + } + }; + + const interval = setInterval(intervalCheck, check); + + //run the check once first, this speeds it up a lot + intervalCheck(); + }); +} + +export const GenericUtils = { + wait +}
\ No newline at end of file |