diff --git a/extension/manifest.json b/extension/manifest.json index 9cfa4e0..fbe713f 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "CleanMyPosts", - "version": "1.0.0", + "version": "1.0.1", "description": "Bulk-delete your posts, replies, reposts, likes and followings on X, and your comments and liked videos on YouTube.", "icons": { "32": "icons/32x32.png", diff --git a/extension/src/background.ts b/extension/src/background.ts index 3d15428..61f98e8 100644 --- a/extension/src/background.ts +++ b/extension/src/background.ts @@ -43,7 +43,7 @@ function targetUrl(platform: Platform, action: Action, userName: string): string case 'deleteReplies': return `https://x.com/${user}/with_replies`; case 'deleteReposts': - return `https://x.com/${user}`; + return `https://x.com/${user}/reposts`; case 'deleteLikes': return `https://x.com/${user}/likes`; case 'deleteFollowing': diff --git a/release-notes/v3.5.1.md b/release-notes/v3.5.1.md new file mode 100644 index 0000000..6f0478d --- /dev/null +++ b/release-notes/v3.5.1.md @@ -0,0 +1,20 @@ +### What's Changed + +**Reposts moved, the app follows** + +- Fix: X moved reposts off the profile timeline into a Reposts tab of their own. Deleting + reposts still opened the profile, found nothing there that could be unretweeted, and + reported nothing to remove (#64). The app and the extension now open + `x.com//reposts`, which is where the reposts went. +- Fix: The standalone script for reposts names the Reposts tab as the page to run it on. + Download it again if you kept a copy. + +**Elsewhere** + +- New: A reload button in the header, beside the assistant's, while X or YouTube is up — + triggering the same reload a finished run already does. +- Fix: The address in the header now follows every navigation the page makes, wherever a + link leads. It used to freeze on the last platform page once the user left the + platform's own hosts. + +The extension carries the same fix on its own version line as v1.0.1. diff --git a/scripts/build-standalone.mjs b/scripts/build-standalone.mjs index ffb8355..d34a839 100644 --- a/scripts/build-standalone.mjs +++ b/scripts/build-standalone.mjs @@ -41,7 +41,7 @@ const ACTIONS = [ file: 'cleanmyposts-x-delete-reposts', from: 'x/reposts', name: 'repostsAction', - page: 'your profile' + page: 'your profile’s Reposts tab' }, { file: 'cleanmyposts-x-delete-likes', from: 'x/likes', name: 'likesAction', page: 'your likes' }, { diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 1b4a9f3..e15a499 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -518,7 +518,7 @@ dependencies = [ [[package]] name = "cleanmyposts" -version = "3.5.0" +version = "3.5.1" dependencies = [ "dirs", "keyring", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 2174371..0131812 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cleanmyposts" -version = "3.5.0" +version = "3.5.1" description = "Bulk-delete your posts, reposts, replies, likes and comments on X and YouTube" authors = ["thorstenalpers"] repository = "https://github.com/thorstenalpers/CleanMyPosts" diff --git a/src-tauri/src/commands/site.rs b/src-tauri/src/commands/site.rs index f6bc74f..5772032 100644 --- a/src-tauri/src/commands/site.rs +++ b/src-tauri/src/commands/site.rs @@ -23,7 +23,7 @@ fn target_url(platform: &str, action: &str, user_name: &str) -> Option { ("x", "showReplies" | "deleteReplies") => { format!("https://x.com/{user}/with_replies") } - ("x", "showReposts" | "deleteReposts") => format!("https://x.com/{user}"), + ("x", "showReposts" | "deleteReposts") => format!("https://x.com/{user}/reposts"), ("x", "showLikes" | "deleteLikes") => format!("https://x.com/{user}/likes"), ("x", "showFollowing" | "deleteFollowing") => { format!("https://x.com/{user}/following") @@ -663,7 +663,7 @@ mod tests { ); assert_eq!( target_url("x", "deleteReposts", "someuser").unwrap(), - "https://x.com/someuser" + "https://x.com/someuser/reposts" ); assert_eq!( target_url("x", "deleteLikes", "someuser").unwrap(), diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e254d7b..b79bd8a 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -533,13 +533,31 @@ pub fn run() { // may land a beat early. let auto_consent = app.state::().settings.get().auto_consent; let site_window = window.clone(); + let nav_handle = app.handle().clone(); let _ = window.run_on_main_thread(move || { for (label, url) in SITE_WEBVIEWS { + // The injected reporter goes quiet the moment the user leaves the + // platform's hosts, and the header would keep showing the last address it + // heard. This hook fires on every document navigation, whatever the host. + let platform = if label == "site-youtube" { + "youtube" + } else { + "x" + }; + let handle = nav_handle.clone(); let built = site_window.add_child( WebviewBuilder::new( label, WebviewUrl::External(url.parse().expect("static url")), ) + .on_navigation(move |url| { + crate::bridge::push_event( + &handle, + "siteUrl", + serde_json::json!({ "platform": platform, "url": url.as_str() }), + ); + true + }) .initialization_script_for_all_frames(site_init_script(auto_consent)), LogicalPosition::new(size.width, f64::from(DEFAULT_HEADER_HEIGHT)), LogicalSize::new( diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 958eb0c..2c1e204 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "CleanMyPosts", - "version": "3.5.0", + "version": "3.5.1", "identifier": "com.thorstenalpers.cleanmyposts", "build": { "frontendDist": "../build", diff --git a/src/lib/bridge/contract.ts b/src/lib/bridge/contract.ts index 0882be1..fec1ba2 100644 --- a/src/lib/bridge/contract.ts +++ b/src/lib/bridge/contract.ts @@ -467,6 +467,17 @@ export const SiteLoginPayloadSchema = z.object({ }); export type SiteLoginPayload = z.infer; +/** + * Every document navigation of a platform's webview, reported by the host. The injected + * reporter only speaks on the platform's own hosts; this is what keeps the header's address + * honest when the user follows a link off them. + */ +export const SiteUrlPayloadSchema = z.object({ + platform: PlatformSchema, + url: z.string() +}); +export type SiteUrlPayload = z.infer; + export const UpdateProgressPayloadSchema = z.object({ downloaded: z.number().int().nonnegative(), /** Absent when the server sends no length; the bar runs indeterminate then. */ @@ -479,7 +490,8 @@ export const PushEventSchema = z.discriminatedUnion('event', [ z.object({ event: z.literal('progress'), payload: ProgressPayloadSchema }), z.object({ event: z.literal('updateProgress'), payload: UpdateProgressPayloadSchema }), z.object({ event: z.literal('settingsChanged'), payload: AppSettingsSchema }), - z.object({ event: z.literal('siteLogin'), payload: SiteLoginPayloadSchema }) + z.object({ event: z.literal('siteLogin'), payload: SiteLoginPayloadSchema }), + z.object({ event: z.literal('siteUrl'), payload: SiteUrlPayloadSchema }) ]); export type PushEvent = z.infer; diff --git a/src/lib/components/page-header.svelte b/src/lib/components/page-header.svelte index 720d3a0..799b31e 100644 --- a/src/lib/components/page-header.svelte +++ b/src/lib/components/page-header.svelte @@ -4,6 +4,7 @@ import LanguageMenu from '$lib/components/language-menu.svelte'; import ModeToggle from '$lib/components/mode-toggle.svelte'; import PanelLeftIcon from '@lucide/svelte/icons/panel-left'; + import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw'; import SparklesIcon from '@lucide/svelte/icons/sparkles'; import { cn } from '$lib/utils'; import type { SettingsStore } from '$lib/stores/settings.svelte'; @@ -28,6 +29,8 @@ * it, so this is the way back. */ onOpenActions?: () => void; + /** Given only while a platform is up: the app's own pages have nothing to reload. */ + onReload?: () => void; /** The assistant lives in the app's own column, so its way in belongs in this bar. */ onToggleAssistant?: () => void; assistantOpen?: boolean; @@ -40,6 +43,7 @@ iconOnly = false, settingsStore, onMenuOpenChange, + onReload, onOpenActions, onToggleAssistant, assistantOpen = false @@ -76,6 +80,17 @@
+ {#if onReload} + + {/if} {#if onToggleAssistant}