Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

View and Edit Queue #100

Merged
merged 7 commits into from
May 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/icon-128-greyscale.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ export const DefaultOptions = {
skipVerified: true,
skipAffiliated: true,
skip1Mplus: true,
skipFollowerCount: 1e6,
blockNftAvatars: false,
blockInterval: 15,
popupTimer: 30,

// this isn't set, but is used
// TODO: when migrating to firefox manifest v3, check to see if sets can be stored yet
Expand Down
12 changes: 11 additions & 1 deletion injected/inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@
// determine if request is a timeline/tweet-returning request
const parsedUrl = RequestRegex.exec(this._url);
if(this._url && parsedUrl) {
document.dispatchEvent(new CustomEvent("blue-blocker-event", { detail: { url : this._url, parsedUrl, body: this.response, request: { headers: this._requestHeaders } } }));
document.dispatchEvent(new CustomEvent("blue-blocker-event", {
detail: {
parsedUrl,
url : this._url,
body: this.response,
request: {
headers: this._requestHeaders,
},
status: this.status,
},
}));
}
});
return send.apply(this, arguments);
Expand Down
4 changes: 4 additions & 0 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ firefox:
mv manifest.json chrome-manifest.json
mv firefox-manifest.json manifest.json
zip "blue-blocker-firefox-${VERSION}.zip" \
assets/icon-128-greyscale.png \
assets/icon-128.png \
assets/icon.png \
assets/error.png \
injected/* \
models/* \
parsers/* \
popup/* \
pages/* \
manifest.json \
LICENSE \
readme.md \
Expand All @@ -30,13 +32,15 @@ chrome:
# rm "blue-blocker-chrome-${VERSION}.zip"
# endif
zip "blue-blocker-chrome-${VERSION}.zip" \
assets/icon-128-greyscale.png \
assets/icon-128.png \
assets/icon.png \
assets/error.png \
injected/* \
models/* \
parsers/* \
popup/* \
pages/* \
manifest.json \
LICENSE \
readme.md \
Expand Down
2 changes: 1 addition & 1 deletion models/block_counter.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class BlockCounter {
// try to access the critical point
await this.storage.set({ [criticalPointKey]: { refId: this._refId, time: (new Date()).valueOf() + interval * 1.5 } });
await new Promise(r => setTimeout(r, 10)); // wait a second to make sure any other sets have resolved
cpRefId = (await this.storage.get({ [criticalPointKey]: null }))[criticalPointKey].refId;
cpRefId = (await this.storage.get({ [criticalPointKey]: null }))[criticalPointKey]?.refId;
}
else {
// sleep for a little bit to let the other tab(s) release the critical point
Expand Down
2 changes: 1 addition & 1 deletion models/block_queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class BlockQueue {
// try to access the critical point
await this.storage.set({ [criticalPointKey]: { refId: this._refId, time: (new Date()).valueOf() + interval * 1.5 } });
await new Promise(r => setTimeout(r, 10)); // wait a second to make sure any other sets have resolved
cpRefId = (await this.storage.get({ [criticalPointKey]: null }))[criticalPointKey].refId;
cpRefId = (await this.storage.get({ [criticalPointKey]: null }))[criticalPointKey]?.refId;
}
else {
// sleep for a little bit to let the other tab(s) release the critical point
Expand Down
2 changes: 1 addition & 1 deletion models/queue_consumer.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class QueueConsumer {
// try to access the critical point
await this.storage.set({ [criticalPointKey]: { refId: this._refId, time: (new Date()).valueOf() + this._interval * 1.5 } });
await new Promise(r => setTimeout(r, 10)); // wait a second to make sure any other sets have resolved
cpRefId = (await this.storage.get({ [criticalPointKey]: null }))[criticalPointKey].refId;
cpRefId = (await this.storage.get({ [criticalPointKey]: null }))[criticalPointKey]?.refId;
}
else {
return false;
Expand Down
22 changes: 22 additions & 0 deletions pages/queue.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Blue Blocker Queue</title>
<link href="./style.css" rel="stylesheet">
<link href="../assets/icon-128.png" rel="icon" type="image/png" sizes="128x128">
</head>
<!-- ♀ -->
<body>
<main>
<div class="inner">
<h1>Block Queue</h1>
<p class="subtitle">Users will not be added to the queue or blocked while this page is open</p>
<div id="block-queue">
loading...
</div>
</div>
</main>
</body>
<script src="./queue.js" type="module"></script>
</html>
81 changes: 81 additions & 0 deletions pages/queue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { BlockQueue } from "../models/block_queue.js";
import { FormatLegacyName } from "../utilities.js";
import { api, logstr } from "../constants.js";

// Define constants that shouldn't be exported to the rest of the addon
const queue = new BlockQueue(api.storage.local);

// we need to obtain and hold on to the critical point as long as this tab is
// open so that any twitter tabs that are open are unable to block users
setInterval(async () => {
await queue.getCriticalPoint()
}, 500);

async function unqueueUser(user_id, safelist) {
// because this page holds onto the critical point, we can modify the queue
// without worrying about if it'll affect another tab
if (safelist) {
api.storage.sync.get({ unblocked: { } }).then(items => {
items.unblocked[String(user_id)] = null;
api.storage.sync.set(items);
});
}

const items = await api.storage.local.get({ BlockQueue: [] });

for (let i = 0; i < items.BlockQueue.length; i++) {
if (items.BlockQueue[i].user_id === user_id) {
items.BlockQueue.splice(i, 1);
break;
}
}

await api.storage.local.set(items);
}

// interval doesn't run immediately, so do that here
queue.getCriticalPoint()
.then(() => api.storage.local.get({ BlockQueue: [] }))
.then(items => {
const queueDiv = document.getElementById("block-queue");

if (items.BlockQueue.length === 0) {
queueDiv.textContent = "your block queue is empty";
return;
}

queueDiv.innerHTML = null;

items.BlockQueue.forEach(item => {
const { user, user_id } = item;
const div = document.createElement("div");

const p = document.createElement("p");
p.innerHTML = `${user.legacy.name} (<a href="https://twitter.com/${user.legacy.screen_name}" target="_blank">@${user.legacy.screen_name}</a>)`;
div.appendChild(p);

const remove = document.createElement("button");
remove.onclick = () => {
div.removeChild(remove);
unqueueUser(user_id, false).then(() => {
queueDiv.removeChild(div);
});
console.log(logstr, `removed ${FormatLegacyName(user)} from queue`);
};
remove.textContent = "remove";
div.appendChild(remove);

const never = document.createElement("button");
never.onclick = () => {
div.removeChild(never);
unqueueUser(user_id, true).then(() => {
queueDiv.removeChild(div);
});
console.log(logstr, `removed and safelisted ${FormatLegacyName(user)} from queue`);
};
never.textContent = "never block";
div.appendChild(never);

queueDiv.appendChild(div);
});
});
124 changes: 124 additions & 0 deletions pages/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
html {
--transition: ease;
--fadetime: 0.15s;
--interact: #1da1f2;
--bg0color: #000000;
--bg1color: #1e1f25;
--bg2color: #151416;
--bg3color: var(--bordercolor);
--blockquote: var(--bordercolor);
--textcolor: #DDD;
--bordercolor: #2D333A;
--linecolor: var(--bordercolor);
--borderhover: var(--interact);
--subtle: #EEEEEE80;
--shadowcolor: #00000080;
--activeshadowcolor: #000000B3;
--screen-cover: #00000080;
--border-size: 1px;
--border-radius: 3px;

background: var(--bg0color);
}
html * {
font-family: Bitstream Vera Sans, DejaVu Sans, Arial, Helvetica, sans-serif;
}
body {
background: var(--bg1color);
color: var(--textcolor);
}
html, body, main, h1, p {
margin: 0;
}
main {
color: var(--textcolor);
text-align: center;
width: 100%;
background: #1E1F25;
}
.inner {
padding: 25px;
}
.subtitle {
margin-bottom: 25px;
}


#block-queue {
bottom: 0;
display: inline-flex;
align-items: flex-start;
justify-content: center;
flex-direction: column;
}

#block-queue div {
display: flex;
justify-content: center;
align-items: center;
background: url('../assets/icon.png') var(--bg2color);
background-repeat: no-repeat;
background-size: 2.5em;
background-position-x: 1em;
background-position-y: center;
pointer-events: all;
padding: 1em 1.5em 1em 4.25em;
margin: 0 0 25px;
border: var(--border-size) solid var(--bordercolor);
border-radius: var(--border-radius);
color: var(--textcolor);
box-shadow: 0 2px 3px 1px var(--shadowcolor);
min-height: calc(2em + 4px);
}
#block-queue div:last-child {
margin-bottom: 0;
}
#block-queue div.error {
background: url('../assets/error.png') var(--bg1color);
background-repeat: no-repeat;
background-size: 2.5em;
background-position-x: 1em;
background-position-y: center;
}

#block-queue div a,
#block-queue div a:active,
#block-queue div a:focus {
color: var(--textcolor);
text-shadow: 0 2px 3px 1px var(--shadowcolor);
-webkit-transition: var(--transition) var(--fadetime);
-moz-transition: var(--transition) var(--fadetime);
-o-transition: var(--transition) var(--fadetime);
transition: var(--transition) var(--fadetime);
cursor: pointer;
}

#block-queue div a:hover {
text-shadow: 0 0 10px 3px var(--activeshadowcolor);
color: var(--interact);
}

#block-queue div button,
#block-queue div button:active,
#block-queue div button:focus {
margin-left: 1em;
font-size: 1em;
padding: 0.5em 1em;
color: var(--textcolor);
background: var(--bg1color);
box-shadow: 0 2px 3px 1px var(--shadowcolor);
border: var(--border-size) solid var(--bordercolor);
border-radius: var(--border-radius);
-webkit-transition: var(--transition) var(--fadetime);
-moz-transition: var(--transition) var(--fadetime);
-o-transition: var(--transition) var(--fadetime);
transition: var(--transition) var(--fadetime);
cursor: pointer;
}

#block-queue div button:hover {
box-shadow: 0 0 10px 3px var(--activeshadowcolor);
border-color: var(--interact);
color: var(--interact);
}

21 changes: 8 additions & 13 deletions parsers/instructions.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const IgnoreTweetTypes = new Set([
"TimelineTimelineCursor",
]);

function handleTweetObject(obj, headers, config) {
function handleTweetObject(obj, config) {
let ptr = obj;
for (const key of UserObjectPath) {
if (ptr.hasOwnProperty(key)) {
Expand All @@ -62,21 +62,21 @@ function handleTweetObject(obj, headers, config) {
console.error(logstr, "could not parse tweet", obj);
return;
}
BlockBlueVerified(ptr, headers, config);
BlockBlueVerified(ptr, config);
}

export function ParseTimelineTweet(tweet, headers, config) {
export function ParseTimelineTweet(tweet, config) {
if(tweet.itemType=="TimelineTimelineCursor") {
return;
}

// Handle retweets and quoted tweets (check the retweeted user, too)
if(tweet?.tweet_results?.result?.quoted_status_result) {
handleTweetObject(tweet.tweet_results.result.quoted_status_result.result, headers, config);
handleTweetObject(tweet.tweet_results.result.quoted_status_result.result, config);
} else if(tweet?.tweet_results?.result?.legacy?.retweeted_status_result) {
handleTweetObject(tweet.tweet_results.result.legacy.retweeted_status_result.result, headers, config);
handleTweetObject(tweet.tweet_results.result.legacy.retweeted_status_result.result, config);
}
handleTweetObject(tweet, headers, config);
handleTweetObject(tweet, config);
}

export function HandleInstructionsResponse(e, body, config) {
Expand Down Expand Up @@ -124,13 +124,13 @@ export function HandleInstructionsResponse(e, body, config) {

case "TimelineTimelineItem":
if (tweet.content.itemContent.itemType=="TimelineTweet") {
ParseTimelineTweet(tweet.content.itemContent, e.detail.request.headers, config);
ParseTimelineTweet(tweet.content.itemContent, config);
}
break;

case "TimelineTimelineModule":
for (const innerTweet of tweet.content.items) {
ParseTimelineTweet(innerTweet.item.itemContent, e.detail.request.headers, config)
ParseTimelineTweet(innerTweet.item.itemContent, config)
}
break;

Expand All @@ -144,9 +144,4 @@ export function HandleInstructionsResponse(e, body, config) {
}
}
}

if (isAddToModule) {
tweets.moduleItems = tweets.entries[0]?.content?.items || [];
delete tweets.entries;
}
}
4 changes: 2 additions & 2 deletions parsers/search.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BlockBlueVerified } from "../shared.js";
// This file handles requests made pertaining to search results.

export function HandleTypeahead(e, body, config) {
export function HandleTypeahead(_, body, config) {
// This endpoints appears to be extra/miscilaneous response data returned
// when doing a search. it has a user list in it, so run it through the gamut!
if (!body.users) {
Expand All @@ -25,6 +25,6 @@ export function HandleTypeahead(e, body, config) {
},
super_following: false, // meh
rest_id: user.id_str,
}, e.detail.request.headers, config);
}, config);
}
}
Loading