diff --git a/app/lish/sorry-boris/fixFeeds.ts b/app/lish/sorry-boris/fixFeeds.ts
new file mode 100644
index 00000000..abca7b02
--- /dev/null
+++ b/app/lish/sorry-boris/fixFeeds.ts
@@ -0,0 +1,35 @@
+"use server";
+
+import { AppBskyActorDefs, Agent as BskyAgent } from "@atproto/api";
+import { getIdentityData } from "actions/getIdentityData";
+import { createOauthClient } from "src/atproto-oauth";
+const leafletFeedURI =
+ "at://did:plc:btxrwcaeyodrap5mnjw2fvmz/app.bsky.feed.generator/subscribedPublications";
+
+export async function fixFeeds() {
+ const oauthClient = await createOauthClient();
+ let identity = await getIdentityData();
+ if (!identity || !identity.atp_did) {
+ throw new Error("Invalid identity data");
+ }
+
+ let credentialSession = await oauthClient.restore(identity.atp_did);
+ let bsky = new BskyAgent(credentialSession);
+ let prefs = await bsky.app.bsky.actor.getPreferences();
+ let savedFeeds = prefs.data.preferences.find(
+ (pref) => pref.$type === "app.bsky.actor.defs#savedFeedsPrefV2",
+ ) as AppBskyActorDefs.SavedFeedsPrefV2;
+
+ let pubFeeds = savedFeeds.items.filter(
+ (feed) => feed.value === leafletFeedURI,
+ );
+ await bsky.removeSavedFeeds(pubFeeds.map((f) => f.id));
+
+ await bsky.addSavedFeeds([
+ {
+ value: leafletFeedURI,
+ pinned: true,
+ type: "feed",
+ },
+ ]);
+}
diff --git a/app/lish/sorry-boris/fixFeedsButton.tsx b/app/lish/sorry-boris/fixFeedsButton.tsx
new file mode 100644
index 00000000..545d4425
--- /dev/null
+++ b/app/lish/sorry-boris/fixFeedsButton.tsx
@@ -0,0 +1,31 @@
+"use client";
+
+import { useSmoker } from "components/Toast";
+import { fixFeeds } from "./fixFeeds";
+import { ButtonPrimary } from "components/Buttons";
+import { DotLoader } from "components/utils/DotLoader";
+import { useState } from "react";
+
+export function FixFeedsButton() {
+ let smoker = useSmoker();
+ let [loading, setLoading] = useState(false);
+ return (
+ {
+ let rect = e.currentTarget?.getBoundingClientRect();
+ setLoading(true);
+ await fixFeeds();
+ setLoading(false);
+ smoker({
+ position: {
+ x: rect.left + (rect.right - rect.left) / 2,
+ y: rect.top - 16,
+ },
+ text: "Fixed your feeds!",
+ });
+ }}
+ >
+ {loading ? : "Fix My Feeds"}
+
+ );
+}
diff --git a/app/lish/sorry-boris/page.tsx b/app/lish/sorry-boris/page.tsx
new file mode 100644
index 00000000..6131343a
--- /dev/null
+++ b/app/lish/sorry-boris/page.tsx
@@ -0,0 +1,69 @@
+import { getIdentityData } from "actions/getIdentityData";
+import { BlueskyLogin } from "app/login/LoginForm";
+import { codeToHtml } from "shiki";
+import { FixFeedsButton } from "./fixFeedsButton";
+
+export default async function Page() {
+ let identity = await getIdentityData();
+ if (!identity) {
+ return (
+
+
+
+ );
+ }
+ let code = await codeToHtml(source, {
+ lang: "typescript",
+ theme: "catppuccin-latte",
+ });
+ return (
+
+
+
+
+ );
+}
+
+function Layout(props: { children: React.ReactNode }) {
+ return (
+
+ {props.children}
+
+ );
+}
+
+let source = `
+ import { AppBskyActorDefs, Agent as BskyAgent } from "@atproto/api";
+ import { getIdentityData } from "actions/getIdentityData";
+ import { createOauthClient } from "src/atproto-oauth";
+ const leafletFeedURI =
+ "at://did:plc:btxrwcaeyodrap5mnjw2fvmz/app.bsky.feed.generator/subscribedPublications";
+
+ export async function fixFeeds() {
+ const oauthClient = await createOauthClient();
+ let identity = await getIdentityData();
+ if (!identity || !identity.atp_did) {
+ throw new Error("Invalid identity data");
+ }
+
+ let credentialSession = await oauthClient.restore(identity.atp_did);
+ let bsky = new BskyAgent(credentialSession);
+ let prefs = await bsky.app.bsky.actor.getPreferences();
+ let savedFeeds = prefs.data.preferences.find(
+ (pref) => pref.$type === "app.bsky.actor.defs#savedFeedsPrefV2",
+ ) as AppBskyActorDefs.SavedFeedsPrefV2;
+
+ let pubFeeds = savedFeeds.items.filter(
+ (feed) => feed.value === leafletFeedURI,
+ );
+ await bsky.removeSavedFeeds(pubFeeds.map((f) => f.id));
+
+ await bsky.addSavedFeeds([
+ {
+ value: leafletFeedURI,
+ pinned: true,
+ type: "feed",
+ },
+ ]);
+ }
+ `;