diff --git a/.github/workflows/push-master-environment.yaml b/.github/workflows/push-master-environment.yaml index 3e275d77..0bae43a9 100644 --- a/.github/workflows/push-master-environment.yaml +++ b/.github/workflows/push-master-environment.yaml @@ -2,8 +2,8 @@ name: Build images from latest master branch on: push: - branches: - - master + tags: + - v* jobs: create-master-image: @@ -87,7 +87,9 @@ jobs: with: args: apply -f build_k8s -n mainnet-eosrate -# - name: Verify deployment -# id: verify_deployment -# run: | -# kubectl rollout status deployment/hapi + - name: Create Release + id: create_release + uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + prerelease: false diff --git a/docs/create-a-release.md b/docs/create-a-release.md new file mode 100644 index 00000000..d6bc3a0d --- /dev/null +++ b/docs/create-a-release.md @@ -0,0 +1,57 @@ +# Creating a new release + +These are the steps: +```bash +# Set and replace the version you need to tag and deploy +VERSION='v0.1.x' + +# Checked out into develop branch +git checkout staging + +# Fetched all remote updates +git remote update + +# Update local develop branch with remote copy +git pull origin staging + +# Created a release branch that tracks origin/develop +git checkout -b release/$VERSION origin/staging + +# Pushed release branch to remote repository +git push origin release/$VERSION + +# Opened a "pull request" in GitHub for team to verify the release + +# Checkout into master branch +git checkout master + +# Updated local master branch with remote copy +git pull origin master + +# Merged release branch into master branch +git merge release/$VERSION + +# Tagged the release point by creating a new tag +git tag -a $VERSION -m "Create release tag $VERSION" + +# Pushed master branch to remote repository +git push origin master + +# Pushed the tags to remote repository +git push origin --tags + +# Checkout into develop branch +git checkout staging + +# Merged release branch into develop branch +git merge release/$VERSION + +# Pushed develop branch to remote repository +git push origin staging + +# Removed release branch from the local repository +git branch -D release/$VERSION + +# Removed release branch from the remote repository +git push origin :release/$VERSION +``` diff --git a/kubernetes/postgres-statefulset.yaml b/kubernetes/postgres-statefulset.yaml index 1e000d7b..99c8219d 100644 --- a/kubernetes/postgres-statefulset.yaml +++ b/kubernetes/postgres-statefulset.yaml @@ -34,7 +34,7 @@ spec: name: postgres spec: accessModes: ["ReadWriteOnce"] - storageClassName: jungle + storageClassName: eosrate-storage resources: requests: storage: 100Gi diff --git a/kubernetes/storageclass.yml b/kubernetes/storageclass.yml new file mode 100644 index 00000000..4393c06f --- /dev/null +++ b/kubernetes/storageclass.yml @@ -0,0 +1,14 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: eosrate-storage + labels: + version: ${VERSION} +parameters: + fromBackup: "" + numberOfReplicas: "1" + staleReplicaTimeout: "2880" +provisioner: driver.longhorn.io +reclaimPolicy: Retain +volumeBindingMode: WaitForFirstConsumer +allowVolumeExpansion: true diff --git a/readme.md b/readme.md index a933da0c..d78c5652 100644 --- a/readme.md +++ b/readme.md @@ -40,6 +40,7 @@ Each EOS account can submit their rating for each BP as many times as they like - [Development Setup](#development-setup) - [Install Global Dependencies](#install-global-dependencies) - [Run EOS Rate on your Computer](#run-eos-rate-on-your-computer) + - [Create a new release](docs/create-a-release.md) - [Design Assets](#design-assets) - [Contributing](#contributing) - [About EOS Costa Rica](#about-eos-costa-rica) diff --git a/services/frontend/.env b/services/frontend/.env index 72c7b6f7..b12785e2 100644 --- a/services/frontend/.env +++ b/services/frontend/.env @@ -1,4 +1,8 @@ +REACT_APP_GRAPHQL_HTTP_URL=https://graphql-jungle.eosrate.io/v1alpha1/graphql REACT_APP_API_URL=https://jungle.eosio.cr -REACT_APP_GRAPHQL_HTTP_URL=http://localhost:8088/v1alpha1/graphql -REACT_APP_GRAPHQL_WS_URL=ws://localhost:8088/v1alpha1/graphql -REACT_APP_EOS_API_URL=https://jungle.eosio.cr \ No newline at end of file +REACT_APP_GRAPHQL_WS_URL=wss://graphql-jungle.eosrate.io/v1alpha1/graphql +REACT_APP_EOS_API_URL=https://jungle.eosio.cr +REACT_APP_EOS_API_HOST=jungle.eosio.cr +REACT_APP_EOS_API_PORT=443 +REACT_APP_EOS_API_PROTOCOL=https +REACT_APP_EOS_CHAIN_ID=2a02a0053e5a8cf73a56ba0fda11e4d92e0238a4a2aa74fccf46d5a910746840 diff --git a/services/frontend/.env.staging b/services/frontend/.env.staging index 068128c5..b12785e2 100644 --- a/services/frontend/.env.staging +++ b/services/frontend/.env.staging @@ -5,4 +5,4 @@ REACT_APP_EOS_API_URL=https://jungle.eosio.cr REACT_APP_EOS_API_HOST=jungle.eosio.cr REACT_APP_EOS_API_PORT=443 REACT_APP_EOS_API_PROTOCOL=https -REACT_APP_EOS_CHAIN_ID=e70aaab8997e1dfce58fbfac80cbbb8fecec7b99cf982a9444273cbc64c41473 +REACT_APP_EOS_CHAIN_ID=2a02a0053e5a8cf73a56ba0fda11e4d92e0238a4a2aa74fccf46d5a910746840 diff --git a/services/frontend/.eslintrc b/services/frontend/.eslintrc index 16d62b64..ec671bfa 100644 --- a/services/frontend/.eslintrc +++ b/services/frontend/.eslintrc @@ -1,11 +1,17 @@ { + "parser": "babel-eslint", "extends": [ "standard", - "standard-react" + "standard-react", + "plugin:react/recommended", + "plugin:prettier/recommended" ], - "parser": "babel-eslint", "env": { "browser": true, "node": true + }, + "rules": { + "multiline-ternary": "off", + "space-before-function-paren": "off" } } diff --git a/services/frontend/package.json b/services/frontend/package.json index 936d45b0..cb5159e7 100644 --- a/services/frontend/package.json +++ b/services/frontend/package.json @@ -21,30 +21,30 @@ "now-start": "serve --single ./build" }, "dependencies": { - "@eoscostarica/eoscr-components": "^1.0.5", + "@eoscostarica/eoscr-components": "^3.1.0", "@material-ui/core": "^4.9.12", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.51", "@reach/router": "^1.3.3", - "@rematch/core": "^1.4.0", - "@rematch/persist": "^1.1.6", + "@rematch/core": "^2.0.0", + "@rematch/persist": "^2.0.0", "apollo-cache-inmemory": "^1.6.5", "apollo-client": "^2.6.8", "apollo-link": "^1.2.13", "apollo-link-http": "^1.5.16", "apollo-link-ws": "^1.0.19", "apollo-utilities": "^1.3.3", - "babel-loader": "~8.0.6", + "babel-loader": "~8.1.0", "chart.js": "^2.9.3", "classnames": "^2.2.6", "env-cmd": "^10.1.0", - "eosjs": "^20.0.3", + "eosjs": "^21.0.3", "filter-objects": "^2.1.1", - "graphql": "^14.6.0", + "graphql": "^15.5.0", "graphql-tag": "^2.10.3", - "i18n-iso-countries": "^5.0.0", + "i18n-iso-countries": "^6.5.0", "i18next": "^19.3.2", - "i18next-browser-languagedetector": "^4.0.2", + "i18next-browser-languagedetector": "^6.0.1", "lodash.get": "^4.4.2", "lodash.isempty": "^4.4.0", "lodash.set": "^4.3.2", @@ -53,13 +53,13 @@ "qs": "^6.9.1", "random-color-rgb": "^1.1.1", "rc-slider": "^9.2.2", - "react": "^16.13.0", + "react": "17.0.1", "react-apollo": "^3.1.3", - "react-autosuggest": "^9.4.3", + "react-autosuggest": "^10.1.0", "react-chartjs-2": "^2.9.0", - "react-dom": "16.13.0", - "react-ga": "^2.7.0", - "react-helmet": "^5.2.1", + "react-dom": "17.0.1", + "react-ga": "^3.3.0", + "react-helmet": "^6.1.0", "react-highlighter": "^0.4.3", "react-i18next": "^11.3.3", "react-placeholder": "^4.0.0", @@ -69,27 +69,35 @@ "serve": "^11.3.0", "subscriptions-transport-ws": "^0.9.16", "text-encoding": "^0.7.0", - "ual-anchor": "^0.5.0", + "ual-anchor": "^1.0.0", "ual-ledger": "^0.3.0", "ual-lynx": "^0.4.0", "ual-meetone": "^0.1.1", "ual-reactjs-renderer": "^0.3.1", "ual-scatter": "^0.3.0", "ual-token-pocket": "^0.3.0", - "yup": "0.28.3" + "yup": "0.32.9" }, "devDependencies": { - "eslint-config-standard": "^14.1.0", - "eslint-config-standard-react": "^9.2.0", + "eslint-config-prettier": "^8.0.0", + "eslint-config-standard": "^16.0.2", + "eslint-config-standard-react": "^11.0.1", + "eslint-plugin-prettier": "^3.3.1", "eslint-plugin-node": "^11.0.0", - "eslint-plugin-standard": "^4.0.1", - "husky": "^4.2.3", + "eslint-plugin-standard": "^5.0.0", + "husky": "^5.1.1", "lint-staged": "^10.0.8", - "prettier": "^1.19.1", - "pretty-quick": "^2.0.1", - "react-scripts": "3.4.0", - "snazzy": "^8.0.0", - "standard": "^14.3.1" + "prettier": "^2.2.1", + "pretty-quick": "^3.1.0", + "react-scripts": "4.0.2", + "snazzy": "^9.0.0", + "standard": "^16.0.3" + }, + "resolutions": { + "react": "17.0.1", + "react-dom": "17.0.1", + "**/react": "17.0.1", + "**/react-dom": "17.0.1" }, "lint-staged": { "*.js": [ diff --git a/services/frontend/src/app.js b/services/frontend/src/app.js index f16eeb6b..0fb830b4 100644 --- a/services/frontend/src/app.js +++ b/services/frontend/src/app.js @@ -1,6 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' -import { connect } from 'react-redux' +import { useSelector } from 'react-redux' import { Router } from '@reach/router' import Spinner from 'components/spinner' @@ -8,31 +8,26 @@ import Layout from 'components/layout' import NotFound from 'routes/not-found' import routes from 'routes' -const App = ({ isContentLoading, ual }) => ( - <> - - - - {routes.map(({ path, Component }) => ( - - ))} - - - - -) +const App = ({ ual }) => { + const { isContentLoading } = useSelector((state) => state.isLoading) -App.propTypes = { - isContentLoading: PropTypes.bool, - ual: PropTypes.object + return ( + <> + + + + {(routes || []).map(({ path, Component }) => ( + + ))} + + + + + ) } -App.defaultProps = { - isContentLoading: false +App.propTypes = { + ual: PropTypes.object } -const mapStateToProps = state => ({ - isContentLoading: state.isLoading.isContentLoading -}) - -export default connect(mapStateToProps, null)(App) +export default App diff --git a/services/frontend/src/components/app-bar.js b/services/frontend/src/components/app-bar/index.js similarity index 64% rename from services/frontend/src/components/app-bar.js rename to services/frontend/src/components/app-bar/index.js index 528ccf27..41aa0f33 100644 --- a/services/frontend/src/components/app-bar.js +++ b/services/frontend/src/components/app-bar/index.js @@ -2,7 +2,7 @@ import React, { useEffect } from 'react' import PropTypes from 'prop-types' import { useTranslation } from 'react-i18next' import AccountCircleIcon from '@material-ui/icons/AccountCircle' -import { connect } from 'react-redux' +import { useDispatch } from 'react-redux' import AppBar from '@material-ui/core/AppBar' import CircularProgress from '@material-ui/core/CircularProgress' import FingerprintIcon from '@material-ui/icons/Fingerprint' @@ -20,105 +20,34 @@ import InputAutocomplete from 'components/input-autocomplete' import MobileSearch from 'components/mobile-search' import LanguageSelect from 'components/language-select' -const useStyles = makeStyles(theme => ({ - root: { - flexGrow: 1 - }, - link: { - color: 'white', - textDecoration: 'none' - }, - linkHover: { - '&:hover': { - backgroundColor: 'rgba(255, 255, 255, 0.1)', - borderRadius: 10 - } - }, - grow: { - flexGrow: 1 - }, - title: { - width: 140, - [theme.breakpoints.up('sm')]: { - display: 'block', - width: 210 - } - }, - menuButton: { - marginLeft: -18, - [theme.breakpoints.up('sm')]: { - marginRight: 10 - } - }, - search: { - position: 'relative', - flexGrow: 1, - borderRadius: theme.shape.borderRadius, - backgroundColor: fade(theme.palette.common.white, 0.15), - '&:hover': { - backgroundColor: fade(theme.palette.common.white, 0.25) - }, - marginRight: theme.spacing(2), - marginLeft: 0, - width: '100%', - display: 'none', - [theme.breakpoints.up('md')]: { - display: 'block', - width: 'auto' - } - }, - mobileSearch: { - [theme.breakpoints.up('md')]: { - display: 'none' - } - }, - inputRoot: { - color: 'inherit', - width: '100%' - }, - inputInput: { - paddingTop: theme.spacing(1), - paddingRight: theme.spacing(1), - paddingBottom: theme.spacing(1), - paddingLeft: theme.spacing(10), - transition: theme.transitions.create('width'), - width: '100%', - [theme.breakpoints.up('md')]: { - width: 200 - } - }, - sessionText: { - marginLeft: 5, - display: 'none', - [theme.breakpoints.up('sm')]: { - display: 'inline' - } - } -})) +import styles from './styles' + +const useStyles = makeStyles((theme) => styles(theme, fade)) const MainTopBar = ({ isSearchOpen, handleDrawerToggle, handleSearchDialogOpen, handleSearchDialogClose, - ual, - getUserChainData, - setUser + ual }) => { const classes = useStyles() + const dispatch = useDispatch() const { t } = useTranslation('translations') + const handleSetUser = () => dispatch.user.removeBlockProducersVotedByUser() + useEffect(() => { const getData = async () => { if (ual.activeUser) { - await getUserChainData({ accountName: ual.activeUser.accountName }) + await dispatch.user.getUserChainData({ ual }) } else { - setUser() + handleSetUser() } } getData() - }, [ual.loading]) + }, []) return ( @@ -162,7 +91,7 @@ const MainTopBar = ({ color='inherit' onClick={() => { ual.logout() - setUser() + handleSetUser() }} > @@ -198,14 +127,7 @@ MainTopBar.propTypes = { handleSearchDialogOpen: PropTypes.func, handleSearchDialogClose: PropTypes.func, isSearchOpen: PropTypes.bool, - ual: PropTypes.object, - getUserChainData: PropTypes.func, - setUser: PropTypes.func + ual: PropTypes.object } -const mapDispatchToProps = ({ user }) => ({ - getUserChainData: user.getUserChainData, - setUser: user.removeBlockProducersVotedByUser -}) - -export default connect(null, mapDispatchToProps)(MainTopBar) +export default MainTopBar diff --git a/services/frontend/src/components/app-bar/styles.js b/services/frontend/src/components/app-bar/styles.js new file mode 100644 index 00000000..f9c47643 --- /dev/null +++ b/services/frontend/src/components/app-bar/styles.js @@ -0,0 +1,75 @@ +export default (theme, fade) => ({ + root: { + flexGrow: 1 + }, + link: { + color: 'white', + textDecoration: 'none' + }, + linkHover: { + '&:hover': { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + borderRadius: 10 + } + }, + grow: { + flexGrow: 1 + }, + title: { + width: 140, + [theme.breakpoints.up('sm')]: { + display: 'block', + width: 210 + } + }, + menuButton: { + marginLeft: -18, + [theme.breakpoints.up('sm')]: { + marginRight: 10 + } + }, + search: { + position: 'relative', + flexGrow: 1, + borderRadius: theme.shape.borderRadius, + backgroundColor: fade(theme.palette.common.white, 0.15), + '&:hover': { + backgroundColor: fade(theme.palette.common.white, 0.25) + }, + marginRight: theme.spacing(2), + marginLeft: 0, + width: '100%', + display: 'none', + [theme.breakpoints.up('md')]: { + display: 'block', + width: 'auto' + } + }, + mobileSearch: { + [theme.breakpoints.up('md')]: { + display: 'none' + } + }, + inputRoot: { + color: 'inherit', + width: '100%' + }, + inputInput: { + paddingTop: theme.spacing(1), + paddingRight: theme.spacing(1), + paddingBottom: theme.spacing(1), + paddingLeft: theme.spacing(10), + transition: theme.transitions.create('width'), + width: '100%', + [theme.breakpoints.up('md')]: { + width: 200 + } + }, + sessionText: { + marginLeft: 5, + display: 'none', + [theme.breakpoints.up('sm')]: { + display: 'inline' + } + } +}) diff --git a/services/frontend/src/components/bottom-navbar.js b/services/frontend/src/components/bottom-navbar/index.js similarity index 90% rename from services/frontend/src/components/bottom-navbar.js rename to services/frontend/src/components/bottom-navbar/index.js index ec0fb665..08919a63 100644 --- a/services/frontend/src/components/bottom-navbar.js +++ b/services/frontend/src/components/bottom-navbar/index.js @@ -8,15 +8,9 @@ import { useTranslation } from 'react-i18next' import SettingsIcon from '@material-ui/icons/Settings' import { navigate } from '@reach/router' -const useStyles = makeStyles(() => ({ - root: { - width: '100%', - background: '#fafafa', - position: 'fixed', - bottom: 0, - left: 0 - } -})) +import styles from './styles' + +const useStyles = makeStyles(styles) const SimpleBottomNavigation = () => { const [value] = useState(0) diff --git a/services/frontend/src/components/bottom-navbar/styles.js b/services/frontend/src/components/bottom-navbar/styles.js new file mode 100644 index 00000000..6230aaef --- /dev/null +++ b/services/frontend/src/components/bottom-navbar/styles.js @@ -0,0 +1,9 @@ +export default () => ({ + root: { + width: '100%', + background: '#fafafa', + position: 'fixed', + bottom: 0, + left: 0 + } +}) diff --git a/services/frontend/src/components/bp-chip-avatar.js b/services/frontend/src/components/bp-chip-avatar/index.js similarity index 64% rename from services/frontend/src/components/bp-chip-avatar.js rename to services/frontend/src/components/bp-chip-avatar/index.js index f3055f74..87b7abf6 100644 --- a/services/frontend/src/components/bp-chip-avatar.js +++ b/services/frontend/src/components/bp-chip-avatar/index.js @@ -1,24 +1,22 @@ -import React from 'react' +import React, { memo } from 'react' import PropTypes from 'prop-types' import Chip from '@material-ui/core/Chip' import Avatar from '@material-ui/core/Avatar' import _get from 'lodash.get' import { makeStyles } from '@material-ui/core/styles' -const useStyles = makeStyles({ - root: { - margin: 8, - color: 'white', - backgroundColor: props => props.color - } -}) +import styles from './styles' -const ProducerChipAvatar = ({ data, classNames, onHandleRemove, imageURL, defaultName }) => { - const backgroundColor = _get( - data, - 'data.pointBackgroundColor', - '#597a81' - ) +const useStyles = makeStyles(styles) + +const ProducerChipAvatar = ({ + data, + classNames, + onHandleRemove, + imageURL, + defaultName +}) => { + const backgroundColor = _get(data, 'data.pointBackgroundColor', '#597a81') const classes = useStyles({ color: backgroundColor }) return ( @@ -29,7 +27,7 @@ const ProducerChipAvatar = ({ data, classNames, onHandleRemove, imageURL, defaul {!imageURL ? defaultName : } } - onDelete={onHandleRemove(data.owner)} + onDelete={() => onHandleRemove(data.owner)} label={data.owner} /> ) @@ -43,4 +41,4 @@ ProducerChipAvatar.propTypes = { defaultName: PropTypes.string } -export default ProducerChipAvatar +export default memo(ProducerChipAvatar) diff --git a/services/frontend/src/components/bp-chip-avatar/styles.js b/services/frontend/src/components/bp-chip-avatar/styles.js new file mode 100644 index 00000000..2b03b89d --- /dev/null +++ b/services/frontend/src/components/bp-chip-avatar/styles.js @@ -0,0 +1,7 @@ +export default (props) => ({ + root: { + margin: 8, + color: 'white', + backgroundColor: (props) => props.color + } +}) diff --git a/services/frontend/src/components/button.js b/services/frontend/src/components/button/index.js similarity index 84% rename from services/frontend/src/components/button.js rename to services/frontend/src/components/button/index.js index 7982047a..18c2dc7f 100644 --- a/services/frontend/src/components/button.js +++ b/services/frontend/src/components/button/index.js @@ -1,7 +1,7 @@ import React from 'react' import { Button as MaterialButton } from '@material-ui/core' -import FormControl from './formControl' +import FormControl from '../formControl' const Button = ({ ...props }) => ( diff --git a/services/frontend/src/components/card.js b/services/frontend/src/components/card/index.js similarity index 79% rename from services/frontend/src/components/card.js rename to services/frontend/src/components/card/index.js index 960be3f8..b7496117 100644 --- a/services/frontend/src/components/card.js +++ b/services/frontend/src/components/card/index.js @@ -18,87 +18,9 @@ import { Link } from '@reach/router' import Radar from 'components/radar' -const useStyles = makeStyles(theme => ({ - card: { - backgroundColor: theme.palette.surface.dark - }, - title: { - textDecoration: 'none', - color: theme.palette.primary.main, - padding: theme.spacing(2, 2, 1, 2) - }, - unsafeChip: { - marginLeft: theme.spacing(2), - backgroundColor: '#E91E63', - color: 'white' - }, - unsafeAvatar: { - backgroundColor: '#AD1457', - color: 'white' - }, - actions: { - display: 'flex', - justifyContent: 'space-between' - }, - radar: { - background: theme.palette.surface.light, - paddingBottom: theme.spacing(2) - }, - avatar: { - backgroundColor: theme.palette.surface.main - }, - helpIcon: { - width: '90%', - height: '90%' - }, - btnRate: { - backgroundColor: theme.palette.secondary.main, - color: '#ffffff', - '&:hover': { - backgroundColor: theme.palette.secondary.dark - } - }, - warningBox: { - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between' - }, - warningIcon: { - color: 'rgb(255, 152, 0)' - }, - marginRightElem: { - marginRight: 5 - }, - blockIcons: { - display: 'flex', - justifyContent: 'flex-end', - alignItems: 'center', - width: '100%', - height: 25, - padding: theme.spacing(0, 1) - }, - showOnlySm: { - display: 'flex', +import styles from './styles' - [theme.breakpoints.up('sm')]: { - display: 'none' - } - }, - showOnlyLg: { - display: 'flex', - - [theme.breakpoints.down('sm')]: { - display: 'none' - } - }, - mobileBox: { - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - padding: theme.spacing(0, 2, 1, 2), - color: theme.palette.primary.main - } -})) +const useStyles = makeStyles(styles) const TooltipWrapper = ({ open, onHandleTooltip, isClickable, t, classes }) => { if (isClickable) { @@ -137,7 +59,7 @@ const CardData = ({ const theme = useTheme() const isMobile = useMediaQuery(theme.breakpoints.only('xs')) - const handleTooltip = e => { + const handleTooltip = (e) => { setOpen(!open) e.preventDefault() } @@ -249,6 +171,7 @@ const CardData = ({ {useRateButton && (