Skip to content

Commit

Permalink
Merge pull request #232 from FleetAdmiralJakob/profile-redesign
Browse files Browse the repository at this point in the history
  • Loading branch information
Gamius00 authored Aug 2, 2024
2 parents f1245f5 + 3756ac6 commit 0cc87e1
Show file tree
Hide file tree
Showing 19 changed files with 1,020 additions and 104 deletions.
1 change: 1 addition & 0 deletions convex/chats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export const initialConvexSetup = mutation({
username: identity.nickname,
clerkId: identity.tokenIdentifier,
firstName: identity.givenName,
email: identity.email,
lastName: identity.familyName,
})
.get();
Expand Down
108 changes: 108 additions & 0 deletions convex/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,56 @@ export const getMessages = query({
},
});

export const createDeleteRequest = mutation({
args: { chatId: v.string() },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();

if (identity === null) {
console.error("Unauthenticated call to mutation");
return null;
}

const convexUser = await ctx
.table("users")
.get("clerkId", identity.tokenIdentifier);

const parsedChatId = ctx.table("privateChats").normalizeId(args.chatId);

if (!parsedChatId) {
throw new ConvexError("chatId was invalid");
}

if (!convexUser) {
throw new ConvexError(
"Mismatch between Clerk and Convex. This is an error by us.",
);
}

const usersInChat = await ctx
.table("privateChats")
.getX(parsedChatId)
.edge("users");

if (
!usersInChat.some((user) => user.clerkId === identity.tokenIdentifier)
) {
throw new ConvexError(
"UNAUTHORIZED REQUEST: User tried to send a message in a chat in which he is not in.",
);
}

await ctx.table("messages").insert({
userId: convexUser._id,
privateChatId: parsedChatId,
content: "",
type: "request",
deleted: false,
readBy: [convexUser._id],
});
},
});

export const createMessage = mutation({
args: { chatId: v.string(), content: v.string() },
handler: async (ctx, args) => {
Expand Down Expand Up @@ -84,12 +134,62 @@ export const createMessage = mutation({
userId: convexUser._id,
privateChatId: parsedChatId,
content: args.content.trim(),
type: "message",
deleted: false,
readBy: [convexUser._id],
});
},
});

export const deleteAllMessagesInChat = mutation({
args: { chatId: v.string() },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();

if (identity === null) {
console.error("Unauthenticated call to mutation");
return null;
}

const parsedChatId = ctx.table("privateChats").normalizeId(args.chatId);

if (!parsedChatId) {
throw new ConvexError("chatId was invalid");
}

const chat = ctx.table("privateChats").getX(parsedChatId);
const messagesInChat = await chat.edge("messages");

for (const message of messagesInChat) {
await message.delete();
}
},
});

export const rejectRequest = mutation({
args: { messageId: v.string(), chatId: v.string() },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();

if (identity === null) {
console.error("Unauthenticated call to mutation");
return null;
}

const parsedMessageId = ctx.table("messages").normalizeId(args.messageId);

if (!parsedMessageId) {
throw new ConvexError("chatId was invalid");
}

const message = await ctx.table("messages").getX(parsedMessageId);

await message.patch({
type: "rejected",
});
},
});

export const deleteMessage = mutation({
args: { messageId: v.string(), chatId: v.string() },
handler: async (ctx, args) => {
Expand Down Expand Up @@ -145,6 +245,12 @@ export const markMessageRead = mutation({
);
}

const message = await ctx.table("messages").get(args.messageId);

if (!message) {
return null;
}

await ctx
.table("messages")
.getX(args.messageId)
Expand All @@ -153,5 +259,7 @@ export const markMessageRead = mutation({
add: [convexUser._id],
},
});

return { success: true };
},
});
2 changes: 2 additions & 0 deletions convex/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const schema = defineEntSchema({
.field("clerkId", v.string(), { unique: true })
.field("username", v.string(), { unique: true })
.field("firstName", v.optional(v.string()))
.field("email", v.optional(v.string()))
.field("lastName", v.optional(v.string()))
.edges("privateChats")
.edges("messages", { ref: true })
Expand All @@ -22,6 +23,7 @@ const schema = defineEntSchema({

messages: defineEnt({})
.field("content", v.string())
.field("type", v.string(), { default: "message" })
.field("deleted", v.boolean(), { default: false })
.edge("privateChat")
.edge("user")
Expand Down
42 changes: 40 additions & 2 deletions convex/users.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { query } from "./lib/functions";
import { ConvexError } from "convex/values";
import { mutation, query } from "./lib/functions";
import { v } from "convex/values";

export const getUserData = query({
handler: async (ctx) => {
Expand All @@ -13,3 +13,41 @@ export const getUserData = query({
return ctx.table("users").getX("clerkId", identity.tokenIdentifier);
},
});

export const updateUserData = mutation({
args: {
data: v.object({
firstName: v.optional(v.string()),
lastName: v.optional(v.string()),
email: v.optional(v.string()),
}),
},
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();

if (identity === null) {
console.error("Unauthenticated call to mutation");
return null;
}

const user = ctx.table("users").getX("clerkId", identity.tokenIdentifier);

if (args.data.email) {
await user.patch({
email: args.data.email,
});
}

if (args.data.lastName) {
await user.patch({
lastName: args.data.lastName,
});
}

if (args.data.firstName) {
await user.patch({
firstName: args.data.firstName,
});
}
},
});
34 changes: 30 additions & 4 deletions src/app/(auth)/sign-up/signup-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from "~/components/ui/form";
import { Input } from "~/components/ui/input";
import React, { useEffect } from "react";
import { formSchema } from "~/lib/validators";
import { formSchemaSignUp } from "~/lib/validators";
import { useSignIn } from "@clerk/nextjs";
import { useRouter } from "next/navigation";
import { cn } from "~/lib/utils";
Expand All @@ -38,8 +38,8 @@ export function SignUpForm() {
const { isLoading, isAuthenticated } = useConvexAuth();
const router = useRouter();

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
const form = useForm<z.infer<typeof formSchemaSignUp>>({
resolver: zodResolver(formSchemaSignUp),
defaultValues: {
username: "",
usernameId: "",
Expand All @@ -62,7 +62,7 @@ export function SignUpForm() {
}
}, [initialConvexSetup, isAuthenticated, router, signUpComplete]);

async function onSubmit(values: z.infer<typeof formSchema>) {
async function onSubmit(values: z.infer<typeof formSchemaSignUp>) {
if (isAuthenticated || isLoading) {
// TODO: Make a toast or something to tell the user has to sign out first
return;
Expand All @@ -89,6 +89,15 @@ export function SignUpForm() {
return;
}

if (parsedResponseBody.data?.statusText === "email_is_taken") {
form.setError("email", {
message: "Email is already taken. Please choose another.",
});

setFormIsLoading(false);
return;
}

if (parsedResponseBody.data?.statusText === "form_password_pwned") {
form.setError("password", {
message:
Expand Down Expand Up @@ -244,6 +253,23 @@ export function SignUpForm() {
>
This is optional, so you can stay anonymous.
</span>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem className="flex-2">
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="email" type="text" {...field} />
</FormControl>
<FormDescription>
This is optional, but if you forgot your password, we can send
you an email.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
Expand Down
13 changes: 12 additions & 1 deletion src/app/(internal-sites)/chats/[chatId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export default function Page({ params }: { params: { chatId: string } }) {
_creationTime: now,
content,
deleted: false,
type: "message",
privateChatId: chatId,
from: userInfo.data,
readBy: [userInfo.data],
Expand Down Expand Up @@ -203,6 +204,13 @@ export default function Page({ params }: { params: { chatId: string } }) {
setInputValue("");
scrollToBottom(true);
}

const createDeleteRequest = useMutation(api.messages.createDeleteRequest);

const createMessageRequestHandler = (chatId: string) => async () => {
await createDeleteRequest({ chatId });
};

const [menuActive, setMenuActive] = useState(false);

const menuClick = () => {
Expand Down Expand Up @@ -234,7 +242,10 @@ export default function Page({ params }: { params: { chatId: string } }) {
className="relative flex flex-col"
>
<DevMode className="top-20 z-10">
chatId: {params.chatId}
<button onClick={createMessageRequestHandler(params.chatId)}>
Delete Chat Request
</button>
<p>chatId: {params.chatId}</p>
<div onClick={() => devMode$.set(false)}>Disable dev mode</div>
</DevMode>
<div className="flex h-20 w-full items-center justify-between bg-primary py-6">
Expand Down
5 changes: 5 additions & 0 deletions src/app/(internal-sites)/profile/chats/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const ChatsPage = () => {
return <div className="ml-24">Chats</div>;
};

export default ChatsPage;
5 changes: 5 additions & 0 deletions src/app/(internal-sites)/profile/notification/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const NotificationPage = () => {
return <div className="ml-24">Notification</div>;
};

export default NotificationPage;
Loading

0 comments on commit 0cc87e1

Please sign in to comment.