Skip to content

Commit 60fdbe2

Browse files
committed
feat(web): improve messaging context and notifications
1 parent c2c02c1 commit 60fdbe2

16 files changed

Lines changed: 735 additions & 85 deletions

File tree

apps/web/app/messages/[conversationId]/__tests__/conversation-content.test.tsx

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const mocks = vi.hoisted(() => ({
1111
markRead: vi.fn(),
1212
messages: [] as unknown,
1313
messagingUsage: null as unknown,
14+
peerProfile: null as unknown,
1415
sendMessage: vi.fn(),
1516
useSession: vi.fn(),
1617
}));
@@ -29,6 +30,7 @@ vi.mock("@/data/api", () => ({
2930
},
3031
queries: {
3132
messages: {
33+
getConversationPeerProfile: "getConversationPeerProfile",
3234
getMessages: "getMessages",
3335
getMessagingUsage: "getMessagingUsage",
3436
getMyConversations: "getMyConversations",
@@ -41,6 +43,7 @@ vi.mock("@/data/react", () => ({
4143
useMutation: (mutation: unknown) =>
4244
mutation === "sendMessage" ? mocks.sendMessage : mocks.markRead,
4345
useQuery: (query: unknown) => {
46+
if (query === "getConversationPeerProfile") return mocks.peerProfile;
4447
if (query === "getMessages") return mocks.messages;
4548
if (query === "getMyConversations") return mocks.conversations;
4649
if (query === "getMessagingUsage") return mocks.messagingUsage;
@@ -74,6 +77,20 @@ beforeEach(() => {
7477
otherOwner: "akshara",
7578
},
7679
];
80+
mocks.peerProfile = {
81+
avatarUrl: "https://github.com/akshara.png",
82+
bio: "Builds thoughtful developer tools for secure teams.",
83+
company: "Stackmatch Labs",
84+
followersCount: 1280,
85+
followingCount: 5,
86+
isClaimed: true,
87+
location: "Toronto, Canada",
88+
name: "Akshara Hegde",
89+
owner: "akshara",
90+
ownerType: "organization",
91+
website: "https://akshara.dev",
92+
x: "akshara",
93+
};
7794
mocks.messagingUsage = {
7895
canMessage: true,
7996
conversationCount: 1,
@@ -98,6 +115,34 @@ afterEach(() => {
98115
});
99116

100117
describe("ConversationContent", () => {
118+
it("renders a compact social profile strip for the conversation peer", () => {
119+
render(<ConversationContent />);
120+
121+
const profileLink = screen.getByRole("link", { name: /view profile/i });
122+
const bio = screen.getByText("Builds thoughtful developer tools for secure teams.");
123+
124+
expect(profileLink).toHaveAttribute("href", "/akshara");
125+
expect(screen.getByText("Akshara Hegde")).toBeInTheDocument();
126+
expect(screen.getByText("@akshara")).toBeInTheDocument();
127+
expect(screen.getByText("Verified")).toBeInTheDocument();
128+
expect(screen.getByText("Org")).toBeInTheDocument();
129+
expect(bio).toHaveClass("line-clamp-1");
130+
expect(screen.getByText("Stackmatch Labs")).toBeInTheDocument();
131+
expect(screen.getByText("1.3K followers")).toBeInTheDocument();
132+
});
133+
134+
it("falls back to the lean conversation preview while peer profile context loads", () => {
135+
mocks.peerProfile = undefined;
136+
137+
render(<ConversationContent />);
138+
139+
expect(screen.getByRole("link", { name: /view profile/i })).toHaveAttribute("href", "/akshara");
140+
expect(screen.getByText("Akshara Hegde")).toBeInTheDocument();
141+
expect(screen.getByText("@akshara")).toBeInTheDocument();
142+
expect(screen.queryByText("Verified")).not.toBeInTheDocument();
143+
expect(screen.queryByText("Stackmatch Labs")).not.toBeInTheDocument();
144+
});
145+
101146
it("keeps the empty conversation composer in the starter section", () => {
102147
render(<ConversationContent />);
103148

@@ -124,6 +169,54 @@ describe("ConversationContent", () => {
124169
expect(results).toHaveNoViolations();
125170
});
126171

172+
it("uses a neutral readable treatment for sent message bubbles", () => {
173+
mocks.messages = [
174+
{
175+
_id: "msg_1",
176+
body: "Hey Akshara! Just testing that the message feature is working :D",
177+
createdAt: Date.now(),
178+
isMine: true,
179+
senderOwner: "test-user",
180+
},
181+
];
182+
183+
render(<ConversationContent />);
184+
185+
const message = screen.getByText(
186+
"Hey Akshara! Just testing that the message feature is working :D"
187+
);
188+
const bubble = message.parentElement;
189+
190+
expect(bubble).toHaveClass("bg-neutral-800", "text-neutral-100");
191+
expect(bubble?.querySelector("p:nth-child(2)")).toHaveClass("text-neutral-400");
192+
});
193+
194+
it("keeps populated desktop conversations in natural document flow", () => {
195+
mocks.messages = [
196+
{
197+
_id: "msg_1",
198+
body: "Hey Akshara! Just testing that the message feature is working :D",
199+
createdAt: Date.now(),
200+
isMine: true,
201+
senderOwner: "test-user",
202+
},
203+
];
204+
205+
render(<ConversationContent />);
206+
207+
const shell = screen.getByTestId("conversation-shell");
208+
const messageList = screen.getByTestId("conversation-message-list");
209+
210+
expect(shell).not.toHaveClass("h-[calc(100vh-4rem)]");
211+
expect(shell).toHaveClass(
212+
"pb-8",
213+
"max-md:h-[calc(100svh-var(--header-height))]",
214+
"max-md:pb-0"
215+
);
216+
expect(messageList).not.toHaveClass("flex-1");
217+
expect(messageList).toHaveClass("min-h-0", "max-md:flex-1", "max-md:overflow-y-auto");
218+
});
219+
127220
it("sends a typed empty-state message through the conversation mutation", async () => {
128221
const user = userEvent.setup();
129222
render(<ConversationContent />);

0 commit comments

Comments
 (0)