├── .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 |
5 |
6 |
7 |
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 |
28 | {isLoading ? (
29 | <>
30 |
31 | >
32 | ) : (
33 | <>
34 | {logoPosition == "left" ? logo : ""}
35 | {title}
36 | {logoPosition == "right" ? logo : ""}
37 | >
38 | )}
39 |
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 |
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 |
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 |
22 | #{tag}
23 |
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 |
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 |
248 |
249 |
250 | {user ? (
251 | <>
252 |
253 |
254 |
50 Orbs
255 |
256 |
257 |
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 |
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 |
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 |
27 | {title}
28 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
12 |
13 |
14 |
Top Articles
15 |
This Week
16 |
17 |
18 |
30 |
31 |
32 |
33 |
}
35 | logoPosition="right"
36 | title="Show more"
37 | className="mt-8 bottom-0 w-full border-secondary border-2 text-secondary py-3 font-medium rounded-xl hover:bg-secondary hover:text-white "
38 | />
39 |
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 |
26 |
27 |
28 |
What's Geeking
29 |
This Week
30 |
31 |
32 |
44 |
45 |
46 |
47 | {tags.map((data, key) => (
48 |
49 |
50 | #{data.name}
51 |
52 |
53 | {data.posts} Posts
54 |
55 |
56 | {key !== tags?.length - 1 ?
: <>>}
57 |
58 |
59 | ))}
60 |
61 |
}
63 | logoPosition="right"
64 | title="Show more"
65 | className="mt-2 bottom-0 w-full border-secondary border-2 text-secondary py-3 font-medium rounded-xl hover:bg-secondary hover:text-white "
66 | />
67 |
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 |
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 |
44 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------