{"id":21607644,"url":"https://github.com/pronoia9/dush","last_synced_at":"2026-04-10T02:03:01.912Z","repository":{"id":206450419,"uuid":"716136930","full_name":"pronoia9/dush","owner":"pronoia9","description":"Join the electrifying world of social connections with DUSH! This Next.js marvel reshapes social media as we know it, blending sleek design with cutting-edge tech. Engage, connect, and share seamlessly on a platform built for genuine connections. Dive into DUSH today and redefine your social experience.","archived":false,"fork":false,"pushed_at":"2024-04-25T23:12:05.000Z","size":3108,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-24T19:36:21.630Z","etag":null,"topics":["appwrite","attr-accept","class-variance-authority","clsx","hookform","lucide-react","radix-ui","react-dropzone","react-hook-form","react-intersection-observer","react-router-dom","reactjs","solidjs-dropzone","tailwind-merge","tailwindcss","typescript","zod"],"latest_commit_sha":null,"homepage":"https://dush-pi.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pronoia9.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-08T14:14:51.000Z","updated_at":"2024-05-21T13:03:51.000Z","dependencies_parsed_at":"2024-04-24T20:50:33.658Z","dependency_job_id":"abe3a246-f836-4c96-b4c3-c92dd0eaa0b2","html_url":"https://github.com/pronoia9/dush","commit_stats":null,"previous_names":["pronoia9/dush"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pronoia9%2Fdush","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pronoia9%2Fdush/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pronoia9%2Fdush/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pronoia9%2Fdush/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pronoia9","download_url":"https://codeload.github.com/pronoia9/dush/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244246567,"owners_count":20422461,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appwrite","attr-accept","class-variance-authority","clsx","hookform","lucide-react","radix-ui","react-dropzone","react-hook-form","react-intersection-observer","react-router-dom","reactjs","solidjs-dropzone","tailwind-merge","tailwindcss","typescript","zod"],"created_at":"2024-11-24T20:32:39.763Z","updated_at":"2025-12-31T00:15:58.890Z","avatar_url":"https://github.com/pronoia9.png","language":"TypeScript","readme":"\u003cdiv align=\"center\"\u003e\n  \u003cbr /\u003e\n    \u003ca href=\"https://youtu.be/_W3R2VwRyF4?feature=shared\" target=\"_blank\"\u003e\n      \u003cimg src=\"https://github.com/adrianhajdin/social_media_app/assets/151519281/be514a19-3cbb-48b7-9acd-2cf4d2e319c4\" alt=\"Project Banner\"\u003e\n    \u003c/a\u003e\n  \u003cbr /\u003e\n\n  \u003cdiv\u003e\n    \u003cimg src=\"https://img.shields.io/badge/-React_JS-black?style=for-the-badge\u0026logoColor=white\u0026logo=react\u0026color=61DAFB\" alt=\"react.js\" /\u003e\n    \u003cimg src=\"https://img.shields.io/badge/-Appwrite-black?style=for-the-badge\u0026logoColor=white\u0026logo=appwrite\u0026color=FD366E\" alt=\"appwrite\" /\u003e\n    \u003cimg src=\"https://img.shields.io/badge/-Tailwind_CSS-black?style=for-the-badge\u0026logoColor=white\u0026logo=tailwindcss\u0026color=06B6D4\" alt=\"tailwindcss\" /\u003e\n    \u003cimg src=\"https://img.shields.io/badge/-React_Query-black?style=for-the-badge\u0026logoColor=white\u0026logo=reactquery\u0026color=FF4154\" alt=\"reactquery\" /\u003e\n    \u003cimg src=\"https://img.shields.io/badge/-Typescript-black?style=for-the-badge\u0026logoColor=white\u0026logo=typescript\u0026color=3178C6\" alt=\"typescript\" /\u003e\n  \u003c/div\u003e\n\n  \u003ch3 align=\"center\"\u003eA Social Media Application\u003c/h3\u003e\n\u003c/div\u003e\n\n## 📋 \u003ca name=\"table\"\u003eTable of Contents\u003c/a\u003e\n\n1. 🤖 [Introduction](#introduction)\n2. ⚙️ [Tech Stack](#tech-stack)\n3. 🔋 [Features](#features)\n4. 🤸 [Quick Start](#quick-start)\n5. 🕸️ [Snippets](#snippets)\n6. 🔗 [Links](#links)\n\n## \u003ca name=\"introduction\"\u003e🤖 Introduction\u003c/a\u003e\n\nExplore social media with this user-friendly platform that has a nice look and lots of features. Easily create and explore posts, and enjoy a strong authentication system and quick data fetching using React Query for a smooth user experience.\n\nIf you're getting started and need assistance or face any bugs, join our active Discord community with over 27k+ members. It's a place where people help each other out.\n\n\u003ca href=\"https://discord.com/invite/n6EdbFJ\" target=\"_blank\"\u003e\u003cimg src=\"https://github.com/sujatagunale/EasyRead/assets/151519281/618f4872-1e10-42da-8213-1d69e486d02e\" /\u003e\u003c/a\u003e\n\n## \u003ca name=\"tech-stack\"\u003e⚙️ Tech Stack\u003c/a\u003e\n\n- React.js\n- Appwrite\n- React Query\n- TypeScript\n- Shadcn\n- Tailwind CSS\n\n## \u003ca name=\"features\"\u003e🔋 Features\u003c/a\u003e\n\n👉 **Authentication System**: A robust authentication system ensuring security and user privacy\n\n👉 **Explore Page**: Homepage for users to explore posts, with a featured section for top creators\n\n👉 **Like and Save Functionality**: Enable users to like and save posts, with dedicated pages for managing liked and saved content\n\n👉 **Detailed Post Page**: A detailed post page displaying content and related posts for an immersive user experience\n\n👉 **Profile Page**: A user profile page showcasing liked posts and providing options to edit the profile\n\n👉 **Browse Other Users**: Allow users to browse and explore other users' profiles and posts\n\n👉 **Create Post Page**: Implement a user-friendly create post page with effortless file management, storage, and drag-drop feature\n\n👉 **Edit Post Functionality**: Provide users with the ability to edit the content of their posts at any time\n\n👉 **Responsive UI with Bottom Bar**: A responsive UI with a bottom bar, enhancing the mobile app feel for seamless navigation\n\n👉 **React Query Integration**: Incorporate the React Query (Tanstack Query) data fetching library for, Auto caching to enhance performance, Parallel queries for efficient data retrieval, First-class Mutations, etc\n\n👉 **Backend as a Service (BaaS) - Appwrite**: Utilize Appwrite as a Backend as a Service solution for streamlined backend development, offering features like authentication, database, file storage, and more\n\nand many more, including code architecture and reusability\n\n## \u003ca name=\"quick-start\"\u003e🤸 Quick Start\u003c/a\u003e\n\nFollow these steps to set up the project locally on your machine.\n\n**Prerequisites**\n\nMake sure you have the following installed on your machine:\n\n- [Git](https://git-scm.com/)\n- [Node.js](https://nodejs.org/en)\n- [npm](https://www.npmjs.com/) (Node Package Manager)\n\n**Cloning the Repository**\n\n```bash\ngit clone https://github.com/pronoia9/dush\ncd dush\n```\n\n**Installation**\n\nInstall the project dependencies using npm:\n\n```bash\nnpm install\n```\n\n**Set Up Environment Variables**\n\nCreate a new file named `.env` in the root of your project and add the following content:\n\n```env\nVITE_APPWRITE_URL=\nVITE_APPWRITE_PROJECT_ID=\nVITE_APPWRITE_STORAGE_ID=\nVITE_APPWRITE_DATABASE_ID=\nVITE_APPWRITE_COLLECTION_USERS_ID=\nVITE_APPWRITE_COLLECTION_POSTS_ID=\nVITE_APPWRITE_COLLECTION_SAVES_ID=\n```\n\nReplace the placeholder values with your actual Appwrite credentials. You can obtain these credentials by signing up on the [Appwrite website](https://appwrite.io/).\n\n**Running the Project**\n\n```bash\nnpm start\n```\n\nOpen [http://localhost:5173](http://localhost:5173) in your browser to view the project.\n\n## \u003ca name=\"snippets\"\u003e🕸️ Snippets\u003c/a\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ccode\u003econstants.index.ts\u003c/code\u003e\u003c/summary\u003e\n\n```typescript\nexport const sidebarLinks = [\n  {\n    imgURL: '/assets/icons/home.svg',\n    route: '/',\n    label: 'Home',\n  },\n  {\n    imgURL: '/assets/icons/wallpaper.svg',\n    route: '/explore',\n    label: 'Explore',\n  },\n  {\n    imgURL: '/assets/icons/people.svg',\n    route: '/all-users',\n    label: 'People',\n  },\n  {\n    imgURL: '/assets/icons/bookmark.svg',\n    route: '/saved',\n    label: 'Saved',\n  },\n  {\n    imgURL: '/assets/icons/gallery-add.svg',\n    route: '/create-post',\n    label: 'Create Post',\n  },\n];\n\nexport const bottombarLinks = [\n  {\n    imgURL: '/assets/icons/home.svg',\n    route: '/',\n    label: 'Home',\n  },\n  {\n    imgURL: '/assets/icons/wallpaper.svg',\n    route: '/explore',\n    label: 'Explore',\n  },\n  {\n    imgURL: '/assets/icons/bookmark.svg',\n    route: '/saved',\n    label: 'Saved',\n  },\n  {\n    imgURL: '/assets/icons/gallery-add.svg',\n    route: '/create-post',\n    label: 'Create',\n  },\n];\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ccode\u003eglobals.css\u003c/code\u003e\u003c/summary\u003e\n\n```css\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800\u0026display=swap');\n\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  * {\n    @apply box-border list-none p-0 m-0 scroll-smooth;\n  }\n\n  body {\n    @apply bg-dark-1 text-white min-h-screen font-inter;\n  }\n}\n\n@layer utilities {\n  /* TYPOGRAPHY */\n  .h1-bold {\n    @apply text-[36px] font-bold leading-[140%] tracking-tighter;\n  }\n\n  .h1-semibold {\n    @apply text-[36px] font-semibold leading-[140%] tracking-tighter;\n  }\n\n  .h2-bold {\n    @apply text-[30px] font-bold leading-[140%] tracking-tighter;\n  }\n\n  .h3-bold {\n    @apply text-[24px] font-bold leading-[140%] tracking-tighter;\n  }\n\n  .base-semibold {\n    @apply text-[16px] font-semibold leading-[140%] tracking-tighter;\n  }\n\n  .base-medium {\n    @apply text-[16px] font-medium leading-[140%];\n  }\n\n  .base-regular {\n    @apply text-[16px] font-normal leading-[140%];\n  }\n\n  .body-bold {\n    @apply text-[18px] font-bold leading-[140%];\n  }\n\n  .body-medium {\n    @apply text-[18px] font-medium leading-[140%];\n  }\n\n  .small-semibold {\n    @apply text-[14px] font-semibold leading-[140%] tracking-tighter;\n  }\n\n  .small-medium {\n    @apply text-[14px] font-medium leading-[140%];\n  }\n\n  .small-regular {\n    @apply text-[14px] font-normal leading-[140%];\n  }\n\n  .subtle-semibold {\n    @apply text-[12px] font-semibold leading-[140%];\n  }\n\n  .tiny-medium {\n    @apply text-[10px] font-medium leading-[140%];\n  }\n\n  /* UTILITIES */\n  .invert-white {\n    @apply invert brightness-0 transition;\n  }\n\n  .flex-center {\n    @apply flex justify-center items-center;\n  }\n\n  .flex-between {\n    @apply flex justify-between items-center;\n  }\n\n  .flex-start {\n    @apply flex justify-start items-center;\n  }\n\n  .custom-scrollbar::-webkit-scrollbar {\n    width: 3px;\n    height: 3px;\n    border-radius: 2px;\n  }\n\n  .custom-scrollbar::-webkit-scrollbar-track {\n    background: #09090a;\n  }\n\n  .custom-scrollbar::-webkit-scrollbar-thumb {\n    background: #5c5c7b;\n    border-radius: 50px;\n  }\n\n  .custom-scrollbar::-webkit-scrollbar-thumb:hover {\n    background: #7878a3;\n  }\n\n  .common-container {\n    @apply flex flex-col flex-1 items-center gap-10 overflow-scroll py-10 px-5 md:px-8 lg:p-14 custom-scrollbar;\n  }\n\n  /* All Users */\n  .user-container {\n    @apply max-w-5xl flex flex-col items-start w-full gap-6 md:gap-9;\n  }\n\n  .user-grid {\n    @apply w-full grid grid-cols-1 xs:grid-cols-2 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-7 max-w-5xl;\n  }\n\n  /* Explore */\n  .explore-container {\n    @apply flex flex-col flex-1 items-center overflow-scroll py-10 px-5 md:p-14 custom-scrollbar;\n  }\n\n  .explore-inner_container {\n    @apply max-w-5xl flex flex-col items-center w-full gap-6 md:gap-9;\n  }\n\n  .explore-search {\n    @apply h-12 bg-dark-4 border-none placeholder:text-light-4 focus-visible:ring-0 focus-visible:ring-offset-0 ring-offset-0 !important;\n  }\n\n  /* Home */\n  .home-container {\n    @apply flex flex-col flex-1 items-center gap-10 overflow-scroll py-10 px-5 md:px-8 lg:p-14 custom-scrollbar;\n  }\n\n  .home-posts {\n    @apply max-w-screen-sm flex flex-col items-center w-full gap-6 md:gap-9;\n  }\n\n  .home-creators {\n    @apply hidden xl:flex flex-col w-72 2xl:w-465 px-6 py-10 gap-10  overflow-scroll custom-scrollbar;\n  }\n\n  /* Post Details */\n  .post_details-container {\n    @apply flex flex-col flex-1 gap-10 overflow-scroll py-10 px-5 md:p-14 custom-scrollbar items-center;\n  }\n\n  .post_details-card {\n    @apply bg-dark-2 w-full max-w-5xl rounded-[30px] flex-col flex xl:flex-row border border-dark-4 xl:rounded-l-[24px];\n  }\n\n  .post_details-img {\n    @apply h-80 lg:h-[480px] xl:w-[48%] rounded-t-[30px] xl:rounded-l-[24px] xl:rounded-tr-none object-cover p-5 bg-dark-1;\n  }\n\n  .post_details-info {\n    @apply bg-dark-2 flex flex-col gap-5 lg:gap-7 flex-1 items-start p-8 rounded-[30px];\n  }\n\n  .post_details-delete_btn {\n    @apply p-0 flex gap-3 hover:bg-transparent hover:text-light-1  text-light-1 small-medium lg:base-medium;\n  }\n\n  /* Profile */\n  .profile-container {\n    @apply flex flex-col items-center flex-1 gap-10 overflow-scroll py-10 px-5 md:p-14 custom-scrollbar;\n  }\n\n  .profile-inner_container {\n    @apply flex items-center md:mb-8 xl:items-start gap-8 flex-col xl:flex-row relative max-w-5xl w-full;\n  }\n\n  .profile-tab {\n    @apply flex-center gap-3 py-4 w-48 bg-dark-2  transition flex-1 xl:flex-initial;\n  }\n\n  /* Saved */\n  .saved-container {\n    @apply flex flex-col flex-1 items-center gap-10 overflow-scroll py-10 px-5 md:p-14 custom-scrollbar;\n  }\n\n  /* Bottom bar */\n  .bottom-bar {\n    @apply z-50 flex-between w-full sticky bottom-0 rounded-t-[20px] bg-dark-2 px-5 py-4 md:hidden;\n  }\n\n  /* File uploader */\n  .file_uploader-img {\n    @apply h-80 lg:h-[480px] w-full rounded-[24px] object-cover object-top;\n  }\n\n  .file_uploader-label {\n    @apply text-light-4 text-center small-regular w-full p-4 border-t border-t-dark-4;\n  }\n\n  .file_uploader-box {\n    @apply flex-center flex-col p-7 h-80 lg:h-[612px];\n  }\n\n  /* Grid Post List */\n  .grid-container {\n    @apply w-full grid grid-cols-1 sm:grid-cols-2 md:grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-7 max-w-5xl;\n  }\n\n  .grid-post_link {\n    @apply flex rounded-[24px] border border-dark-4 overflow-hidden cursor-pointer w-full h-full;\n  }\n\n  .grid-post_user {\n    @apply absolute bottom-0 p-5 flex-between w-full bg-gradient-to-t from-dark-3 to-transparent rounded-b-[24px] gap-2;\n  }\n\n  /* Left sidebar */\n  .leftsidebar {\n    @apply hidden md:flex px-6 py-10 flex-col justify-between min-w-[270px] bg-dark-2;\n  }\n\n  .leftsidebar-link {\n    @apply rounded-lg base-medium hover:bg-primary-500 transition;\n  }\n\n  /* Post Card */\n  .post-card {\n    @apply bg-dark-2 rounded-3xl border border-dark-4 p-5 lg:p-7 w-full max-w-screen-sm;\n  }\n\n  .post-card_img {\n    @apply h-64 xs:h-[400px] lg:h-[450px] w-full rounded-[24px] object-cover mb-5;\n  }\n\n  /* Topbar */\n  .topbar {\n    @apply sticky top-0 z-50 md:hidden bg-dark-2 w-full;\n  }\n\n  /* User card */\n  .user-card {\n    @apply flex-center flex-col gap-4 border border-dark-4 rounded-[20px] px-5 py-8;\n  }\n}\n\n@layer components {\n  /* SHADCN COMPONENTS */\n  /* Form */\n  .shad-form_label {\n    @apply text-white !important;\n  }\n\n  .shad-form_message {\n    @apply text-red !important;\n  }\n\n  .shad-input {\n    @apply h-12 bg-dark-4 border-none placeholder:text-light-4 focus-visible:ring-1 focus-visible:ring-offset-1 ring-offset-light-3 !important;\n  }\n\n  .shad-textarea {\n    @apply h-36 bg-dark-3 rounded-xl border-none focus-visible:ring-1 focus-visible:ring-offset-1 ring-offset-light-3 !important;\n  }\n\n  /* Button */\n  .shad-button_primary {\n    @apply bg-primary-500 hover:bg-primary-500 text-light-1 flex gap-2 !important;\n  }\n\n  .shad-button_dark_4 {\n    @apply h-12 bg-dark-4 px-5 text-light-1 flex gap-2 !important;\n  }\n\n  .shad-button_ghost {\n    @apply flex gap-4 items-center justify-start hover:bg-transparent hover:text-white !important;\n  }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ccode\u003equeryKeys.ts\u003c/code\u003e\u003c/summary\u003e\n\n```typescript\nexport enum QUERY_KEYS {\n  // AUTH KEYS\n  CREATE_USER_ACCOUNT = 'createUserAccount',\n\n  // USER KEYS\n  GET_CURRENT_USER = 'getCurrentUser',\n  GET_USERS = 'getUsers',\n  GET_USER_BY_ID = 'getUserById',\n\n  // POST KEYS\n  GET_POSTS = 'getPosts',\n  GET_INFINITE_POSTS = 'getInfinitePosts',\n  GET_RECENT_POSTS = 'getRecentPosts',\n  GET_POST_BY_ID = 'getPostById',\n  GET_USER_POSTS = 'getUserPosts',\n  GET_FILE_PREVIEW = 'getFilePreview',\n\n  //  SEARCH KEYS\n  SEARCH_POSTS = 'getSearchPosts',\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ccode\u003etailwind.config.js\u003c/code\u003e\u003c/summary\u003e\n\n```javascript\n/** @type {import('tailwindcss').Config} */\nconst defaultTheme = require('tailwindcss/defaultTheme');\n\nmodule.exports = {\n  darkMode: ['class'],\n  content: ['./pages/**/*.{ts,tsx}', './components/**/*.{ts,tsx}', './app/**/*.{ts,tsx}', './src/**/*.{ts,tsx}'],\n  theme: {\n    container: {\n      center: true,\n      padding: '2rem',\n      screens: {\n        '2xl': '1400px',\n      },\n    },\n    extend: {\n      colors: {\n        'primary-500': '#877EFF',\n        'primary-600': '#5D5FEF',\n        'secondary-500': '#FFB620',\n        'off-white': '#D0DFFF',\n        red: '#FF5A5A',\n        'dark-1': '#000000',\n        'dark-2': '#09090A',\n        'dark-3': '#101012',\n        'dark-4': '#1F1F22',\n        'light-1': '#FFFFFF',\n        'light-2': '#EFEFEF',\n        'light-3': '#7878A3',\n        'light-4': '#5C5C7B',\n      },\n      screens: {\n        xs: '480px',\n      },\n      width: {\n        420: '420px',\n        465: '465px',\n      },\n      fontFamily: {\n        inter: ['Inter', 'sans-serif'],\n      },\n      keyframes: {\n        'accordion-down': {\n          from: { height: 0 },\n          to: { height: 'var(--radix-accordion-content-height)' },\n        },\n        'accordion-up': {\n          from: { height: 'var(--radix-accordion-content-height)' },\n          to: { height: 0 },\n        },\n      },\n      animation: {\n        'accordion-down': 'accordion-down 0.2s ease-out',\n        'accordion-up': 'accordion-up 0.2s ease-out',\n      },\n    },\n  },\n  plugins: [require('tailwindcss-animate')],\n};\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ccode\u003etypes.index.ts\u003c/code\u003e\u003c/summary\u003e\n\n```typescript\nexport type INavLink = {\n  imgURL: string;\n  route: string;\n  label: string;\n};\n\nexport type IUpdateUser = {\n  userId: string;\n  name: string;\n  bio: string;\n  imageId: string;\n  imageUrl: URL | string;\n  file: File[];\n};\n\nexport type INewPost = {\n  userId: string;\n  caption: string;\n  file: File[];\n  location?: string;\n  tags?: string;\n};\n\nexport type IUpdatePost = {\n  postId: string;\n  caption: string;\n  imageId: string;\n  imageUrl: URL;\n  file: File[];\n  location?: string;\n  tags?: string;\n};\n\nexport type IUser = {\n  id: string;\n  name: string;\n  username: string;\n  email: string;\n  imageUrl: string;\n  bio: string;\n};\n\nexport type INewUser = {\n  name: string;\n  email: string;\n  username: string;\n  password: string;\n};\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ccode\u003euseDebounce.ts\u003c/code\u003e\u003c/summary\u003e\n\n```typescript\nimport { useEffect, useState } from 'react';\n\n// https://codesandbox.io/s/react-query-debounce-ted8o?file=/src/useDebounce.js\nexport default function useDebounce\u003cT\u003e(value: T, delay: number): T {\n  // State and setters for debounced value\n  const [debouncedValue, setDebouncedValue] = useState\u003cT\u003e(value);\n\n  useEffect(() =\u003e {\n    // Update debounced value after delay\n    const handler = setTimeout(() =\u003e {\n      setDebouncedValue(value);\n    }, delay);\n\n    // Cancel the timeout if value changes (also on delay change or unmount)\n    // This is how we prevent debounced value from updating if value is changed ...\n    // .. within the delay period. Timeout gets cleared and restarted.\n    return () =\u003e {\n      clearTimeout(handler);\n    };\n  }, [value, delay]); // Only re-call effect if value or delay changes\n\n  return debouncedValue;\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ccode\u003eutils.ts\u003c/code\u003e\u003c/summary\u003e\n\n```typescript\nimport { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n\nexport const convertFileToUrl = (file: File) =\u003e URL.createObjectURL(file);\n\nexport function formatDateString(dateString: string) {\n  const options: Intl.DateTimeFormatOptions = {\n    year: 'numeric',\n    month: 'short',\n    day: 'numeric',\n  };\n\n  const date = new Date(dateString);\n  const formattedDate = date.toLocaleDateString('en-US', options);\n\n  const time = date.toLocaleTimeString([], {\n    hour: 'numeric',\n    minute: '2-digit',\n  });\n\n  return `${formattedDate} at ${time}`;\n}\n\n//\nexport const multiFormatDateString = (timestamp: string = ''): string =\u003e {\n  const timestampNum = Math.round(new Date(timestamp).getTime() / 1000);\n  const date: Date = new Date(timestampNum * 1000);\n  const now: Date = new Date();\n\n  const diff: number = now.getTime() - date.getTime();\n  const diffInSeconds: number = diff / 1000;\n  const diffInMinutes: number = diffInSeconds / 60;\n  const diffInHours: number = diffInMinutes / 60;\n  const diffInDays: number = diffInHours / 24;\n\n  switch (true) {\n    case Math.floor(diffInDays) \u003e= 30:\n      return formatDateString(timestamp);\n    case Math.floor(diffInDays) === 1:\n      return `${Math.floor(diffInDays)} day ago`;\n    case Math.floor(diffInDays) \u003e 1 \u0026\u0026 diffInDays \u003c 30:\n      return `${Math.floor(diffInDays)} days ago`;\n    case Math.floor(diffInHours) \u003e= 1:\n      return `${Math.floor(diffInHours)} hours ago`;\n    case Math.floor(diffInMinutes) \u003e= 1:\n      return `${Math.floor(diffInMinutes)} minutes ago`;\n    default:\n      return 'Just now';\n  }\n};\n\nexport const checkIsLiked = (likeList: string[], userId: string) =\u003e {\n  return likeList.includes(userId);\n};\n```\n\n\u003c/details\u003e\n\n## \u003ca name=\"links\"\u003e🔗 Links\u003c/a\u003e\n\nAssets used in the project are [here](https://drive.google.com/file/d/13_7FofRAC3wARqPtAVPi53QNJJRd5RH_/view?usp=sharing)\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpronoia9%2Fdush","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpronoia9%2Fdush","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpronoia9%2Fdush/lists"}