From 9e762fd8ad7abf903521f1a1c93058618887b884 Mon Sep 17 00:00:00 2001 From: Grace Kind Date: Sat, 5 Sep 2026 06:10:45 -0500 Subject: [PATCH] Gate subscriptions on profile settings --- src/js/templates/profileCard.template.js | 17 +++- tests/e2e/specs/views/profile.view.test.js | 23 +++++ .../templates/profileCard.template.test.js | 86 +++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/js/templates/profileCard.template.js b/src/js/templates/profileCard.template.js index 92147587..5a0a6e46 100644 --- a/src/js/templates/profileCard.template.js +++ b/src/js/templates/profileCard.template.js @@ -279,6 +279,20 @@ async function openProfileContextMenu(event, props) { .addEventListener("close", () => menu.remove(), { once: true }); } +function canSubscribeToActivity(profile) { + const isFollowing = !!profile.viewer?.following; + const allowSubscriptions = + profile.associated?.activitySubscription?.allowSubscriptions ?? "followers"; + switch (allowSubscriptions) { + case "followers": + return isFollowing; + case "mutuals": + return isFollowing && !!profile.viewer?.followedBy; + default: + return false; + } +} + export function profileCardTemplate({ profile, identityResolver, @@ -311,6 +325,7 @@ export function profileCardTemplate({ const isBlocking = !!profile.viewer?.blocking; const isBlockedBy = !!profile.viewer?.blockedBy; const canChat = profileChatStatus?.canChat || !!profileChatStatus?.convo; + const canSubscribe = canSubscribeToActivity(profile); return html`
{ ).not.toBeVisible(); }); + test("should not show bell button when the user disallows subscriptions", async ({ + page, + }) => { + const closedUser = { + ...followedUser, + associated: { activitySubscription: { allowSubscriptions: "none" } }, + }; + + const mockServer = new MockServer(); + mockServer.addProfile(closedUser); + await mockServer.setup(page); + await login(page); + await page.goto(`/profile/${closedUser.did}`); + + const view = page.locator("#profile-view"); + await expect(view.locator('[data-testid="chat-button"]')).toBeVisible({ + timeout: 10000, + }); + await expect( + view.locator('[data-testid="post-notifications-button"]'), + ).not.toBeVisible(); + }); + test("should not show bell button on own profile", async ({ page }) => { const currentUserProfile = { ...userProfile, diff --git a/tests/unit/specs/templates/profileCard.template.test.js b/tests/unit/specs/templates/profileCard.template.test.js index ab7bac8f..aeb33a3d 100644 --- a/tests/unit/specs/templates/profileCard.template.test.js +++ b/tests/unit/specs/templates/profileCard.template.test.js @@ -357,6 +357,92 @@ describe("profileCardTemplate - post notifications button", () => { ); }); + it("should not render post notifications button when the profile allows no subscriptions", () => { + const profile = { + ...mockProfile, + associated: { activitySubscription: { allowSubscriptions: "none" } }, + viewer: { following: true, followedBy: true }, + }; + const result = profileCardTemplate({ + profile, + isAuthenticated: true, + isCurrentUser: false, + onClickPostNotifications: () => {}, + }); + const container = document.createElement("div"); + render(result, container); + assert.deepEqual( + container.querySelector("[data-testid='post-notifications-button']"), + null, + ); + }); + + it("should render post notifications button for mutuals only when followed back", () => { + const mutualsProfile = { + ...mockProfile, + associated: { activitySubscription: { allowSubscriptions: "mutuals" } }, + }; + const renderWith = (viewer) => { + const container = document.createElement("div"); + render( + profileCardTemplate({ + profile: { ...mutualsProfile, viewer }, + isAuthenticated: true, + isCurrentUser: false, + onClickPostNotifications: () => {}, + }), + container, + ); + return container.querySelector( + "[data-testid='post-notifications-button']", + ); + }; + assert.deepEqual(renderWith({ following: true, followedBy: false }), null); + assert(renderWith({ following: true, followedBy: true }) !== null); + }); + + it("should render post notifications button for followers when the declaration is followers", () => { + const profile = { + ...mockProfile, + associated: { activitySubscription: { allowSubscriptions: "followers" } }, + viewer: { following: true, followedBy: false }, + }; + const result = profileCardTemplate({ + profile, + isAuthenticated: true, + isCurrentUser: false, + onClickPostNotifications: () => {}, + }); + const container = document.createElement("div"); + render(result, container); + assert( + container.querySelector("[data-testid='post-notifications-button']") !== + null, + ); + }); + + it("should not render post notifications button for an unknown declaration value", () => { + const profile = { + ...mockProfile, + associated: { + activitySubscription: { allowSubscriptions: "something-new" }, + }, + viewer: { following: true, followedBy: true }, + }; + const result = profileCardTemplate({ + profile, + isAuthenticated: true, + isCurrentUser: false, + onClickPostNotifications: () => {}, + }); + const container = document.createElement("div"); + render(result, container); + assert.deepEqual( + container.querySelector("[data-testid='post-notifications-button']"), + null, + ); + }); + it("should call onClickPostNotifications when bell clicked", () => { let notificationsCallArg = null; const profile = { -- 2.51.2