diff --git a/src/features/quick_reblog/index.css b/src/features/quick_reblog/index.css index 4f7f617732..6adadeca94 100644 --- a/src/features/quick_reblog/index.css +++ b/src/features/quick_reblog/index.css @@ -277,20 +277,20 @@ /* === AlreadyReblogged === */ -footer.published :is(a, button):has(svg use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"])) { color: rgb(var(--green)); } -footer.queue :is(a, button):has(svg use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"])) { color: rgb(var(--purple)); } -footer.draft :is(a, button):has(svg use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"])) { color: rgb(var(--red)); } +[data-already-reblogged-state="published"] :is(a, button):has(svg use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"])) { color: rgb(var(--green)); } +[data-already-reblogged-state="queue"] :is(a, button):has(svg use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"])) { color: rgb(var(--purple)); } +[data-already-reblogged-state="draft"] :is(a, button):has(svg use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"])) { color: rgb(var(--red)); } -footer:is(.published, .queue, .draft) :is(a, button) svg use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"]) { +[data-already-reblogged-state] :is(a, button) svg use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"]) { --icon-color-primary: currentColor; /* Ensure that colouration is applied to icon */ } -footer:is(.published, .queue, .draft) :is(a, button):has(svg use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"])) { +[data-already-reblogged-state] :is(a, button):has(svg use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"])) { position: relative; /* For absolute positioning of tick pseudo-element */ } /* Cut-out for tick in 2025 post footer */ -footer:is(.published, .queue, .draft) :is(a, button) svg:has(use[href="#managed-icon__ds-reblog-24"]) { +[data-already-reblogged-state] :is(a, button) svg:has(use[href="#managed-icon__ds-reblog-24"]) { -webkit-mask-image: radial-gradient( calc(14.7px / 2) calc(18px / 2) at bottom 4px left 21px, transparent 99%, @@ -304,7 +304,7 @@ footer:is(.published, .queue, .draft) :is(a, button) svg:has(use[href="#managed- } /* Styles for tick pseudo-element */ -footer:is(.published, .queue, .draft) :is(a, button):has(use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"]))::after { +[data-already-reblogged-state] :is(a, button):has(use:is([href="#managed-icon__reblog"], [href="#managed-icon__ds-reblog-24"]))::after { position: absolute; display: inline-block; @@ -320,14 +320,14 @@ footer:is(.published, .queue, .draft) :is(a, button):has(use:is([href="#managed- } /* Pre-2025 post footer */ -footer:is(.published, .queue, .draft) a:has(use[href="#managed-icon__reblog"])::after { +[data-already-reblogged-state] a:has(use[href="#managed-icon__reblog"])::after { background-color: var(--content-panel); bottom: -5px; right: -5px; } /* 2025 post footer */ -footer:is(.published, .queue, .draft) :is(a, button):has(use[href="#managed-icon__ds-reblog-24"])::after { +[data-already-reblogged-state] :is(a, button):has(use[href="#managed-icon__ds-reblog-24"])::after { bottom: 4px; left: 21px; } diff --git a/src/features/quick_reblog/index.js b/src/features/quick_reblog/index.js index 74629aeb0a..549366e5f2 100644 --- a/src/features/quick_reblog/index.js +++ b/src/features/quick_reblog/index.js @@ -7,7 +7,7 @@ import { onNewPosts } from '../../utils/mutations.js'; import { notify } from '../../utils/notifications.js'; import { popoverStackingContextFix } from '../../utils/post_popovers.js'; import { getPreferences } from '../../utils/preferences.js'; -import { timelineObject } from '../../utils/react_props.js'; +import { timelineObject, trailItem } from '../../utils/react_props.js'; import { apiFetch } from '../../utils/tumblr_helpers.js'; import { joinedCommunities, joinedCommunityUuids, primaryBlog, userBlogs } from '../../utils/user.js'; @@ -84,7 +84,7 @@ const blogHashes = new Map(); const avatarUrls = new Map(); const buttonSelector = `${postSelector} footer a, ${postSelector} footer button`; -const reblogButtonSelector = `${postSelector} footer :is(a[href*="/reblog/"], button:has(use[href="#managed-icon__ds-reblog-24"])):not(${keyToCss('reblog')} *)`; +const reblogButtonSelector = `${postSelector} footer :is(a[href*="/reblog/"], button:has(use[href="#managed-icon__ds-reblog-24"]))`; const buttonDivSelector = `${keyToCss('controls', 'reblogsControl', 'engagementControls')} > *`; export const styleElement = buildStyle(` @@ -176,8 +176,13 @@ const showPopupOnHover = async ({ currentTarget }) => { const buttonDiv = currentTarget.closest(buttonDivSelector) ?? currentTarget.parentElement; if (buttonDiv.matches(':has([aria-expanded="true"])')) return; - const thisPost = currentTarget.closest(postSelector); - const { blog, canReblog } = await timelineObject(thisPost); + const timelineObjectData = await timelineObject(currentTarget); + const trailItemData = await trailItem(currentTarget); + + const { blog } = trailItemData ?? timelineObjectData; + const { canReblog, id: thisPostID } = trailItemData?.post ?? timelineObjectData; + + // todo: check what happens if you reblog a post from a blog, password protect the blog, then hover the reblog if (canReblog === false || blog?.isPasswordProtected) return; clearTimeout(timeoutID); @@ -187,14 +192,13 @@ const showPopupOnHover = async ({ currentTarget }) => { popupElement.parentNode.addEventListener('mouseleave', removePopupOnLeave); document.body.addEventListener('touchend', removePopupOnOutsideTouch); - const thisPostID = thisPost.dataset.id; if (thisPostID !== lastPostID) { if (!rememberLastBlog) { blogSelector.value = blogSelector.options[0].value; onBlogSelectorChange(); } resetPopupState(); - timelineObject(thisPost).then(renderTagSuggestions); + renderTagSuggestions(timelineObjectData); } lastPostID = thisPostID; }; @@ -223,20 +227,26 @@ const removePopupOnOutsideTouch = async ({ target }) => { } }; -const markPostReblogged = ({ footer, state }) => { - footer.classList.remove('published', 'queue', 'draft'); - footer.classList.add(state); +const markPostReblogged = ({ postElement, state }) => { + postElement.dataset.alreadyRebloggedState = state; }; /** @param {PointerEvent} event actionButtons.children[*] click event object */ async function reblogPost ({ currentTarget }) { - const footer = popupElement.closest('footer'); - currentTarget.blur(); actionButtons.disabled = true; - const postElement = currentTarget.closest(postSelector); - const postID = postElement.dataset.id; + const footer = currentTarget.closest('footer'); + + const timelineObjectData = await timelineObject(footer); + const trailItemData = await trailItem(footer); + + const { blog: { uuid: parentTumblelogUUID } } = trailItemData ?? timelineObjectData; + const { id: postID } = trailItemData?.post ?? timelineObjectData; + + // trail items have the same reblog key as their parent post + const { reblogKey } = timelineObjectData; + const { state } = currentTarget.dataset; const blog = blogSelector.value; @@ -245,7 +255,6 @@ async function reblogPost ({ currentTarget }) { ...reblogTag ? [reblogTag] : [], ...(state === 'queue' && queueTag) ? [queueTag] : [], ].join(','); - const { blog: { uuid: parentTumblelogUUID }, reblogKey, rebloggedRootId } = await timelineObject(postElement); const requestPath = `/v2/blog/${blog}/posts`; @@ -261,7 +270,7 @@ async function reblogPost ({ currentTarget }) { try { const { meta, response } = await apiFetch(requestPath, { method: 'POST', body: requestBody }); if (meta.status === 201) { - markPostReblogged({ footer, state }); + markPostReblogged({ postElement: currentTarget.closest(postSelector), state }); if (lastPostID === postID) { popupElement.remove(); @@ -274,7 +283,7 @@ async function reblogPost ({ currentTarget }) { if (alreadyRebloggedEnabled) { const { [alreadyRebloggedStorageKey]: alreadyRebloggedList = [] } = await browser.storage.local.get(alreadyRebloggedStorageKey); - const rootID = rebloggedRootId || postID; + const rootID = timelineObjectData.rebloggedRootId || postID; if (alreadyRebloggedList.includes(rootID) === false) { alreadyRebloggedList.push(rootID); @@ -299,9 +308,7 @@ const processPosts = async function (postElements) { const rootID = rebloggedRootId || id; if (alreadyRebloggedList.includes(rootID)) { - const reblogLink = postElement.querySelector(reblogButtonSelector); - const footer = reblogLink?.closest('footer'); - if (footer) markPostReblogged({ footer, state: 'published' }); + markPostReblogged({ postElement, state: 'published' }); } }); };