Skip to content

Commit

Permalink
Merge branch 'master' into no-stringify-inject
Browse files Browse the repository at this point in the history
  • Loading branch information
marcustyphoon committed Jul 20, 2024
2 parents 8e39174 + 9383a00 commit 0c5de6b
Show file tree
Hide file tree
Showing 13 changed files with 103 additions and 125 deletions.
19 changes: 12 additions & 7 deletions src/content_scripts/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,24 +76,29 @@
return installedScripts;
};

const initMainWorld = () => {
const initMainWorld = () => new Promise(resolve => {
document.documentElement.addEventListener('xkitinjectionready', resolve, { once: true });

const { nonce } = [...document.scripts].find(script => script.getAttributeNames().includes('nonce'));
const script = document.createElement('script');
script.type = 'module';
script.nonce = nonce;
script.src = browser.runtime.getURL('/main_world/index.js');
document.documentElement.append(script);
};
});

const init = async function () {
$('style.xkit').remove();

initMainWorld();

browser.storage.onChanged.addListener(onStorageChanged);

const installedScripts = await getInstalledScripts();
const { enabledScripts = [] } = await browser.storage.local.get('enabledScripts');
const [
installedScripts,
{ enabledScripts = [] }
] = await Promise.all([
getInstalledScripts(),
browser.storage.local.get('enabledScripts'),
initMainWorld()
]);

/**
* fixes WebKit (Chromium, Safari) simultaneous import failure of files with unresolved top level await
Expand Down
10 changes: 6 additions & 4 deletions src/features/postblock.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ const meatballButtonLabel = 'Block this post';
const hiddenAttribute = 'data-postblock-hidden';
const storageKey = 'postblock.blockedPostRootIDs';

const processPosts = async function (postElements) {
const { [storageKey]: blockedPostRootIDs = [] } = await browser.storage.local.get(storageKey);
let blockedPostRootIDs = [];

const processPosts = postElements =>
filterPostElements(postElements, { includeFiltered: true }).forEach(async postElement => {
const postID = postElement.dataset.id;
const { rebloggedRootId } = await timelineObject(postElement);
Expand All @@ -25,9 +25,8 @@ const processPosts = async function (postElements) {
getTimelineItemWrapper(postElement).removeAttribute(hiddenAttribute);
}
});
};

const onButtonClicked = async function ({ currentTarget }) {
const onButtonClicked = ({ currentTarget }) => {
const { id, rebloggedRootId } = currentTarget.__timelineObjectData;
const rootID = rebloggedRootId || id;

Expand All @@ -52,11 +51,14 @@ const blockPost = async rootID => {

export const onStorageChanged = async function (changes, areaName) {
if (Object.keys(changes).includes(storageKey)) {
({ newValue: blockedPostRootIDs = [] } = changes[storageKey]);
pageModifications.trigger(processPosts);
}
};

export const main = async function () {
({ [storageKey]: blockedPostRootIDs = [] } = await browser.storage.local.get(storageKey));

registerMeatballItem({ id: meatballButtonId, label: meatballButtonLabel, onclick: onButtonClicked });

onNewPosts.addListener(processPosts);
Expand Down
9 changes: 6 additions & 3 deletions src/features/quick_reblog.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ ${postSelector} footer button[aria-label="${translate('Reblog')}"]:not([role])
const renderBlogAvatar = async () => {
const { value: selectedUuid } = blogSelector;
const { avatar } = userBlogs.find(({ uuid }) => uuid === selectedUuid);
const { url } = avatar[avatar.length - 1];
const { url } = avatar.at(-1);
blogAvatar.style.backgroundImage = `url(${url})`;
};
blogSelector.addEventListener('change', renderBlogAvatar);
Expand Down Expand Up @@ -175,7 +175,6 @@ const reblogPost = async function ({ currentTarget }) {

currentTarget.blur();
actionButtons.disabled = true;
lastPostID = null;

const postElement = currentTarget.closest(postSelector);
const postID = postElement.dataset.id;
Expand Down Expand Up @@ -204,8 +203,12 @@ const reblogPost = async function ({ currentTarget }) {
const { meta, response } = await apiFetch(requestPath, { method: 'POST', body: requestBody });
if (meta.status === 201) {
makeButtonReblogged({ buttonDiv: currentReblogButton, state });
if (lastPostID === null) {

if (lastPostID === postID) {
popupElement.remove();
lastPostID = null;
} else {
// popup was moved to another post during apiFetch
}

notify(response.displayText);
Expand Down
25 changes: 19 additions & 6 deletions src/features/scroll_to_bottom.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ $(`[id="${scrollToBottomButtonId}"]`).remove();
const activeClass = 'xkit-scroll-to-bottom-active';

const loaderSelector = `
${keyToCss('timeline', 'blogRows')} > :last-child,
${keyToCss('notifications')} + ${keyToCss('loader')}
${keyToCss('timeline', 'blogRows')} > :is(${keyToCss('scrollContainer')}, .sortableContainer) + div,
${keyToCss('notifications')} + div
`;
const knightRiderLoaderSelector = `:is(${loaderSelector}) > ${keyToCss('knightRiderLoader')}`;

Expand All @@ -22,13 +22,23 @@ const styleElement = buildStyle(`
}
`);

let timeoutID;

const onLoadersAdded = loaders => {
if (active) {
clearTimeout(timeoutID);
}
};

const scrollToBottom = () => {
clearTimeout(timeoutID);
window.scrollTo({ top: document.documentElement.scrollHeight });
const loaders = [...document.querySelectorAll(knightRiderLoaderSelector)];

if (loaders.length === 0) {
stopScrolling();
}
timeoutID = setTimeout(() => {
if (!document.querySelector(knightRiderLoaderSelector)) {
stopScrolling();
}
}, 500);
};
const observer = new ResizeObserver(scrollToBottom);

Expand All @@ -40,6 +50,7 @@ const startScrolling = () => {
};

const stopScrolling = () => {
clearTimeout(timeoutID);
observer.disconnect();
active = false;
scrollToBottomButton?.classList.remove(activeClass);
Expand Down Expand Up @@ -79,12 +90,14 @@ const addButtonToPage = async function ([scrollToTopButton]) {

export const main = async function () {
pageModifications.register(`button[aria-label="${translate('Scroll to top')}"]`, addButtonToPage);
pageModifications.register(knightRiderLoaderSelector, onLoadersAdded);
document.documentElement.append(styleElement);
};

export const clean = async function () {
pageModifications.unregister(addButtonToPage);
pageModifications.unregister(checkForButtonRemoved);
pageModifications.unregister(onLoadersAdded);
stopScrolling();
scrollToBottomButton?.remove();
styleElement.remove();
Expand Down
5 changes: 0 additions & 5 deletions src/features/tag_tracking_plus.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,3 @@
#tag-tracking-plus[data-only-show-new="true"] li:not([data-new="true"]) {
display: none;
}

[data-tag-tracking-plus-show-sidebar="false"] #tag-tracking-plus,
[data-tag-tracking-plus-show-search="false"] .xkit-tag-tracking-plus-search-count {
display: none;
}
66 changes: 9 additions & 57 deletions src/features/tag_tracking_plus.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
import { apiFetch, onClickNavigate } from '../utils/tumblr_helpers.js';
import { filterPostElements } from '../utils/interface.js';
import { timelineObject } from '../utils/react_props.js';
import { keyToCss } from '../utils/css_map.js';
import { onNewPosts, pageModifications } from '../utils/mutations.js';
import { dom } from '../utils/dom.js';
import { onNewPosts } from '../utils/mutations.js';
import { addSidebarItem, removeSidebarItem } from '../utils/sidebar.js';
import { getPreferences } from '../utils/preferences.js';
import { tagTimelineFilter } from '../utils/timeline_id.js';

const storageKey = 'tag_tracking_plus.trackedTagTimestamps';
let timestamps;

const searchCountClass = 'xkit-tag-tracking-plus-search-count';

const excludeClass = 'xkit-tag-tracking-plus-done';
const includeFiltered = true;

const tagLinkSelector = `${keyToCss('searchResult')} h3 ~ a${keyToCss('typeaheadRow')}[href^="/tagged/"]`;
const tagTextSelector = keyToCss('tagText');

let trackedTags;
const unreadCounts = new Map();

Expand Down Expand Up @@ -65,19 +58,12 @@ const refreshCount = async function (tag) {
console.error(exception);
}

[document, ...(!sidebarItem || document.contains(sidebarItem) ? [] : [sidebarItem])]
.flatMap(node =>
[...node.querySelectorAll('[data-count-for]')].filter(
({ dataset: { countFor } }) => countFor === `#${tag}`
)
)
.filter((value, index, array) => array.indexOf(value) === index)
.forEach(unreadCountElement => {
unreadCountElement.textContent = unreadCountString;
if (unreadCountElement.closest('li')) {
unreadCountElement.closest('li').dataset.new = unreadCountString !== '0';
}
});
const unreadCountElement = sidebarItem.querySelector(`[data-count-for="#${tag}"]`);

unreadCountElement.textContent = unreadCountString;
if (unreadCountElement.closest('li')) {
unreadCountElement.closest('li').dataset.new = unreadCountString !== '0';
}

unreadCounts.set(tag, unreadCountString);
updateSidebarStatus();
Expand Down Expand Up @@ -141,36 +127,12 @@ const processPosts = async function (postElements) {
}
};

const processTagLinks = function (tagLinkElements) {
tagLinkElements.forEach(tagLinkElement => {
if (tagLinkElement.querySelector('[data-count-for]') !== null) return;

const tagTextElement = tagLinkElement.querySelector(tagTextSelector);
const tag = tagTextElement.textContent;
const unreadCountElement = dom(
'span',
{
class: searchCountClass,
'data-count-for': `#${tag}`,
style: 'margin-left: auto; margin-right: 1ch; opacity: 0.65;'
},
null,
[unreadCounts.get(tag) ?? '\u22EF']
);

tagTextElement.after(unreadCountElement);
});
};

export const onStorageChanged = async (changes, areaName) => {
if (Object.keys(changes).includes(storageKey)) {
timestamps = changes[storageKey].newValue;
}
if (Object.keys(changes).some(key => key.startsWith('tag_tracking_plus.preferences'))) {
const { showUnread, onlyShowNew } = await getPreferences('tag_tracking_plus');

document.body.dataset.tagTrackingPlusShowSearch = showUnread === 'both' || showUnread === 'search';
document.body.dataset.tagTrackingPlusShowSidebar = showUnread === 'both' || showUnread === 'sidebar';
const { onlyShowNew } = await getPreferences('tag_tracking_plus');
sidebarItem.dataset.onlyShowNew = onlyShowNew;
}
};
Expand All @@ -183,11 +145,7 @@ export const main = async function () {

({ [storageKey]: timestamps = {} } = await browser.storage.local.get(storageKey));

const { showUnread, onlyShowNew } = await getPreferences('tag_tracking_plus');
document.body.dataset.tagTrackingPlusShowSearch = showUnread === 'both' || showUnread === 'search';
document.body.dataset.tagTrackingPlusShowSidebar = showUnread === 'both' || showUnread === 'sidebar';

pageModifications.register(tagLinkSelector, processTagLinks);
const { onlyShowNew } = await getPreferences('tag_tracking_plus');

sidebarItem = addSidebarItem({
id: 'tag-tracking-plus',
Expand All @@ -209,16 +167,10 @@ export const main = async function () {
export const clean = async function () {
stopRefreshInterval();
onNewPosts.removeListener(processPosts);
pageModifications.unregister(processTagLinks);

removeSidebarItem('tag-tracking-plus');
$(`.${searchCountClass}`).remove();

document.body.removeAttribute('data-tag-tracking-plus-show-sidebar');
document.body.removeAttribute('data-tag-tracking-plus-show-search');

unreadCounts.clear();
sidebarItem = undefined;
};

export const stylesheet = true;
14 changes: 2 additions & 12 deletions src/features/tag_tracking_plus.json
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
{
"title": "Tag Tracking+",
"description": "Unread counts on followed tags",
"description": "Unread counts for followed tags",
"icon": {
"class_name": "ri-hashtag",
"color": "white",
"background_color": "#c538ff"
},
"help": "https://github.com/AprilSylph/XKit-Rewritten/wiki/Features#tag-tracking",
"preferences": {
"showUnread": {
"type": "select",
"label": "Show unread counts",
"options": [
{ "value": "both", "label": "Both in search & sidebar" },
{ "value": "search", "label": "Only in the search dropdown" },
{ "value": "sidebar", "label": "Only in the sidebar" }
],
"default": "both"
},
"onlyShowNew": {
"type": "checkbox",
"label": "Only show tags with new posts in the sidebar",
"label": "Only show tags with new posts",
"default": false
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/features/trim_reblogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const onButtonClicked = async function ({ currentTarget: controlButton }) {

const createPreviewItem = ({ blog, brokenBlog, content, disableCheckbox = false }) => {
const { avatar, name } = blog ?? brokenBlog ?? blogPlaceholder;
const { url: src } = avatar[avatar.length - 1];
const { url: src } = avatar.at(-1);
const textContent = content.map(({ text }) => text).find(Boolean) ?? '\u22EF';

const checkbox = dom('input', { type: 'checkbox' });
Expand Down
4 changes: 2 additions & 2 deletions src/features/tweaks/restore_attribution_links.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ const processPosts = async function (postElements) {
} = await timelineObject(postElement);
const postAttributionLink = postElement.querySelector(postAttributionLinkSelector);

if (postAttributionLink !== null) {
if (postAttributionLink && postAttributionLink.textContent === blogName) {
postAttributionLink.href = postUrl;
postAttributionLink.dataset.blogName = blogName;
postAttributionLink.dataset.postId = id;
postAttributionLink.addEventListener('click', onLinkClick, { capture: true });
}

const reblogAttributionLink = postElement.querySelector(reblogAttributionLinkSelector);
if (reblogAttributionLink !== null && rebloggedFromUrl !== undefined) {
if (reblogAttributionLink && reblogAttributionLink.textContent === rebloggedFromName) {
reblogAttributionLink.href = rebloggedFromUrl;
reblogAttributionLink.dataset.blogName = rebloggedFromName;
reblogAttributionLink.dataset.postId = rebloggedFromId;
Expand Down
42 changes: 25 additions & 17 deletions src/main_world/index.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
const moduleCache = {};
'use strict';

document.documentElement.addEventListener('xkitinjectionrequest', async event => {
const { detail, target } = event;
const { id, path, args } = JSON.parse(detail);
{
const moduleCache = {};

try {
moduleCache[path] ??= await import(path);
const func = moduleCache[path].default;
document.documentElement.addEventListener('xkitinjectionrequest', async event => {
const { detail, target } = event;
const { id, path, args } = JSON.parse(detail);

const result = await func.apply(target, args);
target.dispatchEvent(
new CustomEvent('xkitinjectionresponse', { detail: { id, result } })
);
} catch (exception) {
target.dispatchEvent(
new CustomEvent('xkitinjectionresponse', { detail: { id, exception } })
);
}
});
try {
moduleCache[path] ??= await import(path);
const func = moduleCache[path].default;

if (target.isConnected === false) return;

const result = await func.apply(target, args);
target.dispatchEvent(
new CustomEvent('xkitinjectionresponse', { detail: { id, result } })
);
} catch (exception) {
target.dispatchEvent(
new CustomEvent('xkitinjectionresponse', { detail: { id, exception } })
);
}
});

document.documentElement.dispatchEvent(new CustomEvent('xkitinjectionready'));
}
Loading

0 comments on commit 0c5de6b

Please sign in to comment.