diff --git a/.husky/.gitignore b/.husky/.gitignore deleted file mode 100644 index c9cdc63..0000000 --- a/.husky/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_ \ No newline at end of file diff --git a/fauna/migrations/2021-02-25T06:56:23.853Z/create-collection-comment_likes.fql b/fauna/migrations/2021-02-25T06:56:23.853Z/create-collection-comment_likes.fql new file mode 100644 index 0000000..42bedb6 --- /dev/null +++ b/fauna/migrations/2021-02-25T06:56:23.853Z/create-collection-comment_likes.fql @@ -0,0 +1,3 @@ +CreateCollection({ + name: "comment_likes" +}) \ No newline at end of file diff --git a/fauna/migrations/2021-02-25T06:56:23.853Z/create-index-all_likes_by_comment.fql b/fauna/migrations/2021-02-25T06:56:23.853Z/create-index-all_likes_by_comment.fql new file mode 100644 index 0000000..f0336e7 --- /dev/null +++ b/fauna/migrations/2021-02-25T06:56:23.853Z/create-index-all_likes_by_comment.fql @@ -0,0 +1,7 @@ +CreateIndex({ + name: "all_likes_by_comment", + source: Collection("comment_likes"), + terms: [{ + field: ["data", "comment"] + }] +}) \ No newline at end of file diff --git a/fauna/migrations/2021-02-25T06:56:23.853Z/create-index-unique_comment_user_like.fql b/fauna/migrations/2021-02-25T06:56:23.853Z/create-index-unique_comment_user_like.fql new file mode 100644 index 0000000..90d5a1d --- /dev/null +++ b/fauna/migrations/2021-02-25T06:56:23.853Z/create-index-unique_comment_user_like.fql @@ -0,0 +1,10 @@ +CreateIndex({ + name: "unique_comment_user_like", + source: Collection("comment_likes"), + unique: true, + terms: [{ + field: ["data", "comment"] + }, { + field: ["data", "user"] + }] +}) \ No newline at end of file diff --git a/fauna/migrations/2021-02-27T20:33:58.638Z/create-collection-activities.fql b/fauna/migrations/2021-02-27T20:33:58.638Z/create-collection-activities.fql new file mode 100644 index 0000000..9112554 --- /dev/null +++ b/fauna/migrations/2021-02-27T20:33:58.638Z/create-collection-activities.fql @@ -0,0 +1,3 @@ +CreateCollection({ + name: "activities" +}) \ No newline at end of file diff --git a/fauna/migrations/2021-02-27T20:33:58.638Z/create-collection-notifications.fql b/fauna/migrations/2021-02-27T20:33:58.638Z/create-collection-notifications.fql new file mode 100644 index 0000000..d41472e --- /dev/null +++ b/fauna/migrations/2021-02-27T20:33:58.638Z/create-collection-notifications.fql @@ -0,0 +1,3 @@ +CreateCollection({ + name: "notifications" +}) \ No newline at end of file diff --git a/fauna/migrations/2021-02-27T20:33:58.638Z/create-index-all_notifications_by_user.fql b/fauna/migrations/2021-02-27T20:33:58.638Z/create-index-all_notifications_by_user.fql new file mode 100644 index 0000000..4b079b7 --- /dev/null +++ b/fauna/migrations/2021-02-27T20:33:58.638Z/create-index-all_notifications_by_user.fql @@ -0,0 +1,7 @@ +CreateIndex({ + name: "all_notifications_by_user", + source: Collection("notifications"), + terms: [{ + field: ["data", "user"] + }] +}) \ No newline at end of file diff --git a/fauna/migrations/2021-02-28T03:12:40.528Z/create-collection-notification_statuses.fql b/fauna/migrations/2021-02-28T03:12:40.528Z/create-collection-notification_statuses.fql new file mode 100644 index 0000000..212576b --- /dev/null +++ b/fauna/migrations/2021-02-28T03:12:40.528Z/create-collection-notification_statuses.fql @@ -0,0 +1,3 @@ +CreateCollection({ + name: "notification_statuses" +}) \ No newline at end of file diff --git a/fauna/migrations/2021-02-28T03:12:40.528Z/create-index-notification_status_by_user.fql b/fauna/migrations/2021-02-28T03:12:40.528Z/create-index-notification_status_by_user.fql new file mode 100644 index 0000000..30d6f2c --- /dev/null +++ b/fauna/migrations/2021-02-28T03:12:40.528Z/create-index-notification_status_by_user.fql @@ -0,0 +1,8 @@ +CreateIndex({ + name: "notification_status_by_user", + source: Collection("notification_statuses"), + unique: true, + terms: [{ + field: ["data", "user"] + }] +}) \ No newline at end of file diff --git a/fauna/resources/collections/activities.fql b/fauna/resources/collections/activities.fql new file mode 100644 index 0000000..493cb03 --- /dev/null +++ b/fauna/resources/collections/activities.fql @@ -0,0 +1,20 @@ +CreateCollection({ + name: 'activities' +}) + +/** +# "data": { +# // this user performed this activity +# "user": User, + +# // This resource has been added/changed as a result of this action +# "resource": User | UPDATE_LIKE | COMMENT_LIKE | UPDATE_COMMENT + +# "type": "LIKED_UPDATE" | "LIKED_COMMENT" | "COMMENTED" | "FOLLOWED" + +# "timestamps": { +# "createdAt": Time, +# "updatedAt": Time +# } +# } +**/ \ No newline at end of file diff --git a/fauna/resources/collections/comment_likes.fql b/fauna/resources/collections/comment_likes.fql new file mode 100644 index 0000000..384bec0 --- /dev/null +++ b/fauna/resources/collections/comment_likes.fql @@ -0,0 +1,15 @@ +CreateCollection({ + name: 'comment_likes' +}) + +/** +# data: { +# comment: UpdateComment +# user: User +# liked: boolean +# timestamps { +# createdAt: Time +# updatedAt: Time +# } +# } +**/ \ No newline at end of file diff --git a/fauna/resources/collections/notification_statuses.fql b/fauna/resources/collections/notification_statuses.fql new file mode 100644 index 0000000..2c5987e --- /dev/null +++ b/fauna/resources/collections/notification_statuses.fql @@ -0,0 +1,13 @@ +CreateCollection({ + name: 'notification_statuses' +}) + +/** +# "data": { +# "userId": String, +# "count": Number, +# "timestamps: { +# "createdAt": Time, +# "updatedAt": Time, +# } +**/ \ No newline at end of file diff --git a/fauna/resources/collections/notifications.fql b/fauna/resources/collections/notifications.fql new file mode 100644 index 0000000..ebb7803 --- /dev/null +++ b/fauna/resources/collections/notifications.fql @@ -0,0 +1,16 @@ +CreateCollection({ + name: 'notifications' +}) + +/** +# "data": { +# // this user will receive the notification +# "user": User, +# "activity": ACTIVITY +# isRead: Boolean, +# "timestamps": { +# "createdAt": Time, +# "updatedAt": Time +# } +# } +**/ \ No newline at end of file diff --git a/fauna/resources/indexes/all_likes_by_comment.fql b/fauna/resources/indexes/all_likes_by_comment.fql new file mode 100644 index 0000000..38bb84b --- /dev/null +++ b/fauna/resources/indexes/all_likes_by_comment.fql @@ -0,0 +1,9 @@ +CreateIndex({ + name: 'all_likes_by_comment', + source: Collection('comment_likes'), + terms: [ + { + field: ['data', 'comment'], + }, + ], +}) \ No newline at end of file diff --git a/fauna/resources/indexes/all_notifications_by_user.fql b/fauna/resources/indexes/all_notifications_by_user.fql new file mode 100644 index 0000000..07a8382 --- /dev/null +++ b/fauna/resources/indexes/all_notifications_by_user.fql @@ -0,0 +1,9 @@ +CreateIndex({ + name: 'all_notifications_by_user', + source: Collection('notifications'), + terms: [ + { + field: ['data', 'user'], + }, + ], +}) \ No newline at end of file diff --git a/fauna/resources/indexes/notification_status_by_user.fql b/fauna/resources/indexes/notification_status_by_user.fql new file mode 100644 index 0000000..a3ad00e --- /dev/null +++ b/fauna/resources/indexes/notification_status_by_user.fql @@ -0,0 +1,10 @@ +CreateIndex({ + name: 'notification_status_by_user', + source: Collection('notification_statuses'), + unique: true, + terms: [ + { + field: ['data', 'user'], + }, + ], +}) \ No newline at end of file diff --git a/fauna/resources/indexes/unique_comment_user_like.fql b/fauna/resources/indexes/unique_comment_user_like.fql new file mode 100644 index 0000000..b618a2a --- /dev/null +++ b/fauna/resources/indexes/unique_comment_user_like.fql @@ -0,0 +1,13 @@ +CreateIndex({ + name: 'unique_comment_user_like', + source: Collection('comment_likes'), + unique: true, + terms: [ + { + field: ['data', 'comment'], + }, + { + field: ['data', 'user'], + }, + ], +}) \ No newline at end of file diff --git a/faviconData.json b/faviconData.json index cf1b306..0856e2b 100644 --- a/faviconData.json +++ b/faviconData.json @@ -1 +1,39 @@ -{"result":{"status":"success"},"favicon":{"package_url":"https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/favicon_package_v0.16.zip","files_urls":["https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/android-chrome-192x192.png","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/android-chrome-512x512.png","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/apple-touch-icon.png","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/browserconfig.xml","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/favicon-16x16.png","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/favicon-32x32.png","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/favicon.ico","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/mstile-144x144.png","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/mstile-150x150.png","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/mstile-310x150.png","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/mstile-310x310.png","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/mstile-70x70.png","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/safari-pinned-tab.svg","https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/package_files/site.webmanifest"],"html_code":"\n\n\n\n\n\n\n","compression":"false","overlapping_markups":["link[rel=\"apple-touch-icon\"]","link[rel=\"shortcut\"]","link[rel=\"shortcut icon\"]","link[rel=\"icon\",sizes=\"16x16\"]","link[rel=\"icon\",sizes=\"32x32\"]","meta[name=\"msapplication-TileColor\"]","meta[name=\"msapplication-TileImage\"]","link[rel=\"manifest\"]","meta[name=\"theme-color\"]","link[rel=\"mask-icon\"]"]},"files_location":{"type":"path","path":"/"},"preview_picture_url":"https://realfavicongenerator.net/files/2a2b8756afff5accc44ccef6f5741f25e74184fc/favicon_preview.png","version":"0.16"} \ No newline at end of file +{ + "result": { "status": "success" }, + "favicon": { + "package_url": "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/favicon_package_v0.16.zip", + "files_urls": [ + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/android-chrome-192x192.png", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/android-chrome-512x512.png", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/apple-touch-icon.png", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/browserconfig.xml", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/favicon-16x16.png", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/favicon-32x32.png", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/favicon.ico", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/mstile-144x144.png", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/mstile-150x150.png", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/mstile-310x150.png", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/mstile-310x310.png", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/mstile-70x70.png", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/safari-pinned-tab.svg", + "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/package_files/site.webmanifest" + ], + "html_code": "\n\n\n\n\n\n\n", + "compression": "false", + "overlapping_markups": [ + "link[rel=\"apple-touch-icon\"]", + "link[rel=\"shortcut\"]", + "link[rel=\"shortcut icon\"]", + "link[rel=\"icon\",sizes=\"16x16\"]", + "link[rel=\"icon\",sizes=\"32x32\"]", + "meta[name=\"msapplication-TileColor\"]", + "meta[name=\"msapplication-TileImage\"]", + "link[rel=\"manifest\"]", + "meta[name=\"theme-color\"]", + "link[rel=\"mask-icon\"]" + ] + }, + "files_location": { "type": "path", "path": "/" }, + "preview_picture_url": "https://realfavicongenerator.net/files/f17e6c0db77d4e9e5f8a5f3cda6fd36320309e91/favicon_preview.png", + "version": "0.16" +} diff --git a/faviconDescription.json b/faviconDescription.json index f84b0e7..6ded377 100644 --- a/faviconDescription.json +++ b/faviconDescription.json @@ -1,11 +1,9 @@ { - "masterPicture": "./public/blue-logo.svg", + "masterPicture": "./public/logo.svg", "iconsPath": "/", "design": { "ios": { - "pictureAspect": "backgroundAndMargin", - "backgroundColor": "#ffffff", - "margin": "14%", + "pictureAspect": "noChange", "assets": { "ios6AndPriorIcons": false, "ios7AndLaterIcons": false, @@ -14,13 +12,10 @@ } }, "desktopBrowser": { - "design": "background", - "backgroundColor": "#ffffff", - "backgroundRadius": 0.45, - "imageScale": 0.9 + "design": "raw" }, "windows": { - "pictureAspect": "whiteSilhouette", + "pictureAspect": "noChange", "backgroundColor": "#da532c", "onConflict": "override", "assets": { @@ -34,12 +29,9 @@ } }, "androidChrome": { - "pictureAspect": "backgroundAndMargin", - "margin": "17%", - "backgroundColor": "#ffffff", + "pictureAspect": "noChange", "themeColor": "#ffffff", "manifest": { - "name": "Coderplex", "display": "standalone", "orientation": "notSet", "onConflict": "override", diff --git a/package.json b/package.json index d42e0ba..1087199 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "start": "next start", "lint": "eslint --fix --ext .js,.ts,.tsx --ignore-path .gitignore .", "check-types": "tsc", - "prettier": "prettier --ignore-path .gitignore --write \"**/*.+(js|json|ts|tsx|fql)\"", + "prettier": "prettier --ignore-path .gitignore --write \"**/*.+(js|json|ts|tsx)\"", "format": "npm run prettier -- --write", "check-format": "npm run prettier -- --list-different", "validate": "npm-run-all --parallel check-types check-format lint build", @@ -21,21 +21,26 @@ "dependencies": { "@badrap/bar-of-progress": "0.1.2", "@headlessui/react": "0.3.1", + "@mdx-js/mdx": "1.6.22", + "@mdx-js/react": "1.6.22", + "@tailwindcss/line-clamp": "0.2.0", "classnames": "2.2.6", - "fauna-schema-migrate": "0.1.3-beta", + "fauna-schema-migrate": "0.1.10", "faunadb": "4.1.0", "luxon": "1.26.0", "markdown-to-jsx": "7.1.1", + "mdx-embed": "0.0.19", "next": "10.0.7", "next-auth": "3.5.0", - "next-themes": "0.0.10", + "next-themes": "0.0.11", "nightwind": "1.1.6", "phosphor-react": "1.1.2", + "prism-react-renderer": "1.2.0", "react": "17.0.1", "react-dom": "17.0.1", - "react-hook-form": "6.15.3", + "react-hook-form": "6.15.4", "react-hot-toast": "1.0.2", - "react-query": "3.9.8", + "react-query": "3.11.0", "slugify": "1.4.7", "tabler-icons": "1.35.0" }, @@ -48,23 +53,23 @@ "@types/next-auth": "3.1.24", "@types/node": "14.14.31", "@types/react": "17.0.2", - "@typescript-eslint/eslint-plugin": "4.15.1", - "@typescript-eslint/parser": "4.15.1", + "@typescript-eslint/eslint-plugin": "4.15.2", + "@typescript-eslint/parser": "4.15.2", "autoprefixer": "10.2.4", "babel-eslint": "10.1.0", "cli-real-favicon": "0.0.8", "eslint": "7.20.0", "eslint-config-prettier": "8.0.0", "eslint-config-react-app": "6.0.0", - "eslint-plugin-flowtype": "5.2.2", + "eslint-plugin-flowtype": "5.3.0", "eslint-plugin-import": "2.22.1", "eslint-plugin-jest": "24.1.5", "eslint-plugin-jsx-a11y": "6.4.1", "eslint-plugin-prettier": "3.3.1", "eslint-plugin-react": "7.22.0", "eslint-plugin-react-hooks": "4.2.0", - "husky": ">=4.3.8", - "lint-staged": ">=10.5.4", + "husky": "4", + "lint-staged": "10.5.4", "npm-run-all": "4.1.5", "postcss": "8.2.6", "prettier": "2.2.1", diff --git a/public/favicons/android-chrome-192x192.png b/public/favicons/android-chrome-192x192.png index ce4967d..8498cda 100644 Binary files a/public/favicons/android-chrome-192x192.png and b/public/favicons/android-chrome-192x192.png differ diff --git a/public/favicons/android-chrome-512x512.png b/public/favicons/android-chrome-512x512.png index 96788b5..11ec2a2 100644 Binary files a/public/favicons/android-chrome-512x512.png and b/public/favicons/android-chrome-512x512.png differ diff --git a/public/favicons/apple-touch-icon.png b/public/favicons/apple-touch-icon.png index c7549f4..56209ef 100644 Binary files a/public/favicons/apple-touch-icon.png and b/public/favicons/apple-touch-icon.png differ diff --git a/public/favicons/favicon-16x16.png b/public/favicons/favicon-16x16.png index 44a2c07..c84302f 100644 Binary files a/public/favicons/favicon-16x16.png and b/public/favicons/favicon-16x16.png differ diff --git a/public/favicons/favicon-32x32.png b/public/favicons/favicon-32x32.png index 167064b..8b97c08 100644 Binary files a/public/favicons/favicon-32x32.png and b/public/favicons/favicon-32x32.png differ diff --git a/public/favicons/favicon.ico b/public/favicons/favicon.ico index f0df6de..a59b05e 100644 Binary files a/public/favicons/favicon.ico and b/public/favicons/favicon.ico differ diff --git a/public/favicons/mstile-144x144.png b/public/favicons/mstile-144x144.png index d75fff6..2a3cab2 100644 Binary files a/public/favicons/mstile-144x144.png and b/public/favicons/mstile-144x144.png differ diff --git a/public/favicons/mstile-150x150.png b/public/favicons/mstile-150x150.png index 4fa1097..2aef4dd 100644 Binary files a/public/favicons/mstile-150x150.png and b/public/favicons/mstile-150x150.png differ diff --git a/public/favicons/mstile-310x150.png b/public/favicons/mstile-310x150.png index 05181ca..144c7bd 100644 Binary files a/public/favicons/mstile-310x150.png and b/public/favicons/mstile-310x150.png differ diff --git a/public/favicons/mstile-310x310.png b/public/favicons/mstile-310x310.png index a1b1d13..7ff141b 100644 Binary files a/public/favicons/mstile-310x310.png and b/public/favicons/mstile-310x310.png differ diff --git a/public/favicons/mstile-70x70.png b/public/favicons/mstile-70x70.png index 30641ac..5ecc34f 100644 Binary files a/public/favicons/mstile-70x70.png and b/public/favicons/mstile-70x70.png differ diff --git a/src/adapters/fauna/clear-db.mjs b/src/adapters/fauna/clear-db.mjs index 058c5ce..748c2b9 100644 --- a/src/adapters/fauna/clear-db.mjs +++ b/src/adapters/fauna/clear-db.mjs @@ -9,7 +9,7 @@ const client = new faunadb.Client({ }) async function main() { - client.query( + await client.query( q.Do( q.Map(q.Paginate(q.Documents(q.Collection('users'))), (userRef) => q.Delete(userRef) @@ -49,6 +49,7 @@ async function main() { ) ) ) + console.log('THE END') } main().catch((e) => console.error(e)) diff --git a/src/adapters/fauna/shell.mjs b/src/adapters/fauna/shell.mjs index 241fb2b..d3a85f9 100644 --- a/src/adapters/fauna/shell.mjs +++ b/src/adapters/fauna/shell.mjs @@ -9,8 +9,32 @@ const client = new faunadb.Client({ }) async function main() { - const response = client.query(q.Do()) + const userId = '291732880734814720' + const userRef = q.Ref(q.Collection('users'), userId) + const response = await client.query( + // q.If( + // q.Exists(q.Match(q.Index('notification_status_by_user'), userRef)), + // q.Subtract( + // q.Count(q.Match(q.Index('all_notifications_by_user'), userRef)), + // q.Select( + // ['data', 'count'], + // q.Get(q.Match(q.Index('notification_status_by_user'), userRef)), + // 0 + // ) + // ), + // q.Count( + // q.Match( + // q.Index('all_notifications_by_user'), + // q.Ref(q.Collection('users'), userId) + // ) + // ) + // ) + q.Map(q.Paginate(q.Documents(q.Collection('notifications'))), (userRef) => + q.Delete(userRef) + ) + ) console.log(JSON.stringify(response, null, 2)) + console.log('THE_END') } main().catch((e) => console.error(e)) diff --git a/src/components/AppNavBar.tsx b/src/components/AppNavBar.tsx index 395cb69..a8500bb 100644 --- a/src/components/AppNavBar.tsx +++ b/src/components/AppNavBar.tsx @@ -1,12 +1,14 @@ import { NavBar, Button, Menu, Avatar } from '@/ui' import { signIn, signOut, useSession } from 'next-auth/client' -import { Logo, DonateModal } from '@/components' +import { Logo, DonateModal, GlobalLoadingIndicator, A } from '@/components' import Link from 'next/link' import { useRouter } from 'next/router' import { User } from 'src/pages/members' import { Toggle } from '@/ui' import { Gear, RocketLaunch, SignOut, UserCircle } from 'phosphor-react' import { useState } from 'react' +import { useQuery } from 'react-query' +import classNames from 'classnames' const navbarItems = [ { @@ -20,9 +22,9 @@ const navbarItems = [ href: '/members', }, { - title: 'Our Chatroom', - value: 'our-chatroom', - href: '/chat', + title: 'Chatroom', + value: 'chatroom', + href: 'https://chat.coderplex.org', }, ] @@ -49,9 +51,20 @@ export default function AppNavBar() { value: 'settings', href: '/profile/settings', }, + { + title: 'Report Bug', + value: 'report-bug', + href: 'https://github.com/coderplex-org/coderplex-org/issues/new', + }, ] : []), ] + const { data: notificationsCount } = useQuery( + 'api/fauna/has-notifications', + () => { + return fetch(`/api/fauna/has-notifications`).then((res) => res.json()) + } + ) return ( + + + + + = { + markup: [], + bash: ['sh', 'shellscript'], + clike: [], + c: [], + cpp: [], + css: [], + javascript: ['js'], + jsx: [], + coffeescript: [], + actionscript: [], + 'css-extr': [], + diff: [], + git: [], + go: [], + graphql: [], + handlebars: [], + json: [], + less: [], + makefile: [], + markdown: [], + objectivec: [], + ocaml: [], + python: ['py', 'py3'], + reason: [], + sass: [], + scss: [], + sql: [], + stylus: [], + tsx: [], + typescript: ['ts'], + wasm: [], + yaml: [], +} + +function Code({ + children, + className, +}: { + children: string + className: string +}) { + let languageOfCode = (className?.split('-')?.[1] ?? '') as any + const languages = Object.keys(aliasesMap) as Array + if (!languages.includes(languageOfCode)) { + for (const lang of languages) { + const aliases = aliasesMap[lang] + const match = aliases.find( + // eslint-disable-next-line no-loop-func + (language) => language === languageOfCode + ) + if (match) { + languageOfCode = lang + break + } + } + } + + return ( + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+          
+            {tokens.map((line, i) => (
+              
+ {line.map((token, key) => ( + + ))} +
+ ))} +
+
+ )} +
+ ) +} + +export function Pre({ children }) { + const props = children.props + return +} diff --git a/src/components/GlobalLoadingIndicator.tsx b/src/components/GlobalLoadingIndicator.tsx new file mode 100644 index 0000000..81d9121 --- /dev/null +++ b/src/components/GlobalLoadingIndicator.tsx @@ -0,0 +1,32 @@ +import { useIsFetching } from 'react-query' + +export default function GlobalLoadingIndicator() { + const isFetching = useIsFetching() + + if (!isFetching) { + return null + } + + return ( + + + + + ) +} diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 76d59e2..699138a 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -12,8 +12,8 @@ export default function Hero() {

- Achieve your goals with a community of passionate self-learners by - joining the Coderplex Community + Want to progress in your career? Achieve your goals with a community + of passionate self-learners by joining the Coderplex Community.

diff --git a/src/components/HomePageFeed.tsx b/src/components/HomePageFeed.tsx index a503c93..924dee4 100644 --- a/src/components/HomePageFeed.tsx +++ b/src/components/HomePageFeed.tsx @@ -1,19 +1,21 @@ -import { Avatar } from '@/ui' +import { Avatar, Menu } from '@/ui' import { ChatCenteredDots, + DotsThreeOutlineVertical, Gear, + Pencil, RocketLaunch, - ShareNetwork, ThumbsUp, + Trash, UserCircle, Users, } from 'phosphor-react' import * as React from 'react' import { HomePageFeedUpdateType } from 'src/pages' import { DateTime } from 'luxon' -import { useMutation, useQuery } from 'react-query' +import { useMutation, useQuery, useQueryClient } from 'react-query' import classNames from 'classnames' -import { useEffect, useReducer, useState } from 'react' +import { useState, useRef } from 'react' import { signIn, useSession } from 'next-auth/client' import { User } from 'src/pages/members' import useFollowUser from './profile/useFollowUser' @@ -31,33 +33,15 @@ import { UpdateCommentsList, FollowModal, LikeModal, + useLikes, + EditUpdate, + Goal, } from '@/components' -import { Goal } from './goals' import type { GoalResponse } from 'src/pages/[username]' import { scrollToContentWithId } from 'src/utils' -import { IconBrandDiscord } from 'tabler-icons' - -type LikeData = { - count: number - hasLiked: boolean -} -const initialState: LikeData = { count: 0, hasLiked: false } - -function reducer(state: LikeData, action: { type: string; payload?: any }) { - switch (action.type) { - case 'toggle': - return { - hasLiked: !state.hasLiked, - count: state.hasLiked - ? Number(state.count) - 1 - : Number(state.count) + 1, - } - case 'set': - return { count: action.payload.count, hasLiked: action.payload.hasLiked } - default: - throw new Error() - } -} +import { IconBrandDiscord, IconBug } from 'tabler-icons' +import toast, { Toaster } from 'react-hot-toast' +import ListModal from './modal/ListModal' export function HomePageFeedUpdate({ update, @@ -66,172 +50,270 @@ export function HomePageFeedUpdate({ update: HomePageFeedUpdateType setGoalId: () => void }) { + const queryClient = useQueryClient() + const [isInEditMode, setIsInEditMode] = useState(false) const [isLikeModalOpen, setIsLikeModalOpen] = useState(false) const [session] = useSession() const [showComments, setShowComments] = useState(false) const { postedBy, createdAt: createdAtInMillis, goal, description } = update const createdAt = DateTime.fromMillis(createdAtInMillis) - const { isLoading, isError, data } = useQuery( - ['api/fauna/has-liked', update.id], + const toastId = useRef('') + + const { mutate: deleteUpdate } = useMutation( () => { - return fetch(`/api/fauna/has-liked`, { + return fetch(`/api/fauna/goals/delete-update`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ - updateId: update.id, + id: update.id, }), - }).then((res) => res.json()) + }).then((res) => { + if (!res.ok) { + throw new Error('something went wrong!!!') + } + return res.json() + }) + }, + { + onSuccess: (data, variables, context) => { + toast.success('Deleted your update!!', { + id: toastId.current, + icon: , + }) + + if (queryClient.getQueryState('/api/fauna/all-updates')) { + queryClient.setQueryData<{ updates: HomePageFeedUpdateType[] }>( + '/api/fauna/all-updates', + (oldData) => ({ + updates: oldData.updates.filter( + (_update) => _update.id !== update.id + ), + }) + ) + } + + if (queryClient.getQueryState(['/api/fauna/recent-updates', goal.id])) { + queryClient.setQueryData<{ response: GoalResponse }>( + ['/api/fauna/recent-updates', goal.id], + (oldData) => ({ + response: { + ...oldData.response, + updates: { + data: oldData.response.updates.data.filter( + (_update) => _update.id !== update.id + ), + }, + }, + }) + ) + } + }, + onError: () => { + toast.error('Something went wrong!!!', { + id: toastId.current, + }) + }, } ) - const { mutate } = useMutation(() => { - return fetch(`/api/fauna/toggle-like`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ + + const { count: likesCount, hasLiked, toggleLike } = useLikes({ + initialCount: update.likes.data.length, + initialHasLiked: update.hasLiked, + updateId: update.id, + type: 'UPDATE_LIKE', + mutation: { + endpoint: '/api/fauna/toggle-update-like', + body: { updateId: update.id, - }), - }).then((res) => { - if (!res.ok) { - throw new Error('something went wrong!!!') - } - return res.json() - }) + }, + }, }) - const [{ count: likesCount, hasLiked }, dispatch] = useReducer( - reducer, - initialState - ) - - useEffect(() => { - if (!isLoading && !isError) { - dispatch({ - type: 'set', - payload: { hasLiked: data.liked, count: update.likes.data }, - }) - } - }, [data?.liked, isError, isLoading, update.likes.data]) + const [isOpen, setIsOpen] = useState(false) return ( -
  • -
    -
    -
    -
    - - - -
    -
    -

    - - {postedBy.name} - -

    -

    - -

    -
    -
    -
    - - - - 🚀 Goal: {goal.title} - - -
    -
    -
    -
    - {description} -
    -
    -
    -
    - - - - - - - -
    -
    -
    + <> + + +
  • + {isInEditMode ? ( + setIsInEditMode(false)} + updateFromHomePage={true} + /> + ) : ( + <> +
    +
    +
    +
    + + + +
    +
    +

    + + {postedBy.account?.firstName} + +

    +

    + +

    +
    - {showComments && ( - <> - - - {update.comments.data.map((comment, index) => ( - - {comment.description} - - ))} - - {session && } - - - )} -
  • +
    +
    + + Open quick actions + + + } + > + {session && + (session.user as User).id === update.postedBy.id && ( + <> + setIsInEditMode(true)} + > + Edit + + { + deleteUpdate() + const id = toast.loading( + 'Deleting your update...' + ) + toastId.current = id + }} + > + Delete + + + )} + + setIsOpen(true)} + > + See who liked + + +
    +
    +
    +
    + + + + 🚀 Goal: {goal.title} + + +
    +
    +
    +
    + {description} +
    +
    +
    +
    + + + + + + + +
    +
    + + + {showComments && ( + <> + + + {update.comments.data.map((comment, index) => ( + + {comment.description} + + ))} + + {session && } + + + )} + + )} + + ) } @@ -249,6 +331,7 @@ export default function HomePageFeed({ }) { const [session, loading] = useSession() const [goalId, setgoalId] = useState('') + const [isGoalPreviewOpen, setIsGoalPreviewOpen] = useState(false) return (
    @@ -296,7 +379,10 @@ export default function HomePageFeed({ setgoalId(update.goal.id)} + setGoalId={() => { + setIsGoalPreviewOpen(true) + setgoalId(update.goal.id) + }} /> ))} @@ -304,7 +390,11 @@ export default function HomePageFeed({
    @@ -354,13 +444,22 @@ function HomePageSideNavBar() { Our Chatroom + + + + Report a Bug + )} {!session && ( @@ -374,7 +473,7 @@ function HomePageSideNavBar() { Members void +}) { + const { isLoading, isError, data } = useQuery<{ response: GoalResponse }>( ['/api/fauna/recent-updates', goalId], () => { if (!goalId) { @@ -475,6 +581,93 @@ function HomePageAside({ goalId }: { goalId: string }) { }) } ) + const shouldShowRecentUpdates = + Boolean(goalId) && goalId !== '' && !isLoading && !isError + + const goal: GoalResponse = data?.response + + if (!isOpen) { + return <> + } + return ( +
    + +
    + ) +} + +function HomePageAside({ + isGoalPreviewOpen, + setIsGoalPreviewOpen, + goalId, +}: { + goalId: string + isGoalPreviewOpen: boolean + setIsGoalPreviewOpen: (open: boolean) => void +}) { + const [session] = useSession() const { isLoading: isWhoToFollowLoading, isError: isWhoToFollowError, @@ -483,61 +676,15 @@ function HomePageAside({ goalId }: { goalId: string }) { return fetch(`/api/fauna/who-to-follow`).then((res) => res.json()) }) - const shouldShowRecentUpdates = - Boolean(goalId) && goalId !== '' && !isLoading && !isError - const goal: GoalResponse = data?.response ?? {} - return ( <>
    - {Boolean(goalId) && ( -
    -
    -
    - {isLoading &&

    loading...

    } - {isError &&

    Something went wrong!!!

    } - {shouldShowRecentUpdates && ( - <> - - {goal.title} - - {goal.description} - - - {goal.updates.data.map((update, index) => ( - - {update.description} - - ))} - - - - - )} -
    -
    -
    - )} + + {!isWhoToFollowLoading && !isWhoToFollowError && whoToFollowResponse.users.length > 0 && ( diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx index 8b41b95..f8be4d0 100644 --- a/src/components/Markdown.tsx +++ b/src/components/Markdown.tsx @@ -1,6 +1,58 @@ import MarkdownOriginal, { MarkdownToJSX } from 'markdown-to-jsx' import React from 'react' -import { A } from '.' +import { A, Pre } from '@/components' +import { CodePen, Gist, Tweet, YouTube, CodeSandbox } from 'mdx-embed' + +function Repl({ id }: { id: string }) { + return ( + <> +
    + Show Repl +