From 16f689a598e61071d863ef4e9436c3abf71d54e8 Mon Sep 17 00:00:00 2001 From: "Thomas G. Lopes" <26071571+TGlide@users.noreply.github.com> Date: Thu, 19 Jun 2025 01:07:27 +0100 Subject: [PATCH] better share modal --- src/lib/backend/convex/conversations.ts | 15 ++ src/lib/backend/convex/messages.ts | 22 +++ src/lib/components/ui/share-button/index.ts | 1 + .../ui/share-button/share-button.svelte | 145 ++++++++++++++++++ src/lib/components/ui/switch/switch.svelte | 2 +- src/routes/account/models/+page.svelte | 28 +++- src/routes/chat/+layout.svelte | 81 +--------- src/routes/share/[id]/+page.server.ts | 32 ++++ src/routes/share/[id]/+page.svelte | 118 ++++++++++++++ 9 files changed, 360 insertions(+), 84 deletions(-) create mode 100644 src/lib/components/ui/share-button/index.ts create mode 100644 src/lib/components/ui/share-button/share-button.svelte create mode 100644 src/routes/share/[id]/+page.server.ts create mode 100644 src/routes/share/[id]/+page.svelte diff --git a/src/lib/backend/convex/conversations.ts b/src/lib/backend/convex/conversations.ts index 946760b..243e7a2 100644 --- a/src/lib/backend/convex/conversations.ts +++ b/src/lib/backend/convex/conversations.ts @@ -304,6 +304,21 @@ export const remove = mutation({ }, }); +export const getPublicById = query({ + args: { + conversation_id: v.id('conversations'), + }, + handler: async (ctx, args) => { + const conversation = await ctx.db.get(args.conversation_id); + + if (!conversation || !conversation.public) { + return null; + } + + return conversation; + }, +}); + export const search = query({ args: { session_token: v.string(), diff --git a/src/lib/backend/convex/messages.ts b/src/lib/backend/convex/messages.ts index fc3a0b8..edb01f5 100644 --- a/src/lib/backend/convex/messages.ts +++ b/src/lib/backend/convex/messages.ts @@ -169,6 +169,28 @@ export const updateMessage = mutation({ }, }); +export const getByConversationPublic = query({ + args: { + conversation_id: v.id('conversations'), + }, + handler: async (ctx, args) => { + // First check if the conversation is public + const conversation = await ctx.db.get(args.conversation_id); + + if (!conversation || !conversation.public) { + return null; + } + + const messages = await ctx.db + .query('messages') + .withIndex('by_conversation', (q) => q.eq('conversation_id', args.conversation_id)) + .order('asc') + .collect(); + + return messages; + }, +}); + export const updateError = mutation({ args: { session_token: v.string(), diff --git a/src/lib/components/ui/share-button/index.ts b/src/lib/components/ui/share-button/index.ts new file mode 100644 index 0000000..ee003e6 --- /dev/null +++ b/src/lib/components/ui/share-button/index.ts @@ -0,0 +1 @@ +export { default as ShareButton } from './share-button.svelte'; \ No newline at end of file diff --git a/src/lib/components/ui/share-button/share-button.svelte b/src/lib/components/ui/share-button/share-button.svelte new file mode 100644 index 0000000..aae5ced --- /dev/null +++ b/src/lib/components/ui/share-button/share-button.svelte @@ -0,0 +1,145 @@ + + + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Share +
+ +
+
+
+

Share conversation

+ +
+ +
+
+

Public sharing

+

Anyone with the link can view this conversation

+
+ isPublic, (v) => toggleSharing(v)} disabled={isToggling} /> +
+ + {#if isPublic} +
+

Share link

+
+ {shareUrl} + +
+ + Open in new tab + +
+ {:else} +

+ Enable public sharing to generate a shareable link +

+ {/if} +
+
+ diff --git a/src/lib/components/ui/switch/switch.svelte b/src/lib/components/ui/switch/switch.svelte index 75e39f2..5845e59 100644 --- a/src/lib/components/ui/switch/switch.svelte +++ b/src/lib/components/ui/switch/switch.svelte @@ -19,7 +19,7 @@ - {/snippet} - Share - + } + isPublic={currentConversationQuery.data.public} + /> {/if} {#snippet trigger(tooltip)} diff --git a/src/routes/share/[id]/+page.server.ts b/src/routes/share/[id]/+page.server.ts new file mode 100644 index 0000000..a180cb4 --- /dev/null +++ b/src/routes/share/[id]/+page.server.ts @@ -0,0 +1,32 @@ +import { api } from '$lib/backend/convex/_generated/api.js'; +import { type Id } from '$lib/backend/convex/_generated/dataModel.js'; +import { ConvexHttpClient } from 'convex/browser'; +import { error } from '@sveltejs/kit'; + +const client = new ConvexHttpClient(import.meta.env.VITE_CONVEX_URL); + +export const load = async ({ params }: { params: { id: string } }) => { + try { + // Get the conversation without requiring authentication + const conversation = await client.query(api.conversations.getPublicById, { + conversation_id: params.id as Id<'conversations'>, + }); + + if (!conversation) { + error(404, 'Conversation not found or not shared publicly'); + } + + // Get messages for this conversation + const messages = await client.query(api.messages.getByConversationPublic, { + conversation_id: params.id as Id<'conversations'>, + }); + + return { + conversation, + messages, + }; + } catch (e) { + console.error('Error loading shared conversation:', e); + error(404, 'Conversation not found or not shared publicly'); + } +}; \ No newline at end of file diff --git a/src/routes/share/[id]/+page.svelte b/src/routes/share/[id]/+page.svelte new file mode 100644 index 0000000..cd0dcfd --- /dev/null +++ b/src/routes/share/[id]/+page.svelte @@ -0,0 +1,118 @@ + + + + {data.conversation.title} | Shared Chat + + + +
+ +
+
+
+ + + Thom.chat + +
+ Shared conversation +
+
+
+ + {#snippet trigger(tooltip)} + + {/snippet} + Create your own conversation + + +
+
+
+ + +
+
+ +
+

{data.conversation.title}

+
+ {#if data.conversation.updated_at} + Updated {formatDate(data.conversation.updated_at)} + {/if} + Public conversation +
+
+ + + {#if data.messages && data.messages.length > 0} +
+ {#each data.messages as message (message._id)} +
+ +
+ {/each} +
+ {:else} +
+

No messages in this conversation yet.

+

The conversation appears to be empty.

+
+ {/if} +
+
+ + + +
\ No newline at end of file