From 6fdadbec28c67511c3b65ed681f32df03bcf02a5 Mon Sep 17 00:00:00 2001 From: Izan Gil <66965250+SrIzan10@users.noreply.github.com> Date: Sun, 1 Feb 2026 15:30:31 +0100 Subject: [PATCH] feat: add ability to change usernames --- .../channel/[channelName]/page.client.tsx | 143 ++++++++++++++---- .../components/app/ChatPanel/ChatPanel.tsx | 5 - .../app/UniversalForm/UniversalForm.tsx | 26 ++-- apps/web/src/lib/form/actions.ts | 130 +++++++++++++++- apps/web/src/lib/form/zod.ts | 5 + .../migration.sql | 2 + packages/db/prisma/schema.prisma | 5 +- 7 files changed, 268 insertions(+), 48 deletions(-) create mode 100644 packages/db/prisma/migrations/20260131224649_add_name_last_changed_to_channel/migration.sql diff --git a/apps/web/src/app/(ui)/(protected)/settings/channel/[channelName]/page.client.tsx b/apps/web/src/app/(ui)/(protected)/settings/channel/[channelName]/page.client.tsx index 7483b7a..f729080 100644 --- a/apps/web/src/app/(ui)/(protected)/settings/channel/[channelName]/page.client.tsx +++ b/apps/web/src/app/(ui)/(protected)/settings/channel/[channelName]/page.client.tsx @@ -30,6 +30,7 @@ import { deleteChannel, toggleGlobalChannelNotifs, editStreamInfo, + changeUsername, } from '@/lib/form/actions'; import { Switch } from '@/components/ui/switch'; import { toast } from 'sonner'; @@ -74,6 +75,7 @@ interface ChannelSettingsClientProps { followers: (Follow & { user: { id: string; slack_id: string } })[]; followerPersonalChannels: (Channel | null)[]; is247: boolean; + nameLastChanged: Date | null; }; isOwner: boolean; currentUser: User; @@ -112,6 +114,32 @@ export default function ChannelSettingsClient({ } }, []); + const handleUsernameChangeComplete = useCallback( + (result: any) => { + if (result?.success && result?.newUsername) { + toast.success('Username changed successfully! Redirecting...'); + router.push(`/settings/channel/${result.newUsername}?tab=${selTab}`); + } + }, + [router, selTab] + ); + + const getUsernameChangeCooldownInfo = () => { + if (!channel.nameLastChanged) { + return { canChange: true, daysRemaining: 0 }; + } + const daysSinceLastChange = Math.floor( + (Date.now() - new Date(channel.nameLastChanged).getTime()) / (1000 * 60 * 60 * 24) + ); + const cooldownDays = 30; + if (daysSinceLastChange >= cooldownDays) { + return { canChange: true, daysRemaining: 0 }; + } + return { canChange: false, daysRemaining: cooldownDays - daysSinceLastChange }; + }; + + const cooldownInfo = getUsernameChangeCooldownInfo(); + const copyStreamKey = async () => { if (streamKey) { await navigator.clipboard.writeText(streamKey); @@ -179,10 +207,10 @@ export default function ChannelSettingsClient({ -
+Current profile picture
-Click "Upload new image" to replace
++ Click "Upload new image" to replace +
- Uploading... -
+Uploading...
)} - + {uploadError && ( -- {uploadError} -
+{uploadError}
)} - + {!field.value && !isUploading && !uploadError && (Upload a profile picture for your channel. @@ -351,7 +377,8 @@ export default function ChannelSettingsClient({
- Mark this channel as always live. It will disable notifications on #hctv-streams. + Mark this channel as always live. It will disable notifications on + #hctv-streams.
+ Your username is how others find and mention you on hctv. You can change it once + every 30 days. +
+ {!cooldownInfo.canChange && ( ++ You can change your username again in {cooldownInfo.daysRemaining} day + {cooldownInfo.daysRemaining === 1 ? '' : 's'}. +
+Need help getting started? Check out our{' '} { - if (await confirm({ - title: 'Remove Manager', - description: `Are you sure you want to remove ${personalChannel?.name} as a manager? They will no longer be able to stream or moderate this channel.`, - confirmText: 'Remove', - cancelText: 'Cancel', - })) { + if ( + await confirm({ + title: 'Remove Manager', + description: `Are you sure you want to remove ${personalChannel?.name} as a manager? They will no longer be able to stream or moderate this channel.`, + confirmText: 'Remove', + cancelText: 'Cancel', + }) + ) { removeChannelManager(channel.id, manager.id); } }} @@ -727,7 +810,7 @@ export default function ChannelSettingsClient({
- Add a 300x600 browser source with this and enjoy! + Add a 300x600 browser source with this and enjoy!