Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b75f2cc
Adds activity log URI endpoints and action selector scripts
SaptakS Jun 17, 2026
b85503b
add additional variants to `FacebookProgress`
redshiftzero Jun 21, 2026
a90f23c
add categories
redshiftzero Jun 21, 2026
9871776
facebook: update account
redshiftzero Jun 21, 2026
0d7ed66
facebook: in shared type track desired deletion categories
redshiftzero Jun 21, 2026
e04d92c
facebook: add `deleteActivity` jobs in view model
redshiftzero Jun 21, 2026
ecc8542
facebook: add migrations for data deletion
redshiftzero Jun 21, 2026
4e8f273
facebook: track aggregate deletion activity for server
redshiftzero Jun 21, 2026
83faf4f
temp: sum all deletion activity for `totalWallPostsDeleted`
redshiftzero Jun 21, 2026
7b44393
facebook: add helper function for `incrementCumulativeTotal`
redshiftzero Jun 21, 2026
b73ee47
facebook: add `FacebookDeleteCounter`
redshiftzero Jun 21, 2026
786a2ef
facebook: update and generalize the delete runner
redshiftzero Jun 21, 2026
47dfb6c
facebook: add i18n strings
redshiftzero Jun 21, 2026
f2a3004
facebook: update UI elements in `FacebookProgressComponent`
redshiftzero Jun 21, 2026
16695b5
facebook: update UI elements in `FacebookDeleteOptionsPage`
redshiftzero Jun 21, 2026
cd2ecf7
facebook: update UI elements in `FacebookReviewPage`
redshiftzero Jun 21, 2026
25cbcfc
facebook: update UI elements in `FacebookFinishedPage`
redshiftzero Jun 21, 2026
aa6f2e8
facebook: add string for upper right while deleting
redshiftzero Jun 21, 2026
7cac843
facebook: fix trash clicking
redshiftzero Jun 21, 2026
4e34519
facebook: remove bullet points in the deletion feedback
redshiftzero Jun 21, 2026
40ceb21
facebook: fixes from interactive testing, including throttling
redshiftzero Jun 21, 2026
d83add6
test: after Facebook activity log changes
redshiftzero Jun 21, 2026
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
34 changes: 11 additions & 23 deletions src/account_facebook/controller/stats/getProgressInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { FacebookAccountController } from "../../facebook_account_controlle
import {
FacebookProgressInfo,
emptyFacebookProgressInfo,
FACEBOOK_DELETE_COUNTERS,
} from "../../../shared_types";

export async function getProgressInfo(
Expand All @@ -11,33 +12,20 @@ export async function getProgressInfo(
controller.initDB();
}

const totalWallPostsDeletedConfig: string | null = await controller.getConfig(
"totalWallPostsDeleted",
);
let totalWallPostsDeleted: number = 0;
if (totalWallPostsDeletedConfig) {
totalWallPostsDeleted = parseInt(totalWallPostsDeletedConfig);
}

const totalWallPostsUntaggedConfig: string | null =
await controller.getConfig("totalWallPostsUntagged");
let totalWallPostsUntagged: number = 0;
if (totalWallPostsUntaggedConfig) {
totalWallPostsUntagged = parseInt(totalWallPostsUntaggedConfig);
}

const totalWallPostsHiddenConfig: string | null = await controller.getConfig(
"totalWallPostsHidden",
);
let totalWallPostsHidden: number = 0;
if (totalWallPostsHiddenConfig) {
totalWallPostsHidden = parseInt(totalWallPostsHiddenConfig);
// temp: we need to update the server, currently it is summing all deletion
// activity and stuffing it into totalWallPostsDeleted
let totalWallPostsDeleted = 0;
for (const counter of FACEBOOK_DELETE_COUNTERS) {
const value = await controller.getConfig(`total_${counter}`);
if (value) {
totalWallPostsDeleted += parseInt(value);
}
}

const progressInfo = emptyFacebookProgressInfo();
progressInfo.accountUUID = controller.accountUUID;
progressInfo.totalWallPostsDeleted = totalWallPostsDeleted;
progressInfo.totalWallPostsUntagged = totalWallPostsUntagged;
progressInfo.totalWallPostsHidden = totalWallPostsHidden;
progressInfo.totalWallPostsUntagged = 0;
progressInfo.totalWallPostsHidden = 0;
return progressInfo;
}
28 changes: 28 additions & 0 deletions src/database/facebook_account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ interface FacebookAccountRow {
profileImageDataURI: string;
accountID: string | null;
deleteWallPosts: number;
deleteComments: number;
deleteReactions: number;
deletePostsOnOthers: number;
deleteOthersPosts: number;
deleteCheckins: number;
deleteTaggedPosts: number;
deleteTaggedMedia: number;
userLang: string;
}

Expand All @@ -25,6 +32,13 @@ function facebookAccountRowToFacebookAccount(
profileImageDataURI: row.profileImageDataURI,
accountID: row.accountID,
deleteWallPosts: row.deleteWallPosts === 1,
deleteComments: row.deleteComments === 1,
deleteReactions: row.deleteReactions === 1,
deletePostsOnOthers: row.deletePostsOnOthers === 1,
deleteOthersPosts: row.deleteOthersPosts === 1,
deleteCheckins: row.deleteCheckins === 1,
deleteTaggedPosts: row.deleteTaggedPosts === 1,
deleteTaggedMedia: row.deleteTaggedMedia === 1,
userLang: row.userLang || "English (US)",
};
}
Expand Down Expand Up @@ -81,6 +95,13 @@ export const saveFacebookAccount = (account: FacebookAccount) => {
profileImageDataURI = ?,
accountID = ?,
deleteWallPosts = ?,
deleteComments = ?,
deleteReactions = ?,
deletePostsOnOthers = ?,
deleteOthersPosts = ?,
deleteCheckins = ?,
deleteTaggedPosts = ?,
deleteTaggedMedia = ?,
userLang = ?
WHERE id = ?
`,
Expand All @@ -89,6 +110,13 @@ export const saveFacebookAccount = (account: FacebookAccount) => {
account.profileImageDataURI,
account.accountID,
account.deleteWallPosts ? 1 : 0,
account.deleteComments ? 1 : 0,
account.deleteReactions ? 1 : 0,
account.deletePostsOnOthers ? 1 : 0,
account.deleteOthersPosts ? 1 : 0,
account.deleteCheckins ? 1 : 0,
account.deleteTaggedPosts ? 1 : 0,
account.deleteTaggedMedia ? 1 : 0,
account.userLang || "English (US)",
account.id,
],
Expand Down
14 changes: 14 additions & 0 deletions src/database/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,5 +202,19 @@ export const runMainMigrations = () => {
);`,
],
},
// Add per-category delete settings so users can choose which activity-log
// data to delete (comments, reactions, etc.) independently of wall posts.
{
name: "add per-category delete settings to facebookAccount table",
sql: [
`ALTER TABLE facebookAccount ADD COLUMN deleteComments INTEGER DEFAULT 0;`,
`ALTER TABLE facebookAccount ADD COLUMN deleteReactions INTEGER DEFAULT 0;`,
`ALTER TABLE facebookAccount ADD COLUMN deletePostsOnOthers INTEGER DEFAULT 0;`,
`ALTER TABLE facebookAccount ADD COLUMN deleteOthersPosts INTEGER DEFAULT 0;`,
`ALTER TABLE facebookAccount ADD COLUMN deleteCheckins INTEGER DEFAULT 0;`,
`ALTER TABLE facebookAccount ADD COLUMN deleteTaggedPosts INTEGER DEFAULT 0;`,
`ALTER TABLE facebookAccount ADD COLUMN deleteTaggedMedia INTEGER DEFAULT 0;`,
],
},
]);
};
35 changes: 28 additions & 7 deletions src/renderer/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"savePosts": "Saving posts",
"savePostsHTML": "Saving posts HTML",
"deleteWallPosts": "Removing wall posts",
"deleteActivity": "Deleting Facebook data",
"restoreUserLang": "Restoring language"
},
"progress": {
Expand Down Expand Up @@ -420,20 +421,39 @@
"deleteWallDescription": "Remove all posts from your Facebook wall."
},
"deleteOptions": {
"title": "Delete My Wall",
"description": "Select the data you want to remove from your Facebook wall.",
"deleteWallPosts": "Remove all posts from my wall"
"title": "Delete My Facebook Data",
"description": "Select the data you want to delete from your Facebook activity log.",
"deleteWallPosts": "Posts, photos, and videos",
"deleteComments": "Comments",
"deleteReactions": "Likes and reactions",
"deletePostsOnOthers": "Your posts on other people's timelines",
"deleteOthersPosts": "Other people's posts on your timeline",
"deleteCheckins": "Check-ins",
"deleteTaggedPosts": "Posts you're tagged in",
"deleteTaggedMedia": "Photos and videos you're tagged in"
},
"review": {
"deleteWallPosts": "All posts from your Facebook wall",
"deleteWallPosts": "Your posts, photos, and videos",
"deleteComments": "Your comments",
"deleteReactions": "Your likes and reactions",
"deletePostsOnOthers": "Your posts on other people's timelines",
"deleteOthersPosts": "Other people's posts on your timeline",
"deleteCheckins": "Your check-ins",
"deleteTaggedPosts": "Posts you're tagged in",
"deleteTaggedMedia": "Photos and videos you're tagged in",
"languageSettingsMightChange": "Your Facebook language settings might temporarily change.",
"languageSettingsDescription": "If your Facebook account isn't already set to English, Cyd will set it to English, delete your data, and then change it back to your preferred language."
},
"finished": {
"title": "Jobs Completed",
"wallPostsDeleted": "wall posts deleted",
"wallPostsUntagged": "wall posts untagged",
"wallPostsHidden": "wall posts hidden"
"wallPostsDeleted": "posts, photos, and videos deleted",
"commentsDeleted": "comments deleted",
"reactionsDeleted": "likes and reactions deleted",
"postsOnOthersDeleted": "posts on others' timelines deleted",
"othersPostsDeleted": "others' posts on your timeline deleted",
"checkinsDeleted": "check-ins deleted",
"taggedPostsDeleted": "tagged posts deleted",
"taggedMediaDeleted": "tagged photos and videos deleted"
},
"premium": {
"readyToDelete": "You're all set! Let's continue to configure what you want to delete.",
Expand Down Expand Up @@ -632,6 +652,7 @@
"savingLanguage": "I'm checking your language settings.",
"settingLanguageToEnglish": "I'm temporarily changing your language to English (US) for automation.",
"restoringLanguage": "I'm restoring your original language setting.",
"deletingCategory": "# I'm deleting **{category}** from your Facebook activity log.",
"removingWallPosts": "# I'm removing all posts from your Facebook wall.",
"managePostsLoading": "# I'm checking what posts are left to remove.",
"checkBatchActionWallPosts": "# I'm looking for a batch of posts to **{action}**...",
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/src/test_util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ export function createMockFacebookAccount(
profileImageDataURI: "",
accountID: null,
deleteWallPosts: false,
deleteComments: false,
deleteReactions: false,
deletePostsOnOthers: false,
deleteOthersPosts: false,
deleteCheckins: false,
deleteTaggedPosts: false,
deleteTaggedMedia: false,
userLang: "English (US)",
...overrides,
};
Expand Down
8 changes: 4 additions & 4 deletions src/renderer/src/util_facebook.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ describe("util_facebook", () => {
});

describe("facebookGetLastDelete", () => {
test("returns null when no lastFinishedJob_deleteWallPosts config exists", async () => {
test("returns null when no lastFinishedJob_deleteActivity config exists", async () => {
mockFacebookGetConfig.mockResolvedValue(null);

const result = await UtilFacebook.facebookGetLastDelete(1);

expect(result).toBeNull();
expect(mockFacebookGetConfig).toHaveBeenCalledWith(
1,
"lastFinishedJob_deleteWallPosts",
"lastFinishedJob_deleteActivity",
);
});

test("returns Date when lastFinishedJob_deleteWallPosts config exists", async () => {
test("returns Date when lastFinishedJob_deleteActivity config exists", async () => {
const testDate = "2024-01-15T10:30:00.000Z";
mockFacebookGetConfig.mockResolvedValue(testDate);

Expand All @@ -45,7 +45,7 @@ describe("util_facebook", () => {
expect(result?.toISOString()).toBe(testDate);
expect(mockFacebookGetConfig).toHaveBeenCalledWith(
1,
"lastFinishedJob_deleteWallPosts",
"lastFinishedJob_deleteActivity",
);
});
});
Expand Down
8 changes: 4 additions & 4 deletions src/renderer/src/util_facebook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ export async function facebookPostProgress(
export async function facebookGetLastDelete(
accountID: number,
): Promise<Date | null> {
const lastFinishedJob_deleteWallPosts =
const lastFinishedJob_deleteActivity =
await window.electron.Facebook.getConfig(
accountID,
"lastFinishedJob_deleteWallPosts",
"lastFinishedJob_deleteActivity",
);
if (lastFinishedJob_deleteWallPosts) {
return new Date(lastFinishedJob_deleteWallPosts);
if (lastFinishedJob_deleteActivity) {
return new Date(lastFinishedJob_deleteActivity);
}
return null;
}
Expand Down
96 changes: 96 additions & 0 deletions src/renderer/src/view_models/FacebookViewModel/categories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import type { FacebookAccount } from "../../../../shared_types";

// The account boolean settings that enable deleting each data category.
export type FacebookDeleteSetting =
| "deleteWallPosts"
| "deleteComments"
| "deleteReactions"
| "deletePostsOnOthers"
| "deleteOthersPosts"
| "deleteCheckins"
| "deleteTaggedPosts"
| "deleteTaggedMedia";

// The numeric FacebookProgress fields, one per category.
export type FacebookDeleteCounter =
| "wallPostsDeleted"
| "commentsDeleted"
| "reactionsDeleted"
| "postsOnOthersDeleted"
| "othersPostsDeleted"
| "checkinsDeleted"
| "taggedPostsDeleted"
| "taggedMediaDeleted";

export type FacebookDeleteCategory = {
// The FacebookAccount boolean field that enables deleting this category.
setting: FacebookDeleteSetting & keyof FacebookAccount;
// The category_key URL parameter for the Facebook activity log.
categoryKey: string;
// The FacebookProgress counter incremented as items in this category are deleted.
counter: FacebookDeleteCounter;
// The i18n key for the checkbox label shown on the delete options page.
labelKey: string;
};

// Data categories the user can choose to delete.
// The deleteActivity job, defineJobs(), and the delete options UI all derive from this.
export const FACEBOOK_DELETE_CATEGORIES: FacebookDeleteCategory[] = [
{
setting: "deleteWallPosts",
categoryKey: "MANAGEPOSTSPHOTOSANDVIDEOS",
counter: "wallPostsDeleted",
labelKey: "facebook.deleteOptions.deleteWallPosts",
},
{
setting: "deleteComments",
categoryKey: "COMMENTSCLUSTER",
counter: "commentsDeleted",
labelKey: "facebook.deleteOptions.deleteComments",
},
{
setting: "deleteReactions",
categoryKey: "LIKEDPOSTS",
counter: "reactionsDeleted",
labelKey: "facebook.deleteOptions.deleteReactions",
},
{
setting: "deletePostsOnOthers",
categoryKey: "POSTSONOTHERSTIMELINES",
counter: "postsOnOthersDeleted",
labelKey: "facebook.deleteOptions.deletePostsOnOthers",
},
{
setting: "deleteOthersPosts",
categoryKey: "WALLCLUSTER",
counter: "othersPostsDeleted",
labelKey: "facebook.deleteOptions.deleteOthersPosts",
},
{
setting: "deleteCheckins",
categoryKey: "CHECKINS",
counter: "checkinsDeleted",
labelKey: "facebook.deleteOptions.deleteCheckins",
},
{
setting: "deleteTaggedPosts",
categoryKey: "MANAGETAGSBYOTHERSCLUSTER",
counter: "taggedPostsDeleted",
labelKey: "facebook.deleteOptions.deleteTaggedPosts",
},
{
setting: "deleteTaggedMedia",
categoryKey: "TAGGEDPHOTOS",
counter: "taggedMediaDeleted",
labelKey: "facebook.deleteOptions.deleteTaggedMedia",
},
];

// Returns the categories the user has enabled for deletion on this account.
export function selectedDeleteCategories(
account: FacebookAccount,
): FacebookDeleteCategory[] {
return FACEBOOK_DELETE_CATEGORIES.filter((category) =>
Boolean(account[category.setting]),
);
}
19 changes: 19 additions & 0 deletions src/renderer/src/view_models/FacebookViewModel/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@ export async function errorJob(
vm.log("errorJob", vm.jobs[jobIndex].jobType);
}

// Persist a cumulative deletion total for a progress counter (for the server)
export async function incrementCumulativeTotal(
vm: FacebookViewModel,
counter: string,
count: number,
): Promise<void> {
if (count <= 0) {
return;
}
const key = `total_${counter}`;
const current = await window.electron.Facebook.getConfig(vm.account.id, key);
const newValue = (current ? parseInt(current) : 0) + count;
await window.electron.Facebook.setConfig(
vm.account.id,
key,
newValue.toString(),
);
}

export async function syncProgress(vm: FacebookViewModel): Promise<void> {
// For now, just log progress - can be expanded to persist to database
vm.log("syncProgress", JSON.stringify(vm.progress));
Expand Down
Loading
Loading