Skip to content

Commit 1952c20

Browse files
chat sharing funnel
1 parent 9b3f88d commit 1952c20

5 files changed

Lines changed: 72 additions & 7 deletions

File tree

packages/web/src/app/[domain]/chat/[id]/page.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ChatVisibility } from '@sourcebot/db';
1818
import { Metadata } from 'next';
1919
import { SBChatMessage } from '@/features/chat/types';
2020
import { env, hasEntitlement } from '@sourcebot/shared';
21+
import { captureEvent } from '@/lib/posthog';
2122

2223
interface PageProps {
2324
params: Promise<{
@@ -123,7 +124,17 @@ export default async function Page(props: PageProps) {
123124
throw new ServiceErrorException(chatInfo);
124125
}
125126

126-
const { messages, name, visibility, isOwner } = chatInfo;
127+
const { messages, name, visibility, isOwner, isSharedWithUser } = chatInfo;
128+
129+
// Track when a non-owner views a shared chat
130+
if (!isOwner) {
131+
captureEvent('wa_shared_chat_viewed', {
132+
chatId: params.id,
133+
visibility,
134+
viewerType: session ? 'authenticated' : 'anonymous',
135+
accessType: isSharedWithUser ? 'direct_invite' : 'public_link',
136+
});
137+
}
127138

128139
const sharedWithUsers = (session && isOwner) ? await getSharedWithUsersForChat({ chatId: params.id }) : [];
129140

packages/web/src/app/[domain]/chat/components/shareChatPopover/index.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { isServiceError } from "@/lib/utils";
1515
import { Link2Icon, LockIcon } from "lucide-react";
1616
import { SessionUser } from "@/auth";
1717
import { InvitePanel } from "./ee/invitePanel";
18+
import { captureEvent } from "@/hooks/useCaptureEvent";
1819

1920
interface ShareChatPopoverProps {
2021
chatId: string;
@@ -42,22 +43,28 @@ export const ShareChatPopover = ({
4243
const { toast } = useToast();
4344
const [view, setView] = useState<View>('main');
4445

45-
const onChatVisibilityChange = useCallback(async (visibility: ChatVisibility) => {
46-
const response = await updateChatVisibility({ chatId, visibility });
46+
const onChatVisibilityChange = useCallback(async (newVisibility: ChatVisibility) => {
47+
const previousVisibility = visibility;
48+
const response = await updateChatVisibility({ chatId, visibility: newVisibility });
4749
if (isServiceError(response)) {
4850
toast({
4951
description: `Failed to update visibility: ${response.message}`,
5052
variant: "destructive",
5153
});
5254
return false;
5355
} else {
54-
setVisibility(visibility);
56+
setVisibility(newVisibility);
57+
captureEvent('wa_chat_visibility_changed', {
58+
chatId,
59+
fromVisibility: previousVisibility,
60+
toVisibility: newVisibility,
61+
});
5562
toast({
5663
description: "✅ Chat visibility updated"
5764
});
5865
return true;
5966
}
60-
}, [chatId, toast]);
67+
}, [chatId, toast, visibility]);
6168

6269
const onUnshareChatWithUser = useCallback(async (userId: string) => {
6370
const response = await unshareChatWithUser({ chatId, userId });
@@ -69,6 +76,7 @@ export const ShareChatPopover = ({
6976
return false;
7077
} else {
7178
setSharedWithUsers(sharedWithUsers.filter(user => user.id !== userId));
79+
captureEvent('wa_chat_user_removed', { chatId });
7280
toast({
7381
description: "✅ Access removed"
7482
});
@@ -91,6 +99,10 @@ export const ShareChatPopover = ({
9199
return false;
92100
} else {
93101
setSharedWithUsers([...sharedWithUsers, ...users]);
102+
captureEvent('wa_chat_users_invited', {
103+
chatId,
104+
numUsersInvited: users.length,
105+
});
94106
toast({
95107
description: `✅ Invited ${users.length} user${users.length > 1 ? 's' : ''}`
96108
});
@@ -101,13 +113,19 @@ export const ShareChatPopover = ({
101113

102114
const onOpenChange = useCallback((open: boolean) => {
103115
_setIsOpen(open);
116+
if (open) {
117+
captureEvent('wa_chat_share_dialog_opened', {
118+
chatId,
119+
currentVisibility: visibility,
120+
});
121+
}
104122
// Small delay to ensure the popover close animation completes
105123
setTimeout(() => {
106124
if (!open) {
107125
setView('main');
108126
}
109127
}, 100);
110-
}, []);
128+
}, [chatId, visibility]);
111129

112130

113131
return (
@@ -129,6 +147,7 @@ export const ShareChatPopover = ({
129147
<PopoverContent align="end" className="w-[420px] p-0">
130148
{view === 'main' ? (
131149
<ShareSettings
150+
chatId={chatId}
132151
visibility={visibility}
133152
onVisibilityChange={onChatVisibilityChange}
134153
onRemoveSharedWithUser={onUnshareChatWithUser}

packages/web/src/app/[domain]/chat/components/shareChatPopover/shareSettings.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import { Info, Link2Icon, Loader2, Lock, X } from "lucide-react";
2020
import Link from "next/link";
2121
import { usePathname } from "next/navigation";
2222
import { useCallback, useState } from "react";
23+
import { captureEvent } from "@/hooks/useCaptureEvent";
2324

2425
interface ShareSettingsProps {
26+
chatId: string;
2527
visibility: ChatVisibility;
2628
onVisibilityChange: (visibility: ChatVisibility) => Promise<boolean>;
2729
onRemoveSharedWithUser: (userId: string) => Promise<boolean>;
@@ -33,6 +35,7 @@ interface ShareSettingsProps {
3335
}
3436

3537
export const ShareSettings = ({
38+
chatId,
3639
visibility,
3740
onVisibilityChange,
3841
onRemoveSharedWithUser,
@@ -50,10 +53,14 @@ export const ShareSettings = ({
5053

5154
const handleCopyLink = useCallback(() => {
5255
navigator.clipboard.writeText(window.location.href);
56+
captureEvent('wa_chat_link_copied', {
57+
chatId,
58+
visibility,
59+
});
5360
toast({
5461
description: "✅ Link copied to clipboard",
5562
});
56-
}, [toast]);
63+
}, [chatId, visibility, toast]);
5764

5865
const getInitials = (name?: string | null, email?: string | null) => {
5966
if (name) {

packages/web/src/features/chat/actions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ export const getChatInfo = async ({ chatId }: { chatId: string }) => sew(() =>
141141
visibility: chat.visibility,
142142
name: chat.name,
143143
isOwner,
144+
isSharedWithUser,
144145
};
145146
})
146147
);

packages/web/src/lib/posthogEvents.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,33 @@ export type PosthogEventMap = {
158158
toolName: string,
159159
success: boolean,
160160
},
161+
// Chat Sharing Funnel
162+
wa_chat_share_dialog_opened: {
163+
chatId: string,
164+
currentVisibility: 'PUBLIC' | 'PRIVATE',
165+
},
166+
wa_chat_visibility_changed: {
167+
chatId: string,
168+
fromVisibility: 'PUBLIC' | 'PRIVATE',
169+
toVisibility: 'PUBLIC' | 'PRIVATE',
170+
},
171+
wa_chat_link_copied: {
172+
chatId: string,
173+
visibility: 'PUBLIC' | 'PRIVATE',
174+
},
175+
wa_chat_users_invited: {
176+
chatId: string,
177+
numUsersInvited: number,
178+
},
179+
wa_chat_user_removed: {
180+
chatId: string,
181+
},
182+
wa_shared_chat_viewed: {
183+
chatId: string,
184+
visibility: 'PUBLIC' | 'PRIVATE',
185+
viewerType: 'authenticated' | 'anonymous',
186+
accessType: 'public_link' | 'direct_invite',
187+
},
161188
//////////////////////////////////////////////////////////////////
162189
wa_demo_docs_link_pressed: {},
163190
wa_demo_search_example_card_pressed: {

0 commit comments

Comments
 (0)