├── .github └── workflows │ └── lint.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── components ├── Custom │ ├── InputField.tsx │ ├── custombutton.component.tsx │ ├── custominput.component.tsx │ ├── header.component.tsx │ ├── searchbar.component.tsx │ └── tag.component.tsx ├── Misc │ ├── authRequired.tsx │ ├── branding.tsx │ ├── mention.component.tsx │ ├── navlink.component.tsx │ ├── popup.component.tsx │ ├── profilecrumb.component.tsx │ └── switcher.component.tsx ├── PageAssets │ ├── navbar.component.tsx │ ├── page404.component.tsx │ └── signin.component.tsx ├── Post │ ├── postcard.component.tsx │ └── postcrumb.component.tsx ├── SignIn │ ├── GoBackContinueButton.tsx │ ├── SignInStages │ │ ├── stage1.section.tsx │ │ ├── stage2.section.tsx │ │ └── stage3.section.tsx │ ├── firebaseSignin.component.tsx │ ├── firebaseUI.componet.tsx │ ├── progressbar.section.tsx │ ├── signInCard.component.tsx │ ├── signin.component.tsx │ ├── signin.provider.tsx │ └── types.ts └── Skeletons │ └── SkeletonPost.tsx ├── global.d.ts ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── 404.tsx ├── _app.tsx ├── api │ └── hello.js ├── auth.tsx ├── index.tsx ├── onboarding.tsx └── search │ └── [query].tsx ├── postcss.config.js ├── public ├── OnboardingBackground.svg ├── Orb.svg ├── Orbits-Banner.png ├── Orbits-Banner.svg ├── artwork.svg ├── background.svg ├── default.svg ├── favicon.ico ├── favicon.png ├── glitch.gif ├── illustration.svg ├── logo.png ├── manifest.json ├── sidebar_login_prompt.svg ├── spinner.svg ├── successfull.svg ├── sw.js ├── sw.js.map ├── vercel.svg ├── workbox-6b19f60b.js └── workbox-6b19f60b.js.map ├── sections ├── Dashboard │ ├── SideBar │ │ ├── loginprompt.sidebar.tsx │ │ ├── trendingposts.sidebar.tsx │ │ └── trendingtags.sidebar.tsx │ ├── dashboard.section.tsx │ ├── modal.section.tsx │ ├── tabchanger.section.tsx │ └── writepost.section.tsx ├── Post │ ├── posts.section.tsx │ └── trendingposts.section.tsx └── SearchResults │ ├── articles.section.tsx │ ├── nothingFound.component.tsx │ ├── people.section.tsx │ ├── results.component.jsx │ └── searching.component.tsx ├── styles ├── Home.module.css └── globals.css ├── tailwind.config.js ├── tsconfig.json ├── types ├── data.types.ts └── ui.types.ts └── utils ├── firebase.ts ├── helpers ├── post │ ├── add_new_post.ts │ ├── get_post.ts │ └── search_post.ts └── user │ ├── add_new_user.ts │ ├── get_user.ts │ ├── search_user.ts │ └── username_suggestion.ts ├── popular_tags.ts ├── providers ├── api.provider.tsx ├── auth.provider.tsx └── popup.provider.tsx └── topicsInterested.ts /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | paths: 6 | - "components/**" 7 | - "pages/**" 8 | - "sections/**" 9 | - "types/**" 10 | - "utils/**" 11 | 12 | jobs: 13 | run-linters: 14 | name: Run linters 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Check out Git repository 19 | uses: actions/checkout@v2 20 | 21 | - name: Set up Node.js 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: 12 25 | 26 | # ESLint and Prettier must be in `package.json` 27 | - name: Install Node.js dependencies 28 | run: npm ci 29 | 30 | - name: Run linters 31 | uses: wearerequired/lint-action@v1 32 | with: 33 | prettier: true 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # code editor 37 | /.idea -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts 2 | 3 | build/ 4 | coverage/ 5 | .next/ 6 | node_modules/ -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "tabWidth": 2, 4 | "printWidth": 100, 5 | "trailingComma": "none", 6 | "jsxBracketSameLine": true, 7 | "jsxSingleQuote": false, 8 | "singleQuote": false 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Saptarshi Basu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | Stars 5 | GitHub contributors 6 | Forks 7 | Stars 8 |
9 |
10 | 11 | ## 🪐 Orbits (Under Construction 🚧) 12 | 13 | > Orbits help you reserach, learn and spread 🧑‍🔬 14 | 15 | It help you learn about more on your favorite subject, help others on their reserach. Help others to discover, create new things in new fields. Daily feed about your interests. 16 | 17 | At Orbits we care about: 18 | 19 | - 🌟 **Maintenance**: We are working continuously to introduce new features, fix bugs, and improve user experience. 20 | - ❣ **Being Helpful**: Orbits's article feed is constantly updated making your day worhty. Discover brand-new content as soon as it is published. 21 | - 💫 **Open-source**: Orbits is completely open-source. We believe in transparency and giving back to the community, so we decided to publish the source code to GitHub. Suggest a feature, report a bug, or even contribute. Everyone is welcome! 22 | 23 | ## 📯 Philosophy 24 | 25 | We, as science nerds, spend a lot of time looking for valuable articles and blog posts about our favorite field of science. We believe that searching through old and broken sites for new researches isn't a thing science fans should do anymore. It's hard to catch up with all the latest happenings,as it's spread on very less blogs and consumes tons of time to find correct one. 26 | 27 | That's why we built Orbits, to help you: 28 | 29 | - 🧑‍🔬 Stay productive 30 | - 😀 Make Friends 31 | - 📰 Discover articles in one click 32 | 33 | ## 🗂 Tech Stack 34 | 35 | Below is a list of technologies we use at Orbits. 36 | 37 | - 🎨 **Frontend:** Nextjs 38 | - 🌳 **Backend:** Node.js 39 | - 🧰 **Services:** Firebase 40 | - 🗄️ **Database:** MongoDB 41 | - ☁ **Deployment:** Vercel 42 | 43 | ## 🙌 Want to Contribute? 44 | 45 | We would be very happy and grateful if you want to contribute. 46 | There are two repos:- 47 | 48 | - 🏗 **Frontend Repo:** Orbits Frontend 49 | - 🏗 **Backend Repo:** Orbits Backend 50 | 51 | We are open to all kinds of contributions. If you want to: 52 | 53 | - 🤔 Suggest a feature 54 | - 🐛 Report an issue 55 | - 📖 Improve documentation 56 | - 👨‍💻 Contribute to the code 57 | 58 | Made for, by & with community 💖. 59 | 60 | Still Under Construction 🚧 61 | -------------------------------------------------------------------------------- /components/Custom/InputField.tsx: -------------------------------------------------------------------------------- 1 | import { useField } from "formik"; 2 | import React from "react"; 3 | 4 | const InputField: React.FC< 5 | React.DetailedHTMLProps< 6 | React.InputHTMLAttributes, 7 | HTMLInputElement 8 | > & { 9 | name: string; 10 | } 11 | > = ({ className, ...props }) => { 12 | const [field, { error }] = useField(props); 13 | 14 | return ( 15 | <> 16 | {!!error &&
{error}
} 17 | 22 | 23 | ); 24 | }; 25 | 26 | export default InputField; 27 | -------------------------------------------------------------------------------- /components/Custom/custombutton.component.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Logo } from "../../types/ui.types"; 3 | 4 | interface CustomButton { 5 | title: string; 6 | className: string; 7 | logo?: Logo; 8 | logoPosition?: string; 9 | isLoading?: boolean; 10 | [otherProps: string]: any; 11 | } 12 | 13 | const CustomButton = ({ 14 | title, 15 | className, 16 | logo, 17 | isLoading, 18 | logoPosition, 19 | promise, 20 | ...otherProps 21 | }: CustomButton): JSX.Element => { 22 | return ( 23 | 40 | ); 41 | }; 42 | 43 | export default CustomButton; 44 | -------------------------------------------------------------------------------- /components/Custom/custominput.component.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Logo } from "../../types/ui.types"; 3 | 4 | interface CustomInput { 5 | className: string; 6 | onSubmit?: (...args: any[]) => void; 7 | logo?: Logo; 8 | [otherProps: string]: any; 9 | } 10 | 11 | const CustomInput = ({ 12 | className, 13 | onSubmit, 14 | logo, 15 | ...otherProps 16 | }: CustomInput): JSX.Element => { 17 | const [isActive, setIsActive] = useState(false); 18 | 19 | //form typed buttons 20 | if (onSubmit) { 21 | return ( 22 |
28 |
{logo}
29 | setIsActive(true)} 31 | onBlur={() => setIsActive(false)} 32 | className="w-full outline-none text-base text-primary px-1 bg-transparent rounded-lg" 33 | {...otherProps} 34 | /> 35 |
36 | ); 37 | } 38 | return ( 39 |
44 |
{logo}
45 | setIsActive(true)} 47 | onBlur={() => setIsActive(false)} 48 | className="w-full outline-none text-base text-primary px-1 bg-transparent rounded-lg" 49 | {...otherProps} 50 | /> 51 |
52 | ); 53 | }; 54 | 55 | export default CustomInput; 56 | -------------------------------------------------------------------------------- /components/Custom/header.component.tsx: -------------------------------------------------------------------------------- 1 | interface Header { 2 | title: string; 3 | } 4 | 5 | const Header = ({ title }: Header): JSX.Element => { 6 | return ( 7 |
8 |
9 | 10 |
11 | logo 17 |
orbits
18 |
19 | | 20 |
21 |
22 | {title} 23 |
24 |
25 |
26 |
27 |
28 | ); 29 | }; 30 | 31 | export default Header; 32 | -------------------------------------------------------------------------------- /components/Custom/searchbar.component.tsx: -------------------------------------------------------------------------------- 1 | import CustomInput from "./custominput.component"; 2 | import { Search } from "react-iconly"; 3 | import { useState } from "react"; 4 | interface SearchBar { 5 | className?: string; 6 | } 7 | 8 | function SearchBar({ className }: SearchBar) { 9 | const [query, setQuery] = useState(""); 10 | 11 | const search = (q: string) => { 12 | q = q.trim(); 13 | if (q && q !== "") { 14 | window.location.href = `/search/${q}`; 15 | } 16 | }; 17 | 18 | return ( 19 | ) => { 25 | setQuery(e.target.value); 26 | }} 27 | onSubmit={(e) => { 28 | e.preventDefault(); 29 | search(query); 30 | }} 31 | logo={ 32 | 33 | } 34 | /> 35 | ); 36 | } 37 | 38 | export default SearchBar; 39 | -------------------------------------------------------------------------------- /components/Custom/tag.component.tsx: -------------------------------------------------------------------------------- 1 | import { tags } from "../../utils/popular_tags"; 2 | 3 | interface Tag { 4 | tag: string; 5 | noColor?: boolean; 6 | } 7 | 8 | const Tag = ({ tag, noColor }: Tag) => { 9 | return ( 10 | 24 | ); 25 | }; 26 | 27 | export default Tag; 28 | -------------------------------------------------------------------------------- /components/Misc/authRequired.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { useRouter } from "next/router"; 3 | import { useAuth } from "../../utils/providers/auth.provider"; 4 | 5 | export enum CheckAuthOn { 6 | render, 7 | click, 8 | } 9 | 10 | interface AuthRequired { 11 | children: React.ReactNode; 12 | checkAuthOn: CheckAuthOn; 13 | } 14 | 15 | const AuthRequired = ({ children, checkAuthOn }: AuthRequired) => { 16 | const router = useRouter(); 17 | const { user, loading } = useAuth(); 18 | const [userExists, setUserExists] = useState(false); 19 | 20 | useEffect(() => { 21 | if (!user && !loading) { 22 | setUserExists(false); 23 | } else { 24 | setUserExists(true); 25 | } 26 | }, [loading]); 27 | 28 | useEffect(() => { 29 | if (checkAuthOn === CheckAuthOn.render) { 30 | if (!userExists) { 31 | redirectToAuth(); 32 | } 33 | } 34 | }, [userExists]); 35 | 36 | const redirectToAuth = () => { 37 | router.push("/auth"); 38 | }; 39 | 40 | if (checkAuthOn === CheckAuthOn.click && !userExists) { 41 | return
redirectToAuth()}>{children}
; 42 | } 43 | 44 | return <>{children}; 45 | }; 46 | 47 | export default AuthRequired; 48 | -------------------------------------------------------------------------------- /components/Misc/branding.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | const Branding = () => { 4 | return ( 5 | <> 6 |
7 |
8 |
9 | 10 |
orbits
11 |
12 |
13 |
14 | 15 | ); 16 | }; 17 | 18 | export default Branding; 19 | -------------------------------------------------------------------------------- /components/Misc/mention.component.tsx: -------------------------------------------------------------------------------- 1 | interface Mention { 2 | username: string; 3 | } 4 | 5 | const Mention = ({ username }: Mention): JSX.Element => { 6 | return ( 7 |
8 | @{username} 9 |
10 | ); 11 | }; 12 | 13 | export default Mention; 14 | -------------------------------------------------------------------------------- /components/Misc/navlink.component.tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from "next/router"; 2 | import Link from "next/link"; 3 | import React, { Children } from "react"; 4 | 5 | interface NavLink { 6 | children: JSX.Element; 7 | activeClassName: string; 8 | href: string; 9 | [props: string]: any; 10 | } 11 | 12 | const NavLink = ({ children, activeClassName, href, ...props }: NavLink) => { 13 | const { asPath } = useRouter(); 14 | const child = Children.only(children); 15 | const childClassName = child.props.className || ""; 16 | 17 | // pages/index.js will be matched via props.href 18 | // pages/about.js will be matched via props.href 19 | // pages/[slug].js will be matched via props.as 20 | const className = 21 | asPath === href || asPath === props.as 22 | ? `${childClassName} ${activeClassName}`.trim() 23 | : childClassName; 24 | 25 | return ( 26 | 27 | {React.cloneElement(child, { 28 | className: className || null, 29 | })} 30 | 31 | ); 32 | }; 33 | 34 | export default NavLink; 35 | -------------------------------------------------------------------------------- /components/Misc/popup.component.tsx: -------------------------------------------------------------------------------- 1 | interface Popup { 2 | className: string; 3 | children: JSX.Element; 4 | isOpen: boolean; 5 | } 6 | 7 | const Popup = ({ className, children, isOpen }: Popup): JSX.Element => { 8 | return ( 9 |
14 | {children} 15 |
16 | ); 17 | }; 18 | 19 | export default Popup; 20 | -------------------------------------------------------------------------------- /components/Misc/profilecrumb.component.tsx: -------------------------------------------------------------------------------- 1 | import Mention from "./mention.component"; 2 | import { User } from "../../types/data.types"; 3 | 4 | const ProfileCrumb = ({ user }: { user: User }): JSX.Element => { 5 | return ( 6 |
7 |
8 | 9 |
10 |
11 |

{user?.name}

12 |
13 | 14 |
15 |
16 |
17 | ); 18 | }; 19 | 20 | export default ProfileCrumb; 21 | -------------------------------------------------------------------------------- /components/Misc/switcher.component.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Logo } from "../../types/ui.types"; 3 | 4 | interface SwitcherItem { 5 | logo?: Logo; 6 | title: string; 7 | } 8 | interface Switcher { 9 | items: SwitcherItem[]; 10 | switcherClassName?: string; 11 | switcherItemClassName?: string; 12 | switcherItemActiveClassName?: string; 13 | } 14 | 15 | const Switcher = ({ 16 | items, 17 | switcherClassName = "", 18 | switcherItemClassName = "", 19 | switcherItemActiveClassName = "", 20 | }: Switcher): JSX.Element => { 21 | const [activeItem, setActiveItem] = useState(0); 22 | 23 | return ( 24 |
27 | {items.map((item, idx) => { 28 | return ( 29 |
setActiveItem(idx)} 34 | > 35 |
36 | {item.logo ?
{item.logo}
: <>} 37 | {item.title} 38 |
39 |
40 | ); 41 | })} 42 |
43 | ); 44 | }; 45 | 46 | export default Switcher; 47 | -------------------------------------------------------------------------------- /components/PageAssets/navbar.component.tsx: -------------------------------------------------------------------------------- 1 | import CustomButton from "../Custom/custombutton.component"; 2 | import { usePopup } from "../../utils/providers/popup.provider"; 3 | import Popup from "../Misc/popup.component"; 4 | import OutsideClickHandler from "react-outside-click-handler"; 5 | import { X, Menu as MenuFeather } from "react-feather"; 6 | import { 7 | ChevronDown, 8 | Notification, 9 | Search, 10 | Logout, 11 | Bookmark, 12 | Discovery, 13 | Edit, 14 | } from "react-iconly"; 15 | 16 | import { useState } from "react"; 17 | import { useAuth } from "../../utils/providers/auth.provider"; 18 | import { useApi } from "../../utils/providers/api.provider"; 19 | import SearchBar from "../Custom/searchbar.component"; 20 | import firebase from "firebase/app"; 21 | import NavLink from "../Misc/navlink.component"; 22 | import ProfileCrumb from "../Misc/profilecrumb.component"; 23 | import { Menu, MenuItem, MenuDivider } from "@szhsin/react-menu"; 24 | import "@szhsin/react-menu/dist/index.css"; 25 | import { User } from "../../types/data.types"; 26 | 27 | interface Navbar { 28 | className?: string; 29 | loginText?: string; 30 | } 31 | 32 | const Navbar = ({ className, loginText }: Navbar) => { 33 | const { setIsOpen } = usePopup(); 34 | const [isSearchBarOpen, setIsSearchBarOpen] = useState(false); 35 | const { user } = useAuth(); 36 | const { getUser } = useApi(); 37 | const res: any = getUser(user?.uid); 38 | 39 | const MobileNavbar = () => ( 40 |
41 | 42 | { 44 | if (isSearchBarOpen) { 45 | setIsSearchBarOpen(false); 46 | } 47 | }} 48 | > 49 |
50 | { 53 | setIsSearchBarOpen(false); 54 | }} 55 | /> 56 | 57 |
58 |
59 |
60 | 72 |
73 |
    74 | {!!user ? ( 75 |
  • 76 |
    77 | 78 |
    79 |
  • 80 | ) : ( 81 | <> 82 | )} 83 | {!!user ? ( 84 |
  • 85 |
    86 | { 88 | setIsSearchBarOpen(true); 89 | }} 90 | className="relative inline text-primary duration-300 cursor-pointer hover:bg-gray-200 p-1 rounded-full" 91 | size="large" 92 | /> 93 |
    94 |
  • 95 | ) : ( 96 | <> 97 | )} 98 |
  • 99 | {!!user ? ( 100 |
    101 | 105 | { 106 | /*user.ping || */ true ? ( 107 | 108 | 109 | 110 | 111 | ) : ( 112 | <> 113 | ) 114 | } 115 |
    116 | ) : ( 117 | { 119 | window.location.href = "/auth"; 120 | }} 121 | title="Login" 122 | className={`rounded-full mb-2 font-semibold text-sm px-5 py-2 login bg-transparent border-2 border-blue-500 lg:mr-3 md:mr-3 ${ 123 | loginText ? loginText : "text-secondary" 124 | } hover:bg-secondary hover:text-white `} 125 | /> 126 | )} 127 |
  • 128 |
  • 129 | {!!user ? ( 130 | 134 | 138 |
139 | } 140 | > 141 | Your profile 142 | Start writing 143 | 144 | { 146 | firebase.auth().signOut(); 147 | window.location.href = "/"; 148 | }} 149 | > 150 | Logout 151 | 152 | 153 | ) : ( 154 | { 156 | setIsOpen(true); 157 | }} 158 | title="Sign up" 159 | className="sign-up mb-2 rounded-full border-2 border-blue-500 hover:border-blue-700 text-sm font-semibold px-5 py-2 lg:inline md:inline hidden bg-secondary text-white hover:text-white hover:bg-blue-700" 160 | /> 161 | )} 162 | 163 | 164 |
165 | 166 | ); 167 | 168 | const StartWriting = () => { 169 | return ( 170 | } 172 | title="Write" 173 | logoPosition="left" 174 | className="bg-secondary py-3.5 w-full text-white font-bold text-sm rounded-full hover:bg-blue-700" 175 | onClick={() => { 176 | if (!user) { 177 | setIsOpen(true); 178 | } 179 | }} 180 | /> 181 | ); 182 | }; 183 | 184 | const DesktopNavbar = () => ( 185 | <> 186 |
187 |
188 |
189 |
190 | 203 |
204 | 205 |
206 |
207 | 211 | 212 |
My Feed
213 |
214 |
215 | 219 | 220 |
Explore
221 |
222 |
223 | 227 | 228 |
Notifications
229 |
230 |
231 | 235 | 236 |
Bookmarks
237 |
238 |
239 | 243 | 244 |
More
245 |
246 |
247 |
248 |
249 |
250 | {user ? ( 251 | <> 252 |
253 |
254 | 50 Orbs 255 |
256 |
257 |
258 | 259 |
260 | 261 | ) : ( 262 | { 266 | window.location.href = "/auth"; 267 | }} 268 | /> 269 | )} 270 |
271 |
272 |
273 |
274 |
275 | 276 | ); 277 | 278 | return ( 279 |
280 |
281 | 282 |
283 |
284 | 285 |
286 |
287 | ); 288 | }; 289 | 290 | export default Navbar; 291 | -------------------------------------------------------------------------------- /components/PageAssets/page404.component.tsx: -------------------------------------------------------------------------------- 1 | interface Page404 { 2 | message: string; 3 | } 4 | 5 | const Page404 = ({ message }: Page404): JSX.Element => { 6 | return ( 7 |
8 |
12 | Oops! 13 |
14 |
{message || ""}
15 |
16 |
17 | ); 18 | }; 19 | 20 | export default Page404; 21 | -------------------------------------------------------------------------------- /components/PageAssets/signin.component.tsx: -------------------------------------------------------------------------------- 1 | import StyledFirebaseAuth from "react-firebaseui/StyledFirebaseAuth"; 2 | import firebase from "firebase/app"; 3 | import "firebase/auth"; 4 | import firebaseClient from "../../utils/firebase"; 5 | import useRouter from "next/router"; 6 | 7 | const SignIn = () => { 8 | const { router } = useRouter; 9 | 10 | const uiConfig: any = { 11 | signInFlow: "redirect", 12 | signInOptions: [ 13 | firebase.auth.GoogleAuthProvider.PROVIDER_ID, 14 | firebase.auth.GithubAuthProvider.PROVIDER_ID, 15 | firebase.auth.TwitterAuthProvider.PROVIDER_ID, 16 | ], 17 | 18 | callbacks: { 19 | signInSuccessWithAuthResult: () => { 20 | router?.push("/"); 21 | return false; 22 | }, 23 | }, 24 | }; 25 | 26 | const FirebaseApp = () => { 27 | return ( 28 | <> 29 | 33 | 34 | ); 35 | }; 36 | 37 | firebaseClient(); 38 | 39 | return ( 40 |
41 |
42 | Login or Create an Account 43 |
44 |
45 | Login to kick start sharing and making new ideas! 46 |
47 | 48 |
49 | By continuing, you are indicating that you accept our
50 | 51 | Terms Of Service 52 | {" "} 53 | and{" "} 54 | 55 | Privacy Policy 56 | 57 |
58 |
59 | ); 60 | }; 61 | 62 | export default SignIn; 63 | -------------------------------------------------------------------------------- /components/Post/postcard.component.tsx: -------------------------------------------------------------------------------- 1 | import { useApi } from "../../utils/providers/api.provider"; 2 | import { useEffect, useState } from "react"; 3 | import { Post } from "../../types/data.types"; 4 | import ProfileCrumb from "../Misc/profilecrumb.component"; 5 | import Tag from "../Custom/tag.component"; 6 | import { Heart2, Chat, Bookmark, Calendar } from "react-iconly"; 7 | import SkeletonPost from "../Skeletons/SkeletonPost"; 8 | 9 | interface PostCard { 10 | post: Post; 11 | } 12 | 13 | const PostCard = ({ post }: PostCard): JSX.Element => { 14 | const { getUser } = useApi(); 15 | const { user, isLoading }: any = getUser(post.author_id); 16 | 17 | const [isBookmarked, setIsBookmarked] = useState(false); 18 | 19 | const EngageSection = () => { 20 | return ( 21 |
22 |
23 |
24 | 25 |
{post?.likes.length}
26 |
27 |
28 | 29 |
{post?.comments.length}
30 |
31 |
32 |
setIsBookmarked(!isBookmarked)} 34 | className={`cursor-pointer ${ 35 | isBookmarked ? "text-secondary" : "text-white-800" 36 | } mt-1`} 37 | > 38 | 39 |
40 |
41 | ); 42 | }; 43 | 44 | if (user && post) { 45 | return ( 46 |
47 |
48 |
49 | 50 |
51 |
52 | 53 |
54 |
{post?.date}
55 |
56 |
57 |
58 | {post?.tags.map((data, idx) => ( 59 | 60 | ))} 61 |
62 |
63 | 64 |
65 |
66 |
67 | {post?.title} 68 |
69 |
70 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do 71 | eiusmod tempor incididunt ut labore et dolore magna aliqua... 72 |
73 |
74 | 75 |
76 |
77 |
78 |
86 |
87 |
88 |
89 | 90 |
91 |
92 | ); 93 | } 94 | return ; 95 | }; 96 | 97 | export default PostCard; 98 | -------------------------------------------------------------------------------- /components/Post/postcrumb.component.tsx: -------------------------------------------------------------------------------- 1 | import Tag from "../Custom/tag.component"; 2 | import { Heart2, Chat } from "react-iconly"; 3 | import Mention from "../Misc/mention.component"; 4 | import { useApi } from "../../utils/providers/api.provider"; 5 | import { Post, User } from "../../types/data.types"; 6 | interface PostCrumb { 7 | post: Post; 8 | } 9 | 10 | const PostCrumb = ({ post }: PostCrumb) => { 11 | const { getUser } = useApi(); 12 | const { user }: any = getUser(post?.author_id); 13 | 14 | return ( 15 |
18 |
19 |
20 | 21 |
22 |
23 | {post?.title} 24 |
25 |
26 |
27 | 28 |
29 |
30 | 31 |
{post?.likes.length}
32 |
33 |
34 |
35 | 36 |
37 |
{post?.comments.length}
38 |
39 |
40 |
41 |
42 | ); 43 | }; 44 | 45 | export default PostCrumb; 46 | -------------------------------------------------------------------------------- /components/SignIn/GoBackContinueButton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const buttonVariants = { 4 | continue: 5 | "px-5 bg-secondary border-secondary border-2 py-3 rounded-xl hover:bg-blue-700 text-white text-sm", 6 | goBack: "duration-300 px-6 py-3 text-secondary hover:text-blue-700", 7 | }; 8 | 9 | const GoBackContinueButton: React.FC< 10 | React.DetailedHTMLProps< 11 | React.ButtonHTMLAttributes, 12 | HTMLButtonElement 13 | > & { 14 | variant: keyof typeof buttonVariants; 15 | } 16 | > = ({ title, disabled, variant, ...props }): JSX.Element => { 17 | return ( 18 | 29 | ); 30 | }; 31 | 32 | export default GoBackContinueButton; 33 | -------------------------------------------------------------------------------- /components/SignIn/SignInStages/stage1.section.tsx: -------------------------------------------------------------------------------- 1 | import { Form, Formik } from "formik"; 2 | import React, { useState } from "react"; 3 | import ProgressBar from "../progressbar.section"; 4 | import { useSignIn } from "../signin.provider"; 5 | import InputField from "../../Custom/InputField"; 6 | import GoBackContinueButton from "../GoBackContinueButton"; 7 | import { StageType } from "../types"; 8 | 9 | interface Data { 10 | username: string; 11 | fullname: string; 12 | dob: string; 13 | } 14 | 15 | const Details = () => { 16 | const { moveToStage } = useSignIn(); 17 | if (!moveToStage) return null; 18 | 19 | const initialValues: Data = { username: "", fullname: "", dob: "" }; 20 | const [data, setData] = useState(initialValues); 21 | 22 | const onSubmit = ( 23 | values: Data, 24 | { setSubmitting }: { setSubmitting: (isSubmitting: boolean) => void } 25 | ) => { 26 | values.fullname.trim(); 27 | values.username.trim(); 28 | 29 | setData(values); 30 | 31 | alert(JSON.stringify(values)); 32 | 33 | setSubmitting(false); 34 | moveToStage(StageType.TOPICS, data); 35 | }; 36 | 37 | return ( 38 |
39 | 40 |
41 |
42 |
Let's set up your Account
43 |
Before getting started with Orbits
44 |
45 | 46 | 50 | {({ isSubmitting }) => ( 51 |
52 |
53 |
User Name
54 | 60 |
61 |
62 |
Full Name
63 | 69 |
70 |
71 |
Date of Birth
72 | 79 |
80 |
81 | 88 |
89 |
90 | )} 91 |
92 |
93 |
94 | ); 95 | }; 96 | 97 | export default Details; 98 | -------------------------------------------------------------------------------- /components/SignIn/SignInStages/stage2.section.tsx: -------------------------------------------------------------------------------- 1 | import ProgressBar from "../progressbar.section"; 2 | import React, { useState } from "react"; 3 | import { useSignIn } from "../signin.provider"; 4 | import GoBackContinueButton from "../GoBackContinueButton"; 5 | import { StageType } from "../types"; 6 | import { TopicsInterested } from "../../../utils/topicsInterested"; 7 | 8 | export const Tag: React.FC<{ 9 | tag: TopicsInterested; 10 | }> = ({ tag }) => { 11 | const [isSelected, setIsSelected] = useState(false); 12 | 13 | const handleClick = () => { 14 | if (isSelected) { 15 | // removeTopic 16 | } else { 17 | // addTopic 18 | } 19 | 20 | setIsSelected(!isSelected); 21 | }; 22 | 23 | return ( 24 |
31 | {"img"} 36 |
{TopicsInterested[tag]}
37 |
38 | ); 39 | }; 40 | 41 | const Topics: React.FC = () => { 42 | const { moveToStage } = useSignIn(); 43 | if (!moveToStage) return null; 44 | 45 | return ( 46 |
47 | 48 |
49 |
Pick your favourite Topics
50 |
51 | Choose at least 3 of them! You can change them later 52 |
53 |
54 |
55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
66 |
67 | moveToStage(StageType.DETAILS, undefined)} 72 | /> 73 | moveToStage(StageType.SUCCESS, [])} 78 | /> 79 |
80 |
81 |
82 | ); 83 | }; 84 | 85 | export default Topics; 86 | -------------------------------------------------------------------------------- /components/SignIn/SignInStages/stage3.section.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ProgressBar from "../progressbar.section"; 3 | import GoBackContinueButton from "../GoBackContinueButton"; 4 | import { useSignIn } from "../signin.provider"; 5 | import { StageType } from "../types"; 6 | 7 | const Success: React.FC = () => { 8 | const { moveToStage } = useSignIn(); 9 | if (!moveToStage) return null; 10 | 11 | return ( 12 |
13 | 14 |
15 |
16 | 17 |
18 |
19 |
20 | You are all good to go! 21 |
22 |
23 | 24 | Welcome to the Orbits Community! We made Orbits for everyone who 25 | love to learn and grow together. Hope you have a great time here! 26 | Also here’s{" "} 27 | 50 Orbs 😉 28 | 29 |
30 |
31 | 32 | With love, 33 |
34 | Team Orbits. 35 |
36 |
37 |
38 | { 40 | moveToStage(StageType.END); 41 | }} 42 | title="Let's Go!" 43 | variant="continue" 44 | /> 45 |
46 |
47 | ); 48 | }; 49 | 50 | export default Success; 51 | -------------------------------------------------------------------------------- /components/SignIn/firebaseSignin.component.tsx: -------------------------------------------------------------------------------- 1 | import StyledFirebaseAuth from "react-firebaseui/StyledFirebaseAuth"; 2 | import firebase from "firebase/app"; 3 | import "firebase/auth"; 4 | import firebaseClient from "../../utils/firebase"; 5 | import useRouter from "next/router"; 6 | 7 | const SignIn = () => { 8 | const { router } = useRouter; 9 | 10 | const uiConfig: any = { 11 | signInFlow: "redirect", 12 | signInOptions: [ 13 | firebase.auth.GoogleAuthProvider.PROVIDER_ID, 14 | firebase.auth.GithubAuthProvider.PROVIDER_ID, 15 | firebase.auth.TwitterAuthProvider.PROVIDER_ID, 16 | ], 17 | 18 | callbacks: { 19 | signInSuccessWithAuthResult: () => { 20 | router?.push("/"); 21 | return false; 22 | }, 23 | }, 24 | }; 25 | 26 | const FirebaseApp = () => { 27 | return ( 28 | <> 29 | 33 | 34 | ); 35 | }; 36 | 37 | firebaseClient(); 38 | 39 | return ( 40 |
41 |
42 | Login or Create an Account 43 |
44 |
45 | Login to kick start sharing and making new ideas! 46 |
47 | 48 |
49 | By continuing, you are indicating that you accept our
50 | 51 | Terms Of Service 52 | {" "} 53 | and{" "} 54 | 55 | Privacy Policy 56 | 57 |
58 |
59 | ); 60 | }; 61 | 62 | export default SignIn; 63 | -------------------------------------------------------------------------------- /components/SignIn/firebaseUI.componet.tsx: -------------------------------------------------------------------------------- 1 | import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth'; 2 | import firebase from 'firebase/app'; 3 | import 'firebase/auth'; 4 | import firebaseClient from '../../utils/firebase'; 5 | import useRouter from 'next/router'; 6 | 7 | const SignIn = () => { 8 | const { router } = useRouter; 9 | 10 | const uiConfig: any = { 11 | signInFlow: 'redirect', 12 | signInOptions: [ 13 | firebase.auth.GoogleAuthProvider.PROVIDER_ID, 14 | firebase.auth.GithubAuthProvider.PROVIDER_ID, 15 | firebase.auth.TwitterAuthProvider.PROVIDER_ID 16 | ], 17 | 18 | callbacks: { 19 | signInSuccessWithAuthResult: () => { 20 | router?.push('/'); 21 | return false; 22 | } 23 | } 24 | }; 25 | 26 | const FirebaseApp = () => { 27 | return ( 28 | <> 29 | 30 | 31 | ); 32 | }; 33 | 34 | firebaseClient(); 35 | 36 | return ( 37 |
38 |
Login or Create an Account
39 |
40 | Login to kick start sharing and making new ideas! 41 |
42 | 43 |
44 | By continuing, you are indicating that you accept our
45 | 46 | Terms Of Service 47 | {' '} 48 | and{' '} 49 | 50 | Privacy Policy 51 | 52 |
53 |
54 | ); 55 | }; 56 | 57 | export default SignIn; 58 | -------------------------------------------------------------------------------- /components/SignIn/progressbar.section.tsx: -------------------------------------------------------------------------------- 1 | import { useSignIn } from "./signin.provider"; 2 | 3 | const ProgressBar = () => { 4 | const { signInStage } = useSignIn(); 5 | 6 | return ( 7 |
8 | {[0, 1, 2].map((idx) => { 9 | return ( 10 |
16 | ); 17 | })} 18 |
19 | ); 20 | }; 21 | 22 | export default ProgressBar; 23 | -------------------------------------------------------------------------------- /components/SignIn/signInCard.component.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface SignInCard { 4 | children: React.ReactChild; 5 | } 6 | 7 | const SignInCard = ({ children }: SignInCard) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | export default SignInCard; 16 | -------------------------------------------------------------------------------- /components/SignIn/signin.component.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Details from "../../components/SignIn/SignInStages/stage1.section"; 3 | import Topics from "../../components/SignIn/SignInStages/stage2.section"; 4 | import Success from "../../components/SignIn/SignInStages/stage3.section"; 5 | import { useSignIn } from "../../components/SignIn/signin.provider"; 6 | import { StageType } from "../../components/SignIn/types"; 7 | 8 | function OnBoarding() { 9 | const { signInStage } = useSignIn(); 10 | 11 | const CurrentStage = () => { 12 | if (signInStage === StageType.DETAILS) return
; 13 | else if (signInStage === StageType.TOPICS) return ; 14 | else return ; 15 | }; 16 | 17 | return ( 18 |
28 | 29 |
30 | ); 31 | } 32 | 33 | export default OnBoarding; 34 | -------------------------------------------------------------------------------- /components/SignIn/signin.provider.tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from "next/router"; 2 | import React, { createContext, useContext, useState } from "react"; 3 | import { TopicsInterested } from "../../utils/topicsInterested"; 4 | import { StageType } from "./types"; 5 | 6 | interface SignInData { 7 | username: string; 8 | fullname: string; 9 | topicsInterested: TopicsInterested; 10 | } 11 | 12 | const SignInContext = createContext<{ 13 | signInStage?: StageType; 14 | moveToStage?: (stage: StageType, values?: any) => void; 15 | data?: SignInData; 16 | }>({}); 17 | 18 | const SignInProvider: React.FC = ({ children }) => { 19 | const [signInStage, setSignInStage] = useState(StageType.DETAILS); 20 | const [data, setData] = useState(); 21 | 22 | const router = useRouter(); 23 | 24 | const moveToStage = (stage: StageType, values?: any) => { 25 | setData({ ...data, ...values }); 26 | 27 | if (stage !== StageType.END) { 28 | setSignInStage(stage); 29 | return; 30 | } 31 | 32 | router.push("/"); 33 | }; 34 | 35 | return ( 36 | 37 | {children} 38 | 39 | ); 40 | }; 41 | 42 | export default SignInProvider; 43 | 44 | export const useSignIn = () => useContext(SignInContext); 45 | -------------------------------------------------------------------------------- /components/SignIn/types.ts: -------------------------------------------------------------------------------- 1 | export enum StageType { 2 | DETAILS, 3 | TOPICS, 4 | SUCCESS, 5 | END, 6 | } 7 | -------------------------------------------------------------------------------- /components/Skeletons/SkeletonPost.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function SkeletonPost(): JSX.Element { 4 | return ( 5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module "react-iconly"; 2 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const withPWA = require("next-pwa"); 2 | 3 | module.exports = withPWA({ 4 | pwa: { 5 | dest: "public", 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "orbits", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "npx prettier --write ." 10 | }, 11 | "lint-staged": { 12 | "./**/*.{js,jsx,css}": [ 13 | "npm run lint" 14 | ] 15 | }, 16 | "husky": { 17 | "hooks": { 18 | "pre-commit": "lint-staged" 19 | } 20 | }, 21 | "dependencies": { 22 | "@badrap/bar-of-progress": "^0.1.2", 23 | "@szhsin/react-menu": "^1.10.0", 24 | "@tailwindcss/typography": "^0.4.0", 25 | "axios": "^0.21.1", 26 | "dotenv": "^8.2.0", 27 | "eslint": "^7.28.0", 28 | "firebase": "^8.5.0", 29 | "firebaseui": "^4.8.0", 30 | "formik": "^2.2.9", 31 | "fs": "0.0.1-security", 32 | "next": "^11.0.0", 33 | "next-pwa": "^5.2.21", 34 | "nookies": "^2.5.2", 35 | "postcss": "^8.2.13", 36 | "postcss-preset-env": "^6.7.0", 37 | "react": "^17.0.2", 38 | "react-dom": "^17.0.2", 39 | "react-feather": "^2.0.9", 40 | "react-firebaseui": "^5.0.2", 41 | "react-hot-toast": "^2.0.0", 42 | "react-iconly": "^2.2.2", 43 | "react-markdown": "^6.0.2", 44 | "react-outside-click-handler": "^1.3.0", 45 | "react-router-dom": "^5.2.0", 46 | "react-toast-notifications": "^2.4.4", 47 | "remark": "^13.0.0", 48 | "remark-gfm": "^1.0.0", 49 | "remark-html": "^13.0.1", 50 | "remark-preset-lint-markdown-style-guide": "^4.0.0", 51 | "swr": "^0.5.6", 52 | "tailwindcss": "^2.1.2" 53 | }, 54 | "devDependencies": { 55 | "@types/react": "^17.0.13", 56 | "@fullhuman/postcss-purgecss": "^4.0.3", 57 | "@types/react-outside-click-handler": "^1.3.0", 58 | "autoprefixer": "^10.2.5", 59 | "husky": "^6.0.0", 60 | "lint-staged": "^11.0.0", 61 | "prettier": "^2.3.1", 62 | "typescript": "^4.3.2" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pages/404.tsx: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import Page404 from "../components/PageAssets/page404.component"; 3 | 4 | export default function NotFound() { 5 | return ( 6 |
7 | 8 | Orbits 9 | 10 | 11 | 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "../styles/globals.css"; 2 | import "tailwindcss/tailwind.css"; 3 | import { PopupProvider } from "../utils/providers/popup.provider"; 4 | import { AuthProvider } from "../utils/providers/auth.provider"; 5 | import { ApiProvider } from "../utils/providers/api.provider"; 6 | import Head from "next/head"; 7 | import ProgressBar from "@badrap/bar-of-progress"; 8 | import Router from "next/router"; 9 | import SignInProvider from "../components/SignIn/signin.provider"; 10 | 11 | const progress = new ProgressBar({ 12 | size: 2, 13 | color: "#347efd", 14 | className: "bar-of-progress", 15 | delay: 100 16 | }); 17 | 18 | Router.events.on("routeChangeStart", progress.start); 19 | Router.events.on("routeChangeComplete", progress.finish); 20 | Router.events.on("routeChangeError", progress.finish); 21 | 22 | function MyApp({ Component, pageProps }: any) { 23 | return ( 24 | 25 | 26 | 27 | 28 | <> 29 | 30 | Orbits 31 | 32 | 33 | 37 | 38 | 39 | 40 | 41 | 45 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | 68 |
69 | 70 |
71 |
72 |
73 |
74 | ); 75 | } 76 | 77 | export default MyApp; 78 | -------------------------------------------------------------------------------- /pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default (req, res) => { 4 | res.status(200).json({ name: "John Doe" }); 5 | }; 6 | -------------------------------------------------------------------------------- /pages/auth.tsx: -------------------------------------------------------------------------------- 1 | import Branding from "../components/Misc/branding"; 2 | import SignIn from "../components/PageAssets/signin.component"; 3 | 4 | const Auth = () => { 5 | return ( 6 |
7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 | ); 15 | }; 16 | 17 | export default Auth; 18 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Dashboard from "../sections/Dashboard/dashboard.section"; 2 | 3 | export default function Home() { 4 | return ( 5 |
6 |
7 | 8 |
9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /pages/onboarding.tsx: -------------------------------------------------------------------------------- 1 | import OnBoarding from "../components/SignIn/signin.component"; 2 | 3 | function Onboarding() { 4 | return ; 5 | } 6 | 7 | export default Onboarding; 8 | -------------------------------------------------------------------------------- /pages/search/[query].tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from "next/router"; 2 | import { useState } from "react"; 3 | import People from "../../sections/SearchResults/people.section"; 4 | import Articles from "../../sections/SearchResults/articles.section"; 5 | import NavBar from "../../components/PageAssets/navbar.component"; 6 | import TrendingPosts from "../../sections/Post/trendingposts.section"; 7 | import Header from "../../components/Custom/header.component"; 8 | import NothingFound from "../../sections/SearchResults/nothingFound.component"; 9 | import { searchUser } from "../../utils/helpers/user/search_user"; 10 | import { searchPost } from "../../utils/helpers/post/search_post"; 11 | import { getUser } from "../../utils/helpers/user/get_user"; 12 | import { Post, User } from "../../types/data.types"; 13 | 14 | export const getServerSideProps = async (ctx: { query: { query: any } }) => { 15 | const query = ctx.query.query; 16 | const people = await searchUser(query); 17 | let articles = await searchPost(query); 18 | 19 | for (let i = 0; i < articles.length; i++) { 20 | const article = articles[i]; 21 | article.author = await getUser(article.author_id); 22 | } 23 | 24 | return { props: { people, articles } }; 25 | }; 26 | 27 | interface Search { 28 | people: User[]; 29 | articles: Post[]; 30 | } 31 | 32 | enum SearchFilter { 33 | TOP, 34 | PEOPLE, 35 | ARTICLES 36 | } 37 | 38 | function Search({ people, articles }: Search) { 39 | const router = useRouter(); 40 | const { query } = router.query; 41 | const [filter, setFilter] = useState(SearchFilter.TOP); 42 | 43 | const SearchResults = () => { 44 | if (filter === SearchFilter.TOP && people && articles) { 45 | return ( 46 | <> 47 | 48 | 49 | 50 | ); 51 | } else if (filter === SearchFilter.PEOPLE && people) { 52 | return ; 53 | } else if (filter === SearchFilter.ARTICLES && articles) { 54 | return ; 55 | } else { 56 | return ; 57 | } 58 | }; 59 | 60 | return ( 61 |
62 |
63 | 64 |
65 |
66 |
67 | Search Results for{" "} 68 | {query} 69 |
70 |
setFilter(SearchFilter.TOP)}> 75 | TOP 76 |
77 |
setFilter(SearchFilter.PEOPLE)}> 82 | PEOPLE 83 |
84 |
setFilter(SearchFilter.ARTICLES)}> 89 | ARTICLES 90 |
91 |
92 |
93 |
94 | 95 |
96 |
97 |
98 |
99 |
100 |
101 | 102 |
103 |
104 |
105 |
106 | ); 107 | } 108 | 109 | export default Search; 110 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/Orb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /public/Orbits-Banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Orbits-Inc/orbits/25f985ed912919c407c24e967740e213244c9c67/public/Orbits-Banner.png -------------------------------------------------------------------------------- /public/Orbits-Banner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /public/background.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /public/default.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Orbits-Inc/orbits/25f985ed912919c407c24e967740e213244c9c67/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Orbits-Inc/orbits/25f985ed912919c407c24e967740e213244c9c67/public/favicon.png -------------------------------------------------------------------------------- /public/glitch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Orbits-Inc/orbits/25f985ed912919c407c24e967740e213244c9c67/public/glitch.gif -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Orbits-Inc/orbits/25f985ed912919c407c24e967740e213244c9c67/public/logo.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Orbits", 3 | "name": "Orbits", 4 | "start_url": ".", 5 | "shortcuts": [ 6 | { 7 | "name": "", 8 | "short_name": "", 9 | "description": "", 10 | "url": "" 11 | } 12 | ], 13 | "icons": [ 14 | { 15 | "src": "favicon.png", 16 | "sizes": "192x192", 17 | "type": "image/png", 18 | "purpose": "any maskable" 19 | }, 20 | { 21 | "src": "favicon.png", 22 | "sizes": "512x512", 23 | "type": "image/png", 24 | "purpose": "any maskable" 25 | } 26 | ], 27 | "display": "standalone", 28 | "theme_color": "#3763ff", 29 | "background_color": "white" 30 | } 31 | -------------------------------------------------------------------------------- /public/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/successfull.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/sw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | // If the loader is already loaded, just stop. 15 | if (!self.define) { 16 | const singleRequire = name => { 17 | if (name !== 'require') { 18 | name = name + '.js'; 19 | } 20 | let promise = Promise.resolve(); 21 | if (!registry[name]) { 22 | 23 | promise = new Promise(async resolve => { 24 | if ("document" in self) { 25 | const script = document.createElement("script"); 26 | script.src = name; 27 | document.head.appendChild(script); 28 | script.onload = resolve; 29 | } else { 30 | importScripts(name); 31 | resolve(); 32 | } 33 | }); 34 | 35 | } 36 | return promise.then(() => { 37 | if (!registry[name]) { 38 | throw new Error(`Module ${name} didn’t register its module`); 39 | } 40 | return registry[name]; 41 | }); 42 | }; 43 | 44 | const require = (names, resolve) => { 45 | Promise.all(names.map(singleRequire)) 46 | .then(modules => resolve(modules.length === 1 ? modules[0] : modules)); 47 | }; 48 | 49 | const registry = { 50 | require: Promise.resolve(require) 51 | }; 52 | 53 | self.define = (moduleName, depsNames, factory) => { 54 | if (registry[moduleName]) { 55 | // Module is already loading or loaded. 56 | return; 57 | } 58 | registry[moduleName] = Promise.resolve().then(() => { 59 | let exports = {}; 60 | const module = { 61 | uri: location.origin + moduleName.slice(1) 62 | }; 63 | return Promise.all( 64 | depsNames.map(depName => { 65 | switch(depName) { 66 | case "exports": 67 | return exports; 68 | case "module": 69 | return module; 70 | default: 71 | return singleRequire(depName); 72 | } 73 | }) 74 | ).then(deps => { 75 | const facValue = factory(...deps); 76 | if(!exports.default) { 77 | exports.default = facValue; 78 | } 79 | return exports; 80 | }); 81 | }); 82 | }; 83 | } 84 | define("./sw.js",['./workbox-6b19f60b'], function (workbox) { 'use strict'; 85 | 86 | /** 87 | * Welcome to your Workbox-powered service worker! 88 | * 89 | * You'll need to register this file in your web app. 90 | * See https://goo.gl/nhQhGp 91 | * 92 | * The rest of the code is auto-generated. Please don't update this file 93 | * directly; instead, make changes to your Workbox build configuration 94 | * and re-run your build process. 95 | * See https://goo.gl/2aRDsh 96 | */ 97 | 98 | importScripts(); 99 | self.skipWaiting(); 100 | workbox.clientsClaim(); 101 | workbox.registerRoute("/", new workbox.NetworkFirst({ 102 | "cacheName": "start-url", 103 | plugins: [{ 104 | cacheWillUpdate: async ({ 105 | request, 106 | response, 107 | event, 108 | state 109 | }) => { 110 | if (response && response.type === 'opaqueredirect') { 111 | return new Response(response.body, { 112 | status: 200, 113 | statusText: 'OK', 114 | headers: response.headers 115 | }); 116 | } 117 | 118 | return response; 119 | } 120 | }] 121 | }), 'GET'); 122 | workbox.registerRoute(/.*/i, new workbox.NetworkOnly({ 123 | "cacheName": "dev", 124 | plugins: [] 125 | }), 'GET'); 126 | 127 | }); 128 | //# sourceMappingURL=sw.js.map 129 | -------------------------------------------------------------------------------- /public/sw.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sw.js","sources":["../../../../../../private/var/folders/_3/yz8qxx4j0sndy4rwrb7z3zf00000gn/T/a03bc22b93b94ecc29593ee140e47d2e/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from '/Users/mukeshmunjal/Desktop/Monkies/Code/orbits/node_modules/workbox-routing/registerRoute.mjs';\nimport {NetworkFirst as workbox_strategies_NetworkFirst} from '/Users/mukeshmunjal/Desktop/Monkies/Code/orbits/node_modules/workbox-strategies/NetworkFirst.mjs';\nimport {NetworkOnly as workbox_strategies_NetworkOnly} from '/Users/mukeshmunjal/Desktop/Monkies/Code/orbits/node_modules/workbox-strategies/NetworkOnly.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from '/Users/mukeshmunjal/Desktop/Monkies/Code/orbits/node_modules/workbox-core/clientsClaim.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\nimportScripts(\n \n);\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n\nworkbox_routing_registerRoute(\"/\", new workbox_strategies_NetworkFirst({ \"cacheName\":\"start-url\", plugins: [{ cacheWillUpdate: async ({request, response, event, state}) => { if (response && response.type === 'opaqueredirect') { return new Response(response.body, {status: 200, statusText: 'OK', headers: response.headers}); } return response; } }] }), 'GET');\nworkbox_routing_registerRoute(/.*/i, new workbox_strategies_NetworkOnly({ \"cacheName\":\"dev\", plugins: [] }), 'GET');\n\n\n\n\n"],"names":["importScripts","self","skipWaiting","workbox_core_clientsClaim","workbox_routing_registerRoute","workbox_strategies_NetworkFirst","plugins","cacheWillUpdate","request","response","event","state","type","Response","body","status","statusText","headers","workbox_strategies_NetworkOnly"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGqJ;EACrJ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAGAA,aAAa;EAUbC,IAAI,CAACC,WAAL;AAEAC,sBAAyB;AAIzBC,uBAA6B,CAAC,GAAD,EAAM,IAAIC,oBAAJ,CAAoC;EAAE,eAAY,WAAd;EAA2BC,EAAAA,OAAO,EAAE,CAAC;EAAEC,IAAAA,eAAe,EAAE,OAAO;EAACC,MAAAA,OAAD;EAAUC,MAAAA,QAAV;EAAoBC,MAAAA,KAApB;EAA2BC,MAAAA;EAA3B,KAAP,KAA6C;EAAE,UAAIF,QAAQ,IAAIA,QAAQ,CAACG,IAAT,KAAkB,gBAAlC,EAAoD;EAAE,eAAO,IAAIC,QAAJ,CAAaJ,QAAQ,CAACK,IAAtB,EAA4B;EAACC,UAAAA,MAAM,EAAE,GAAT;EAAcC,UAAAA,UAAU,EAAE,IAA1B;EAAgCC,UAAAA,OAAO,EAAER,QAAQ,CAACQ;EAAlD,SAA5B,CAAP;EAAiG;;EAAC,aAAOR,QAAP;EAAkB;EAA5O,GAAD;EAApC,CAApC,CAAN,EAAmU,KAAnU,CAA7B;AACAL,uBAA6B,CAAC,KAAD,EAAQ,IAAIc,mBAAJ,CAAmC;EAAE,eAAY,KAAd;EAAqBZ,EAAAA,OAAO,EAAE;EAA9B,CAAnC,CAAR,EAAgF,KAAhF,CAA7B;;"} -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /sections/Dashboard/SideBar/loginprompt.sidebar.tsx: -------------------------------------------------------------------------------- 1 | import CustomButton from "../../../components/Custom/custombutton.component"; 2 | import { ChevronRight } from "react-iconly"; 3 | import { X } from "react-feather"; 4 | function LoginPrompt() { 5 | return ( 6 |
16 |
17 | 18 |
19 |
20 | } 22 | logoPosition="right" 23 | title="Get Started" 24 | className="font-semibold w-full bg-secondary border-secondary border-2 py-3 rounded-xl hover:bg-blue-700 text-white " 25 | /> 26 |
27 |
28 | ); 29 | } 30 | 31 | export default LoginPrompt; 32 | -------------------------------------------------------------------------------- /sections/Dashboard/SideBar/trendingposts.sidebar.tsx: -------------------------------------------------------------------------------- 1 | import TrendingPosts from "../../Post/trendingposts.section"; 2 | import Switcher from "../../../components/Misc/switcher.component"; 3 | import CustomButton from "../../../components/Custom/custombutton.component"; 4 | import { ChevronRight } from "react-iconly"; 5 | 6 | function TrendingSection() { 7 | return ( 8 | 40 | ); 41 | } 42 | 43 | export default TrendingSection; 44 | -------------------------------------------------------------------------------- /sections/Dashboard/SideBar/trendingtags.sidebar.tsx: -------------------------------------------------------------------------------- 1 | import Switcher from "../../../components/Misc/switcher.component"; 2 | import CustomButton from "../../../components/Custom/custombutton.component"; 3 | import { ChevronRight } from "react-iconly"; 4 | 5 | function TrendingTags() { 6 | const tags = [ 7 | { 8 | name: "SpaceExploration", 9 | posts: 110, 10 | }, 11 | { 12 | name: "Astronomy", 13 | posts: 90, 14 | }, 15 | { 16 | name: "EducationSystem", 17 | posts: 50, 18 | }, 19 | ]; 20 | 21 | return ( 22 | 68 | ); 69 | } 70 | 71 | export default TrendingTags; 72 | -------------------------------------------------------------------------------- /sections/Dashboard/dashboard.section.tsx: -------------------------------------------------------------------------------- 1 | import Navbar from "../../components/PageAssets/navbar.component"; 2 | import TrendingSection from "./SideBar/trendingposts.sidebar"; 3 | import TrendingTags from "./SideBar/trendingtags.sidebar"; 4 | import LoginPrompt from "./SideBar/loginprompt.sidebar"; 5 | import Modal from "./modal.section"; 6 | import SearchBar from "../../components/Custom/searchbar.component"; 7 | import WritePost from "./writepost.section"; 8 | import Posts from "../Post/posts.section"; 9 | import TabChanger from "./tabchanger.section"; 10 | import { useAuth } from "../../utils/providers/auth.provider"; 11 | 12 | function Dashboard() { 13 | const { user } = useAuth(); 14 | 15 | return ( 16 |
17 |
18 | 19 |
20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | 29 | 30 | {!user && } 31 |
32 |
33 |
34 | ); 35 | } 36 | 37 | export default Dashboard; 38 | -------------------------------------------------------------------------------- /sections/Dashboard/modal.section.tsx: -------------------------------------------------------------------------------- 1 | import Navbar from "../../components/PageAssets/navbar.component"; 2 | import CustomButton from "../../components/Custom/custombutton.component"; 3 | import { X, ChevronDown, ChevronRight } from "react-feather"; 4 | import { usePopup } from "../../utils/providers/popup.provider"; 5 | import { useAuth } from "../../utils/providers/auth.provider"; 6 | import { useState } from "react"; 7 | 8 | function Modal() { 9 | const StartWriting = () => { 10 | return ( 11 | } 14 | logoPosition="right" 15 | className="mx-auto lg:mx-none md:lg-none glow bg-secondary text-white font-bold text-sm rounded-full py-4 px-12 hover:bg-blue-700" 16 | onClick={() => { 17 | if (!user) { 18 | setIsOpen(true); 19 | } 20 | }} 21 | /> 22 | ); 23 | }; 24 | const { user } = useAuth(); 25 | const [isOpen, setIsOpen] = useState(true); 26 | 27 | return ( 28 |
40 | { 43 | setIsOpen(false); 44 | }} 45 | /> 46 |
47 |
48 |
Research.
Write. 49 |
50 | Spread. 51 |
52 |
53 | Science, space & tech is what all we talk and write about! 54 |
55 |
56 |
57 | 58 |
59 |
{ 61 | window.location.href = "#write-post"; 62 | }} 63 | className="flex justify-center lg:block md:block duration-300 cursor-pointer hover:text-secondary text-xs ml-2 text-blue-700 py-5" 64 | > 65 | I'll just look around for now 66 | 67 |
68 |
69 |
70 | 75 |
76 |
77 | ); 78 | } 79 | 80 | export default Modal; 81 | -------------------------------------------------------------------------------- /sections/Dashboard/tabchanger.section.tsx: -------------------------------------------------------------------------------- 1 | import { TimeCircle, Activity, Filter } from "react-iconly"; 2 | import NavLink from "../../components/Misc/navlink.component"; 3 | import Switcher from "../../components/Misc/switcher.component"; 4 | 5 | function TabChanger() { 6 | const tabs = [ 7 | { 8 | title: "All", 9 | href: "/", 10 | }, 11 | { 12 | title: "Questions", 13 | href: "/questions", 14 | }, 15 | { title: "Exploration", href: "/space-exploration" }, 16 | { title: "Coding", href: "/coding" }, 17 | ]; 18 | return ( 19 |
23 |
24 |
My Feed
25 |
26 | , 30 | title: "Relevant", 31 | }, 32 | { 33 | logo: , 34 | title: "Recent", 35 | }, 36 | ]} 37 | switcherItemClassName="px-5 hover:bg-white-200 text-gray-500 font-medium rounded-xl py-2 border-white-400 border text-sm" 38 | switcherItemActiveClassName="hover:bg-blue-200 border-blue-200 bg-blue-100 text-blue-600" 39 | /> 40 | 41 |
42 |
43 |
44 | {tabs.map((data, idx) => ( 45 | 51 | {data.title} 52 | 53 | ))} 54 |
55 |
56 | ); 57 | } 58 | 59 | export default TabChanger; 60 | -------------------------------------------------------------------------------- /sections/Dashboard/writepost.section.tsx: -------------------------------------------------------------------------------- 1 | import { useAuth } from "../../utils/providers/auth.provider"; 2 | import CustomButton from "../../components/Custom/custombutton.component"; 3 | import { Edit, Image2, Video, Document } from "react-iconly"; 4 | import AuthRequired, { CheckAuthOn } from "../../components/Misc/authRequired"; 5 | 6 | function WritePost() { 7 | const { user } = useAuth(); 8 | 9 | return ( 10 |
14 |
15 |
16 | 20 |
21 |
22 |
23 | 24 | } 28 | logoPosition="left" 29 | /> 30 | 31 |
32 |
33 |
34 |
Image
35 |
36 |
37 |
39 |
40 |
Article
41 |
42 |
43 |
44 |
45 |
46 |
47 |
Image
48 |
49 |
50 |
52 |
53 |
Article
54 |
55 |
56 |
57 |
58 |
59 | ); 60 | } 61 | 62 | export default WritePost; 63 | -------------------------------------------------------------------------------- /sections/Post/posts.section.tsx: -------------------------------------------------------------------------------- 1 | import { useApi } from "../../utils/providers/api.provider"; 2 | import PostCard from "../../components/Post/postcard.component"; 3 | 4 | function Posts() { 5 | const { getAllPosts } = useApi(); 6 | const { posts }: any = getAllPosts(); 7 | 8 | return ( 9 |
10 | {posts?.map((data: any, idx: any) => ( 11 |
15 | 16 |
17 | ))} 18 |
19 | ); 20 | } 21 | 22 | export default Posts; 23 | -------------------------------------------------------------------------------- /sections/Post/trendingposts.section.tsx: -------------------------------------------------------------------------------- 1 | import { useApi } from "../../utils/providers/api.provider"; 2 | import PostCrumb from "../../components/Post/postcrumb.component"; 3 | 4 | function TrendingPosts() { 5 | const { getTopPosts } = useApi(); 6 | const { trendingPosts, isError }: any = getTopPosts(); 7 | 8 | return ( 9 |
10 | {trendingPosts?.map((data: any, key: any) => ( 11 | <> 12 | 13 | {key !== trendingPosts?.length - 1 ?
: <>} 14 | 15 | ))} 16 |
17 | ); 18 | } 19 | 20 | export default TrendingPosts; 21 | -------------------------------------------------------------------------------- /sections/SearchResults/articles.section.tsx: -------------------------------------------------------------------------------- 1 | import PostCard from "../../components/Post/postcard.component"; 2 | import NothingFound from "./nothingFound.component"; 3 | import Searching from "./searching.component"; 4 | 5 | function Articles({ articles }: any) { 6 | if (articles?.length > 0) { 7 | return ( 8 |
9 |
Articles
10 | {articles?.map((data: any, key: any) => ( 11 |
19 | 20 |
21 | ))} 22 |
23 | ); 24 | } else if (articles?.length === 0) { 25 | return ; 26 | } else { 27 | return ; 28 | } 29 | } 30 | 31 | export default Articles; 32 | -------------------------------------------------------------------------------- /sections/SearchResults/nothingFound.component.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function NothingFound() { 4 | return ( 5 |
6 |
People
7 |
8 | 🐝 Nothing found... 9 |
10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /sections/SearchResults/people.section.tsx: -------------------------------------------------------------------------------- 1 | import ProfileCrumb from "../../components/Misc/profilecrumb.component"; 2 | import NothingFound from "./nothingFound.component"; 3 | 4 | function People({ people }: any): JSX.Element { 5 | if (people?.length > 0) { 6 | return ( 7 |
8 |
People
9 | {people?.map((data: any, key: any) => ( 10 |
16 | 17 |
18 | ))} 19 |
20 | ); 21 | } 22 | return ; 23 | } 24 | 25 | export default People; 26 | -------------------------------------------------------------------------------- /sections/SearchResults/results.component.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Orbits-Inc/orbits/25f985ed912919c407c24e967740e213244c9c67/sections/SearchResults/results.component.jsx -------------------------------------------------------------------------------- /sections/SearchResults/searching.component.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function searching() { 4 | return ( 5 |
6 |
People
7 |
8 | Searching... 9 |
10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | min-height: 100vh; 3 | padding: 0 0.5rem; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .main { 11 | padding: 5rem 0; 12 | flex: 1; 13 | display: flex; 14 | flex-direction: column; 15 | justify-content: center; 16 | align-items: center; 17 | } 18 | 19 | .footer { 20 | width: 100%; 21 | height: 100px; 22 | border-top: 1px solid #eaeaea; 23 | display: flex; 24 | justify-content: center; 25 | align-items: center; 26 | } 27 | 28 | .footer img { 29 | margin-left: 0.5rem; 30 | } 31 | 32 | .footer a { 33 | display: flex; 34 | justify-content: center; 35 | align-items: center; 36 | } 37 | 38 | .title a { 39 | color: #0070f3; 40 | text-decoration: none; 41 | } 42 | 43 | .title a:hover, 44 | .title a:focus, 45 | .title a:active { 46 | text-decoration: underline; 47 | } 48 | 49 | .title { 50 | margin: 0; 51 | line-height: 1.15; 52 | font-size: 4rem; 53 | } 54 | 55 | .title, 56 | .description { 57 | text-align: center; 58 | } 59 | 60 | .description { 61 | line-height: 1.5; 62 | font-size: 1.5rem; 63 | } 64 | 65 | .code { 66 | background: #fafafa; 67 | border-radius: 5px; 68 | padding: 0.75rem; 69 | font-size: 1.1rem; 70 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 71 | Bitstream Vera Sans Mono, Courier New, monospace; 72 | } 73 | 74 | .grid { 75 | display: flex; 76 | align-items: center; 77 | justify-content: center; 78 | flex-wrap: wrap; 79 | max-width: 800px; 80 | margin-top: 3rem; 81 | } 82 | 83 | .card { 84 | margin: 1rem; 85 | flex-basis: 45%; 86 | padding: 1.5rem; 87 | text-align: left; 88 | color: inherit; 89 | text-decoration: none; 90 | border: 1px solid #eaeaea; 91 | border-radius: 10px; 92 | transition: color 0.15s ease, border-color 0.15s ease; 93 | } 94 | 95 | .card:hover, 96 | .card:focus, 97 | .card:active { 98 | color: #0070f3; 99 | border-color: #0070f3; 100 | } 101 | 102 | .card h3 { 103 | margin: 0 0 1rem 0; 104 | font-size: 1.5rem; 105 | } 106 | 107 | .card p { 108 | margin: 0; 109 | font-size: 1.25rem; 110 | line-height: 1.5; 111 | } 112 | 113 | .logo { 114 | height: 1em; 115 | } 116 | 117 | @media (max-width: 600px) { 118 | .grid { 119 | width: 100%; 120 | flex-direction: column; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss/base"; 2 | @import "tailwindcss/components"; 3 | @import "tailwindcss/utilities"; 4 | @import url("https://fonts.googleapis.com/css2?family=Spartan:wght@400;500;600;700;800&display=swap"); 5 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap"); 6 | 7 | html, 8 | body { 9 | scroll-behavior: smooth; 10 | padding: 0; 11 | margin: 0; 12 | } 13 | 14 | a { 15 | color: inherit; 16 | text-decoration: none; 17 | } 18 | 19 | * { 20 | font-family: "Spartan", sans-serif; 21 | } 22 | 23 | button { 24 | transition: background-color 0.3s ease-in-out, border 0.3s ease-in-out; 25 | } 26 | 27 | .oops { 28 | background: url("/glitch.gif"); 29 | background-size: cover; 30 | background-position: center; 31 | } 32 | 33 | #parent { 34 | position: relative; 35 | } 36 | #child { 37 | position: absolute; 38 | top: 50%; 39 | transform: translateY(-50%); 40 | } 41 | 42 | .tagline { 43 | background: linear-gradient( 44 | 90.23deg, 45 | #1a6efc 13.13%, 46 | rgba(26, 110, 252, 0.388062) 133.92%, 47 | rgba(26, 110, 252, 0.29) 153.28% 48 | ); 49 | background-size: 100%; 50 | background-repeat: repeat; 51 | 52 | -webkit-background-clip: text; 53 | -webkit-text-fill-color: transparent; 54 | -moz-background-clip: text; 55 | -moz-text-fill-color: transparent; 56 | } 57 | 58 | .glow { 59 | box-shadow: 4px 4px 37px rgba(26, 110, 252, 0.25); 60 | } 61 | 62 | ::-webkit-scrollbar { 63 | width: 5px; 64 | } 65 | ::-webkit-scrollbar-track { 66 | background: #fff; 67 | } 68 | ::-webkit-scrollbar-thumb { 69 | background: rgba(148, 148, 148, 0.44); 70 | } 71 | 72 | .article link { 73 | font-weight: bold; 74 | color: #347efd; 75 | } 76 | 77 | .bar-of-progress { 78 | z-index: 50; 79 | } 80 | 81 | #login-prompt { 82 | height: 39rem; 83 | } 84 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: [ 3 | "./pages/**/*.{js,ts,jsx,tsx}", 4 | "./components/**/*.{js,ts,jsx,tsx}", 5 | "./sections/**/*.{js,jsx,ts,tsx}", 6 | ], 7 | darkMode: false, 8 | theme: { 9 | extend: { 10 | colors: { 11 | primary: "#3A3A3A", 12 | secondary: "#1A6EFC", 13 | accent: "#9A9A9A", 14 | white: { 15 | DEFAULT: "#FFF", 16 | 200: "#F4F4F4", 17 | 300: "#EAEAEA", 18 | 400: "#D3D3D3", 19 | 500: "#CBCBCB", 20 | 800: "#9A9A9A", 21 | }, 22 | black: { 23 | 100: "#7C8288", 24 | 200: "#76787A", 25 | 300: "#616161", 26 | 400: "#2F3133", 27 | 700: "#313131", 28 | }, 29 | blue: { 30 | 200: "#CDF0FF", 31 | 600: "#2C5DBD", 32 | }, 33 | orange: { 34 | 200: "#FFF7AD", 35 | 600: "#DE8F31", 36 | }, 37 | }, 38 | fontFamily: { 39 | display: ["Spartan", "sans-serif"], 40 | body: ["Inter", "sans-serif"], 41 | }, 42 | animation: { 43 | "spin-slow": "spin 3s linear infinite", 44 | }, 45 | }, 46 | }, 47 | variants: { 48 | extend: {}, 49 | }, 50 | plugins: [require("@tailwindcss/typography")], 51 | }; 52 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "target": "es6", 5 | "jsx": "preserve", 6 | "moduleResolution": "node", 7 | "experimentalDecorators": true, 8 | "noEmitOnError": false, 9 | "resolveJsonModule": true, 10 | "importHelpers": true, 11 | "strict": true, 12 | "lib": ["ESNext", "DOM"], 13 | "module": "commonjs", 14 | "esModuleInterop": true, 15 | "preserveSymlinks": true, 16 | "typeRoots": ["./node_modules/@types"], 17 | "downlevelIteration": true, 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "forceConsistentCasingInFileNames": true, 21 | "noEmit": true, 22 | "outDir": "dist", 23 | "isolatedModules": true 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx" ], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /types/data.types.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | userId: string; 3 | username: string; 4 | name: string; 5 | display_picture: string; 6 | bio: string; 7 | email: string; 8 | posts: string[]; 9 | followers: string[]; 10 | following: string[]; 11 | createdAt: Date; 12 | updatedAt: Date; 13 | notifications: Notification[]; 14 | role: Roles; 15 | } 16 | 17 | enum Roles { 18 | Admin, 19 | Modertor, 20 | User, 21 | } 22 | 23 | interface Notification {} 24 | 25 | export interface Post { 26 | ref_id: string; 27 | title: string; 28 | description?: string; 29 | image?: string; 30 | content: string; 31 | read_time: number; 32 | date: Date; 33 | tags: string[]; 34 | likes: string[]; 35 | comments: CommentType[]; 36 | author_id: string; 37 | createdAt: Date; 38 | updatedAt: Date; 39 | } 40 | 41 | // "Comment" is a default typescript type 42 | export interface CommentType { 43 | user_id: string; 44 | comment: string; 45 | } 46 | -------------------------------------------------------------------------------- /types/ui.types.ts: -------------------------------------------------------------------------------- 1 | import type { Icon } from "react-feather"; 2 | 3 | export type Logo = Icon | JSX.Element; 4 | -------------------------------------------------------------------------------- /utils/firebase.ts: -------------------------------------------------------------------------------- 1 | import firebase from "firebase/app"; 2 | import "firebase/auth"; 3 | 4 | export const firebaseConfig = { 5 | apiKey: process.env.NEXT_PUBLIC_API_KEY, 6 | authDomain: process.env.NEXT_PUBLIC_AUTH_DOMAIN, 7 | }; 8 | 9 | export default function firebaseClient() { 10 | if (!firebase.apps.length) { 11 | firebase.initializeApp(firebaseConfig); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /utils/helpers/post/add_new_post.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default async function addNewUser(post: any) { 4 | const res = await axios.post(`${process.env.NEXT_PUBLIC_API_ENDPOINT}/post`, { 5 | title: post.title, 6 | description: post.description, 7 | tags: post.tags, 8 | content: post.content, 9 | image: post.image, 10 | author_id: post.author_id, 11 | likes: [], 12 | comments: [], 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /utils/helpers/post/get_post.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import useSWR from "swr"; 3 | 4 | export async function getPostById(uid: String) { 5 | const res = await axios.get( 6 | `${process.env.NEXT_PUBLIC_API_ENDPOINT}/post/byId/${uid}` 7 | ); 8 | 9 | return res.data; 10 | } 11 | 12 | export function getAllPosts() { 13 | const { data, error } = useSWR( 14 | `${process.env.NEXT_PUBLIC_API_ENDPOINT}/post` 15 | ); 16 | 17 | return { 18 | posts: data, 19 | isError: error, 20 | }; 21 | } 22 | 23 | export function getTopPosts() { 24 | const { data, error } = useSWR( 25 | `${process.env.NEXT_PUBLIC_API_ENDPOINT}/post/topPosts` 26 | ); 27 | 28 | return { 29 | trendingPosts: data, 30 | isError: error, 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /utils/helpers/post/search_post.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export async function searchPost(query: String) { 4 | const res = await axios.get( 5 | `${process.env.NEXT_PUBLIC_API_ENDPOINT}/post/search/${query}` 6 | ); 7 | return res.data; 8 | } 9 | 10 | export async function searchPostByTag(query: String) { 11 | const res = await axios.get( 12 | `${process.env.NEXT_PUBLIC_API_ENDPOINT}/post/searchByTag/${query}` 13 | ); 14 | return res.data; 15 | } 16 | -------------------------------------------------------------------------------- /utils/helpers/user/add_new_user.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import usernameSuggestion from "./username_suggestion"; 3 | 4 | export default async function addNewUser(user: any) { 5 | const res = await axios.post(`${process.env.NEXT_PUBLIC_API_ENDPOINT}/user`, { 6 | user_id: user.uid, 7 | username: usernameSuggestion(user), 8 | name: user.displayName, 9 | email: user.email, 10 | display_picture: user.photoURL, 11 | bio: "", 12 | role: "user", 13 | followers: [], 14 | following: [], 15 | notifications: [], 16 | posts: [], 17 | }); 18 | console.log(res); 19 | } 20 | -------------------------------------------------------------------------------- /utils/helpers/user/get_user.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import useSWR from "swr"; 3 | 4 | export async function getUser(uid: any) { 5 | const { data, error } = useSWR(`${process.env.NEXT_PUBLIC_API_ENDPOINT}/user/byId/${uid}`); 6 | return { 7 | user: data, 8 | isLoading: !error && !data, 9 | isError: error 10 | }; 11 | } 12 | 13 | export async function getUserByUsername(username: any) { 14 | const res = await axios.get( 15 | `${process.env.NEXT_PUBLIC_API_ENDPOINT}/user/byUsername/${username}` 16 | ); 17 | return res.data; 18 | } 19 | -------------------------------------------------------------------------------- /utils/helpers/user/search_user.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export async function searchUser(query: String) { 4 | const res = await axios.get( 5 | `${process.env.NEXT_PUBLIC_API_ENDPOINT}/user/search/${query}` 6 | ); 7 | return res.data; 8 | } 9 | -------------------------------------------------------------------------------- /utils/helpers/user/username_suggestion.ts: -------------------------------------------------------------------------------- 1 | export default function usernameSuggestion(user: { 2 | displayName: string; 3 | uid: string; 4 | }) { 5 | var username = user.displayName.split(/\s+/).join(""); 6 | username += user.uid.substring( 7 | Math.round(user.uid.length / 2) - 2, 8 | Math.round(user.uid.length / 2) + 1 9 | ); 10 | return username; 11 | } 12 | -------------------------------------------------------------------------------- /utils/popular_tags.ts: -------------------------------------------------------------------------------- 1 | interface Tag { 2 | [x: string]: string; 3 | } 4 | export const tags: Tag = { 5 | science: "#E3AD27", 6 | space: "#E32781", 7 | technology: "#0B60FF", 8 | tech: "#0B60FF", 9 | design: "#300056", 10 | }; 11 | -------------------------------------------------------------------------------- /utils/providers/api.provider.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from "react"; 2 | import { searchUser } from "../helpers/user/search_user"; 3 | import { getUser } from "../helpers/user/get_user"; 4 | import { getTopPosts, getAllPosts } from "../helpers/post/get_post"; 5 | import { searchPost } from "../helpers/post/search_post"; 6 | 7 | type anyArgs = [args: string]; 8 | interface ApiContext { 9 | [functions: string]: (anyArgs?: any) => {}; 10 | } 11 | 12 | const ApiContext = createContext({}); 13 | 14 | export const ApiProvider = ({ children }: any) => { 15 | return ( 16 | 19 | {children} 20 | 21 | ); 22 | }; 23 | 24 | export const useApi = () => useContext(ApiContext); 25 | -------------------------------------------------------------------------------- /utils/providers/auth.provider.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useEffect, useState } from 'react'; 2 | import addNewUser from '../helpers/user/add_new_user'; 3 | import firebaseClient from '../firebase'; 4 | import firebase from 'firebase/app'; 5 | import 'firebase/auth'; 6 | import { User } from '../../types/data.types'; 7 | 8 | // TODO: change User type to FirebaseUserData 9 | 10 | interface UserContext { 11 | user: User | null; 12 | } 13 | 14 | const AuthContext = createContext({ user: null, loading: true }); 15 | 16 | export const AuthProvider = ({ children }: any) => { 17 | firebaseClient(); 18 | const [user, setUser] = useState(undefined); 19 | const [loading, setLoading] = useState(true); 20 | 21 | useEffect(() => { 22 | return firebase.auth().onIdTokenChanged(async (_user) => { 23 | if (!_user) { 24 | setLoading(true); 25 | setUser(undefined); 26 | setLoading(false); 27 | return; 28 | } 29 | setLoading(true); 30 | setUser(_user); 31 | setLoading(false); 32 | try { 33 | // This request always gives an error 34 | await addNewUser(_user); 35 | } catch (err) { 36 | console.log(err); 37 | } 38 | //const __user = await getUser(_user.uid); 39 | }); 40 | }, []); 41 | return {children}; 42 | }; 43 | export const useAuth = () => useContext(AuthContext); 44 | -------------------------------------------------------------------------------- /utils/providers/popup.provider.tsx: -------------------------------------------------------------------------------- 1 | import { useState, createContext, useContext } from "react"; 2 | 3 | const PopupContext = createContext({}); 4 | 5 | interface PopupProvider { 6 | children: JSX.Element; 7 | } 8 | 9 | export const PopupProvider = ({ children }: PopupProvider) => { 10 | const [isOpen, setIsOpen] = useState(false); 11 | 12 | return ( 13 | 14 | {children} 15 | 16 | ); 17 | }; 18 | 19 | export const usePopup = () => useContext(PopupContext); 20 | -------------------------------------------------------------------------------- /utils/topicsInterested.ts: -------------------------------------------------------------------------------- 1 | export enum TopicsInterested { 2 | ASTRONOMY, 3 | SPACE, 4 | PHYSICS, 5 | } 6 | --------------------------------------------------------------------------------