diff --git a/.eslintrc.json b/.eslintrc.json index fcde915..76c069e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,8 @@ { - "extends": "next/core-web-vitals", + "plugins": ["prettier"], + "extends": ["next/core-web-vitals", "plugin:prettier/recommended"], "rules": { - "no-console": "warn" + "no-console": "warn", + "prettier/prettier": "error" } } diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..9d2e484 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "semi": true, + "tabWidth": 2, + "printWidth": 80, + "singleQuote": true, + "trailingComma": "es5", + "arrowParens": "avoid", + "embeddedLanguageFormatting": "auto", + "endOfLine": "auto" +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index c5c3a86..3737ea8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "codemirror", "conve", "doesn", + "Dtos", "Keymap", "lezer", "nextjs", diff --git a/convex/posts.ts b/convex/posts.ts index 1e04167..c93939f 100644 --- a/convex/posts.ts +++ b/convex/posts.ts @@ -1,9 +1,9 @@ -import { PaginationResult, paginationOptsValidator } from 'convex/server'; +import { paginationOptsValidator } from 'convex/server'; import { v } from 'convex/values'; import { PostWithUserDto } from '../src/app/types'; -import { Id } from './_generated/dataModel'; -import { mutation, query } from './_generated/server'; -import { userQuery } from './users'; +import { Doc } from './_generated/dataModel.d'; +import { QueryCtx, mutation, query } from './_generated/server'; +import { createUserDto, userQuery } from './users'; export const createOrUpdatePost = mutation({ args: { @@ -28,8 +28,10 @@ export const createOrUpdatePost = mutation({ return 'CANNOT_POST_ON_BEHALF_OF_ANOTHER_USER'; } + const contentText = args.content.join('\n'); + if (args.id) { - await ctx.db.patch(args.id, { content: args.content }); + await ctx.db.patch(args.id, { content: args.content, contentText }); return; } @@ -37,6 +39,7 @@ export const createOrUpdatePost = mutation({ userId: user._id, content: args.content, parentPostId: args.parentPostId, + contentText, }); }, }); @@ -44,12 +47,16 @@ export const createOrUpdatePost = mutation({ export const getUsersPostById = query({ args: { userId: v.id('users'), paginationOpts: paginationOptsValidator }, handler: async (ctx, args) => { - return await ctx.db + const posts = await ctx.db .query('posts') .withIndex('by_user_id', q => q.eq('userId', args.userId)) .filter(q => q.eq(q.field('parentPostId'), undefined)) .order('desc') .paginate(args.paginationOpts); + + const postDtos = posts.page.map(createPostDto); + + return { ...posts, page: postDtos }; }, }); @@ -81,14 +88,8 @@ export const getPostById = query({ } return { - ...post, - user: { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }, + ...createPostDto(post), + user: createUserDto(user), }; }, }); @@ -102,35 +103,10 @@ export const getRepliesByParentId = query({ .order('desc') .paginate(args.paginationOpts); - const repliesWithUserData = await Promise.all( - replies.page.map(async reply => { - const user = await ctx.db.get(reply.userId); - - if (user === null) { - return { - ...reply, - user: { - _id: '' as Id<'users'>, - _creationTime: 0, - clerkUsername: null, - clerkImageUrl: '', - clerkUserId: '', - }, - }; - } - - return { - ...reply, - user: { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }, - }; - }) - ); + const repliesWithUserData = await getPostsWithUsers({ + ctx, + posts: replies.page, + }); return { ...replies, page: repliesWithUserData }; }, @@ -170,35 +146,7 @@ export const getAllPostsWithUser = query({ .order('desc') .paginate(args.paginationOpts); - const postsWithUser = await Promise.all( - posts.page.map(async post => { - const user = await ctx.db.get(post.userId); - - if (user === null) { - return { - ...post, - user: { - _id: '' as Id<'users'>, - _creationTime: 0, - clerkUsername: null, - clerkImageUrl: '', - clerkUserId: '', - }, - }; - } - - return { - ...post, - user: { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }, - }; - }) - ); + const postsWithUser = await getPostsWithUsers({ ctx, posts: posts.page }); return { ...posts, page: postsWithUser }; }, @@ -206,7 +154,7 @@ export const getAllPostsWithUser = query({ export const getAllPostsForFollowings = query({ args: { paginationOpts: paginationOptsValidator }, - handler: async (ctx, args): Promise> => { + handler: async (ctx, args) => { const currentUser = await ctx.auth.getUserIdentity(); if (currentUser === null) { @@ -239,36 +187,56 @@ export const getAllPostsForFollowings = query({ .order('desc') .paginate(args.paginationOpts); - const postsWithUser = await Promise.all( - posts.page.map(async post => { - const user = await ctx.db.get(post.userId); - - if (user === null) { - return { - ...post, - user: { - _id: '' as Id<'users'>, - _creationTime: 0, - clerkUsername: null, - clerkImageUrl: '', - clerkUserId: '', - }, - }; - } - - return { - ...post, - user: { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }, - }; - }) - ); + const postsWithUser = await getPostsWithUsers({ ctx, posts: posts.page }); return { ...posts, page: postsWithUser }; }, }); + +export const getPostsBySearchTerm = query({ + args: { term: v.string(), paginationOpts: paginationOptsValidator }, + handler: async (ctx, args) => { + const posts = await ctx.db + .query('posts') + .withSearchIndex('search_by_content', q => + q.search('contentText', args.term) + ) + .paginate(args.paginationOpts); + + const postsWithUser = await getPostsWithUsers({ ctx, posts: posts.page }); + + return { ...posts, page: postsWithUser }; + }, +}); + +async function getPostsWithUsers({ + ctx, + posts, +}: { + ctx: QueryCtx; + posts: Doc<'posts'>[]; +}) { + return await Promise.all( + posts.map(async post => { + const postDto = createPostDto(post); + const user = await ctx.db.get(post.userId); + + if (user === null) { + return { + ...postDto, + user: createUserDto(user), + }; + } + + return { + ...postDto, + user: createUserDto(user), + }; + }) + ); +} + +function createPostDto(post: Doc<'posts'>) { + const { contentText, ...postDto } = post; + return postDto; +} diff --git a/convex/schema.ts b/convex/schema.ts index 8d40c56..335ae1a 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -34,14 +34,24 @@ export default defineSchema( created_at: v.number(), updated_at: v.number(), }), - }).index('by_clerk_id', ['clerkUser.id']), + }) + .index('by_clerk_id', ['clerkUser.id']) + .searchIndex('search_by_username', { + searchField: 'clerkUser.username', + filterFields: [], + }), posts: defineTable({ parentPostId: v.optional(v.id('posts')), userId: v.id('users'), content: v.array(v.string()), + contentText: v.string(), }) .index('by_user_id', ['userId']) - .index('by_parent_id', ['parentPostId']), + .index('by_parent_id', ['parentPostId']) + .searchIndex('search_by_content', { + searchField: 'contentText', + filterFields: ['parentPostId'], + }), likes: defineTable({ postId: v.id('posts'), userId: v.id('users'), diff --git a/convex/users.ts b/convex/users.ts index b90f9c1..cc8bf7f 100644 --- a/convex/users.ts +++ b/convex/users.ts @@ -1,5 +1,7 @@ +import { paginationOptsValidator } from 'convex/server'; import { v } from 'convex/values'; import { UserDto } from './../src/app/types/index'; +import { Doc, Id } from './_generated/dataModel'; import { QueryCtx, internalMutation, @@ -23,13 +25,7 @@ export const getUserByClerkId = query({ return 'USER_NOT_FOUND'; } - return { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }; + return createUserDto(user); }, }); @@ -42,13 +38,7 @@ export const getUserById = query({ return 'USER_NOT_FOUND'; } - return { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }; + return createUserDto(user); }, }); @@ -121,3 +111,39 @@ export const deleteUser = internalMutation({ await ctx.db.delete(userRecord._id); }, }); + +export const getUsersBySearchTerm = query({ + args: { term: v.string(), paginationOpts: paginationOptsValidator }, + handler: async (ctx, args) => { + const users = await ctx.db + .query('users') + .withSearchIndex('search_by_username', q => + q.search('clerkUser.username', args.term) + ) + .paginate(args.paginationOpts); + + const userDtos = users.page.map(createUserDto); + + return { ...users, page: userDtos }; + }, +}); + +export function createUserDto(user: Doc<'users'> | null) { + if (user === null) { + return { + _id: '' as Id<'users'>, + _creationTime: 0, + clerkUsername: null, + clerkImageUrl: '', + clerkUserId: '', + }; + } + + return { + _id: user._id, + _creationTime: user._creationTime, + clerkUsername: user.clerkUser.username, + clerkImageUrl: user.clerkUser.image_url, + clerkUserId: user.clerkUser.id, + }; +} diff --git a/package-lock.json b/package-lock.json index 6f94bea..018ef6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,11 +26,13 @@ "encoding": "^0.1.13", "eslint": "8.49.0", "eslint-config-next": "13.4.19", + "eslint-plugin-prettier": "^5.0.0", "next": "13.4.19", "next-themes": "^0.2.1", "nextjs-toploader": "^1.4.2", "nprogress": "^0.2.0", "postcss": "8.4.29", + "prettier": "^3.0.3", "react": "18.2.0", "react-dom": "18.2.0", "react-icons": "^4.11.0", @@ -41,6 +43,9 @@ "svix": "^1.11.0", "tailwindcss": "3.3.3", "typescript": "5.2.2" + }, + "devDependencies": { + "eslint-config-prettier": "^9.0.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1320,6 +1325,25 @@ "node": ">=10.12.0" } }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.3.tgz", @@ -1955,6 +1979,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1963,6 +1995,17 @@ "node": ">=8" } }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2014,6 +2057,20 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -2404,6 +2461,49 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -2776,6 +2876,18 @@ } } }, + "node_modules/eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -2936,6 +3048,34 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-prettier": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react": { "version": "7.33.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", @@ -3091,6 +3231,28 @@ "node": ">=0.10.0" } }, + "node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3101,6 +3263,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==" + }, "node_modules/fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -3320,6 +3487,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -3595,6 +3773,14 @@ "node": "*" } }, + "node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "engines": { + "node": ">=14.18.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -3822,6 +4008,20 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3875,6 +4075,23 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", @@ -3969,6 +4186,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -4042,6 +4270,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -4499,6 +4752,11 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5073,6 +5331,17 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5277,6 +5546,31 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/nprogress": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", @@ -5407,6 +5701,37 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -5694,6 +6019,31 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", @@ -6125,6 +6475,102 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-applescript/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6247,6 +6693,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, "node_modules/skin-tone": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", @@ -6403,6 +6854,17 @@ "node": ">=4" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -6562,6 +7024,21 @@ "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/tailwindcss": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", @@ -6630,6 +7107,17 @@ "node": ">=0.8" } }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/to-no-case": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", @@ -6924,6 +7412,14 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", diff --git a/package.json b/package.json index 6441f5a..e2bfa92 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,13 @@ "encoding": "^0.1.13", "eslint": "8.49.0", "eslint-config-next": "13.4.19", + "eslint-plugin-prettier": "^5.0.0", "next": "13.4.19", "next-themes": "^0.2.1", "nextjs-toploader": "^1.4.2", "nprogress": "^0.2.0", "postcss": "8.4.29", + "prettier": "^3.0.3", "react": "18.2.0", "react-dom": "18.2.0", "react-icons": "^4.11.0", @@ -42,5 +44,8 @@ "svix": "^1.11.0", "tailwindcss": "3.3.3", "typescript": "5.2.2" + }, + "devDependencies": { + "eslint-config-prettier": "^9.0.0" } } diff --git a/src/app/account/[[...account]]/page.tsx b/src/app/account/[[...account]]/page.tsx index a89865d..8fe3d45 100644 --- a/src/app/account/[[...account]]/page.tsx +++ b/src/app/account/[[...account]]/page.tsx @@ -4,7 +4,7 @@ import { UserProfile } from '@clerk/nextjs'; export default function AccountPage() { return ( -
+
-
-

Following

+
+
+

Following

diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 0861fe0..04d3866 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -19,11 +19,7 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - + @@ -32,12 +28,9 @@ export default function RootLayout({ > - + -
+
{children}
diff --git a/src/app/login/[[...login]]/page.tsx b/src/app/login/[[...login]]/page.tsx index 4f7c6e4..4db6deb 100644 --- a/src/app/login/[[...login]]/page.tsx +++ b/src/app/login/[[...login]]/page.tsx @@ -4,7 +4,7 @@ import { SignIn } from '@clerk/nextjs'; export default function LoginPage() { return ( -
+
-
-

Home

+
+
+

Home

diff --git a/src/app/posts/[id]/edit/page.tsx b/src/app/posts/[id]/edit/page.tsx index 214f965..a1c6ed7 100644 --- a/src/app/posts/[id]/edit/page.tsx +++ b/src/app/posts/[id]/edit/page.tsx @@ -51,7 +51,7 @@ export default function EditPostPage({ params }: { params: { id: string } }) { } return ( -
+
{user.isLoaded && post !== undefined ? ( ) : ( -
- +
+ Loading...
)} diff --git a/src/app/posts/[id]/page.tsx b/src/app/posts/[id]/page.tsx index bce3146..b5452cb 100644 --- a/src/app/posts/[id]/page.tsx +++ b/src/app/posts/[id]/page.tsx @@ -68,34 +68,27 @@ export default function PostPage({ } return ( -
-
+
+
{post !== undefined && user.isLoaded ? ( <> -
- +
+
-
+
-
+
user profile image
-
+
) : ( -
- Loading post... +
+ Loading post...
)}
diff --git a/src/app/posts/add/page.tsx b/src/app/posts/add/page.tsx index 636f799..dfd1a01 100644 --- a/src/app/posts/add/page.tsx +++ b/src/app/posts/add/page.tsx @@ -43,15 +43,12 @@ export default function AddPost() { } return ( -
+
{user.isLoaded ? ( - + ) : ( -
- +
+ Loading...
)} diff --git a/src/app/profile/[id]/page.tsx b/src/app/profile/[id]/page.tsx index 0c99239..a9be135 100644 --- a/src/app/profile/[id]/page.tsx +++ b/src/app/profile/[id]/page.tsx @@ -19,8 +19,8 @@ export default async function UserProfilePage({ } return ( -
-
+
+
diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx new file mode 100644 index 0000000..899eaeb --- /dev/null +++ b/src/app/search/page.tsx @@ -0,0 +1,26 @@ +import SearchResults from '@/components/SearchResults'; + +export default function SearchPage({ + searchParams, +}: { + searchParams: { [key: string]: string | string[] | undefined }; +}) { + let term = searchParams.term; + + if (term === undefined) { + term = ''; + } + + if (Array.isArray(term)) { + term = term.length > 0 ? term[0] : ''; + } + + return ( +
+
+

Here's what we found

+ +
+
+ ); +} diff --git a/src/app/signup/[[...signup]]/page.tsx b/src/app/signup/[[...signup]]/page.tsx index c2d2ef4..67b7c29 100644 --- a/src/app/signup/[[...signup]]/page.tsx +++ b/src/app/signup/[[...signup]]/page.tsx @@ -4,7 +4,7 @@ import { SignUp } from '@clerk/nextjs'; export default function SignUpPage() { return ( -
+
& { user: UserDto }; +export type PostWithUserDto = Omit, 'contentText'> & { + user: UserDto; +}; diff --git a/src/components/AllPosts.tsx b/src/components/AllPosts.tsx index 889136f..83fdc2d 100644 --- a/src/components/AllPosts.tsx +++ b/src/components/AllPosts.tsx @@ -19,8 +19,8 @@ export default function AllPosts() { if (pager.isLoading === false && pager.results.length == 0) { return ( -
-

+
+

Hmmm it doesn't look like anyone has posted.

@@ -28,12 +28,12 @@ export default function AllPosts() { } return ( -
-
+
+
{pager.results.map(post => (
diff --git a/src/components/Editor.tsx b/src/components/Editor.tsx index 47cdb8a..7e4e1ff 100644 --- a/src/components/Editor.tsx +++ b/src/components/Editor.tsx @@ -92,30 +92,30 @@ export default function Editor({ return (
-
-
+
+
-
+
-
- diff --git a/src/components/FollowingPosts.tsx b/src/components/FollowingPosts.tsx index 39985c4..247e17e 100644 --- a/src/components/FollowingPosts.tsx +++ b/src/components/FollowingPosts.tsx @@ -20,14 +20,14 @@ export default function FollowingPosts() { if (pager.isLoading === false && pager.results.length == 0) { return ( -
-

+
+

Hmmm it doesn't look like you follow anyone or anyone who has made a post yet.

Find someone to follow @@ -36,12 +36,12 @@ export default function FollowingPosts() { } return ( -
-
+
+
{pager.results.map(post => (
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index bccf5e6..cee649e 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -2,43 +2,31 @@ import Link from 'next/link'; export default function Footer() { return ( -
-