diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index 12f244c..e85cab7 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -14,6 +14,7 @@ import type { FilterApi, FunctionReference, } from "convex/server"; +import type * as follows from "../follows"; import type * as http from "../http"; import type * as likes from "../likes"; import type * as posts from "../posts"; @@ -28,6 +29,7 @@ import type * as users from "../users"; * ``` */ declare const fullApi: ApiFromModules<{ + follows: typeof follows; http: typeof http; likes: typeof likes; posts: typeof posts; diff --git a/convex/follows.ts b/convex/follows.ts new file mode 100644 index 0000000..38895ed --- /dev/null +++ b/convex/follows.ts @@ -0,0 +1,116 @@ +import { v } from 'convex/values'; +import { mutation, query } from './_generated/server'; +import { userQuery } from './users'; + +export const addFollow = mutation({ + args: { followingId: v.id('users') }, + handler: async (ctx, args) => { + const clerkUser = await ctx.auth.getUserIdentity(); + + if (clerkUser === null) { + return; + } + + const user = await userQuery(ctx, clerkUser.subject); + + if (user === null) { + return; + } + + const existingFollow = await ctx.db + .query('follows') + .withIndex('by_following_follower_id', q => + q.eq('following', args.followingId).eq('follower', user._id) + ) + .unique(); + + if (existingFollow !== null) { + return; + } + + await ctx.db.insert('follows', { + following: args.followingId, + follower: user._id, + }); + }, +}); + +export const removeFollow = mutation({ + args: { followingId: v.id('users') }, + handler: async (ctx, args) => { + const clerkUser = await ctx.auth.getUserIdentity(); + + if (clerkUser === null) { + return; + } + + const user = await userQuery(ctx, clerkUser.subject); + + if (user === null) { + return; + } + + const existingFollow = await ctx.db + .query('follows') + .withIndex('by_following_follower_id', q => + q.eq('following', args.followingId).eq('follower', user._id) + ) + .unique(); + + if (existingFollow === null) { + return; + } + + await ctx.db.delete(existingFollow._id); + }, +}); + +export const getUserFollowerCount = query({ + args: { userId: v.id('users') }, + handler: async (ctx, args) => { + const followers = await ctx.db + .query('follows') + .withIndex('by_following', q => q.eq('following', args.userId)) + .collect(); + + return followers.length; + }, +}); + +export const getUserFollowingCount = query({ + args: { userId: v.id('users') }, + handler: async (ctx, args) => { + const followings = await ctx.db + .query('follows') + .withIndex('by_follower', q => q.eq('follower', args.userId)) + .collect(); + + return followings.length; + }, +}); + +export const getFollowing = query({ + args: { followingId: v.id('users') }, + handler: async (ctx, args) => { + const currentClerkUser = await ctx.auth.getUserIdentity(); + + if (currentClerkUser == null) { + return false; + } + + const user = await userQuery(ctx, currentClerkUser.subject); + + if (user == null) { + return false; + } + + const following = await ctx.db + .query('follows') + .withIndex('by_following_follower_id', q => + q.eq('following', args.followingId).eq('follower', user._id) + ) + .unique(); + + return following !== null; + }, +}); diff --git a/convex/posts.ts b/convex/posts.ts index c443cea..f4b45ac 100644 --- a/convex/posts.ts +++ b/convex/posts.ts @@ -147,3 +147,15 @@ export const getPostReplyCount = query({ return replies.length; }, }); + +export const getUserPostCount = query({ + args: { userId: v.id('users') }, + handler: async (ctx, args) => { + const posts = await ctx.db + .query('posts') + .withIndex('by_user_id', q => q.eq('userId', args.userId)) + .collect(); + + return posts.length; + }, +}); diff --git a/convex/schema.ts b/convex/schema.ts index 374df05..8d40c56 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -53,7 +53,8 @@ export default defineSchema( follower: v.id('users'), // person following }) .index('by_following', ['following']) - .index('by_follower', ['follower']), + .index('by_follower', ['follower']) + .index('by_following_follower_id', ['following', 'follower']), }, { schemaValidation: false } ); diff --git a/src/components/Loader.tsx b/src/components/Loader.tsx index 7293e4f..0681dce 100644 --- a/src/components/Loader.tsx +++ b/src/components/Loader.tsx @@ -11,7 +11,7 @@ export default function Loader({ }) { if (isLoading) { return ( -
+
Loading...
@@ -25,7 +25,7 @@ export default function Loader({ return (
+ ) : null}
-
+
+
+ {countDataLoaded ? ( + <> +
+ {postCount}{' '} + posts +
+
+ + {followerCount} + {' '} + followers +
+
+ + {followingCount} + {' '} + following +
+ + ) : null} +
+
); }