Merge /follows/requests into /notifications as a tabbed inbox
Folds the actionable follow-requests surface into the Notifications page as a Requests tab (alongside the existing Activity tab), so the navbar exposes a single bell instead of two adjacent inboxes. The Requests tab shows a count badge for pending rows regardless of read state, while the bell badge keeps reflecting the unread-notifications count (which already covers `follow_request_received` rows). The standalone /follows/requests URL is preserved as a 301 redirect so prior notification deep-links, emails, and bookmarks still resolve. Driven by the IA review captured in docs/information-architecture.md. Specs (notifications, social-follows, journal-landing) are updated in the same change to reflect the new structure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3cd01fe9c0
commit
0306d90de8
15 changed files with 715 additions and 200 deletions
|
|
@ -102,9 +102,10 @@ test.describe("Notifications", () => {
|
|||
await page.getByRole("button", { name: /Request to follow/i }).click();
|
||||
await expect(page.getByRole("button", { name: /Requested/i })).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// B approves the request. After the fetcher.Form revalidates, the
|
||||
// empty-state copy replaces the request row.
|
||||
await bPage.goto("/follows/requests");
|
||||
// B approves the request from the Requests tab on /notifications.
|
||||
// After the fetcher.Form revalidates, the empty-state copy
|
||||
// replaces the request row.
|
||||
await bPage.goto("/notifications?tab=requests");
|
||||
await bPage.getByRole("button", { name: "Approve" }).click();
|
||||
await expect(bPage.getByText(/No pending follow requests/i)).toBeVisible({ timeout: 10000 });
|
||||
|
||||
|
|
|
|||
|
|
@ -46,9 +46,26 @@ test.describe("Social follows + /feed", () => {
|
|||
await expect(page).toHaveURL(/\/auth\/login/, { timeout: 10000 });
|
||||
});
|
||||
|
||||
test("/follows/requests redirects anonymous visitors to login", async ({ page }) => {
|
||||
test("/follows/requests redirects to /notifications?tab=requests", async ({ page, browser }) => {
|
||||
// The route was folded into the Notifications page as a tab. The
|
||||
// 301 still resolves; for anonymous visitors the notifications
|
||||
// loader then redirects to /auth/login.
|
||||
await page.goto("/follows/requests");
|
||||
await expect(page).toHaveURL(/\/auth\/login/, { timeout: 10000 });
|
||||
|
||||
// Signed-in visitors land on /notifications?tab=requests after the
|
||||
// 301.
|
||||
const cdp = await page.context().newCDPSession(page);
|
||||
await setupVirtualAuthenticator(cdp);
|
||||
const stamp = Date.now();
|
||||
const ctx = await browser.newContext();
|
||||
const p2 = await ctx.newPage();
|
||||
const p2cdp = await p2.context().newCDPSession(p2);
|
||||
await setupVirtualAuthenticator(p2cdp);
|
||||
await registerUser(p2, `redir-${stamp}@example.com`, `redir${stamp}`);
|
||||
await p2.goto("/follows/requests");
|
||||
await expect(p2).toHaveURL(/\/notifications\?tab=requests/, { timeout: 10000 });
|
||||
await ctx.close();
|
||||
});
|
||||
|
||||
test("Follow button toggles state on a public profile", async ({ page, browser }) => {
|
||||
|
|
@ -123,8 +140,8 @@ test.describe("Social follows + /feed", () => {
|
|||
await page.getByRole("button", { name: /Request to follow/i }).click();
|
||||
await expect(page.getByRole("button", { name: /Requested/i })).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// B sees the request in /follows/requests with badge in nav.
|
||||
await bPage.goto("/follows/requests");
|
||||
// B sees the request in the Requests tab on /notifications.
|
||||
await bPage.goto("/notifications?tab=requests");
|
||||
await expect(bPage.getByText(`@${aUsername}`)).toBeVisible();
|
||||
await bPage.getByRole("button", { name: "Approve" }).click();
|
||||
// Empty state after approval. The text-visibility assertion below
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue