feat: neues Tier folgt dem Kurator-Tier zurück
Bisher folgte nur "PawFeed Team" dem neuen Tier, der Feed des neuen Nutzers startete leer. welcomeNewPet legt jetzt auch den Follow in Gegenrichtung an und befüllt den Redis-Feed per backfillOnFollow mit den letzten Team-Posts. Gleicher Admin-Schalter (autoFollowEnabled), Beschriftung im Kurator-Panel angepasst. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
80e27a7f0b
commit
f112d36f4a
3 files changed
+42
-3
No files matched your search
@@ -13,6 +13,9 @@ vi.mock("@/lib/team-pet", () => ({
|
||||
ensureTeamPet: vi.fn().mockResolvedValue({ id: "pet-team" }),
|
||||
sendTeamMessage: (...args: unknown[]) => mockSendTeamMessage(...args),
|
||||
}));
|
||||
vi.mock("@/lib/redis", () => ({ redis: {} }));
|
||||
const mockBackfillOnFollow = vi.fn().mockResolvedValue(undefined);
|
||||
vi.mock("@/lib/feed-helpers", () => ({ backfillOnFollow: (...args: unknown[]) => mockBackfillOnFollow(...args) }));
|
||||
const mockAssertAdmin = vi.fn();
|
||||
vi.mock("@/trpc/routers/admin", () => ({ assertAdmin: (...args: unknown[]) => mockAssertAdmin(...args) }));
|
||||
|
||||
@@ -67,6 +70,28 @@ describe("welcomeNewPet", () => {
|
||||
expect(mockSendTeamMessage).toHaveBeenCalledWith(expect.anything(), "pet-luna", expect.stringContaining("Hallo Luna"));
|
||||
});
|
||||
|
||||
it("makes the new pet follow the team back and seeds its feed with the team's posts", async () => {
|
||||
const prisma = createMockPrisma();
|
||||
prisma.follow.upsert.mockResolvedValue({});
|
||||
|
||||
await welcomeNewPet(asDb(prisma), { id: "pet-luna", name: "Luna" }, "de");
|
||||
|
||||
expect(prisma.follow.upsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ create: { followerPetId: "pet-luna", followeePetId: "pet-team" } })
|
||||
);
|
||||
expect(mockBackfillOnFollow).toHaveBeenCalledWith(expect.anything(), "pet-luna", "pet-team", expect.anything());
|
||||
});
|
||||
|
||||
it("still sends the welcome when following the team back fails", async () => {
|
||||
const prisma = createMockPrisma();
|
||||
prisma.follow.upsert.mockResolvedValueOnce({}).mockRejectedValueOnce(new Error("db down"));
|
||||
|
||||
await welcomeNewPet(asDb(prisma), { id: "pet-luna", name: "Luna" }, "de");
|
||||
|
||||
expect(mockBackfillOnFollow).not.toHaveBeenCalled();
|
||||
expect(mockSendTeamMessage).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses the English text for English users", async () => {
|
||||
const prisma = createMockPrisma();
|
||||
prisma.follow.upsert.mockResolvedValue({});
|
||||
@@ -86,6 +111,7 @@ describe("welcomeNewPet", () => {
|
||||
await welcomeNewPet(asDb(prisma), { id: "pet-kiki", name: "Kiki" }, "de");
|
||||
|
||||
expect(prisma.follow.upsert).not.toHaveBeenCalled();
|
||||
expect(mockBackfillOnFollow).not.toHaveBeenCalled();
|
||||
expect(mockSendTeamMessage).toHaveBeenCalledWith(expect.anything(), "pet-kiki", "Servus Kiki!");
|
||||
});
|
||||
|
||||
|
||||
@@ -80,8 +80,8 @@ function CuratorForm({ initial }: { initial: Form }) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<ToggleRow
|
||||
title="Team folgt jedem neuen Tier"
|
||||
hint="Das neue Tier bekommt „PawFeed Team“ als ersten Follower und eine Benachrichtigung."
|
||||
title="Team und neues Tier folgen sich gegenseitig"
|
||||
hint="Das neue Tier bekommt „PawFeed Team“ als ersten Follower (mit Benachrichtigung) und folgt dem Team selbst, damit sein Feed nicht leer startet."
|
||||
checked={form.autoFollowEnabled}
|
||||
onChange={(v) => set("autoFollowEnabled", v)}
|
||||
/>
|
||||
|
||||
@@ -2,12 +2,15 @@ import "server-only";
|
||||
import type { PrismaClient } from "@prisma/client";
|
||||
import { ensureTeamPet, sendTeamMessage } from "@/lib/team-pet";
|
||||
import { notify } from "@/lib/notify";
|
||||
import { redis } from "@/lib/redis";
|
||||
import { backfillOnFollow } from "@/lib/feed-helpers";
|
||||
|
||||
/**
|
||||
* Was beim Anlegen eines neuen Tiers automatisch passiert (aufgerufen aus
|
||||
* pets.create, nie blockierend):
|
||||
* 1. Gründungsmitglied, wenn es eines der ersten FOUNDING_MEMBER_LIMIT Tiere ist.
|
||||
* 2. Das Kurator-Tier „PawFeed Team“ folgt dem neuen Tier.
|
||||
* 2. Das Kurator-Tier „PawFeed Team“ und das neue Tier folgen sich gegenseitig
|
||||
* (der Feed des neuen Tiers wird mit den Team-Posts befüllt).
|
||||
* 3. Das Kurator-Tier schickt eine Begrüßungs-Direktnachricht.
|
||||
* 2 und 3 sind im Admin-Panel abschaltbar, der Text ist dort bearbeitbar
|
||||
* (CuratorSettings). {name} im Text wird durch den Tiernamen ersetzt.
|
||||
@@ -84,6 +87,16 @@ export async function welcomeNewPet(
|
||||
})
|
||||
.then(() => notify(prisma, { recipientPetId: pet.id, type: "FOLLOW", actorPetId: teamPet.id }))
|
||||
.catch((err) => console.error("[welcome] curator follow failed", err));
|
||||
|
||||
// Gegenrichtung: das neue Tier folgt dem Team, damit sein Feed nicht leer startet.
|
||||
await prisma.follow
|
||||
.upsert({
|
||||
where: { followerPetId_followeePetId: { followerPetId: pet.id, followeePetId: teamPet.id } },
|
||||
create: { followerPetId: pet.id, followeePetId: teamPet.id },
|
||||
update: {},
|
||||
})
|
||||
.then(() => backfillOnFollow(redis, pet.id, teamPet.id, prisma))
|
||||
.catch((err) => console.error("[welcome] follow-back of curator failed", err));
|
||||
}
|
||||
|
||||
if (settings.welcomeEnabled) {
|
||||
|
||||
Reference in new issue
Block a user