Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-navigation-menu": "^1.2.0",
"@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-separator": "^1.1.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
'use client';

import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { cn, isServiceError } from "@/lib/utils";
import { ChatVisibility } from "@sourcebot/db";
import { GlobeIcon } from "@radix-ui/react-icons";
import { LockIcon, Router } from "lucide-react";
import { useEffect, useState } from "react";
import { changeChatVisibility } from "@/features/chat/actions";
import { toast } from "@/components/hooks/use-toast";
import { useRouter } from "next/navigation";

interface ShareChatDialogButtonProps {
visibility: ChatVisibility;
chatId: string;
}

export const ShareChatDialogButton = ({ visibility,chatId }: ShareChatDialogButtonProps) => {
const [isOpen, setIsOpen] = useState(false);
const [selectedVisibility, setSelectedVisibility] = useState<ChatVisibility>(visibility);
const router=useRouter()

useEffect(() => {
setSelectedVisibility(visibility);
}, [visibility]);

const onChangeVisibility = (visibility: ChatVisibility) => {
changeChatVisibility({
chatId: chatId,
visibility,
}).then((response)=>{
if (isServiceError(response)) {
toast({
description: `❌ Failed to change chat visibility. Reason: ${response.message}`,
variant: "destructive",
});
}

toast({
description: `Chat visibility changed to ${visibility}`,
});
setSelectedVisibility(visibility);
router.refresh()
})
};

return (
<Dialog
open={isOpen}
onOpenChange={setIsOpen}
>
<Button
size="sm"
onClick={() => setIsOpen(true)}
>
Share
</Button>
<DialogContent>
<DialogHeader>
<DialogTitle>Share chat</DialogTitle>
<DialogDescription>
Choose who can access this chat.
</DialogDescription>
</DialogHeader>
<RadioGroup
className="grid gap-3"
value={selectedVisibility}
onValueChange={(value) => onChangeVisibility(value as ChatVisibility)}
>
<Label
htmlFor="visibility-public"
className={cn(
"flex items-start gap-3 rounded-md border p-3 text-left transition-colors",
selectedVisibility === ChatVisibility.PUBLIC
? "border-primary bg-primary/5"
: "border-input hover:bg-accent"
)}
>
<RadioGroupItem id="visibility-public" value={ChatVisibility.PUBLIC} className="sr-only"/>
<GlobeIcon className="h-4 w-4 mt-0.5" />
<div>
<p className="text-sm font-medium">Public</p>
<p className="text-xs text-muted-foreground">
Anyone with the link can view this chat.
</p>
</div>
</Label>
<Label
htmlFor="visibility-private"
className={cn(
"flex items-start gap-3 rounded-md border p-3 text-left transition-colors",
selectedVisibility === ChatVisibility.PRIVATE
? "border-primary bg-primary/5"
: "border-input hover:bg-accent"
)}
>
<RadioGroupItem id="visibility-private" value={ChatVisibility.PRIVATE} className="sr-only"/>
<LockIcon className="h-4 w-4 mt-0.5" />
<div>
<p className="text-sm font-medium">Private</p>
<p className="text-xs text-muted-foreground">
Only you can view and edit this chat.
</p>
</div>
</Label>
</RadioGroup>
<DialogFooter>
<Button
variant="outline"
onClick={() => setIsOpen(false)}
>
Done
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
20 changes: 12 additions & 8 deletions packages/web/src/app/[domain]/chat/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { auth } from '@/auth';
import { AnimatedResizableHandle } from '@/components/ui/animatedResizableHandle';
import { ChatSidePanel } from '../components/chatSidePanel';
import { ResizablePanelGroup } from '@/components/ui/resizable';
import { ShareChatDialogButton } from './components/shareChatDialogButton';

interface PageProps {
params: Promise<{
Expand Down Expand Up @@ -58,14 +59,17 @@ export default async function Page(props: PageProps) {
domain={params.domain}
homePath={`/${params.domain}/chat`}
>
<div className="flex flex-row gap-2 items-center">
<span className="text-muted mx-2 select-none">/</span>
<ChatName
name={name}
visibility={visibility}
id={params.id}
isReadonly={isReadonly}
/>
<div className='flex flex-row justify-between items-center w-full'>
<div className="flex flex-row gap-2 items-center">
<span className="text-muted mx-2 select-none">/</span>
<ChatName
name={name}
visibility={visibility}
id={params.id}
isReadonly={isReadonly}
/>
</div>
<ShareChatDialogButton visibility={visibility} chatId={params.id}/>
</div>
</TopBar>
<ResizablePanelGroup
Expand Down
44 changes: 44 additions & 0 deletions packages/web/src/components/ui/radio-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client"

import * as React from "react"
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
import { Circle } from "lucide-react"

import { cn } from "@/lib/utils"

const RadioGroup = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
>(({ className, ...props }, ref) => {
return (
<RadioGroupPrimitive.Root
className={cn("grid gap-2", className)}
{...props}
ref={ref}
/>
)
})
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName

const RadioGroupItem = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
>(({ className, ...props }, ref) => {
return (
<RadioGroupPrimitive.Item
ref={ref}
className={cn(
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
<Circle className="h-2.5 w-2.5 fill-current text-current" />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
)
})
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName

export { RadioGroup, RadioGroupItem }
35 changes: 35 additions & 0 deletions packages/web/src/features/chat/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,41 @@ export const submitFeedback = async ({
})
)

export const changeChatVisibility=async ({chatId,visibility}:{chatId:string,visibility:ChatVisibility})=>sew(()=>
withOptionalAuthV2(async ({org,user,prisma})=>{
const chat=await prisma.chat.findUnique({
where:{
id:chatId,
orgId:org.id,
}
})

if (!chat) {
return notFound();
}

if (chat.createdById !== user?.id) {
return notFound();
}

console.log(chat)

await prisma.chat.update({
where: {
id: chatId,
orgId: org.id,
},
data: {
visibility,
},
});

return {
success: true,
}
})
)

/**
* Returns the subset of information about the configured language models
* that we can safely send to the client.
Expand Down
96 changes: 96 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4900,6 +4900,13 @@ __metadata:
languageName: node
linkType: hard

"@radix-ui/primitive@npm:1.1.3":
version: 1.1.3
resolution: "@radix-ui/primitive@npm:1.1.3"
checksum: 10c0/88860165ee7066fa2c179f32ffcd3ee6d527d9dcdc0e8be85e9cb0e2c84834be8e3c1a976c74ba44b193f709544e12f54455d892b28e32f0708d89deda6b9f1d
languageName: node
linkType: hard

"@radix-ui/react-accordion@npm:^1.2.11":
version: 1.2.11
resolution: "@radix-ui/react-accordion@npm:1.2.11"
Expand Down Expand Up @@ -5735,6 +5742,26 @@ __metadata:
languageName: node
linkType: hard

"@radix-ui/react-presence@npm:1.1.5":
version: 1.1.5
resolution: "@radix-ui/react-presence@npm:1.1.5"
dependencies:
"@radix-ui/react-compose-refs": "npm:1.1.2"
"@radix-ui/react-use-layout-effect": "npm:1.1.1"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10c0/d0e61d314250eeaef5369983cb790701d667f51734bafd98cf759072755562018052c594e6cdc5389789f4543cb0a4d98f03ff4e8f37338d6b5bf51a1700c1d1
languageName: node
linkType: hard

"@radix-ui/react-primitive@npm:1.0.3":
version: 1.0.3
resolution: "@radix-ui/react-primitive@npm:1.0.3"
Expand Down Expand Up @@ -5812,6 +5839,61 @@ __metadata:
languageName: node
linkType: hard

"@radix-ui/react-radio-group@npm:^1.3.8":
version: 1.3.8
resolution: "@radix-ui/react-radio-group@npm:1.3.8"
dependencies:
"@radix-ui/primitive": "npm:1.1.3"
"@radix-ui/react-compose-refs": "npm:1.1.2"
"@radix-ui/react-context": "npm:1.1.2"
"@radix-ui/react-direction": "npm:1.1.1"
"@radix-ui/react-presence": "npm:1.1.5"
"@radix-ui/react-primitive": "npm:2.1.3"
"@radix-ui/react-roving-focus": "npm:1.1.11"
"@radix-ui/react-use-controllable-state": "npm:1.2.2"
"@radix-ui/react-use-previous": "npm:1.1.1"
"@radix-ui/react-use-size": "npm:1.1.1"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10c0/23af8e8b833da1fc4aa4e67c3607dedee4fc5b39278d2e2b820bec7f7b3c0891b006a8a35c57ba436ddf18735bbd8dad9a598d14632a328753a875fde447975c
languageName: node
linkType: hard

"@radix-ui/react-roving-focus@npm:1.1.11":
version: 1.1.11
resolution: "@radix-ui/react-roving-focus@npm:1.1.11"
dependencies:
"@radix-ui/primitive": "npm:1.1.3"
"@radix-ui/react-collection": "npm:1.1.7"
"@radix-ui/react-compose-refs": "npm:1.1.2"
"@radix-ui/react-context": "npm:1.1.2"
"@radix-ui/react-direction": "npm:1.1.1"
"@radix-ui/react-id": "npm:1.1.1"
"@radix-ui/react-primitive": "npm:2.1.3"
"@radix-ui/react-use-callback-ref": "npm:1.1.1"
"@radix-ui/react-use-controllable-state": "npm:1.2.2"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10c0/2cd43339c36e89a3bf1db8aab34b939113dfbde56bf3a33df2d74757c78c9489b847b1962f1e2441c67e41817d120cb6177943e0f655f47bc1ff8e44fd55b1a2
languageName: node
linkType: hard

"@radix-ui/react-roving-focus@npm:1.1.2":
version: 1.1.2
resolution: "@radix-ui/react-roving-focus@npm:1.1.2"
Expand Down Expand Up @@ -6145,6 +6227,19 @@ __metadata:
languageName: node
linkType: hard

"@radix-ui/react-use-callback-ref@npm:1.1.1":
version: 1.1.1
resolution: "@radix-ui/react-use-callback-ref@npm:1.1.1"
peerDependencies:
"@types/react": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
checksum: 10c0/5f6aff8592dea6a7e46589808912aba3fb3b626cf6edd2b14f01638b61dbbe49eeb9f67cd5601f4c15b2fb547b9a7e825f7c4961acd4dd70176c969ae405f8d8
languageName: node
linkType: hard

"@radix-ui/react-use-controllable-state@npm:1.0.1":
version: 1.0.1
resolution: "@radix-ui/react-use-controllable-state@npm:1.0.1"
Expand Down Expand Up @@ -8305,6 +8400,7 @@ __metadata:
"@radix-ui/react-label": "npm:^2.1.0"
"@radix-ui/react-navigation-menu": "npm:^1.2.0"
"@radix-ui/react-popover": "npm:^1.1.6"
"@radix-ui/react-radio-group": "npm:^1.3.8"
"@radix-ui/react-scroll-area": "npm:^1.1.0"
"@radix-ui/react-select": "npm:^2.1.6"
"@radix-ui/react-separator": "npm:^1.1.0"
Expand Down