aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAjay Ramachandran <[email protected]>2021-07-29 20:20:24 -0400
committerGitHub <[email protected]>2021-07-29 20:20:24 -0400
commit2373b3406787c38b68a9acbf9fa4c2e8ab1a7cf8 (patch)
treed22144ef23a101944a5a104ddb177f14703f448b
parent1656fae2d4113264400d0da4eb956fc6824fb7cf (diff)
parent839dd4613c44f16455121f8a1b7d8f24dd35a6ff (diff)
downloadSponsorBlock-2373b3406787c38b68a9acbf9fa4c2e8ab1a7cf8.tar.gz
SponsorBlock-2373b3406787c38b68a9acbf9fa4c2e8ab1a7cf8.zip
Merge pull request #862 from ajayyy/warning-chat
Add chat box when getting a warning
-rw-r--r--public/_locales/en/messages.json6
-rw-r--r--public/content.css22
-rw-r--r--src/background.ts6
-rw-r--r--src/components/SkipNoticeComponent.tsx14
-rw-r--r--src/content.ts18
-rw-r--r--src/js-components/chat.ts44
-rw-r--r--src/utils.ts24
7 files changed, 115 insertions, 19 deletions
diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json
index 8a5a2b30..3105f7ed 100644
--- a/public/_locales/en/messages.json
+++ b/public/_locales/en/messages.json
@@ -675,5 +675,11 @@
},
"hideForever": {
"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, and we just want to clarify the rules. You can also join this chat using discord.gg/SponsorBlock or matrix.to/#/+sponsor:ajay.app"
+ },
+ "voteRejectedWarning": {
+ "message": "Vote rejected due to a warning. Click to open a chat to resolve it, or come back later when you have time."
}
}
diff --git a/public/content.css b/public/content.css
index 70b74766..ba0d7016 100644
--- a/public/content.css
+++ b/public/content.css
@@ -121,8 +121,19 @@
margin-left: 5px;
}
+.sbChatNotice {
+ min-width: 350px;
+ height: 70%;
+
+ position: absolute;
+ right: 5px;
+ bottom: 100px;
+ right: 10px;
+}
+
.sponsorSkipNotice {
min-width: 350px;
+ max-width: 50%;
background-color: rgba(28, 28, 28, 0.9);
position: absolute;
right: 5px;
@@ -445,4 +456,15 @@ input::-webkit-inner-spin-button {
}
.helpButton:hover {
filter: brightness(80%);
+}
+
+.sbChatNotice iframe {
+ height: 32px;
+ cursor: pointer;
+ height: 100%;
+}
+
+.sbChatClose {
+ height: 14px;
+ cursor: pointer;
} \ No newline at end of file
diff --git a/src/background.ts b/src/background.ts
index e44dd28a..53f9d415 100644
--- a/src/background.ts
+++ b/src/background.ts
@@ -164,11 +164,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") {
- for (const key in data) {
- const seperator = url.includes("?") ? "&" : "?";
- const value = (typeof(data[key]) === "string") ? data[key]: JSON.stringify(data[key]);
- url += seperator + key + "=" + value;
- }
+ url = utils.objectToURI(url, data, true);
data = null;
}
diff --git a/src/components/SkipNoticeComponent.tsx b/src/components/SkipNoticeComponent.tsx
index 1582c2c9..f8269e88 100644
--- a/src/components/SkipNoticeComponent.tsx
+++ b/src/components/SkipNoticeComponent.tsx
@@ -313,11 +313,15 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
for (let i = 0; i < this.state.messages.length; i++) {
elements.push(
- <NoticeTextSelectionComponent idSuffix={this.idSuffix}
- text={this.state.messages[i]}
- onClick={this.state.messageOnClick}
- key={i}>
- </NoticeTextSelectionComponent>
+ <tr>
+ <td>
+ <NoticeTextSelectionComponent idSuffix={this.idSuffix}
+ text={this.state.messages[i]}
+ onClick={this.state.messageOnClick}
+ key={i + "_messageBox"}>
+ </NoticeTextSelectionComponent>
+ </td>
+ </tr>
)
}
diff --git a/src/content.ts b/src/content.ts
index e0f0572e..6320249a 100644
--- a/src/content.ts
+++ b/src/content.ts
@@ -13,7 +13,7 @@ import SkipNotice from "./render/SkipNotice";
import SkipNoticeComponent from "./components/SkipNoticeComponent";
import SubmissionNotice from "./render/SubmissionNotice";
import { Message, MessageResponse } from "./messageTypes";
-import GenericNotice from "./render/GenericNotice";
+import * as Chat from "./js-components/chat";
// Hack to get the CSS loaded on permission-based sites (Invidious)
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
@@ -1458,7 +1458,15 @@ function vote(type: number, UUID: string, category?: string, skipNotice?: SkipNo
//success (treat rate limits as a success)
skipNotice.afterVote.bind(skipNotice)(utils.getSponsorTimeFromUUID(sponsorTimes, UUID), type, category);
} else if (response.successType == -1) {
- skipNotice.setNoticeInfoMessage.bind(skipNotice)(utils.getErrorMessage(response.statusCode, response.responseText))
+ 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"));
+ } else {
+ skipNotice.setNoticeInfoMessage.bind(skipNotice)(utils.getErrorMessage(response.statusCode, response.responseText))
+ }
+
skipNotice.resetVoteButtonInfo.bind(skipNotice)();
}
}
@@ -1567,7 +1575,11 @@ async function sendSubmitMessage() {
playerButtons.submit.button.style.animation = "unset";
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker.svg");
- alert(utils.getErrorMessage(response.status, response.responseText));
+ if (response.status === 403 && response.responseText.startsWith("Submission rejected due to a warning from a moderator.")) {
+ Chat.openWarningChat(response.responseText);
+ } else {
+ alert(utils.getErrorMessage(response.status, response.responseText));
+ }
}
}
diff --git a/src/js-components/chat.ts b/src/js-components/chat.ts
new file mode 100644
index 00000000..4bbb4a2b
--- /dev/null
+++ b/src/js-components/chat.ts
@@ -0,0 +1,44 @@
+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<void> {
+ 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 want to know what I need to do to improve. ` +
+ `Warning reason: ${warningMessage.match(/Warning reason: '(.+)'/)[1]}`,
+ customDescription: chrome.i18n.getMessage("warningChatInfo")
+ });
+} \ No newline at end of file
diff --git a/src/utils.ts b/src/utils.ts
index e93782cd..0ea27238 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -415,6 +415,19 @@ export default class Utils {
return referenceNode;
}
+ objectToURI<T>(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 {
const hours = Math.floor(seconds / 60 / 60);
const minutes = Math.floor(seconds / 60) % 60;
@@ -479,14 +492,13 @@ export default class Utils {
async getHash(value: string, times = 5000): Promise<string> {
if (times <= 0) return "";
- let hashBuffer = new TextEncoder().encode(value).buffer;
-
+ let hashHex = value;
for (let i = 0; i < times; i++) {
- hashBuffer = await crypto.subtle.digest('SHA-256', hashBuffer);
- }
+ const hashBuffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(hashHex).buffer);
- const hashArray = Array.from(new Uint8Array(hashBuffer));
- const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
+ hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
+ }
return hashHex;
}