├── .eslintrc.json ├── src ├── vite-env.d.ts ├── index.tsx ├── pages │ ├── home │ │ ├── allUsers │ │ │ └── AllUsers.tsx │ │ ├── profile │ │ │ ├── ProfileBio.tsx │ │ │ ├── ProfileUserPosts.tsx │ │ │ ├── ProfileInfo.tsx │ │ │ ├── ProfileStatus.tsx │ │ │ ├── ProfileButtonsAndAmounts.tsx │ │ │ └── Profile.tsx │ │ ├── explore │ │ │ ├── HeaderContainer.tsx │ │ │ ├── LoadMoreButton.tsx │ │ │ ├── FilterPosts.tsx │ │ │ ├── GridPostItem.tsx │ │ │ └── Explore.tsx │ │ ├── index │ │ │ ├── PostItem.tsx │ │ │ ├── PostSkeleton.tsx │ │ │ ├── AddCommentForm.tsx │ │ │ ├── PostContent.tsx │ │ │ ├── LikePost.tsx │ │ │ ├── PostComments.tsx │ │ │ ├── PostCreatorInfo.tsx │ │ │ ├── SavePost.tsx │ │ │ └── Home.tsx │ │ ├── createpost │ │ │ └── CreatePost.tsx │ │ ├── HomeLayout.tsx │ │ ├── postsDetails │ │ │ ├── DetailsImg.tsx │ │ │ ├── PostContent.tsx │ │ │ ├── PostsDetails.tsx │ │ │ └── DetailsInfo.tsx │ │ ├── editPost │ │ │ └── EditPost.tsx │ │ ├── saved │ │ │ ├── SavePostItem.tsx │ │ │ └── Saved.tsx │ │ └── updateProfile │ │ │ └── UpdateProfile.tsx │ └── auth │ │ ├── login │ │ ├── LoginHeader.tsx │ │ └── Index.tsx │ │ ├── signup │ │ ├── SignupHeader.tsx │ │ └── Index.tsx │ │ └── AuthLayout.tsx ├── lib │ ├── utils.ts │ ├── react-query │ │ ├── QueryProvider.tsx │ │ ├── queryKeys.ts │ │ └── QueriesAndMutations.ts │ ├── appwrite │ │ ├── config.ts │ │ └── api.ts │ └── validation │ │ └── index.ts ├── ui │ ├── Logo.tsx │ ├── LoadingSpinner.tsx │ ├── ProtectedPage.tsx │ ├── CollapseText.tsx │ ├── PasswordInput.tsx │ ├── Tooltip.tsx │ ├── ProfileUploader.tsx │ ├── DeletePost.tsx │ ├── FileUploader.tsx │ └── PostForm.tsx ├── features │ ├── left-sidebar │ │ ├── index.tsx │ │ ├── UserInfoSkeleton.tsx │ │ ├── UserInfo.tsx │ │ ├── NavLinks.tsx │ │ └── Logout.tsx │ ├── right-sidebar │ │ ├── useLocalStorage.ts │ │ ├── UserItem.tsx │ │ └── RightSidebar.tsx │ ├── bottom-sidebar │ │ └── BottomSidebar.tsx │ └── top-sidebar │ │ └── TopSidebar.tsx ├── components │ └── ui │ │ ├── textarea.tsx │ │ ├── label.tsx │ │ ├── input.tsx │ │ ├── toaster.tsx │ │ ├── button.tsx │ │ ├── use-toast.ts │ │ ├── form.tsx │ │ └── toast.tsx ├── types │ └── index.ts ├── context │ └── AuthContext.tsx ├── utils │ ├── helper.ts │ └── constants.ts ├── App.tsx └── index.css ├── public ├── logo.webp ├── favicon.webp ├── poster.webp ├── unknown.webp ├── heart-sold.webp ├── save-sold.webp ├── auth-preview.webp ├── save-outline.webp ├── heart-outline.webp ├── no-post-image.webp └── robots.txt ├── vercel.json ├── postcss.config.js ├── tsconfig.node.json ├── .gitignore ├── vite.config.ts ├── components.json ├── tsconfig.json ├── index.html ├── package.json ├── README.md └── tailwind.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "react-app" 3 | } 4 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fares-ahmedd/socialLinx/HEAD/public/logo.webp -------------------------------------------------------------------------------- /public/favicon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fares-ahmedd/socialLinx/HEAD/public/favicon.webp -------------------------------------------------------------------------------- /public/poster.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fares-ahmedd/socialLinx/HEAD/public/poster.webp -------------------------------------------------------------------------------- /public/unknown.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fares-ahmedd/socialLinx/HEAD/public/unknown.webp -------------------------------------------------------------------------------- /public/heart-sold.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fares-ahmedd/socialLinx/HEAD/public/heart-sold.webp -------------------------------------------------------------------------------- /public/save-sold.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fares-ahmedd/socialLinx/HEAD/public/save-sold.webp -------------------------------------------------------------------------------- /public/auth-preview.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fares-ahmedd/socialLinx/HEAD/public/auth-preview.webp -------------------------------------------------------------------------------- /public/save-outline.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fares-ahmedd/socialLinx/HEAD/public/save-outline.webp -------------------------------------------------------------------------------- /public/heart-outline.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fares-ahmedd/socialLinx/HEAD/public/heart-outline.webp -------------------------------------------------------------------------------- /public/no-post-image.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fares-ahmedd/socialLinx/HEAD/public/no-post-image.webp -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [ 3 | { 4 | "source": "/(.*)", 5 | "destination": "/index.html" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | Disallow: /admin 4 | Disallow: /private 5 | Sitemap: https://social-linx.vercel.app/sitemap.xml 6 | Crawl-delay: 10 -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const postcssConfig = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | 8 | export default postcssConfig; 9 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from "react-dom/client"; 2 | import App from "./App"; 3 | import "./index.css"; 4 | ReactDOM.createRoot(document.getElementById("root")!).render(); 5 | -------------------------------------------------------------------------------- /src/pages/home/allUsers/AllUsers.tsx: -------------------------------------------------------------------------------- 1 | import RightSidebar from "@/features/right-sidebar/RightSidebar"; 2 | 3 | function AllUsers() { 4 | return ; 5 | } 6 | 7 | export default AllUsers; 8 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export const convertFileToUrl = (file: File) => URL.createObjectURL(file); 5 | 6 | export function cn(...inputs: ClassValue[]) { 7 | return twMerge(clsx(inputs)); 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /src/pages/home/profile/ProfileBio.tsx: -------------------------------------------------------------------------------- 1 | type Props = { 2 | bio: string; 3 | }; 4 | 5 | function ProfileBio({ bio }: Props) { 6 | return ( 7 |
8 |

bio

9 |

{bio}

10 |
11 | ); 12 | } 13 | 14 | export default ProfileBio; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .env 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | -------------------------------------------------------------------------------- /src/lib/react-query/QueryProvider.tsx: -------------------------------------------------------------------------------- 1 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 2 | 3 | const queryClint = new QueryClient(); 4 | 5 | function QueryProvider({ children }: { children: React.ReactNode }) { 6 | return ( 7 | {children} 8 | ); 9 | } 10 | 11 | export { QueryProvider }; 12 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import react from "@vitejs/plugin-react"; 3 | import { defineConfig } from "vite"; 4 | // @ts-ignore 5 | import eslint from "vite-plugin-eslint"; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | plugins: [react(), eslint], 10 | resolve: { 11 | alias: { 12 | "@": path.resolve(__dirname, "./src"), 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/index.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /src/ui/Logo.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | 3 | export default function Logo() { 4 | return ( 5 | 6 | 7 | Logo 8 | SocialLinx 9 | {" "} 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/pages/home/explore/HeaderContainer.tsx: -------------------------------------------------------------------------------- 1 | type Props = { 2 | totalPosts: number | undefined; 3 | }; 4 | function HeaderContainer({ totalPosts }: Props) { 5 | return ( 6 |
7 |

Popular Today

8 | Total Posts: {totalPosts} 9 |
10 | ); 11 | } 12 | 13 | export default HeaderContainer; 14 | -------------------------------------------------------------------------------- /src/features/left-sidebar/index.tsx: -------------------------------------------------------------------------------- 1 | import Logo from "@/ui/Logo"; 2 | import NavLinks from "./NavLinks"; 3 | import UserInfo from "./UserInfo"; 4 | import Logout from "./Logout"; 5 | 6 | function LeftSidebar() { 7 | return ( 8 | 16 | ); 17 | } 18 | 19 | export default LeftSidebar; 20 | -------------------------------------------------------------------------------- /src/features/left-sidebar/UserInfoSkeleton.tsx: -------------------------------------------------------------------------------- 1 | function UserInfoSkeleton() { 2 | return ( 3 |
4 |
5 |
6 |

7 |

8 |
9 |
10 | ); 11 | } 12 | 13 | export default UserInfoSkeleton; 14 | -------------------------------------------------------------------------------- /src/ui/LoadingSpinner.tsx: -------------------------------------------------------------------------------- 1 | const LoadingSpinner = () => { 2 | return ( 3 |
4 |
5 |
6 |
7 | ); 8 | }; 9 | 10 | export default LoadingSpinner; 11 | -------------------------------------------------------------------------------- /src/pages/auth/login/LoginHeader.tsx: -------------------------------------------------------------------------------- 1 | function LoginHeader() { 2 | return ( 3 |
4 |

5 | Logo 6 | SocialLinx 7 |

8 |

9 | Login To Your Account 10 |

11 |
12 | ); 13 | } 14 | 15 | export default LoginHeader; 16 | -------------------------------------------------------------------------------- /src/pages/auth/signup/SignupHeader.tsx: -------------------------------------------------------------------------------- 1 | function SignupHeader() { 2 | return ( 3 |
4 |

5 | Logo 6 | SocialLinx 7 |

8 |

9 | Create a new Account 10 |

11 |
12 | ); 13 | } 14 | 15 | export default SignupHeader; 16 | -------------------------------------------------------------------------------- /src/pages/home/index/PostItem.tsx: -------------------------------------------------------------------------------- 1 | import { Models } from "appwrite"; 2 | import PostCreatorInfo from "./PostCreatorInfo"; 3 | import PostContent from "./PostContent"; 4 | 5 | type PostItemProps = { 6 | post: Models.Document; 7 | }; 8 | 9 | function PostItem({ post }: PostItemProps) { 10 | return ( 11 |
  • 12 |
    13 | 14 | 15 |
    16 |
  • 17 | ); 18 | } 19 | 20 | export default PostItem; 21 | -------------------------------------------------------------------------------- /src/pages/home/explore/LoadMoreButton.tsx: -------------------------------------------------------------------------------- 1 | type Props = { 2 | setPostsAmount: React.Dispatch>; 3 | }; 4 | 5 | function LoadMoreButton({ setPostsAmount }: Props) { 6 | return ( 7 | 13 | ); 14 | } 15 | 16 | export default LoadMoreButton; 17 | -------------------------------------------------------------------------------- /src/pages/auth/AuthLayout.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from "react-router-dom"; 2 | 3 | function AuthLayout() { 4 | return ( 5 | <> 6 |
    7 | 8 |
    9 |
    10 | App Poster 15 |
    16 | 17 | ); 18 | } 19 | 20 | export default AuthLayout; 21 | -------------------------------------------------------------------------------- /src/lib/react-query/queryKeys.ts: -------------------------------------------------------------------------------- 1 | export enum QUERY_KEYS { 2 | // AUTH KEYS 3 | CREATE_USER_ACCOUNT = "createUserAccount", 4 | 5 | // USER KEYS 6 | GET_CURRENT_USER = "getCurrentUser", 7 | GET_USERS = "getUsers", 8 | GET_USER_BY_ID = "getUserById", 9 | 10 | // POST KEYS 11 | GET_POSTS = "getPosts", 12 | GET_INFINITE_POSTS = "getInfinitePosts", 13 | GET_RECENT_POSTS = "getRecentPosts", 14 | GET_POST_BY_ID = "getPostById", 15 | GET_USER_POSTS = "getUserPosts", 16 | GET_FILE_PREVIEW = "getFilePreview", 17 | 18 | // SEARCH KEYS 19 | SEARCH_POSTS = "getSearchPosts", 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/home/createpost/CreatePost.tsx: -------------------------------------------------------------------------------- 1 | import PostForm from "@/ui/PostForm"; 2 | import { FcAddImage } from "react-icons/fc"; 3 | 4 | function CreatePost() { 5 | return ( 6 |
    7 |
    8 |
    9 | 10 |

    Create a Post

    11 |
    12 | 13 |
    14 |
    15 | ); 16 | } 17 | 18 | export default CreatePost; 19 | -------------------------------------------------------------------------------- /src/pages/home/HomeLayout.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from "react-router-dom"; 2 | import LeftSidebar from "../../features/left-sidebar"; 3 | import TopSidebar from "@/features/top-sidebar/TopSidebar"; 4 | import BottomSidebar from "@/features/bottom-sidebar/BottomSidebar"; 5 | function AppLayout() { 6 | return ( 7 |
    8 | 9 | 10 |
    11 | 12 |
    13 | 14 | 15 |
    16 | ); 17 | } 18 | 19 | export default AppLayout; 20 | -------------------------------------------------------------------------------- /src/ui/ProtectedPage.tsx: -------------------------------------------------------------------------------- 1 | import { useUserContext } from "@/context/AuthContext"; 2 | import { ReactNode } from "react"; 3 | import { Navigate } from "react-router-dom"; 4 | import LoadingSpinner from "./LoadingSpinner"; 5 | 6 | interface ProtectedPageProps { 7 | children: ReactNode; 8 | } 9 | function ProtectedPage({ children }: ProtectedPageProps) { 10 | const { isAuth, isLoading } = useUserContext(); 11 | if (isLoading) 12 | return ( 13 |
    14 | 15 |
    16 | ); 17 | return isAuth ? children : ; 18 | } 19 | 20 | export default ProtectedPage; 21 | -------------------------------------------------------------------------------- /src/pages/home/postsDetails/DetailsImg.tsx: -------------------------------------------------------------------------------- 1 | import { Models } from "appwrite"; 2 | 3 | interface Details { 4 | post: Models.Document | undefined; 5 | } 6 | function DetailsImg({ post }: Details) { 7 | return ( 8 |
    9 |

    10 | Loading image... 11 |

    12 | no post 18 |
    19 | ); 20 | } 21 | 22 | export default DetailsImg; 23 | -------------------------------------------------------------------------------- /src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | export interface TextareaProps 6 | extends React.TextareaHTMLAttributes {} 7 | 8 | const Textarea = React.forwardRef( 9 | ({ className, ...props }, ref) => { 10 | return ( 11 |