Skip to content

Commit

Permalink
Add downvotes
Browse files Browse the repository at this point in the history
  • Loading branch information
ajayyy committed Jan 24, 2024
1 parent 17009af commit bb690c8
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 18 deletions.
2 changes: 1 addition & 1 deletion maze-utils
30 changes: 30 additions & 0 deletions public/content.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@
padding-bottom: 0;
}

.cbThumbnail .cbVoteButtons {
display: flex;
justify-content: center;

margin-top: 4px;
}

.cbThumbnail .cbVoteButtons .cbButton {
margin-left: 5px;
margin-right: 5px;
}

/* For miniplayer */
.ytd-miniplayer .cbCustomTitle {
text-overflow: ellipsis;
Expand Down Expand Up @@ -288,6 +300,19 @@ ytd-compact-playlist-renderer .ytd-compact-playlist-renderer #video-title:not(.c
height: 0.7em;
}

.cbTitle .cbVoteButtons {
width: max-content;
margin-left: auto;
display: flex;
}

.cbTitle .cbVoteButtons .cbButton {
margin-left: 5px;
margin-right: 5px;

height: 16px;
}

.cbBrandingPreview .cbTitle {
cursor: default !important;
}
Expand Down Expand Up @@ -580,4 +605,9 @@ ytd-playlist-video-thumbnail-renderer {
/* Compatibility with Tube Achivist */
#video-title.ytd-rich-grid-media.ta-title-container {
display: flex;
}

@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
38 changes: 34 additions & 4 deletions src/dataFetching.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { VideoID, getVideoID } from "../maze-utils/src/video";
import { VideoID, getVideoID, getYouTubeVideoID } from "../maze-utils/src/video";
import { ThumbnailSubmission, ThumbnailWithRandomTimeResult, fetchVideoMetadata } from "./thumbnails/thumbnailData";
import { TitleResult, TitleSubmission } from "./titles/titleData";
import { FetchResponse, sendRealRequestToCustomServer } from "../maze-utils/src/background-request-proxy";
import { BrandingLocation, BrandingResult, updateBrandingForVideo } from "./videoBranding/videoBranding";
import { BrandingLocation, BrandingResult, replaceCurrentVideoBranding, updateBrandingForVideo } from "./videoBranding/videoBranding";
import { logError } from "./utils/logger";
import { getHash } from "../maze-utils/src/hash";
import Config, { ThumbnailCacheOption, ThumbnailFallbackOption } from "./config/config";
Expand Down Expand Up @@ -471,18 +471,48 @@ export function clearCache(videoID: VideoID) {
delete cache[videoID];
}

export async function submitVideoBranding(videoID: VideoID, title: TitleSubmission | null, thumbnail: ThumbnailSubmission | null): Promise<FetchResponse> {
export async function submitVideoBranding(videoID: VideoID, title: TitleSubmission | null, thumbnail: ThumbnailSubmission | null, downvote = false): Promise<FetchResponse> {
const result = await sendRequestToServer("POST", "/api/branding", {
userID: Config.config!.userID,
videoID,
title,
thumbnail
thumbnail,
downvote
});

clearCache(videoID);
return result;
}

/**
* Also does alerts
*/
export async function submitVideoBrandingAndHandleErrors(title: TitleSubmission | null,
thumbnail: ThumbnailSubmission | null, downvote: boolean): Promise<boolean> {
if (getVideoID() !== getYouTubeVideoID()) {
alert(chrome.i18n.getMessage("videoIDWrongWhenSubmittingError"));
return false;
}

const result = await submitVideoBranding(getVideoID()!, title, thumbnail, downvote);

if (result && result.ok) {
replaceCurrentVideoBranding().catch(logError);

return true;
} else {
const text = result.responseText;

if (text.includes("<head>")) {
alert(chrome.i18n.getMessage("502"));
} else {
alert(text);
}

return false;
}
}

export function sendRequestToThumbnailCache(videoID: string, time?: number, title?: string,
officialTime = false, generateNow = false): Promise<Response> {
const data = {
Expand Down
29 changes: 22 additions & 7 deletions src/submission/SubmissionComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,27 +80,36 @@ export const SubmissionComponent = (props: SubmissionComponentProps) => {
setOriginalTitle(originalTitle);

setTitles([{
title: originalTitle
title: originalTitle,
original: true,
votable: true
}, {
title: ""
title: "",
original: false,
votable: false
}, ...props.submissions.titles
.filter((s) => s.title !== originalTitle)
.map((s) => ({
title: s.title
title: s.title,
original: s.original,
votable: true
}))]);
})();
}, []);

const defaultThumbnails: RenderedThumbnailSubmission[] = [{
type: ThumbnailType.Original
type: ThumbnailType.Original,
votable: true
}, {
type: ThumbnailType.CurrentTime
type: ThumbnailType.CurrentTime,
votable: false
}];
const downloadedThumbnails: RenderedThumbnailSubmission[] = props.submissions.thumbnails
.filter((s) => !s.original)
.map((s: CustomThumbnailResult) => ({
timestamp: s.timestamp,
type: ThumbnailType.SpecifiedTime
type: ThumbnailType.SpecifiedTime,
votable: true
}));
const thumbnails = defaultThumbnails.concat(downloadedThumbnails);

Expand Down Expand Up @@ -349,7 +358,8 @@ function updateUnsubmitted(unsubmitted: UnsubmittedSubmission,
|| s.timestamp !== t.timestamp)))
.map((t) => ({
type: ThumbnailType.SpecifiedTime,
timestamp: (t as CustomThumbnailResult).timestamp
timestamp: (t as CustomThumbnailResult).timestamp,
votable: false
}));

setExtraUnsubmittedThumbnails(thumbnailsResult);
Expand All @@ -359,6 +369,11 @@ function updateUnsubmitted(unsubmitted: UnsubmittedSubmission,
if (unsubmittedTitles) {
titlesResult = unsubmittedTitles
.filter((t) => titles.every((s) => s.title !== t.title))
.map((t) => ({
title: t.title,
original: false,
votable: false
}));

setExtraUnsubmittedTitles?.(titlesResult);
}
Expand Down
5 changes: 4 additions & 1 deletion src/submission/ThumbnailDrawerComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ interface TimeRenderedThumbnailSubmission {
type: ThumbnailType.SpecifiedTime;
}

export type RenderedThumbnailSubmission = (NoTimeRenderedThumbnailSubmission | TimeRenderedThumbnailSubmission);
export type RenderedThumbnailSubmission = (NoTimeRenderedThumbnailSubmission | TimeRenderedThumbnailSubmission) & {
votable: boolean;
};

export const ThumbnailDrawerComponent = (props: ThumbnailDrawerComponentProps) => {
return (
Expand Down Expand Up @@ -49,6 +51,7 @@ function getThumbnails(props: ThumbnailDrawerComponentProps,
type={props.existingSubmissions[i].type}
videoID={props.videoId}
time={time}
votable={props.existingSubmissions[i].votable}
key={time ? `T${time}` : `I${i}`}
></ThumbnailSelectionComponent>
);
Expand Down
49 changes: 46 additions & 3 deletions src/submission/ThumbnailSelectionComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { VideoID } from "../../maze-utils/src/video";
import { ThumbnailSubmission } from "../thumbnails/thumbnailData";
import { ThumbnailComponent, ThumbnailType } from "./ThumbnailComponent";
import AddIcon from "../svgIcons/addIcon";
import UpvoteIcon from "../svgIcons/upvoteIcon";
import DownvoteIcon from "../svgIcons/downvoteIcon";
import { submitVideoBrandingAndHandleErrors } from "../dataFetching";
import { AnimationUtils } from "../../maze-utils/src/animationUtils";

export interface ThumbnailSelectionComponentProps {
video: HTMLVideoElement;
Expand All @@ -14,13 +18,24 @@ export interface ThumbnailSelectionComponentProps {
hideTime?: boolean;
time?: number;
larger?: boolean;
votable?: boolean;
submission?: ThumbnailSubmission;
}

/**
* The selector object for choosing a thumbnail
*/
export const ThumbnailSelectionComponent = (props: ThumbnailSelectionComponentProps) => {
const [error, setError] = React.useState("")
const [error, setError] = React.useState("");

function createThumbnailSubmission(): ThumbnailSubmission | null {
return props.type === ThumbnailType.Original ? {
original: true
} : {
original: false,
timestamp: props.time!
};
}

return (
<ThumbnailComponent
Expand All @@ -43,14 +58,42 @@ export const ThumbnailSelectionComponent = (props: ThumbnailSelectionComponentPr
</div> : null
}
{
!props.hideTime ?
!props.hideTime && props.type !== ThumbnailType.CurrentTime ?
<>
<div style={{ fontWeight: "bold", textAlign: "center", marginTop: "4px" }}>
{error ? <div>{error}</div> : null}
{getText(props.time, props.type)}
</div>

<div className="cbVoteButtons"
style={{ visibility: !props.selected && props.votable ? undefined : "hidden" }}>
<button className="cbButton"
title={chrome.i18n.getMessage("upvote")}
onClick={(e) => {
e.stopPropagation();

const stopAnimation = AnimationUtils.applyLoadingAnimation(e.currentTarget, 0.3);
submitVideoBrandingAndHandleErrors(null, createThumbnailSubmission(), false).then(stopAnimation);
}}>
<UpvoteIcon/>
</button>

<button className="cbButton"
title={chrome.i18n.getMessage("downvote")}
onClick={(e) => {
e.stopPropagation();

const stopAnimation = AnimationUtils.applyLoadingAnimation(e.currentTarget, 0.3);
submitVideoBrandingAndHandleErrors(null, createThumbnailSubmission(), true).then(stopAnimation);
}}>
<DownvoteIcon/>
</button>
</div>
</>
: null
}



</ThumbnailComponent>
);
};
Expand Down
33 changes: 31 additions & 2 deletions src/submission/TitleComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as React from "react";
import { RenderedTitleSubmission } from "./TitleDrawerComponent";
import ResetIcon from "../svgIcons/resetIcon";
import Config from "../config/config";
import UpvoteIcon from "../svgIcons/upvoteIcon";
import DownvoteIcon from "../svgIcons/downvoteIcon";
import { submitVideoBrandingAndHandleErrors } from "../dataFetching";
import { AnimationUtils } from "../../maze-utils/src/animationUtils";

export interface TitleComponentProps {
submission: RenderedTitleSubmission;
Expand Down Expand Up @@ -35,9 +39,9 @@ export const TitleComponent = (props: TitleComponentProps) => {
onClick={() => {
const title = titleRef.current!.innerText;
props.onSelectOrUpdate(title, title);
setFocused(true);

if (document.activeElement !== titleRef.current) {
setFocused(true);
setSelectionToEnd(titleRef.current!);
}
}}
Expand Down Expand Up @@ -94,8 +98,34 @@ export const TitleComponent = (props: TitleComponentProps) => {
}}>
</span>

<div className="cbVoteButtons"
style={{ display: !props.selected && !titleChanged && props.submission.votable ? undefined : "none" }}>
<button className="cbButton"
title={chrome.i18n.getMessage("upvote")}
onClick={(e) => {
e.stopPropagation();

const stopAnimation = AnimationUtils.applyLoadingAnimation(e.currentTarget, 0.3);
submitVideoBrandingAndHandleErrors(props.submission, null, false).then(stopAnimation);
}}>
<UpvoteIcon/>
</button>

<button className="cbButton"
title={chrome.i18n.getMessage("downvote")}
onClick={(e) => {
e.stopPropagation();

const stopAnimation = AnimationUtils.applyLoadingAnimation(e.currentTarget, 0.3);
submitVideoBrandingAndHandleErrors(props.submission, null, true).then(stopAnimation);
}}>
<DownvoteIcon/>
</button>
</div>

<button className="resetCustomTitle cbButton"
title={chrome.i18n.getMessage("resetCustomTitle")}
style={{ display: props.selected && titleChanged ? "block" : "none" }}
onClick={(e) => {
e.stopPropagation();

Expand All @@ -111,7 +141,6 @@ export const TitleComponent = (props: TitleComponentProps) => {
}
}}>
<ResetIcon
style={{ display: props.selected && titleChanged ? "block" : "none" }}
className="resetCustomTitle"
/>
</button>
Expand Down
2 changes: 2 additions & 0 deletions src/submission/TitleDrawerComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export interface TitleDrawerComponentProps {

export interface RenderedTitleSubmission {
title: string;
votable: boolean;
original: boolean;
}

export const TitleDrawerComponent = (props: TitleDrawerComponentProps) => {
Expand Down
40 changes: 40 additions & 0 deletions src/svgIcons/downvoteIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as React from "react";

export interface DownvoteIconProps {
fill?: string;
className?: string;
width?: string;
height?: string;
style?: React.CSSProperties;
onClick?: () => void;
}

const DownvoteIcon = ({
fill = "#ffffff",
className = "",
width = "16",
height = "16",
style = {},
onClick
}: DownvoteIconProps): JSX.Element => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
height={width}
width={height}
className={className}
fill={fill}
style={style}
onClick={onClick} >
<path
d="M0 0h24v24H0z"
fill="none"
id="path2" />
<path
d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"
id="path4"
style={{ fill }} />
</svg>
);

export default DownvoteIcon;
Loading

0 comments on commit bb690c8

Please sign in to comment.