diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json index 1577efb2..2038801d 100644 --- a/public/_locales/en/messages.json +++ b/public/_locales/en/messages.json @@ -867,11 +867,19 @@ "message": "Hide forever" }, "warningChatInfo": { - "message": "You got a warning and cannot submit segments temporarily. This means that we noticed you were making some common mistakes that are not malicious, please just confirm that you understand the rules and we will remove the warning. You can also join this chat using discord.gg/SponsorBlock or matrix.to/#/#sponsor:ajay.app" + "message": "We noticed you were making some common mistakes that are not malicious" }, - "voteRejectedWarning": { - "message": "Vote rejected due to a warning. Click to open a chat to resolve it, or come back later when you have time.", - "description": "This is an integrated chat panel that will appearing allowing them to talk to the Discord/Matrix chat without leaving their browser." + "warningTitle": { + "message": "You got a warning" + }, + "questionButton": { + "message": "I have a question" + }, + "warningConfirmButton": { + "message": "I understand the reason" + }, + "warningError": { + "message": "Error when trying to acknowledge warning:" }, "Donate": { "message": "Donate" diff --git a/public/content.css b/public/content.css index 7ffeac4b..e8e019e2 100644 --- a/public/content.css +++ b/public/content.css @@ -351,6 +351,7 @@ .sponsorTimesInfoMessage { font-size: 13.3333px; color: rgb(235, 235, 235); + overflow-wrap: anywhere; } .sb-guidelines-notice .sponsorTimesInfoMessage td { @@ -543,17 +544,6 @@ input::-webkit-inner-spin-button { opacity: 0.8; } -.sbChatNotice iframe { - height: 32px; - cursor: pointer; - height: 100%; -} - -.sbChatClose { - height: 14px; - cursor: pointer; -} - .skipButtonControlBarContainer { cursor: pointer; display: flex; diff --git a/src/background.ts b/src/background.ts index 1af6038d..b3880dff 100644 --- a/src/background.ts +++ b/src/background.ts @@ -8,6 +8,7 @@ import { Registration } from "./types"; window.SB = Config; import Utils from "./utils"; +import { GenericUtils } from "./utils/genericUtils"; const utils = new Utils({ registerFirefoxContentScript, unregisterFirefoxContentScript @@ -205,7 +206,7 @@ async function asyncRequestToServer(type: string, address: string, data = {}) { async function sendRequestToCustomServer(type: string, url: string, data = {}) { // If GET, convert JSON to parameters if (type.toLowerCase() === "get") { - url = utils.objectToURI(url, data, true); + url = GenericUtils.objectToURI(url, data, true); data = null; } diff --git a/src/components/NoticeTextSectionComponent.tsx b/src/components/NoticeTextSectionComponent.tsx index 71fcb263..122d0fde 100644 --- a/src/components/NoticeTextSectionComponent.tsx +++ b/src/components/NoticeTextSectionComponent.tsx @@ -36,12 +36,31 @@ class NoticeTextSelectionComponent extends React.Component - {this.props.text} + {this.getTextElements(this.props.text)} ); } + + private getTextElements(text: string): Array { + const elements: Array = []; + const textParts = text.split(/(?=\s+)/); + for (const textPart of textParts) { + if (textPart.match(/^\s*http/)) { + elements.push( + + {textPart} + + ); + } else { + elements.push(textPart); + } + + } + + return elements; + } } export default NoticeTextSelectionComponent; \ No newline at end of file diff --git a/src/content.ts b/src/content.ts index 1d352188..759f7c75 100644 --- a/src/content.ts +++ b/src/content.ts @@ -10,7 +10,6 @@ import SkipNotice from "./render/SkipNotice"; import SkipNoticeComponent from "./components/SkipNoticeComponent"; import SubmissionNotice from "./render/SubmissionNotice"; import { Message, MessageResponse, VoteResponse } from "./messageTypes"; -import * as Chat from "./js-components/chat"; import { SkipButtonControlBar } from "./js-components/skipButtonControlBar"; import { getStartTimeFromUrl } from "./utils/urlParser"; import { findValidElement, getControls, getHashParams, isVisible } from "./utils/pageUtils"; @@ -19,6 +18,7 @@ import { CategoryPill } from "./render/CategoryPill"; import { AnimationUtils } from "./utils/animationUtils"; import { GenericUtils } from "./utils/genericUtils"; import { logDebug } from "./utils/logger"; +import { openWarningDialog } from "./utils/warnings"; // Hack to get the CSS loaded on permission-based sites (Invidious) utils.wait(() => Config.config !== null, 5000, 10).then(addCSS); @@ -1837,10 +1837,7 @@ async function vote(type: number, UUID: SegmentUUID, category?: Category, skipNo skipNotice.afterVote.bind(skipNotice)(utils.getSponsorTimeFromUUID(sponsorTimes, UUID), type, category); } else if (response.successType == -1) { if (response.statusCode === 403 && response.responseText.startsWith("Vote rejected due to a warning from a moderator.")) { - skipNotice.setNoticeInfoMessageWithOnClick.bind(skipNotice)(() => { - Chat.openWarningChat(response.responseText); - skipNotice.closeListener.call(skipNotice); - }, chrome.i18n.getMessage("voteRejectedWarning")); + openWarningDialog(response.responseText); } else { skipNotice.setNoticeInfoMessage.bind(skipNotice)(GenericUtils.getErrorMessage(response.statusCode, response.responseText)) } @@ -2028,7 +2025,7 @@ async function sendSubmitMessage() { playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker.svg"); if (response.status === 403 && response.responseText.startsWith("Submission rejected due to a warning from a moderator.")) { - Chat.openWarningChat(response.responseText); + openWarningDialog(skipNoticeContentContainer); } else { alert(GenericUtils.getErrorMessage(response.status, response.responseText)); } diff --git a/src/js-components/chat.ts b/src/js-components/chat.ts deleted file mode 100644 index 9ff30af7..00000000 --- a/src/js-components/chat.ts +++ /dev/null @@ -1,47 +0,0 @@ -import Config from "../config"; -import Utils from "../utils"; -const utils = new Utils(); - -export interface ChatConfig { - displayName: string, - composerInitialValue?: string, - customDescription?: string -} - -export function openChat(config: ChatConfig): void { - const chat = document.createElement("div"); - chat.classList.add("sbChatNotice"); - chat.style.zIndex = "2000"; - - const iframe= document.createElement("iframe"); - iframe.src = "https://chat.sponsor.ajay.app/#" + utils.objectToURI("", config, false); - chat.appendChild(iframe); - - const closeButton = document.createElement("img"); - closeButton.classList.add("sbChatClose"); - closeButton.src = chrome.extension.getURL("icons/close.png"); - closeButton.addEventListener("click", () => { - chat.remove(); - closeButton.remove(); - }); - chat.appendChild(closeButton); - - const referenceNode = utils.findReferenceNode(); - referenceNode.prepend(chat); -} - -export async function openWarningChat(warningMessage: string): Promise { - const warningReasonMatch = warningMessage.match(/Warning reason: '(.+)'/); - alert(chrome.i18n.getMessage("warningChatInfo") + `\n\n${warningReasonMatch ? ` Warning reason: ${warningReasonMatch[1]}` : ``}`); - - const userNameData = await utils.asyncRequestToServer("GET", "/api/getUsername?userID=" + Config.config.userID); - const userName = userNameData.ok ? JSON.parse(userNameData.responseText).userName : ""; - const publicUserID = await utils.getHash(Config.config.userID); - - openChat({ - displayName: `${userName ? userName : ``}${userName !== publicUserID ? ` | ${publicUserID}` : ``}`, - composerInitialValue: `I got a warning and confirm I [REMOVE THIS CAPITAL TEXT TO CONFIRM] reread the guidelines.` + - warningReasonMatch ? ` Warning reason: ${warningReasonMatch[1]}` : ``, - customDescription: chrome.i18n.getMessage("warningChatInfo") - }); -} \ No newline at end of file diff --git a/src/render/GenericNotice.tsx b/src/render/GenericNotice.tsx index d00799fb..60b69835 100644 --- a/src/render/GenericNotice.tsx +++ b/src/render/GenericNotice.tsx @@ -64,7 +64,13 @@ export default class GenericNotice { extraClass={options.extraClass} closeListener={() => this.close()} > - {this.getMessageBox(this.idSuffix, options.textBoxes)} + + + {this.getMessageBoxes(this.idSuffix, options.textBoxes)} + + @@ -81,7 +87,7 @@ export default class GenericNotice { ); } - getMessageBox(idSuffix: string, textBoxes: TextBox[]): JSX.Element[] { + getMessageBoxes(idSuffix: string, textBoxes: TextBox[]): JSX.Element[] { if (textBoxes) { const result = []; for (let i = 0; i < textBoxes.length; i++) { diff --git a/src/utils.ts b/src/utils.ts index f72f4dd0..c1b3f9b0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -376,19 +376,6 @@ export default class Utils { return referenceNode; } - objectToURI(url: string, data: T, includeQuestionMark: boolean): string { - let counter = 0; - for (const key in data) { - const seperator = (url.includes("?") || counter > 0) ? "&" : (includeQuestionMark ? "?" : ""); - const value = (typeof(data[key]) === "string") ? data[key] as unknown as string : JSON.stringify(data[key]); - url += seperator + encodeURIComponent(key) + "=" + encodeURIComponent(value); - - counter++; - } - - return url; - } - getFormattedTime(seconds: number, precise?: boolean): string { seconds = Math.max(seconds, 0); diff --git a/src/utils/genericUtils.ts b/src/utils/genericUtils.ts index ba22afc4..0718d788 100644 --- a/src/utils/genericUtils.ts +++ b/src/utils/genericUtils.ts @@ -72,9 +72,23 @@ function indexesOf(array: T[], value: T): number[] { return array.map((v, i) => v === value ? i : -1).filter(i => i !== -1); } +function objectToURI(url: string, data: T, includeQuestionMark: boolean): string { + let counter = 0; + for (const key in data) { + const seperator = (url.includes("?") || counter > 0) ? "&" : (includeQuestionMark ? "?" : ""); + const value = (typeof(data[key]) === "string") ? data[key] as unknown as string : JSON.stringify(data[key]); + url += seperator + encodeURIComponent(key) + "=" + encodeURIComponent(value); + + counter++; + } + + return url; +} + export const GenericUtils = { wait, getErrorMessage, getLuminance, - indexesOf + indexesOf, + objectToURI } \ No newline at end of file diff --git a/src/utils/warnings.ts b/src/utils/warnings.ts new file mode 100644 index 00000000..09423e03 --- /dev/null +++ b/src/utils/warnings.ts @@ -0,0 +1,66 @@ +import Config from "../config"; +import GenericNotice, { NoticeOptions } from "../render/GenericNotice"; +import { ContentContainer } from "../types"; +import Utils from "../utils"; +import { GenericUtils } from "./genericUtils"; +const utils = new Utils(); + +export interface ChatConfig { + displayName: string, + composerInitialValue?: string, + customDescription?: string +} + +export async function openWarningDialog(contentContainer: ContentContainer): Promise { + const userInfo = await utils.asyncRequestToServer("GET", "/api/userInfo", { + userID: Config.config.userID, + values: ["warningReason"] + }); + + if (userInfo.ok) { + const warningReason = JSON.parse(userInfo.responseText)?.warningReason; + const userNameData = await utils.asyncRequestToServer("GET", "/api/getUsername?userID=" + Config.config.userID); + const userName = userNameData.ok ? JSON.parse(userNameData.responseText).userName : ""; + const publicUserID = await utils.getHash(Config.config.userID); + + let notice: GenericNotice = null; + const options: NoticeOptions = { + title: chrome.i18n.getMessage("warningTitle"), + textBoxes: [{ + text: chrome.i18n.getMessage("warningChatInfo"), + icon: null + }, ...warningReason.split("\n").map((reason) => ({ + text: reason, + icon: null + }))], + buttons: [{ + name: chrome.i18n.getMessage("questionButton"), + listener: () => openChat({ + displayName: `${userName ? userName : ``}${userName !== publicUserID ? ` | ${publicUserID}` : ``}` + }) + }, + { + name: chrome.i18n.getMessage("warningConfirmButton"), + listener: async () => { + const result = await utils.asyncRequestToServer("POST", "/api/warnUser", { + userID: Config.config.userID, + enabled: false + }); + + if (result.ok) { + notice?.close(); + } else { + alert(`${chrome.i18n.getMessage("warningError")} ${result.status}`); + } + } + }], + timed: false + }; + + notice = new GenericNotice(contentContainer, "warningNotice", options); + } +} + +export function openChat(config: ChatConfig): void { + window.open("https://chat.sponsor.ajay.app/#" + GenericUtils.objectToURI("", config, false)); +} \ No newline at end of file