├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .firebaserc ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── BUG.md │ └── FEATURE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── build.yml │ ├── format.yml │ └── lint.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.js ├── .storybook └── main.js ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── README.md ├── craco.config.js ├── package.json ├── patches └── react-scripts+3.4.1.patch ├── public ├── index.html ├── logo.png └── manifest.json ├── src ├── App.tsx ├── __tests__ │ └── util │ │ └── util.test.ts ├── components │ ├── README.md │ ├── atoms │ │ ├── ActiveIcon.tsx │ │ ├── CommunityBarDivider.tsx │ │ └── NavDivider.tsx │ ├── common.ts │ ├── molecules │ │ ├── AddServerButton.tsx │ │ ├── Channel.tsx │ │ ├── CircleButton.tsx │ │ ├── CommunityBarSectionHeader.tsx │ │ ├── CommunityDetailIcon.tsx │ │ ├── CommunityInfo.tsx │ │ ├── CommunityInfoBar.tsx │ │ ├── ExploreCard.tsx │ │ ├── Message.tsx │ │ ├── MessageBar.jsx │ │ └── ServerIcon.tsx │ ├── organisms │ │ ├── CommunityBar.tsx │ │ ├── CommunityDetails.tsx │ │ ├── CommunityHomeMain.tsx │ │ ├── CommunityMessages.tsx │ │ ├── ExplorePage.tsx │ │ ├── NavSidebar.tsx │ │ ├── ServerList.tsx │ │ └── TrendingGrid.tsx │ └── theme │ │ └── index.tsx ├── global.css ├── index.tsx ├── pages │ ├── Communities.tsx │ ├── CommunityHome.tsx │ └── Explore.tsx ├── react-app-env.d.ts ├── serviceWorker.ts ├── setupTests.ts ├── stories │ ├── README.md │ ├── atoms │ │ └── ActiveIcon.stories.js │ └── organisms │ │ ├── CommunityBar.stories.js │ │ ├── CommunityDetails.stories.js │ │ ├── NavSidebar.stories.js │ │ └── ServerList.stories.js └── util │ ├── dummyData.ts │ └── index.ts ├── tsconfig.json ├── webpack.common.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = unset 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [{GNUmakefile,Makefile,makefile,*.mk}] 13 | indent_style = tab 14 | 15 | [*.{yaml,yml}] 16 | indent_style = space 17 | 18 | [*.{diff,md,snap}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | ecmaVersion: 2020, 5 | sourceType: 'module', 6 | ecmaFeatures: { 7 | jsx: true, 8 | }, 9 | }, 10 | settings: { 11 | react: { 12 | version: 'detect', 13 | }, 14 | }, 15 | plugins: ['simple-import-sort'], 16 | extends: [ 17 | 'react-app', 18 | 'plugin:react/recommended', 19 | 'plugin:@typescript-eslint/recommended', 20 | 'prettier/@typescript-eslint', 21 | 'plugin:prettier/recommended', 22 | ], 23 | rules: { 24 | '@typescript-eslint/explicit-module-boundary-types': 'off', 25 | 'react/prop-types': 0, 26 | '@typescript-eslint/no-explicit-any': 'off', 27 | '@typescript-eslint/no-unused-vars': [ 28 | 'error', 29 | { 30 | argsIgnorePattern: 'children', 31 | }, 32 | ], 33 | '@typescript-eslint/no-empty-interface': 'off', 34 | 'prefer-const': 'off', 35 | // simple-import-sort 36 | 'sort-imports': 'off', 37 | 'import/order': 'off', 38 | 'simple-import-sort/sort': 'error', 39 | }, 40 | } 41 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "rapid-ce500" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '🐛 Bug Report' 3 | about: Bug report for the rapid web interface 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | 12 | 13 | ## Description 14 | 15 | 18 | 19 | ## Steps to reproduce 20 | 21 | 25 | 26 | ## Logs/Screenshots 27 | 28 | 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '🚀 Feature request' 3 | about: Request a feature for the rapid web interface 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | 12 | 13 | ## Description 14 | 15 | 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 6 | 7 | ## Steps 8 | 9 | - [ ] My change requires a change to the documentation 10 | - [ ] I have updated the accessible documentation according 11 | - [ ] I have read the **CONTRIBUTING.md** file 12 | - [ ] There is no duplicate open or closed pull request for this fix/addition/issue resolution. 13 | 14 | ## Original Issue 15 | 16 | This PR resolves #ISSUE_NUMBER_HERE 17 | 18 | 22 | 23 | 26 | 27 | ## Original Design Screenshot 28 | 29 | ![]() 30 | 31 | ## Coded Component 32 | 33 | ![]() 34 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v1 13 | - run: yarn install --frozen-lockfile 14 | - run: yarn run build 15 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: format 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v1 13 | - run: yarn install --frozen-lockfile 14 | - run: yarn run format-check 15 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v1 13 | - run: yarn install --frozen-lockfile 14 | - run: yarn run lint-workflow 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules/ 5 | 6 | # testing 7 | coverage/ 8 | 9 | # production 10 | build/ 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | .env 24 | 25 | environment.ts -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'es5', 4 | printWidth: 80, 5 | semi: false, 6 | } 7 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | const { alias } = require('../webpack.common') 2 | 3 | module.exports = { 4 | stories: ['../src/**/*.stories.js'], 5 | addons: [ 6 | '@storybook/preset-create-react-app', 7 | '@storybook/addon-actions', 8 | '@storybook/addon-links', 9 | ], 10 | webpackFinal: (webpackConfig) => { 11 | Object.assign(webpackConfig.resolve.alias, alias) 12 | 13 | return webpackConfig 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": true, 3 | "editor.codeActionsOnSave": { 4 | "source.fixAll.eslint": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 👋 Welcome to Rapid! Thank you for showing interesting in contributing to Rapid, we would love to have your contribution. Below are some requirements for contributing. Please read carefully! 4 | 5 | ## Requesting Features/Reporting Bugs 6 | 7 | 1. Click on the "Issues" tab in the repo. 8 | 2. Make sure that the issue does exist already be searching for it. 9 | 3. Pick the issue template. 10 | 4. Fill in the issue template. 11 | 12 | ## Adding code 13 | 14 | 1. Make an issue (see above) and check to make sure it doesn't already exist. 15 | 2. Create a branch with the name being issue number. 16 | 3. Once done adding commits to branch make a pull request. 17 | 4. Now someone else on the team will review your PR. Congrats! 18 | 5. **Warning** once your PR gets merged the branch for it will automatically get deleted 19 | 20 | ## General 21 | 22 | - When you take on an issue please set yourself as the assignee. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # interface-web 2 | 3 | ![build](https://github.com/rapidotapp/interface-web/workflows/build/badge.svg) 4 | ![format](https://github.com/rapidotapp/interface-web/workflows/format/badge.svg) 5 | ![lint](https://github.com/rapidotapp/interface-web/workflows/lint/badge.svg) 6 | ![test](https://github.com/rapidotapp/interface-web/workflows/test/badge.svg) 7 | 8 | Web app for rapid 9 | 10 | ## 👥 Contributing 11 | 12 | Before making a PR or generally contributing to this project please read the `CONTRIBUTING.md` file. 13 | -------------------------------------------------------------------------------- /craco.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | /* eslint-disable @typescript-eslint/no-var-requires */ 3 | 4 | const { alias } = require('./webpack.common') 5 | 6 | module.exports = function ({ env }) { 7 | return { 8 | reactScriptsVersion: 'react-scripts', 9 | webpack: { 10 | configure: (webpackConfig, { env, paths }) => { 11 | Object.assign(webpackConfig.resolve.alias, alias) 12 | 13 | return webpackConfig 14 | }, 15 | }, 16 | jest: { 17 | configure: (jestConfig, { env, paths, resolve, rootDir }) => { 18 | jestConfig.moduleNameMapper['^~atoms(.*)$'] = 19 | '/src/components/atoms$1' 20 | jestConfig.moduleNameMapper['^~molecules(.*)$'] = 21 | '/src/components/molecules$1' 22 | jestConfig.moduleNameMapper['^~organisms(.*)$'] = 23 | '/src/components/organisms$1' 24 | // this must be added last 25 | jestConfig.moduleNameMapper['^~(.*)$'] = '/src$1' 26 | 27 | return jestConfig 28 | }, 29 | }, 30 | devServer: (devServerConfig, { env, paths, proxy, allowedHost }) => { 31 | // webpackConfig.port doesn't work here 32 | return devServerConfig 33 | }, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rapid", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@chakra-ui/core": "^0.8.0", 7 | "@emotion/core": "^10.0.28", 8 | "@emotion/styled": "^10.0.27", 9 | "@opuscapita/react-markdown": "^2.3.12", 10 | "@types/react-router-dom": "^5.1.5", 11 | "emotion-theming": "^10.0.27", 12 | "firebase": "^7.19.0", 13 | "formik": "^2.1.5", 14 | "framer-motion": "^2.4.1", 15 | "mobx": "^5.15.6", 16 | "mobx-react": "^6.2.5", 17 | "mobx-react-lite": "^2.0.7", 18 | "node-sass": "^4.14.1", 19 | "react": "^16.13.1", 20 | "react-dom": "^16.13.1", 21 | "react-error-boundary": "^2.3.1", 22 | "react-feather": "^2.0.8", 23 | "react-icons": "^3.10.0", 24 | "react-markdown": "^4.3.1", 25 | "react-router-dom": "^5.2.0", 26 | "react-scripts": "3.4.1", 27 | "react-wavify": "^1.3.0", 28 | "reactfire": "^2.0.3" 29 | }, 30 | "scripts": { 31 | "start": "cross-env PORT=8080 craco start", 32 | "build": "craco build", 33 | "test": "craco test", 34 | "eject": "craco eject", 35 | "format": "prettier --write src/", 36 | "format-check": "prettier --check src/", 37 | "lint": "eslint \"*/**/*.{js,ts,tsx}\" --quiet --fix", 38 | "lint-workflow": "eslint \"*/**/*.{js,ts,tsx}\" --quiet", 39 | "storybook": "start-storybook -p 9009 -s public", 40 | "build-storybook": "build-storybook -s public", 41 | "postinstall": "patch-package" 42 | }, 43 | "browserslist": { 44 | "production": [ 45 | ">0.2%", 46 | "not dead", 47 | "not op_mini all" 48 | ], 49 | "development": [ 50 | "last 1 chrome version", 51 | "last 1 firefox version", 52 | "last 1 safari version" 53 | ] 54 | }, 55 | "devDependencies": { 56 | "@craco/craco": "^5.6.4", 57 | "@storybook/addon-actions": "^5.3.19", 58 | "@storybook/addon-links": "^5.3.19", 59 | "@storybook/addons": "^5.3.19", 60 | "@storybook/preset-create-react-app": "^3.1.4", 61 | "@storybook/react": "^5.3.19", 62 | "@testing-library/jest-dom": "^4.2.4", 63 | "@testing-library/react": "^9.3.2", 64 | "@testing-library/user-event": "^7.1.2", 65 | "@types/jest": "^26.0.7", 66 | "@types/node": "^12.0.0", 67 | "@types/react": "^16.9.0", 68 | "@types/react-dom": "^16.9.0", 69 | "@typescript-eslint/eslint-plugin": "^3.7.0", 70 | "@typescript-eslint/parser": "^3.7.0", 71 | "cross-env": "^7.0.2", 72 | "eslint-config-prettier": "^6.11.0", 73 | "eslint-config-react-app": "^5.2.1", 74 | "eslint-plugin-prettier": "^3.1.4", 75 | "eslint-plugin-react": "^7.20.3", 76 | "eslint-plugin-simple-import-sort": "^5.0.3", 77 | "husky": "^4.2.5", 78 | "lint-staged": "^10.2.11", 79 | "patch-package": "^6.2.2", 80 | "postinstall-postinstall": "^2.1.0", 81 | "prettier": "^2.0.5", 82 | "typescript": "^3.7.2" 83 | }, 84 | "husky": { 85 | "hooks": { 86 | "pre-commit": "lint-staged" 87 | } 88 | }, 89 | "lint-staged": { 90 | "*.{js,jsx,ts,tsx}": [ 91 | "eslint --fix" 92 | ], 93 | "*.{json,scss,sass,css,graphql,md,mdx,yaml,yml}": [ 94 | "prettier --write" 95 | ] 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /patches/react-scripts+3.4.1.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js b/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js 2 | index ebd93d7..c94ffbc 100644 3 | --- a/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js 4 | +++ b/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js 5 | @@ -239,7 +239,7 @@ function verifyTypeScriptSetup() { 6 | } else { 7 | console.warn( 8 | chalk.bold( 9 | - 'The following changes are being made to your', 10 | + 'The following changes are [NOT (this has been patched out)] being made to your', 11 | chalk.cyan('tsconfig.json'), 12 | 'file:' 13 | ) 14 | @@ -249,7 +249,7 @@ function verifyTypeScriptSetup() { 15 | }); 16 | console.warn(); 17 | } 18 | - writeJson(paths.appTsConfig, appTsConfig); 19 | + // writeJson(paths.appTsConfig, appTsConfig); 20 | } 21 | 22 | // Reference `react-scripts` types 23 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 26 | React App 27 | 28 | 29 | 30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapidotapp/interface-web/33bc6456666612e55d7c84a9aadbbb3dafa9ce8f/public/logo.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Rapid", 3 | "name": "Rapid App", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Flex } from '@chakra-ui/core' 2 | import React, { FunctionComponent } from 'react' 3 | import { Route, Switch, useLocation } from 'react-router-dom' 4 | 5 | import NavSidebar from '~/components/organisms/NavSidebar' 6 | 7 | import { AuthContext } from './contexts/Auth' 8 | import Communities from './pages/Communities' 9 | import CommunityHome from './pages/CommunityHome' 10 | import Explore from './pages/Explore' 11 | 12 | const AuthPage: React.FC = () => { 13 | const auth = React.useContext(AuthContext) 14 | return 15 | } 16 | 17 | const App: FunctionComponent = () => { 18 | const location = useLocation() 19 | const switchLocation = (location: any): string => { 20 | console.log(location) 21 | switch (location) { 22 | case '/app/explore': 23 | return 'Explore' 24 | default: 25 | return 'Communities' 26 | } 27 | } 28 | 29 | return ( 30 | <> 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ) 43 | } 44 | 45 | export default App 46 | -------------------------------------------------------------------------------- /src/__tests__/util/util.test.ts: -------------------------------------------------------------------------------- 1 | import * as util from '../../util' 2 | 3 | describe('ensure formatNumber works', () => { 4 | test('passing in number', () => { 5 | const numString = util.formatNumber(30000) 6 | 7 | expect(numString).toBe('30,000') 8 | }) 9 | 10 | test('passing in string', () => { 11 | const numString = util.formatNumber('50000') 12 | 13 | expect(numString).toBe('50,000') 14 | }) 15 | 16 | test('small numbers', () => { 17 | const numString = util.formatNumber(40) 18 | 19 | expect(numString).toBe('40') 20 | }) 21 | 22 | test('large numbers', () => { 23 | const numString = util.formatNumber(1_300_500_000) 24 | 25 | expect(numString).toBe('1,300,500,000') 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/components/README.md: -------------------------------------------------------------------------------- 1 | # components 2 | 3 | - The [atoms]('./atoms') folder holds the lowest level of components that all others are derived from. Atoms must not include other atoms,. See [Brad Frost's](https://atomicdesign.bradfrost.com) post and a [React-based interpreteation](https://cheesecakelabs.com/blog/rethinking-atomic-design-react-projects) if you wish to learn more 4 | 5 | - [moledules]('./molecules') are the aggregation of participating atoms 6 | 7 | - [organisms]('./organisms) are the aggregation of paraticipating molecules. they are the components that should be used on pages when routing 8 | 9 | ## Possibly Relevant Guidelines 10 | 11 | When making new components, the following might apply: 12 | 13 | 1. Rest spread the function components `props` object so the first component in the tree gets access to all props (see `./atoms/LogoBackdrop.tsx` for an example of this, and example of how we use this in `./CommunityCard.tsx`) 14 | -------------------------------------------------------------------------------- /src/components/atoms/ActiveIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@chakra-ui/core' 2 | import React from 'react' 3 | 4 | const ActiveIcon = () => { 5 | return 6 | } 7 | 8 | export default ActiveIcon 9 | -------------------------------------------------------------------------------- /src/components/atoms/CommunityBarDivider.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@chakra-ui/core' 2 | import React from 'react' 3 | 4 | const CommunityBarDivider = () => { 5 | return 6 | } 7 | 8 | export default CommunityBarDivider 9 | -------------------------------------------------------------------------------- /src/components/atoms/NavDivider.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@chakra-ui/core' 2 | import React from 'react' 3 | 4 | const NavDivider = () => { 5 | return 6 | } 7 | 8 | export default NavDivider 9 | -------------------------------------------------------------------------------- /src/components/common.ts: -------------------------------------------------------------------------------- 1 | // TODO: cleanup / consistenize these values for theme harmony 2 | export const commonShadow = ` 3 | 0 1px 1px rgba(0,0,0,0.12), 4 | 0 2px 2px rgba(0,0,0,0.12), 5 | 0 4px 4px rgba(0,0,0,0.12), 6 | 0 8px 8px rgba(0,0,0,0.12), 7 | 0 16px 16px rgba(0,0,0,0.12)` 8 | export const smallBorderRadius = '15px' 9 | export const mediumBorderRadius = '25px' 10 | export const stdLightGrey = '#979797' 11 | -------------------------------------------------------------------------------- /src/components/molecules/AddServerButton.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, PseudoBox } from '@chakra-ui/core' 2 | import React from 'react' 3 | import { Plus } from 'react-feather' 4 | 5 | const AddServerButton = () => { 6 | return ( 7 | 14 | 15 | 16 | 17 | 18 | ) 19 | } 20 | 21 | export default AddServerButton 22 | -------------------------------------------------------------------------------- /src/components/molecules/Channel.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, PseudoBox, Text } from '@chakra-ui/core' 2 | import React from 'react' 3 | import { Link } from 'react-router-dom' 4 | 5 | import ActiveIcon from '../atoms/ActiveIcon' 6 | 7 | interface channelProps { 8 | name: string 9 | active: boolean 10 | mentions: number 11 | icon: any 12 | padding: string 13 | unread: boolean 14 | } 15 | 16 | const Channel = (props: channelProps) => { 17 | return ( 18 | 19 | 24 | 31 | {props.icon} 32 | 37 | {props.name} 38 | 39 | {props.mentions !== 0 ? ( 40 | 49 | 50 | {props.mentions} 51 | 52 | 53 | ) : ( 54 | props.unread && 55 | )} 56 | 57 | 58 | 59 | ) 60 | } 61 | 62 | export default Channel 63 | -------------------------------------------------------------------------------- /src/components/molecules/CircleButton.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, PseudoBox } from '@chakra-ui/core' 2 | import React from 'react' 3 | import { Link } from 'react-router-dom' 4 | 5 | interface circleButtonProps { 6 | // If you guys could find the exact type for this, please insert it here 7 | icon: any 8 | bg: string 9 | link: string 10 | } 11 | 12 | const CircleButton = (props: circleButtonProps) => { 13 | return ( 14 | 15 | 24 | 31 | {props.icon} 32 | 33 | 34 | 35 | ) 36 | } 37 | 38 | export default CircleButton 39 | -------------------------------------------------------------------------------- /src/components/molecules/CommunityBarSectionHeader.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Text } from '@chakra-ui/core' 2 | import React from 'react' 3 | import { ChevronDown } from 'react-feather' 4 | 5 | interface communityBarSectionHeaderProps { 6 | title: string 7 | color: string 8 | onClick: any 9 | isOpen: boolean 10 | } 11 | 12 | const CommunityBarSectionHeader = (props: communityBarSectionHeaderProps) => { 13 | return ( 14 | 22 | 23 | {props.title} 24 | 25 | 33 | 34 | ) 35 | } 36 | 37 | export default CommunityBarSectionHeader 38 | -------------------------------------------------------------------------------- /src/components/molecules/CommunityDetailIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Text } from '@chakra-ui/core' 2 | import React from 'react' 3 | 4 | interface communityDetailIconProps { 5 | icon: any 6 | text: string 7 | } 8 | 9 | const CommunityDetailIcon = (props: communityDetailIconProps) => { 10 | return ( 11 | 12 | 21 | {props.icon} 22 | 23 | 24 | {props.text} 25 | 26 | 27 | ) 28 | } 29 | 30 | export default CommunityDetailIcon 31 | -------------------------------------------------------------------------------- /src/components/molecules/CommunityInfo.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Text } from '@chakra-ui/core' 2 | import React from 'react' 3 | 4 | interface communityInfoProps { 5 | name: string 6 | detail: string 7 | } 8 | 9 | const CommunityInfo = (props: communityInfoProps) => { 10 | return ( 11 | 19 | 20 | 21 | {props.name} 22 | 23 | 24 | 25 | {props.detail} 26 | 27 | 28 | ) 29 | } 30 | 31 | export default CommunityInfo 32 | -------------------------------------------------------------------------------- /src/components/molecules/CommunityInfoBar.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, PseudoBox, Text } from '@chakra-ui/core' 2 | import React from 'react' 3 | import { Flag, Info, Users } from 'react-feather' 4 | 5 | const CommunityInfoBar = (props: any) => { 6 | return ( 7 | 16 | 17 | 18 | #general 19 | 20 | 21 | 22 | 23 | 24 | 50 | 25 | 26 | 27 | 28 | 29 | 30 | 5 | 31 | 32 | 33 | 34 | 35 | Random work announcements. Feel free to chill out here! 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ) 45 | } 46 | 47 | export default CommunityInfoBar 48 | -------------------------------------------------------------------------------- /src/components/molecules/ExploreCard.tsx: -------------------------------------------------------------------------------- 1 | import { Badge, Flex, PseudoBox, Text } from '@chakra-ui/core' 2 | // import { motion } from 'framer-motion' 3 | import React from 'react' 4 | 5 | interface exploreCardProps { 6 | image: string 7 | tags: Array 8 | size: 'sm' | 'lg' 9 | title: string 10 | desc?: string 11 | } 12 | 13 | const ExploreCard = (props: exploreCardProps) => { 14 | return ( 15 | 27 | 38 | 39 | {props.tags.map((t, i) => ( 40 | 56 | {`#${t}`} 57 | 58 | ))} 59 | 60 | 67 | {props.title} 68 | 69 | {props.desc && ( 70 | 71 | {props.desc} 72 | 73 | )} 74 | 75 | 76 | ) 77 | } 78 | 79 | export default ExploreCard 80 | -------------------------------------------------------------------------------- /src/components/molecules/Message.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Flex, Image, Text } from '@chakra-ui/core' 2 | import React from 'react' 3 | import ReactMarkdown from 'react-markdown' 4 | import SyntaxHighlighter from 'react-syntax-highlighter' 5 | 6 | interface messageProps { 7 | from: string 8 | iconURL: string 9 | time: string 10 | message: string 11 | replies: number 12 | replyIcon: string 13 | } 14 | 15 | class CodeBlock extends React.Component<{ language: string; value: string }> { 16 | render() { 17 | const { language, value } = this.props 18 | return {value} 19 | } 20 | } 21 | 22 | const Message = (props: messageProps) => { 23 | return ( 24 | 25 | 26 | 33 | 34 | 35 | 36 | {props.from} 37 | 38 | 39 | {props.time} 40 | 41 | 42 | 43 | 49 | 50 | {props.replies > 0 && ( 51 | 52 | 59 | 60 | {props.replies} {props.replies === 1 ? 'reply' : 'replies'} 61 | 62 | 63 | )} 64 | 65 | 66 | 67 | ) 68 | } 69 | 70 | export default Message 71 | -------------------------------------------------------------------------------- /src/components/molecules/MessageBar.jsx: -------------------------------------------------------------------------------- 1 | import { Flex, Input, InputGroup, InputRightAddon } from '@chakra-ui/core' 2 | import { Field, Formik } from 'formik' 3 | import React from 'react' 4 | import { Smile } from 'react-feather' 5 | 6 | const MessageBar = (props) => { 7 | return ( 8 | { 11 | if (values.message) { 12 | props.onSubmit(values.message) 13 | } 14 | values.message = '' 15 | resetForm() 16 | }} 17 | > 18 | {(props) => ( 19 | 27 | 37 |
38 | 39 | {({ field }) => ( 40 | 56 | )} 57 | 58 |
59 | 60 | 61 | 62 |
63 |
64 | )} 65 |
66 | ) 67 | } 68 | 69 | export default MessageBar 70 | -------------------------------------------------------------------------------- /src/components/molecules/ServerIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Image, PseudoBox } from '@chakra-ui/core' 2 | import React from 'react' 3 | 4 | interface serverIconProps { 5 | iconURL: string 6 | active: boolean 7 | } 8 | 9 | const ServerIcon = (props: serverIconProps) => { 10 | return ( 11 | 29 | 30 | 31 | 32 | 33 | ) 34 | } 35 | 36 | export default ServerIcon 37 | -------------------------------------------------------------------------------- /src/components/organisms/CommunityBar.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Collapse, Flex, Text } from '@chakra-ui/core' 2 | import React from 'react' 3 | import { Camera, Hash, Inbox, MessageSquare, Mic, Plus } from 'react-feather' 4 | 5 | import ActiveIcon from '../atoms/ActiveIcon' 6 | import CommunityBarDivider from '../atoms/CommunityBarDivider' 7 | import Channel from '../molecules/Channel' 8 | import CommunityBarSectionHeader from '../molecules/CommunityBarSectionHeader' 9 | 10 | const CommunityBar = () => { 11 | const [show1, setShow1] = React.useState(false) 12 | const [show2, setShow2] = React.useState(false) 13 | const [show3, setShow3] = React.useState(false) 14 | const handleToggle1 = () => setShow1(!show1) 15 | const handleToggle2 = () => setShow2(!show2) 16 | const handleToggle3 = () => setShow3(!show3) 17 | 18 | const communityBG = 19 | 'url("https://images.unsplash.com/photo-1596698867859-a27795736d7e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80")' 20 | return ( 21 | 34 | 35 | art hangout 36 | artists, designers, graphics 37 | 38 | 39 | 40 | 41 | 42 | signed in as 43 | 44 | safin 45 | 46 | 47 | 48 | 49 | } 55 | padding="7px 12px" 56 | /> 57 | } 63 | padding="7px 12px" 64 | /> 65 | } 71 | padding="7px 12px" 72 | /> 73 | 74 | 75 | 76 | 82 | 83 | } 89 | padding="16px 12px" 90 | /> 91 | } 97 | padding="16px 12px" 98 | /> 99 | } 105 | padding="16px 12px" 106 | /> 107 | 108 | 109 | 110 | 111 | 117 | 118 | } 124 | padding="16px 12px" 125 | /> 126 | } 132 | padding="16px 12px" 133 | /> 134 | 135 | 136 | 137 | 138 | 144 | 145 | } 151 | padding="16px 12px" 152 | /> 153 | } 159 | padding="16px 12px" 160 | /> 161 | 162 | 163 | 164 | 165 | 166 | ) 167 | } 168 | 169 | export default CommunityBar 170 | -------------------------------------------------------------------------------- /src/components/organisms/CommunityDetails.tsx: -------------------------------------------------------------------------------- 1 | import { Collapse, Flex, PseudoBox, Text } from '@chakra-ui/core' 2 | import React from 'react' 3 | import { 4 | MoreHorizontal, 5 | PhoneOutgoing, 6 | Search, 7 | UserPlus, 8 | XCircle, 9 | } from 'react-feather' 10 | 11 | import CommunityDetailIcon from '../molecules/CommunityDetailIcon' 12 | import CommunityInfo from '../molecules/CommunityInfo' 13 | 14 | const CommunityDetails = (props: any) => { 15 | return ( 16 | 17 | 24 | 31 | 32 | 33 | Details 34 | 35 | 36 | #general 37 | 38 | 39 | 40 | 41 | 42 | 43 | 51 | 52 | } 55 | /> 56 | } 59 | /> 60 | } 63 | /> 64 | } 67 | /> 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | ) 77 | } 78 | 79 | export default CommunityDetails 80 | -------------------------------------------------------------------------------- /src/components/organisms/CommunityHomeMain.tsx: -------------------------------------------------------------------------------- 1 | import { Badge, Box, Flex, Text } from '@chakra-ui/core' 2 | import React from 'react' 3 | import { Zap } from 'react-feather' 4 | 5 | import TrendingGrid from './TrendingGrid' 6 | 7 | const CommunityHomeMain = () => { 8 | const header = 9 | 'https://images.unsplash.com/photo-1491147334573-44cbb4602074?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop' 10 | 11 | const trending = [ 12 | { 13 | name: 'B1ing', 14 | header: 15 | 'https://images.unsplash.com/photo-1535083252457-6080fe29be45?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9', 16 | desc: '', 17 | tags: [], 18 | }, 19 | { 20 | name: 'Dark Arts', 21 | header: 22 | 'https://images.unsplash.com/photo-1555448248-2571daf6344b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9', 23 | desc: '', 24 | tags: [], 25 | }, 26 | { 27 | name: 'Banff', 28 | header: 29 | 'https://images.unsplash.com/photo-1464054573978-f220a48c764c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format', 30 | desc: '', 31 | tags: [], 32 | }, 33 | { 34 | name: 'BTS', 35 | header: 36 | 'https://images.unsplash.com/photo-1511671782779-c97d3d27a1d4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format', 37 | desc: '', 38 | tags: [], 39 | }, 40 | ] 41 | 42 | return ( 43 | 44 | 55 | 56 | rapid staff favourite 57 | 58 | 59 | Art Hangout 60 | 61 | 62 | Artists, designers, developers, unite! 63 | 64 | 65 | 72 | #sealife 73 | 74 | 81 | #abstract 82 | 83 | 91 | 92 | 93 | 94 | 95 | for you 96 | 97 | 98 | 99 | 100 | 101 | 102 | About 103 | 104 | 105 | Far far away, behind the word mountains, far from the countries 106 | Vokalia and Consonantia, there live the blind texts. Separated they 107 | live in Bookmarksgrove right at the coast of the Semantics, a large 108 | language ocean. A small river named Duden flows by their place and 109 | supplies it with the necessary regelialia. It is a paradisematic 110 | country, in which roasted parts of sentences fly into your mouth. 111 | 112 | 113 | 114 | 115 | 116 | Stories 117 | 118 | 119 | Community ideas 120 | 121 | 122 | 123 | 124 | 125 | Streams 126 | 127 | 128 | The best of, selected by our amazing jellyfish(es)! 129 | 130 | 131 | 132 | 133 | 134 | ) 135 | } 136 | 137 | export default CommunityHomeMain 138 | -------------------------------------------------------------------------------- /src/components/organisms/CommunityMessages.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Flex } from '@chakra-ui/core' 2 | import React, { useState } from 'react' 3 | 4 | import CommunityInfoBar from '~molecules/CommunityInfoBar' 5 | import Message from '~molecules/Message' 6 | import MessageBar from '~molecules/MessageBar' 7 | 8 | interface message { 9 | iconURL: string 10 | from: string 11 | time: string 12 | message: string 13 | replies: number 14 | replyIcon: string 15 | } 16 | 17 | const CommunityMessages = (props: any) => { 18 | const iconURL = 19 | 'https://images.unsplash.com/photo-1557672172-298e090bd0f1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80' 20 | 21 | const [messages, setMessages] = useState([ 22 | { 23 | iconURL: iconURL, 24 | from: 'safinsingh', 25 | time: '9:10', 26 | message: `\`import React from 'react'\` should do the trick. 27 | \`\`\`js 28 | console.log('hello from rapid!') 29 | \`\`\``, 30 | replies: 2, 31 | replyIcon: iconURL, 32 | }, 33 | ]) 34 | 35 | const addMessage = (msg: string) => { 36 | setMessages([ 37 | { 38 | iconURL: iconURL, 39 | from: 'safinsingh', 40 | time: new Date().toISOString(), 41 | message: msg, 42 | replies: 0, 43 | replyIcon: iconURL, 44 | }, 45 | ...messages, 46 | ]) 47 | } 48 | 49 | return ( 50 | 51 | 52 | 60 | {messages.map((el, index) => ( 61 | 70 | ))} 71 | 72 | 73 | 74 | ) 75 | } 76 | 77 | export default CommunityMessages 78 | -------------------------------------------------------------------------------- /src/components/organisms/ExplorePage.tsx: -------------------------------------------------------------------------------- 1 | import { Badge, Box, Flex, Input, Stack, Text } from '@chakra-ui/core' 2 | import React from 'react' 3 | import { Feather, TrendingUp, Zap } from 'react-feather' 4 | 5 | import { communityHeader, trending } from '../../util/dummyData' 6 | import TrendingGrid from './TrendingGrid' 7 | 8 | const ExplorePage = () => { 9 | return ( 10 | 18 | 19 | 20 | 21 | hey there, Safin! 22 | 23 | 24 | 25 | 26 | 27 | 28 | let's find some new 29 | 30 | 31 | 39 | 46 | 47 | communities 48 | 49 | 50 | 58 | 59 | friends 60 | 61 | 62 | 63 | 72 | 73 | 74 | 87 | 88 | rapid staff favourite 89 | 90 | 91 | Jellyfishes 92 | 93 | 94 | A community for the abstract 95 | 96 | 97 | 104 | #sealife 105 | 106 | 113 | #abstract 114 | 115 | 123 | 124 | 125 | 126 | 127 | for you 128 | 129 | 130 | 131 | 143 | 144 | 151 | 158 | 165 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | trending 182 | 183 | 184 | 185 | 191 | the best communities, curated for you. 192 | 193 | 194 | 195 | 196 | 197 | 198 | recommended 199 | 200 | 201 | 202 | 208 | the best picks by our in-house jellyfish! 209 | 210 | 211 | 212 | 213 | 214 | ) 215 | } 216 | 217 | export default ExplorePage 218 | -------------------------------------------------------------------------------- /src/components/organisms/NavSidebar.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Image } from '@chakra-ui/core' 2 | import React from 'react' 3 | import { Bell, FileText, Globe, Inbox, Settings, Users } from 'react-feather' 4 | 5 | import CircleButton from '../molecules/CircleButton' 6 | 7 | interface navSidebarProps { 8 | active: string 9 | } 10 | 11 | export default function NavSidebar(props: navSidebarProps) { 12 | const imageURL = 13 | 'https://images.unsplash.com/photo-1557672172-298e090bd0f1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80' 14 | return ( 15 | 22 | 23 | 30 | 31 | 38 | 44 | } 45 | bg={props.active === 'Communities' ? 'red.300' : 'red.50'} 46 | link="/app/community" 47 | /> 48 | 54 | } 55 | bg={props.active === 'Messaging' ? 'red.300' : 'red.50'} 56 | link="/app/explore" 57 | /> 58 | 64 | } 65 | bg={props.active === 'Posts' ? 'red.300' : 'red.50'} 66 | link="/app/posts/" 67 | /> 68 | 74 | } 75 | bg={props.active === 'Notifications' ? 'red.300' : 'red.50'} 76 | link="/app/notifications" 77 | /> 78 | 84 | } 85 | bg={props.active === 'Explore' ? 'red.300' : 'red.50'} 86 | link="/app/explore" 87 | /> 88 | 89 | 90 | } 92 | bg="red.50" 93 | link="" 94 | /> 95 | 96 | 97 | ) 98 | } 99 | -------------------------------------------------------------------------------- /src/components/organisms/ServerList.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@chakra-ui/core' 2 | import React from 'react' 3 | 4 | import NavDivider from '../atoms/NavDivider' 5 | import AddServerButton from '../molecules/AddServerButton' 6 | import ServerIcon from '../molecules/ServerIcon' 7 | 8 | const ServerList = () => { 9 | const iconURL = 10 | 'https://images.unsplash.com/photo-1557672172-298e090bd0f1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80' 11 | 12 | return ( 13 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ) 33 | } 34 | 35 | export default ServerList 36 | -------------------------------------------------------------------------------- /src/components/organisms/TrendingGrid.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@chakra-ui/core' 2 | import React from 'react' 3 | 4 | import ExploreCard from '../molecules/ExploreCard' 5 | interface community { 6 | name: string 7 | header: string 8 | desc?: string 9 | tags: string[] 10 | } 11 | 12 | interface trendingProps { 13 | trending: community[] 14 | } 15 | 16 | const TrendingGrid = (props: trendingProps) => { 17 | return ( 18 | <> 19 | {[...Array(Math.ceil(props.trending.length / 2))].map((c, index) => 20 | index % 2 === 0 ? ( 21 | 27 | 34 | 41 | 42 | ) : ( 43 | 49 | 56 | 63 | 64 | ) 65 | )} 66 | 67 | ) 68 | } 69 | 70 | export default TrendingGrid 71 | -------------------------------------------------------------------------------- /src/components/theme/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ColorModeProvider, 3 | CSSReset, 4 | theme, 5 | ThemeProvider, 6 | } from '@chakra-ui/core' 7 | import React from 'react' 8 | 9 | const ThemeX: React.FunctionComponent = ({ children }) => ( 10 | 11 | 12 | 13 | {children} 14 | 15 | 16 | ) 17 | 18 | export default ThemeX 19 | -------------------------------------------------------------------------------- /src/global.css: -------------------------------------------------------------------------------- 1 | ::-webkit-scrollbar { 2 | display: none !important; 3 | } 4 | 5 | ::-webkit-scrollbar-track { 6 | display: none !important; 7 | } 8 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import './global.css' 2 | 3 | import { 4 | ColorModeProvider, 5 | CSSReset, 6 | theme, 7 | ThemeProvider, 8 | } from '@chakra-ui/core' 9 | import React from 'react' 10 | import ReactDOM from 'react-dom' 11 | import { BrowserRouter as Router } from 'react-router-dom' 12 | 13 | import App from './App' 14 | import * as serviceWorker from './serviceWorker' 15 | 16 | const customTheme = { 17 | ...theme, 18 | fonts: { 19 | ...theme.fonts, 20 | body: 'Inter, sans-serif', 21 | heading: 'Inter, sans-serif', 22 | monospace: 'JetBrains Mono, monospace', 23 | }, 24 | } 25 | 26 | ReactDOM.render( 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | , 37 | document.getElementById('root') 38 | ) 39 | 40 | // If you want your app to work offline and load faster, you can change 41 | // unregister() to register() below. Note this comes with some pitfalls. 42 | // Learn more about service workers: https://bit.ly/CRA-PWA 43 | serviceWorker.unregister() 44 | -------------------------------------------------------------------------------- /src/pages/Communities.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@chakra-ui/core' 2 | import React, { useState } from 'react' 3 | 4 | import CommunityBar from '~/components/organisms/CommunityBar' 5 | import CommunityDetails from '~/components/organisms/CommunityDetails' 6 | import CommunityMessages from '~/components/organisms/CommunityMessages' 7 | import ServerList from '~/components/organisms/ServerList' 8 | 9 | const Communities = () => { 10 | const [isDetailOpen, setDetailOpen] = useState(true) // TODO: extract all states like this into a context 11 | const handleDetail = () => setDetailOpen(!isDetailOpen) 12 | 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ) 23 | } 24 | 25 | export default Communities 26 | -------------------------------------------------------------------------------- /src/pages/CommunityHome.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@chakra-ui/core' 2 | import React from 'react' 3 | 4 | import CommunityBar from '~/components/organisms/CommunityBar' 5 | import CommunityHomeMain from '~/components/organisms/CommunityHomeMain' 6 | import ServerList from '~/components/organisms/ServerList' 7 | 8 | const CommunityHome = () => { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | ) 16 | } 17 | 18 | export default CommunityHome 19 | -------------------------------------------------------------------------------- /src/pages/Explore.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import ExplorePage from '~/components/organisms/ExplorePage' 4 | 5 | const Explore = () => { 6 | return 7 | } 8 | 9 | export default Explore 10 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/serviceWorker.ts: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ) 22 | 23 | type Config = { 24 | onSuccess?: (registration: ServiceWorkerRegistration) => void 25 | onUpdate?: (registration: ServiceWorkerRegistration) => void 26 | } 27 | 28 | export function register(config?: Config) { 29 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 30 | // The URL constructor is available in all browsers that support SW. 31 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href) 32 | if (publicUrl.origin !== window.location.origin) { 33 | // Our service worker won't work if PUBLIC_URL is on a different origin 34 | // from what our page is served on. This might happen if a CDN is used to 35 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 36 | return 37 | } 38 | 39 | window.addEventListener('load', () => { 40 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` 41 | 42 | if (isLocalhost) { 43 | // This is running on localhost. Let's check if a service worker still exists or not. 44 | checkValidServiceWorker(swUrl, config) 45 | 46 | // Add some additional logging to localhost, pointing developers to the 47 | // service worker/PWA documentation. 48 | navigator.serviceWorker.ready.then(() => { 49 | console.log( 50 | 'This web app is being served cache-first by a service ' + 51 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 52 | ) 53 | }) 54 | } else { 55 | // Is not localhost. Just register service worker 56 | registerValidSW(swUrl, config) 57 | } 58 | }) 59 | } 60 | } 61 | 62 | function registerValidSW(swUrl: string, config?: Config) { 63 | navigator.serviceWorker 64 | .register(swUrl) 65 | .then((registration) => { 66 | registration.onupdatefound = () => { 67 | const installingWorker = registration.installing 68 | if (installingWorker == null) { 69 | return 70 | } 71 | installingWorker.onstatechange = () => { 72 | if (installingWorker.state === 'installed') { 73 | if (navigator.serviceWorker.controller) { 74 | // At this point, the updated precached content has been fetched, 75 | // but the previous service worker will still serve the older 76 | // content until all client tabs are closed. 77 | console.log( 78 | 'New content is available and will be used when all ' + 79 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 80 | ) 81 | 82 | // Execute callback 83 | if (config && config.onUpdate) { 84 | config.onUpdate(registration) 85 | } 86 | } else { 87 | // At this point, everything has been precached. 88 | // It's the perfect time to display a 89 | // "Content is cached for offline use." message. 90 | console.log('Content is cached for offline use.') 91 | 92 | // Execute callback 93 | if (config && config.onSuccess) { 94 | config.onSuccess(registration) 95 | } 96 | } 97 | } 98 | } 99 | } 100 | }) 101 | .catch((error) => { 102 | console.error('Error during service worker registration:', error) 103 | }) 104 | } 105 | 106 | function checkValidServiceWorker(swUrl: string, config?: Config) { 107 | // Check if the service worker can be found. If it can't reload the page. 108 | fetch(swUrl, { 109 | headers: { 'Service-Worker': 'script' }, 110 | }) 111 | .then((response) => { 112 | // Ensure service worker exists, and that we really are getting a JS file. 113 | const contentType = response.headers.get('content-type') 114 | if ( 115 | response.status === 404 || 116 | (contentType != null && contentType.indexOf('javascript') === -1) 117 | ) { 118 | // No service worker found. Probably a different app. Reload the page. 119 | navigator.serviceWorker.ready.then((registration) => { 120 | registration.unregister().then(() => { 121 | window.location.reload() 122 | }) 123 | }) 124 | } else { 125 | // Service worker found. Proceed as normal. 126 | registerValidSW(swUrl, config) 127 | } 128 | }) 129 | .catch(() => { 130 | console.log( 131 | 'No internet connection found. App is running in offline mode.' 132 | ) 133 | }) 134 | } 135 | 136 | export function unregister() { 137 | if ('serviceWorker' in navigator) { 138 | navigator.serviceWorker.ready 139 | .then((registration) => { 140 | registration.unregister() 141 | }) 142 | .catch((error) => { 143 | console.error(error.message) 144 | }) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect' 6 | -------------------------------------------------------------------------------- /src/stories/README.md: -------------------------------------------------------------------------------- 1 | # stories 2 | 3 | ## guidelines 4 | 5 | - it's best if the `default export`'ed `title` was set to the exact _filename_ of the component 6 | -------------------------------------------------------------------------------- /src/stories/atoms/ActiveIcon.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import ActiveIcon from '~atoms/ActiveIcon' 4 | 5 | import ThemeX from '../../components/theme' 6 | 7 | export default { title: 'ActiveIcon' } 8 | 9 | export const activeIcon = () => { 10 | return ( 11 | 12 | 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /src/stories/organisms/CommunityBar.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { BrowserRouter as Router } from 'react-router-dom' 3 | 4 | import CommunityBar from '../../components/organisms/CommunityBar' 5 | import ThemeX from '../../components/theme' 6 | 7 | export default { title: 'Organisms.CommunityBar' } 8 | 9 | export const communityBar = () => { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/stories/organisms/CommunityDetails.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { BrowserRouter as Router } from 'react-router-dom' 3 | 4 | import CommunityDetails from '../../components/organisms/CommunityDetails' 5 | import ThemeX from '../../components/theme' 6 | 7 | export default { title: 'Organisms.CommunityDetails' } 8 | 9 | export const communityDetails = () => { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/stories/organisms/NavSidebar.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { BrowserRouter as Router } from 'react-router-dom' 3 | 4 | import NavSidebar from '../../components/organisms/NavSidebar' 5 | import ThemeX from '../../components/theme' 6 | 7 | export default { title: 'Organisms.NavSidebar' } 8 | 9 | export const navSidebar = () => { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/stories/organisms/ServerList.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { BrowserRouter as Router } from 'react-router-dom' 3 | 4 | import ServerList from '../../components/organisms/ServerList' 5 | import ThemeX from '../../components/theme' 6 | 7 | export default { title: 'Organisms.ServerList' } 8 | 9 | export const serverList = () => { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/util/dummyData.ts: -------------------------------------------------------------------------------- 1 | export const dms = [ 2 | { 3 | name: 'Safin', 4 | username: '@safinsingh', 5 | pfp: 6 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 7 | unread: '13', 8 | online: true, 9 | isPinned: true, 10 | }, 11 | { 12 | name: 'Safin', 13 | username: '@safinsingh', 14 | pfp: 15 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 16 | unread: '13', 17 | online: false, 18 | isPinned: false, 19 | }, 20 | ] 21 | 22 | export const groupDms = [ 23 | { 24 | pfps: [ 25 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 26 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 27 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 28 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 29 | ], 30 | unread: '13', 31 | online: 10, 32 | isPinned: true, 33 | name: "Safin's group chat", 34 | }, 35 | { 36 | pfps: [ 37 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 38 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 39 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 40 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 41 | ], 42 | unread: '13', 43 | online: 10, 44 | isPinned: false, 45 | name: "Safin's group chat", 46 | }, 47 | { 48 | pfps: [ 49 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 50 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 51 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 52 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 53 | ], 54 | unread: '13', 55 | online: 10, 56 | isPinned: false, 57 | name: "Safin's group chat", 58 | }, 59 | ] 60 | 61 | export const onlineUsers = [ 62 | { 63 | name: 'Safin', 64 | username: 'safinsingh', 65 | imageURL: 66 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 67 | drops: ['Google Translate'], 68 | }, 69 | { 70 | name: 'Safin', 71 | username: 'safinsingh', 72 | imageURL: 73 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 74 | drops: ['Google Translate'], 75 | }, 76 | ] 77 | 78 | export const offlineUsers = [ 79 | { 80 | name: 'Safin', 81 | username: 'safinsingh', 82 | imageURL: 83 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 84 | drops: ['Google Translate'], 85 | }, 86 | { 87 | name: 'Safin', 88 | username: 'safinsingh', 89 | imageURL: 90 | 'https://www.yourdictionary.com/images/definitions/lg/10750.person.jpg', 91 | drops: ['Google Translate'], 92 | }, 93 | ] 94 | 95 | export const communityHeader = 96 | 'https://images.unsplash.com/photo-1559291001-693fb9166cba?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjExNjMwfQ' 97 | 98 | export const trending = [ 99 | { 100 | name: 'B1ing', 101 | header: 102 | 'https://images.unsplash.com/photo-1535083252457-6080fe29be45?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9', 103 | desc: 'Show off that ice.', 104 | tags: ['money', 'abstract'], 105 | }, 106 | { 107 | name: 'Dark Arts', 108 | header: 109 | 'https://images.unsplash.com/photo-1555448248-2571daf6344b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9', 110 | tags: ['art', 'abstract'], 111 | }, 112 | { 113 | name: 'Banff', 114 | header: 115 | 'https://images.unsplash.com/photo-1464054573978-f220a48c764c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format', 116 | tags: ['landscapes', 'mountains'], 117 | }, 118 | { 119 | name: 'BTS', 120 | header: 121 | 'https://images.unsplash.com/photo-1511671782779-c97d3d27a1d4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format', 122 | desc: 'Kpop, but better ;D', 123 | tags: ['music', 'korea'], 124 | }, 125 | ] 126 | -------------------------------------------------------------------------------- /src/util/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Converts a number or string to a format that includes commas 3 | * 4 | * @example 5 | * formatNumber(1000) 6 | * // => 1,000 7 | * 8 | * formatNumber(40_000) 9 | * // => 40,000 10 | */ 11 | export function formatNumber(number: string | number): string { 12 | return String(number).replace(/\B(?=(\d{3})+(?!\d))/g, ',') 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, 7 | "module": "ESNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, 8 | "lib": [ 9 | "dom", 10 | "dom.iterable", 11 | "esnext" 12 | ] /* Specify library files to be included in the compilation. */, 13 | "allowJs": true /* Allow javascript files to be compiled. */, 14 | // "checkJs": true, /* Report errors in .js files. */ 15 | "jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, 16 | "declaration": true /* Generates corresponding '.d.ts' file. */, 17 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 18 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 19 | // "outFile": "./", /* Concatenate and emit output to single file. */ 20 | // "outDir": "./", /* Redirect output structure to the directory. */ 21 | // "rootDir": "./" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, 22 | // "composite": true, /* Enable project compilation */ 23 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 24 | // "removeComments": true, /* Do not emit comments to output. */ 25 | "noEmit": true /* Do not emit outputs. */, 26 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 27 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 28 | "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, 29 | /* Strict Type-Checking Options */ 30 | "strict": true /* Enable all strict type-checking options. */, 31 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 32 | // "strictNullChecks": true, /* Enable strict null checks. */ 33 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 34 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 35 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 36 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 37 | "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, 38 | /* Additional Checks */ 39 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 40 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 41 | "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, 42 | "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, 43 | /* Module Resolution Options */ 44 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, 45 | "baseUrl": "./" /* Base directory to resolve non-absolute module names. */, 46 | "paths": { 47 | "~/*": ["src/*"], 48 | "~atoms/*": ["src/components/atoms/*"], 49 | "~molecules/*": ["src/components/molecules/*"], 50 | "~organisms/*": ["src/components/molecules/*"] 51 | } /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, 52 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 53 | // "typeRoots": [], /* List of folders to include type definitions from. */ 54 | // "types": [], /* Type declaration files to be included in compilation. */ 55 | "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, 56 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 57 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 58 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 59 | /* Source Map Options */ 60 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 61 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 62 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 63 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 64 | /* Experimental Options */ 65 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 66 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 67 | /* Advanced Options */ 68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, 69 | "skipLibCheck": true, 70 | "resolveJsonModule": true, 71 | "useDefineForClassFields": true 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | 3 | /** 4 | * @fileoverview Common Webpack config for CRA and Storybook configs 5 | */ 6 | 7 | const path = require('path') 8 | 9 | /** @type Record */ 10 | const alias = {} 11 | alias['~'] = path.join(__dirname, 'src') 12 | alias['~atoms'] = path.join(__dirname, 'src/components/atoms') 13 | alias['~molecules'] = path.join(__dirname, 'src/components/molecules') 14 | alias['~organisms'] = path.join(__dirname, 'src/components/organisms') 15 | 16 | module.exports = { 17 | alias, 18 | } 19 | --------------------------------------------------------------------------------