ShareSnippetModal.tsx99 lines · main
1import { useParams } from 'common'
2import { Eye, Unlock } from 'lucide-react'
3import { toast } from 'sonner'
4import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
5
6import { getContentById } from '@/data/content/content-id-query'
7import { useContentUpsertMutation } from '@/data/content/content-upsert-mutation'
8import { Snippet } from '@/data/content/sql-folders-query'
9import { useSqlEditorV2StateSnapshot } from '@/state/sql-editor-v2'
10import type { SqlSnippets } from '@/types'
11
12export const ShareSnippetModal = ({
13 snippet,
14 onClose,
15 onSuccess,
16}: {
17 snippet?: Snippet
18 onClose: () => void
19 onSuccess?: () => void
20}) => {
21 const { ref: projectRef } = useParams()
22 const snapV2 = useSqlEditorV2StateSnapshot()
23
24 const { mutate: upsertContent, isPending: isUpserting } = useContentUpsertMutation({
25 onError: (error) => {
26 toast.error(`Failed to update query: ${error.message}`)
27 },
28 })
29
30 const onShareSnippet = async () => {
31 if (!projectRef) return console.error('Project ref is required')
32 if (!snippet) return console.error('Snippet ID is required')
33
34 const storeSnippet = snapV2.snippets[snippet.id]
35 let snippetContent = storeSnippet?.snippet?.content
36
37 if (snippetContent === undefined) {
38 const { content } = await getContentById({ projectRef, id: snippet.id })
39 snippetContent = content as unknown as SqlSnippets.Content
40 }
41
42 // [Joshen] Just as a final check - to ensure that the content is minimally there (empty string is fine)
43 if (snippetContent === undefined) {
44 return toast.error('Unable to update snippet visibility: Content is missing')
45 }
46
47 upsertContent(
48 {
49 projectRef,
50 payload: {
51 ...snippet,
52 visibility: 'project',
53 folder_id: null,
54 content: snippetContent,
55 },
56 },
57 {
58 onSuccess: () => {
59 snapV2.updateSnippet({
60 id: snippet.id,
61 snippet: { visibility: 'project', folder_id: null },
62 skipSave: true,
63 })
64 toast.success('Snippet is now shared to the project')
65 onSuccess?.()
66 onClose()
67 },
68 }
69 )
70 }
71
72 return (
73 <ConfirmationModal
74 size="medium"
75 loading={isUpserting}
76 title={`Confirm to share query: ${snippet?.name}`}
77 confirmLabel="Share query"
78 confirmLabelLoading="Sharing query"
79 visible={snippet !== undefined}
80 onCancel={() => onClose()}
81 onConfirm={() => onShareSnippet()}
82 alert={{
83 title: 'This SQL query will become public to all team members',
84 description: 'Anyone with access to the project can view it',
85 }}
86 >
87 <ul className="text-sm text-foreground-light space-y-5">
88 <li className="flex gap-3 items-center">
89 <Eye size={16} />
90 <span>Project members will have read-only access to this query.</span>
91 </li>
92 <li className="flex gap-3 items-center">
93 <Unlock size={16} />
94 <span>Anyone will be able to duplicate it to their personal snippets.</span>
95 </li>
96 </ul>
97 </ConfirmationModal>
98 )
99}