diff --git a/.firebase/hosting.YnVpbGQ.cache b/.firebase/hosting.YnVpbGQ.cache new file mode 100644 index 00000000..9a4e46df --- /dev/null +++ b/.firebase/hosting.YnVpbGQ.cache @@ -0,0 +1,61 @@ +ceramic_user_avatar.svg,1707844292790,d348ec129309f21fa70c7c86fc2917671b1fd53da69afbe0b93ce42c10806892 +buzzWidget.js,1707844292790,6d99823ee80e5af0fae622e877107d10c542cc37310794ec09d3e341631f1665 +asset-manifest.json,1707844326370,3bfc0e70d0dae08595256e6c8737f20c325d64c703f5b3d931ef672d5457ee02 +d.buzz.ico,1707844292791,4c07d0775b946788583dd29d3eb1ab0d580e6aca9323368e7f9bad0ade337c1c +dbuzz-text-logo-white.svg,1707844292792,73b44e660dbc31914dbbbf177c8042ca4c290e6700bc2f598eabe84b33b4d762 +dbuzz-text-logo.svg,1707844292792,616d09cb0170ce6d13004be16b3b2d5b503e0b1e577d7a927f1b87946e6e9ade +dbuzz-logo-icon.svg,1707844292791,f070bdc376a31c9900a74d0c4e5b920d21b42e0d4d45291834dcc31abec61b70 +dbuzz_full.svg,1707844292793,8a4c746b93c81589a4029b809b7482dfa7608cfdfab4de70dcd268d2a9e73ffb +element.svg,1707844292794,e07788aa3b8c6309efb7ce067eca1e2600c35e66ed650de2dc90ac01862e0ab8 +dbuzz-icon.svg,1707844292791,9664e3cdd42d7defc0b96631a6ced055680909e621f563bc0d5903188eb44158 +discord.svg,1707844292794,217ebc9c50726177603af65c07bc3640e7d17d83ed4a5434afaa1010a4afcf3a +index.html,1707844326360,c986b33f2be4d0d8d9e07bdb6cd05f61d3799ce925d8bf1c1a7ae50cf5a34339 +fontface.css,1707844292795,e468119d997e2fb5ee57ce41efd686b1037dab6a22a34046d11977571193fc28 +netlify.toml,1707844292800,931c84c6e2915eb73f4a3494e02883b6ad8f3bc952b199b8eb55ac154f3bf360 +manifest.json,1707844292800,db8d8b5b8c1822df6b1b1c3e83852d65b4d5deff31b1e64c5084726c8f389c66 +dbuzz.svg,1707844292792,a29fe99c9d2327842b04bfe1f93dedbb91a97eefdf64e95696f97231340b9d38 +dbuzz_icon_white.svg,1707844292793,0fd6cba9b0978e8725436086471432cc548e3f2f9b93ffc671ea508c92a24051 +service-worker.js.LICENSE.txt,1707844326360,5bcf7ad56e14554ea655f13f3a79ddf6fb23a153bf6028e5bfea9a7a6cd9154d +favicon.ico,1707844292794,4c07d0775b946788583dd29d3eb1ab0d580e6aca9323368e7f9bad0ade337c1c +robots.txt,1707844292801,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2 +noimage.jpg,1707844292800,ae865d28231c08e1e88a654313e303cc9caa419131566dec93d53cc699efa765 +x-white.svg,1707844292802,6cc1873258f3931e7697193054c1e304212593e2f1db53cca5fd304fb151796a +twitter-icon.svg,1707844292801,d7764f8223d8d18ead68754a441192cf45b39128f62e19e4da6d6a2bbb668a10 +twitter.svg,1707844292801,33ca52ddce7273a197427cf8f24c888c64b2fdb5599b49d9a6e8b901f6d24355 +x.svg,1707844292803,68cd8d35bac74ece53e2fa1dea37d4d696f1ac50d1d301d9245af7f4d1f7cc44 +noimage.svg,1707844292801,ff2460db3bed9667a9c5a2855523f3ea4abc2d8dc142127322c537a06ae1c320 +images/d.buzz-icon-128.png,1707844292798,2cd5255e5e2f57a3279d674090c9cbfa7b9c4a7a3d5aed3e2f495d159090b894 +images/d.buzz-icon-192.png,1707844292798,ce2c69c53fb7f2199e3352cf4c5a123648a8a7d51ab790d63a58eacecf7b3b70 +images/d.buzz-icon-152.png,1707844292798,96cca221ed2f0a6a9d90c4187fbc6a9ac2f7311abd54377874398dd4c237ad4f +static/js/205.561d72fd.chunk.js,1707844326363,315556d3cc7d18fe01974f0677ecefa49a6be3f7afc4ce9bcd87f70ae0fa748b +images/d.buzz-icon-196.png,1707844292799,3a67a6b2d1351aa1139adf0c3bb446277bef03329e32a529bde736528108f597 +static/js/269.4a9c85cf.chunk.js,1707844326362,31a002d90df70d6b6ec6afab14b8defb96a474e91287d19405e1c2f2a9ea4ca3 +images/d.buzz-icon-144.png,1707844292798,a29b1cbfbd84d01eb044a18b093fc37d105baf3e78195d9d488f60504238dea7 +static/js/352.004173fc.chunk.js,1707844326362,2a1123352c1aea5ebb3eaecde00eb9d2dcee9284d3d60af1cfa2135b6d2f3f10 +static/js/504.0af4f7de.chunk.js,1707844326361,004df989121d64ff3fcf101fef2814a1813e4602a8ce27987f5ecc742ea00eb6 +static/js/568.0b800be6.chunk.js,1707844326362,b7583a40723d86dc3b218a2562403e49621a333a654fbefe0ce9afc28ec47ad9 +static/js/564.f5f48581.chunk.js,1707844326362,9a1f1d57fe3ef9255e583bd638093835c14d44d652fc0feefa877a8bc2500f9e +static/js/667.b693845f.chunk.js,1707844326362,eec9718f1e41cadb39b3413336c2daf25c0515b64c5d62f3d4b49c51238581ed +static/js/587.e76283f6.chunk.js,1707844326363,539d4f0872bf38d3351b21bc6a9036eee970e20ddc333c19eaa295e0b497900d +static/js/854.74433dab.chunk.js.LICENSE.txt,1707844326363,9e8d4e5e0b43a9d46b1dab8e86eeb2d645fc763809f71e15ee35fa19019a50c6 +images/d.buzz-icon-256.png,1707844292799,1ad7659cff9726fc67f2f1315bc374eb513ff1525157ae02a97d5ceb08203611 +static/js/774.fae1f24c.chunk.js,1707844326361,e2dacb85daaab0b673eea96d69defd649632068441949fe4a3ec110172ede54c +fonts/Segoe-Bold.woff,1707844292796,e48047c546a4176c10e180a2ed420cf6488481751ba0ee7992506ab0ebf21581 +favicon.ico.png,1707844292795,f022fa3ca8a3a3b7f4a96e8d7f8da3e50e2f7bc32873c97edbc4b49564022f6e +fonts/Segoe-Bold.ttf,1707844292796,def74de77139ce338031f0865dae7d3c03ba5b7f3a9075cdddabb720a04b32d2 +fonts/Segoe.ttf,1707844292797,e3cb2f4b6df27ba3e4714a6e302431e8c479b581947144a203c1856b579bd7a0 +service-worker.js,1707844326360,758d286fee98262553840fa1a280ce767ce870876ec9371d406350b6004ae548 +images/d.buzz-icon-512.png,1707844292799,f022fa3ca8a3a3b7f4a96e8d7f8da3e50e2f7bc32873c97edbc4b49564022f6e +static/js/944.8ce914c1.chunk.js,1707844326370,8359e0f194f54717e454659832c622041f514657b9a4586725e7ff390a527819 +static/js/main.0b4960e8.js.LICENSE.txt,1707844326370,96266417a71a08252297b1a52779dae8c848fa623820d7ad44338d9905bad4e3 +static/media/index.cd351d7c31d0d3fccf96.cjs,1707844326362,97136e0823bc8f99173d1fc926421e9f4108c19160f75e2b5ebef3b00722054f +static/media/resolver.8341148696af66384426.cjs,1707844326361,8597260ce5341da37771ba35f62a1211e89a4bbc2add01bf73ff8be49341cf92 +widgets/buzzWidget.js,1707844292802,a0c48438a03a95fee29f3a290397619d4c56f5112861af845367d99e0fe91774 +widgets/buzz_button.html,1707844292802,5dc8ec6e578b9813c86ac8fedfb00c0325dbe53c1dd290b230c06bd3846d72b5 +static/media/axios.ca46f484446a1aabaf0e.cjs,1707844326361,7b83eb92901dcabab0e4204087e1357b3ff88cdbb0b2dcfdfd82c4012af300e7 +static/css/main.d8bbbb1c.css,1707844326362,fbfea4769941c438341d996756bd0c5af3b7ff7143872eb0fb4c9767d05fa6cf +static/media/Segoe-Bold.21ecd6932d8c6e2ae14e.woff,1707844326362,e48047c546a4176c10e180a2ed420cf6488481751ba0ee7992506ab0ebf21581 +static/js/991.a4fb68a9.chunk.js,1707844326361,72e2c471a1015583c6872a274f1dbaa93306b78ce8bc9993576917be92de3ef0 +static/js/854.74433dab.chunk.js,1707844326370,dd43f5f533c743a4cd6dc1c8e4703b820e6a270040ef15b2aeffee8439b38473 +static/js/984.59caac68.chunk.js,1707844326361,4141460ca6a441721aa25ae7a24205de43dc411a9568da6e82eebde7007dba49 +static/js/main.0b4960e8.js,1707844326370,eb715477a6a4333b0ed0adaf45568bea281e2886c137d902f6b0ab37626ee1af diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..cce9d3c0 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "semi": false +} diff --git a/package.json b/package.json index 4cc2ba91..e756faa4 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,9 @@ { "name": "d.buzz-client", "homepage": ".", - "version": "3.45.1", + "version": "3.47.0", "private": true, "dependencies": { - "3id-blockchain-utils": "^1.3.1", "@apollo/client": "^3.7.10", "@ceramicnetwork/http-client": "^2.35.0", "@ceramicstudio/idx": "^0.12.2", @@ -14,12 +13,14 @@ "@material-ui/core": "^4.12.4", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.56", - "@mintrawa/hive-auth-client": "^0.0.8", + "@sentry/react": "^7.102.1", + "@sentry/tracing": "^7.102.1", "@spknetwork/graph-client": "1.2.2", "@stablelib/sha256": "^1.0.1", "@walletconnect/ethereum-provider": "^2.10.0", "@walletconnect/modal": "^2.6.1", "@walletconnect/time": "^1.0.2", + "3id-blockchain-utils": "^1.3.1", "axios": "^1.6.0", "bluebird": "^3.7.2", "bootstrap": "^4.5.0", @@ -43,7 +44,6 @@ "moment": "^2.27.0", "npm-text-parser": "^1.0.8", "process": "^0.11.10", - "qrcode.react": "^1.0.1", "query-string": "^6.13.1", "react": "^16.13.1", "react-app-polyfill": "^2.0.0", @@ -96,6 +96,7 @@ "start": "PORT=2020 GENERATE_SOURCEMAP=false env-cmd -f .env.development react-app-rewired start", "build:dev": "env-cmd -f .env.development react-app-rewired build && chmod +X ./change-version.sh", "build:prod": "env-cmd -f .env.production react-app-rewired build && chmod +X ./change-version.sh", + "build": "react-app-rewired build && chmod +X ./change-version.sh", "test": "react-scripts test", "eject": "react-scripts eject", "lint": "eslint src", @@ -163,4 +164,4 @@ "browser": { "process": "process/browser.js" } -} +} \ No newline at end of file diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 00000000..619e1afb --- /dev/null +++ b/public/_redirects @@ -0,0 +1 @@ +/* /index.html 200 \ No newline at end of file diff --git a/public/index.html b/public/index.html index 67ca9181..f8888e4f 100644 --- a/public/index.html +++ b/public/index.html @@ -35,8 +35,10 @@ - - + + + + @@ -60,4 +62,4 @@ max-width: 1300px; } } - + \ No newline at end of file diff --git a/public/netlify.toml b/public/netlify.toml deleted file mode 100644 index ff1c0508..00000000 --- a/public/netlify.toml +++ /dev/null @@ -1,4 +0,0 @@ -[[redirects]] - from = "/*" - to = "/index.html" - status = 200 \ No newline at end of file diff --git a/public/widgets/buzzWidget.js b/public/widgets/buzzWidget.js index ac139a61..b65fc685 100644 --- a/public/widgets/buzzWidget.js +++ b/public/widgets/buzzWidget.js @@ -179,4 +179,4 @@ __dbuzzwidget.i(); } }; -})); +})); \ No newline at end of file diff --git a/public/widgets/buzz_button.html b/public/widgets/buzz_button.html index d34e8525..f36a327b 100644 --- a/public/widgets/buzz_button.html +++ b/public/widgets/buzz_button.html @@ -26,4 +26,4 @@ const buzzButton=new BuzzButton();buzzButton.i()}) - + \ No newline at end of file diff --git a/src/App.js b/src/App.js index d37d3b4b..024e41d9 100644 --- a/src/App.js +++ b/src/App.js @@ -1,35 +1,32 @@ -import React, { useEffect } from 'react' -import routes from './routes' -import { withRouter } from 'react-router' -import { Init, AuthGuard, ThemeLoader } from 'components' -import { renderRoutes } from 'react-router-config' -import { LastLocationProvider } from 'react-router-last-location' -import { createUseStyles } from 'react-jss' -import { Helmet } from 'react-helmet' -import { redirectOldLinks } from 'services/helper' -import { useLocation } from 'react-router-dom' -const TwitterEmbedAPI = React.lazy(() => import('components/pages/TwitterEmbedAPI')) - -const useStyles = createUseStyles(theme => ({ +import React, { useEffect } from "react" +import routes from "./routes" +import { withRouter } from "react-router" +import { Init, AuthGuard, ThemeLoader } from "components" +import { renderRoutes } from "react-router-config" +import { LastLocationProvider } from "react-router-last-location" +import { createUseStyles } from "react-jss" +import { Helmet } from "react-helmet" +import { redirectOldLinks } from "services/helper" +import { useLocation } from "react-router-dom" +const TwitterEmbedAPI = React.lazy( + () => import("components/pages/TwitterEmbedAPI"), +) +const useStyles = createUseStyles((theme) => ({ wrapper: { - overflow: 'hidden !important', + overflow: "hidden !important", backgroundColor: theme.background.primary, }, })) const AppWrapper = ({ children }) => { const classes = useStyles() - return ( -
- {children} -
- ) + return
{children}
} const App = () => { const { pathname } = useLocation() const twitterEmbedRoutes = pathname.match(/^\/twitterEmbed/) - const currentSiteUrl = window.location.protocol + '//' + window.location.host + const currentSiteUrl = window.location.protocol + "//" + window.location.host useEffect(() => { // redirect old links to the new ones @@ -42,23 +39,29 @@ const App = () => { - + - + - {!twitterEmbedRoutes ? + {!twitterEmbedRoutes ? ( - - {renderRoutes(routes)} - + {renderRoutes(routes)} - : - } + + ) : ( + + )} diff --git a/src/components/common/HelmetGenerator/index.js b/src/components/common/HelmetGenerator/index.js index 8151acbe..90fcb26b 100644 --- a/src/components/common/HelmetGenerator/index.js +++ b/src/components/common/HelmetGenerator/index.js @@ -90,4 +90,4 @@ const HelmetGenerator = (props) => { ) } -export default HelmetGenerator +export default HelmetGenerator \ No newline at end of file diff --git a/src/components/common/ImageCropper/index.js b/src/components/common/ImageCropper/index.js index 821f6ed4..300a361c 100644 --- a/src/components/common/ImageCropper/index.js +++ b/src/components/common/ImageCropper/index.js @@ -62,11 +62,11 @@ const useStyles = createUseStyles(theme => ({ background: '#F5F8FA', marginRight: 8, color: '#000000 !important', - + '&:hover': { background: '#EDF0F2', }, - + '&:active': { background: '#EDF0F2 !important', }, @@ -79,7 +79,7 @@ const useStyles = createUseStyles(theme => ({ }, })) -const ImageCropper = ({ isOpen, onClose, src, onCropComplete }) => { +const ImageCropper = ({ isOpen, onClose, src, onCropComplete, username }) => { const classes = useStyles() const [cropData, setCropData] = useState("#") const cropperRef = useRef(null) @@ -90,7 +90,7 @@ const ImageCropper = ({ isOpen, onClose, src, onCropComplete }) => { const croppedImage = cropper.getCroppedCanvas().toDataURL() setCropData(croppedImage) if (onCropComplete) { - onCropComplete(croppedImage) + onCropComplete(croppedImage ,username) } console.log(cropData) } else { diff --git a/src/components/common/MoreMenu/index.js b/src/components/common/MoreMenu/index.js index 674da46f..f5ecfe88 100644 --- a/src/components/common/MoreMenu/index.js +++ b/src/components/common/MoreMenu/index.js @@ -1,6 +1,13 @@ import React, { useState, useEffect } from 'react' import Menu from '@material-ui/core/Menu' -import MenuItem from '@material-ui/core/MenuItem' +import ListSubheader from '@material-ui/core/ListSubheader' +import List from '@material-ui/core/List' +import ListItem from '@material-ui/core/ListItem' +import ListItemText from '@material-ui/core/ListItemText' +import Collapse from '@material-ui/core/Collapse' +import ExpandLess from '@material-ui/icons/ExpandLess' +import ExpandMore from '@material-ui/icons/ExpandMore' +import Divider from '@material-ui/core/Divider' const MoreMenu = (props) => { const { @@ -24,7 +31,11 @@ const MoreMenu = (props) => { setcallFunc(onClick) setOverflow('scroll') } - + + const handleClickCollapse = (onClick) => () => { + setcallFunc(onClick) + } + useEffect(() => { // console.log(open, themeModal, switchUserModal) setcallFunc(callFunc) @@ -36,7 +47,6 @@ const MoreMenu = (props) => { } // eslint-disable-next-line }, [open, themeModal, switchUserModal]) - return ( { open={open} onClose={onClose} className={className} - transformOrigin={{vertical: 'bottom', horizontal: 'top'}} + transformOrigin={{vertical: 'top', horizontal: 'top'}} > - {items.map(({onClick, text, visible}) => ( - visible && {text} - ))} + + Advanced + } + style={{ + maxHeight: '400px', // Adjust this value based on your needs + overflowY: 'auto', // This makes the list scrollable + }} + > + + {items.map(({onClick, text, visible, subItems, collapse}) => ( + visible && + ( + Object.keys(subItems).length > 0 ? ( + <> + + {/* + + */} + + {collapse ? : } + + + {subItems.map(({subonClick, subtext, subhref}) => ( + subhref === '' ? + + + {/* + + */} + + + + : + + + {/* + + */} + + + + + ))} + + + ) + : + + {/* + + */} + + + ) + ))} + ) } diff --git a/src/components/common/PostActions/index.js b/src/components/common/PostActions/index.js index 5089d99b..a0066ae3 100644 --- a/src/components/common/PostActions/index.js +++ b/src/components/common/PostActions/index.js @@ -7,7 +7,7 @@ import { BurnIcon, ContainedButton, HeartIconRed, - BookmarkIcon, + AddPocketIcon, Spinner, ShareIcon, ClipboardIcon, @@ -45,7 +45,6 @@ import MenuItem from '@material-ui/core/MenuItem' import Menu from '@material-ui/core/Menu' import { invokeTwitterIntent } from 'services/helper' import { checkForCeramicAccount } from 'services/ceramic' -import { hasUpvoteService } from 'services/api' import { getTheme as currentTheme } from 'services/helper' import AddToPocketModal from 'components/modals/AddToPocketModal' @@ -418,66 +417,20 @@ const PostActions = (props) => { setShowSlider(false) setLoading(true) - if (user.useHAS) { - - hasUpvoteService(author, permlink, sliderValue) - - import('@mintrawa/hive-auth-client').then((HiveAuth) => { - HiveAuth.hacMsg.subscribe((m) => { - if (isMobile) { - broadcastNotification('warning', 'Tap on this link to open Hive Keychain app and confirm the transaction.', 600000, `has://sign_req/${m.msg}`) - } else { - broadcastNotification('warning', 'Please open Hive Keychain app on your phone and confirm the transaction.', 600000) - } - if (m.type === 'sign_wait') { - console.log('%c[HAC Sign wait]', 'color: goldenrod', m.msg ? m.msg.uuid : null) - } - if (m.type === 'tx_result') { - console.log('%c[HAC Sign result]', 'color: goldenrod', m.msg ? m.msg : null) - if (m.msg?.status === 'accepted') { - const status = m.msg?.status - console.log(status) - setVote(vote + 1) - setUpvoted(true) - setLoading(false) - broadcastNotification('success', `Succesfully upvoted @${author}/${permlink} at ${sliderValue}%`) - } else if (m.msg?.status === 'error') { - const error = m.msg?.status.error - console.log(error) - setUpvoted(false) - broadcastNotification('error', error) - setLoading(false) - } else if (m.msg?.status === 'rejected') { - const status = m.msg?.status - console.log(status) - setUpvoted(false) - broadcastNotification('error', 'Your HiveAuth upvote transaction is rejected.') - setLoading(false) - } else { - setUpvoted(false) - broadcastNotification('error', 'Unknown error occurred, please try again in some time.') - setLoading(false) - } - } - }) + upvoteRequest(author, permlink, sliderValue) + .then(({success, errorMessage}) => { + if (success) { + setVote(vote + 1) + setUpvoted(true) + setLoading(false) + broadcastNotification('success', `Succesfully upvoted @${author}/${permlink} at ${sliderValue}%`) + } else { + setUpvoted(false) + broadcastNotification('error', errorMessage) + setLoading(false) + } }) - } else { - upvoteRequest(author, permlink, sliderValue) - .then(({success, errorMessage}) => { - if (success) { - setVote(vote + 1) - setUpvoted(true) - setLoading(false) - broadcastNotification('success', `Succesfully upvoted @${author}/${permlink} at ${sliderValue}%`) - } else { - setUpvoted(false) - broadcastNotification('error', errorMessage) - setLoading(false) - } - }) - } - } else { broadcastNotification('error', 'Voting cannot done with 0% Power!') } @@ -636,7 +589,7 @@ const PostActions = (props) => { className={classes.actionWrapperSpace} inlineClass={classNames(classes.inline, classes.icon)} icon={} + disabled={!is_authenticated}>} hideStats={hideStats} disabled={!is_authenticated} onClick={handleAddToPocket} diff --git a/src/components/common/UserDialog/index.js b/src/components/common/UserDialog/index.js index a2f47158..55d40096 100644 --- a/src/components/common/UserDialog/index.js +++ b/src/components/common/UserDialog/index.js @@ -107,8 +107,8 @@ const UserDialog = React.memo((props) => { const [author, setAuthor] = useState('') const { username, is_authenticated } = user const [shouldStayOpen, setShouldStayOpen] = useState(false) - const [hasRecentlyFollowed, setHasRecentlyFollowed] = useState(false) - const [hasRecentlyUnfollowed, setHasRecentlyUnfollowed] = useState(false) + const [setHasRecentlyFollowed] = useState(false) + const [setHasRecentlyUnfollowed] = useState(false) const [followerCount, setFollowerCount] = useState(0) const [followingCount, setFollowingCount] = useState(0) const [isFollowed, setIsFollowed] = useState(false) @@ -214,6 +214,7 @@ const UserDialog = React.memo((props) => { broadcastNotification('success', `Successfully followed @${author}`) setHasRecentlyFollowed(true) setHasRecentlyUnfollowed(false) + setIsFollowed(true) } else { broadcastNotification('error', `Failed following @${author}`) } @@ -228,6 +229,7 @@ const UserDialog = React.memo((props) => { broadcastNotification('success', `Successfully Unfollowed @${author}`) setHasRecentlyFollowed(false) setHasRecentlyUnfollowed(true) + setIsFollowed(false) } else { broadcastNotification('error', `Failed Unfollowing @${author}`) } @@ -276,7 +278,7 @@ const UserDialog = React.memo((props) => {
{is_authenticated && ( - {((!isFollowed && !hasRecentlyFollowed) || hasRecentlyUnfollowed) && (username !== author) && ( + {!isFollowed && (username !== author) && ( { onClick={followUser} /> )} - {((isFollowed || hasRecentlyFollowed) && !hasRecentlyUnfollowed) && (username !== author) && ( + {isFollowed && (username !== author) && ( { + return ( + // type === 'outline' ? + + + + ) +} + +export default AddPocketIcon diff --git a/src/components/elements/Icons/BookmarkIcon/index.js b/src/components/elements/Icons/BookmarkIcon/index.js index 32adaee9..c894b512 100644 --- a/src/components/elements/Icons/BookmarkIcon/index.js +++ b/src/components/elements/Icons/BookmarkIcon/index.js @@ -1,12 +1,16 @@ import React from 'react' -const BookmarkIcon = () => { +const BookmarkIcon = ({ height = 20, top = 0, type }) => { return ( - - - - - + type === 'outline' ? + + + + : + + + + ) } diff --git a/src/components/elements/Icons/CommunityIcon/index.js b/src/components/elements/Icons/CommunityIcon/index.js new file mode 100644 index 00000000..c4560ab8 --- /dev/null +++ b/src/components/elements/Icons/CommunityIcon/index.js @@ -0,0 +1,15 @@ +import React from 'react' + +const CommunityIcon = ({ height = 20, top = 0, type }) => { + return ( + type === 'outline' ? + + + : + + + + ) +} + +export default CommunityIcon diff --git a/src/components/layout/GuardedAppFrame/index.js b/src/components/layout/GuardedAppFrame/index.js index 400b703e..4bb25c94 100644 --- a/src/components/layout/GuardedAppFrame/index.js +++ b/src/components/layout/GuardedAppFrame/index.js @@ -19,9 +19,11 @@ import { bindActionCreators } from 'redux' import { useLastLocation } from 'react-router-last-location' import { useWindowDimensions } from 'services/helper' import { pending } from 'redux-saga-thunk' -import { SideBarLeft, SideBarRight, SearchField, NotificationFilter } from 'components' +import { SideBarLeft, SideBarRight, SearchField } from 'components' import BuzzFormModal from 'components/modals/BuzzFormModal' import { Fab } from '@material-ui/core' +import { getTheme } from 'services/theme' +import { getUserTheme } from 'services/helper' // import BuzzIcon from 'components/elements/Icons/BuzzIcon' import CreateBuzzIcon from 'components/elements/Icons/CreateBuzzIcon' @@ -90,7 +92,10 @@ const floatStyle = { left: 'auto', position: 'fixed', zIndex: 500, - backgroundColor: '#e61c34', + border: `${getTheme(getUserTheme())?.buzzButton?.border}`, + color: `${getTheme(getUserTheme())?.buzzButton?.color}`, + backgroundColor: `${getTheme(getUserTheme())?.background?.primary}`, + fill: `${getTheme(getUserTheme())?.buzzButton?.fill}`, } const GuardedAppFrame = (props) => { @@ -287,7 +292,7 @@ const GuardedAppFrame = (props) => { )} {title !== 'Search' && ({title})} - {title === 'Notifications' && } + {/*{title === 'Notifications' && }*/} {title === 'Search' && (
@@ -326,7 +331,7 @@ const GuardedAppFrame = (props) => { {renderRoutes(route.routes)} {minify && ( - + )} diff --git a/src/components/layout/MobileAppFrame/index.js b/src/components/layout/MobileAppFrame/index.js index 94bfefe0..c20068f6 100644 --- a/src/components/layout/MobileAppFrame/index.js +++ b/src/components/layout/MobileAppFrame/index.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useRef } from 'react' +import React, { useEffect, useState } from 'react' import Navbar from 'react-bootstrap/Navbar' import { useHistory } from 'react-router-dom' import { renderRoutes } from 'react-router-config' @@ -10,6 +10,12 @@ import { useLastLocation } from 'react-router-last-location' import { connect } from 'react-redux' import { Link } from 'react-router-dom' import { pollNotifRequest } from 'store/polling/actions' +import List from '@material-ui/core/List' +import ListItem from '@material-ui/core/ListItem' +import ListItemText from '@material-ui/core/ListItemText' +import Collapse from '@material-ui/core/Collapse' +import ExpandLess from '@material-ui/icons/ExpandLess' +import ExpandMore from '@material-ui/icons/ExpandMore' import { BackArrowIcon, HomeIcon, @@ -21,11 +27,12 @@ import { Avatar, // BuzzIcon, // AccordionArrowDownIcon, - SettingsIcon, + // SettingsIcon, SearchIcon, - PocketIcon, + // PocketIcon, MessageIcon, - WalletIcon, + BookmarkIcon, + // WalletIcon, } from 'components/elements' import { BuzzFormModal, @@ -33,8 +40,6 @@ import { SwitchUserModal, LoginModal, SearchField, - NotificationFilter, - MoreMenu, } from 'components' import { useLocation } from 'react-router-dom' import Fab from '@material-ui/core/Fab' @@ -46,7 +51,7 @@ import { bindActionCreators } from 'redux' import { broadcastNotification, setRefreshRouteStatus } from 'store/interface/actions' import { signoutUserRequest, setIntentBuzz } from 'store/auth/actions' import { searchRequest, clearSearchPosts } from 'store/posts/actions' -import { getUserCustomData, updateUserCustomData } from 'services/database/api' +// import { getUserCustomData, updateUserCustomData } from 'services/database/api' import { pending } from 'redux-saga-thunk' import queryString from 'query-string' @@ -54,14 +59,15 @@ import moment from 'moment' import SettingsModal from 'components/modals/SettingsModal' import CreateBuzzIcon from 'components/elements/Icons/CreateBuzzIcon' // import MoreIcon from 'components/elements/Icons/MoreIcon' -import { checkCeramicLogin, checkForCeramicAccount } from 'services/ceramic' +import { checkCeramicLogin } from 'services/ceramic' import { generateStyles } from 'store/settings/actions' import { getTheme } from 'services/theme' import { getUserTheme } from 'services/helper' import { Image } from 'react-bootstrap' import ProfileIcon from 'components/elements/Icons/ProfileIcon' -import MoonIcon from 'components/elements/Icons/MoonIcon' -import SunIcon from 'components/elements/Icons/SunIcon' +// import MoonIcon from 'components/elements/Icons/MoonIcon' +// import SunIcon from 'components/elements/Icons/SunIcon' +import CommunityIcon from 'components/elements/Icons/CommunityIcon' // import MoreIcon from 'components/elements/Icons/MoreIcon' const useStyles = createUseStyles(theme => ({ @@ -220,6 +226,7 @@ const useStyles = createUseStyles(theme => ({ display: 'flex', }, navBottom: { + paddingBottom: 25, borderTop: theme.border.primary, backgroundColor: theme.background.primary, zIndex: 2, @@ -386,6 +393,21 @@ const useStyles = createUseStyles(theme => ({ }, '& ul':{ background: theme.background.primary, + '& nav':{ + background: theme.background.primary, + '& div':{ + fontSize: 18, + fontWeight: '500 !important', + background: theme.background.primary, + color: theme.font.color, + '& div':{ + '& div':{ + paddingLeft: '5%', + }, + }, + }, + }, + }, '& li': { fontSize: 18, @@ -396,9 +418,69 @@ const useStyles = createUseStyles(theme => ({ '&:hover': { ...theme.context.view, }, + + '#advanced-subheader': { + // Your specific styles for the id="advanced-subheader" + color: 'red', + fontWeight: 'bold', + }, }, - '& a': { - color: theme.font.color, + }, + nestedList: { + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + fontSize: 18, + fontWeight: 'normal !important', + background: theme.background.secondary, + color: theme.font.color, + '&:hover': { + ...theme.context.view, + }, + '& div':{ + '& div':{ + '& div':{ + paddingLeft: '5%', + // paddingTop: '4px', + // paddingBottom: '4px', + '& a':{ + paddingTop: '0px', + paddingBottom: '0px', + paddingLeft: '5%', + '& div':{ + marginTop: '0px', + marginBottom: '0px', + }, + }, + '& div':{ + paddingTop: '4px', + paddingBottom: '4px', + paddingLeft: '5%', + '& div':{ + marginTop: '0px', + marginBottom: '0px', + }, + }, + }, + }, + }, + '& ul':{ + paddingTop: '0px', + paddingBottom: '0px', + '& div':{ + '& div':{ + '& span':{ + fontWeight: 'normal !important', + }, + }, + }, + '& a':{ + '& div':{ + '& span':{ + fontWeight: 'normal !important', + }, + }, + }, }, }, moreButton: { @@ -513,42 +595,42 @@ const MobileAppFrame = (props) => { setRefreshRouteStatus, generateStyles, } = props - const customUserData = JSON.parse(localStorage.getItem('customUserData')) - const THEME = { - LIGHT: 'light', - NIGHT: 'night', - } - const handleClickSetTheme = (mode) => () => { - const data = { ...customUserData, settings: { ...customUserData?.settings, theme: mode } } - localStorage.setItem('customUserData', JSON.stringify({...data})) - const theme = getTheme(mode) - generateStyles(theme) - handleUpdateTheme(mode) - } - - const handleUpdateTheme = (theme) => { - const { username } = user - - getUserCustomData(username) - .then(res => { - const userData = { - ...res[0], - settings: { - ...res[0].settings, - theme: theme, - }, - } - const responseData = { username, userData: [userData] } - - if(res) { - updateUserCustomData(responseData) - .then(() => { - // setLoading(false) - }) - } - }) - - } + // const customUserData = JSON.parse(localStorage.getItem('customUserData')) + // const THEME = { + // LIGHT: 'light', + // NIGHT: 'night', + // } + // const handleClickSetTheme = (mode) => () => { + // const data = { ...customUserData, settings: { ...customUserData?.settings, theme: mode } } + // localStorage.setItem('customUserData', JSON.stringify({...data})) + // const theme = getTheme(mode) + // generateStyles(theme) + // handleUpdateTheme(mode) + // } + + // const handleUpdateTheme = (theme) => { + // const { username } = user + + // getUserCustomData(username) + // .then(res => { + // const userData = { + // ...res[0], + // settings: { + // ...res[0].settings, + // theme: theme, + // }, + // } + // const responseData = { username, userData: [userData] } + + // if(res) { + // updateUserCustomData(responseData) + // .then(() => { + // // setLoading(false) + // }) + // } + // }) + + // } const mode = getUserTheme() @@ -570,12 +652,14 @@ const MobileAppFrame = (props) => { const [disableSearchTips, setDisableSearchTips] = useState(false) const query = params.q === undefined ? '' : params.q const [searchkey, setSearchkey] = useState(query) - const [openMoreMenu, setOpenMoreMenu] = useState(false) + // const [openMoreMenu, setOpenMoreMenu] = useState(false) // const [showSettings, setShowSettings] = useState(false) - const moreMenuRef = useRef() + // const moreMenuRef = useRef() const classes = useStyles() const [activeView, setActiveView] = useState('trending') + const [showProfessionalTools, setShowProfessionalTools] = useState(false) + const [showSettingsAndSupport, setShowSettingsAndSupport] = useState(false) let title = 'Trending' let openedSubProfile = false @@ -600,6 +684,10 @@ const MobileAppFrame = (props) => { title = 'Latest' } + if (pathname.match(/^\/messages/)) { + title = 'Messages' + } + if (!pathname.match(/(\/c\/)/) && pathname.match(/^\/@/)) { title = 'Profile' } @@ -650,16 +738,16 @@ const MobileAppFrame = (props) => { } } - const handleClickOpenMoreMenu = () => { - setOpenMoreMenu(true) - } + // const handleClickOpenMoreMenu = () => { + // setOpenMoreMenu(true) + // } - const redirectToChatPage = () => { - window.location.href = "https://chat.d.buzz" - } + // const handleClickCloseOpenMoreMenu = () => { + // setOpenMoreMenu(false) + // } - const handleClickCloseOpenMoreMenu = () => { - setOpenMoreMenu(false) + const redirectToChatPage = () => { + window.open("https://chat.d.buzz/") } const floatStyle = { @@ -707,12 +795,15 @@ const MobileAppFrame = (props) => { case 'Trending': refreshTrendingRouteData() break + case 'messages': + redirectToChatPage() + break // case 'Latest': // refreshLatestRouteData() // break - case 'More': - handleClickOpenMoreMenu() - break + // case 'More': + // handleClickOpenMoreMenu() + // break default: return } @@ -739,8 +830,8 @@ const MobileAppFrame = (props) => { case '/latest': setActiveView('latest') break - case '/message': - setActiveView('message') + case '/messages': + setActiveView('messages') break case '/notifications': setActiveView('notifications') @@ -758,7 +849,7 @@ const MobileAppFrame = (props) => { return } // eslint-disable-next-line - }, []) + }, [location]) const NavLinks = [ { @@ -808,9 +899,11 @@ const MobileAppFrame = (props) => { // onClick: () => handelClickItem('wallet'), // }, { - name: 'Message', - icon: activeView === 'message' ? : , - onClick:() => redirectToChatPage(), + name: 'Messages', + icon: activeView === 'messages' ? : , + path: `#`, + preventDefault: false, + onClick: () => handelClickItem('messages'), }, // { // name: 'More' , @@ -841,7 +934,7 @@ const MobileAppFrame = (props) => { name: 'Message', icon: activeView === 'message' ? : , path: `/message`, - onClick: () => handelClickItem('message'), + onClick:() => handelClickItem('messages'), }, // { // name: 'Trending', @@ -914,10 +1007,10 @@ const MobileAppFrame = (props) => { setOpenSwitchModal(true) } - const showSettingsModal = () => { - handleClickCloseOpenMoreMenu() - setOpenSettingsModal(true) - } + // const showSettingsModal = () => { + // handleClickCloseOpenMoreMenu() + // setOpenSettingsModal(true) + // } const onHideSwitchModal = () => { setOpenSwitchModal(false) @@ -940,6 +1033,23 @@ const MobileAppFrame = (props) => { window.open('https://discord.gg/kCZGPs7', '_blank') } + // const handelClickWallet = () => { + // history.push(`/@${username}/wallet/balances`) + // handelClickItem('wallet') + // } + + const toggleProfessionTools = () => { + setShowProfessionalTools(!showProfessionalTools) + } + + const toggleSettingsAndSupport = () => { + setShowSettingsAndSupport(!showSettingsAndSupport) + } + + const showComingSoon = () => { + broadcastNotification('success', `Coming soon`) + } + const { stats, metadata } = profile || '' const {profile: profileMeta} = metadata || '' const {name:userName} = profileMeta || '' @@ -965,7 +1075,7 @@ const MobileAppFrame = (props) => { const NavLinkWrapper = ({ item, active }) => { return (
- + { } } - const showNotificationForMessage = () => { - broadcastNotification('success', 'Coming soon') - } // eslint-disable-next-line useEffect(() => { if (is_authenticated) { @@ -1058,6 +1165,11 @@ const MobileAppFrame = (props) => { // eslint-disable-next-line const [showSideBarNavigation, setShowSideBarNavigation] = useState(false) + const handleRedirectToWallet = () => { + setShowSideBarNavigation(false) + history.push(`/@${username}/wallet`) + } + return ( @@ -1078,18 +1190,18 @@ const MobileAppFrame = (props) => {
- {userName || username} + {userName || username} - @{username} + @{username}
-
{following} Following
-
{followers} Followers
+
{following} Following
+
{followers} Followers
@@ -1098,7 +1210,7 @@ const MobileAppFrame = (props) => { setShowSideBarNavigation(false)} className={classNames(classes.marginTop8,classes.displayFlex,classes.positionRelative)}>
-
+
{/*
@@ -1111,7 +1223,7 @@ const MobileAppFrame = (props) => {
*/}
-
history.push(`/@${username}`)} className={classNames((mode === 'night' || mode === 'gray')?'text-white':'text-black',classes.width100,classes.lineHeight24,classes.fontsize20,classes.fontWeight700,classes.displayFlex,classes.positionRelative,classes.justifyContentStart, classes.alignItemsCenter)}>

Profile

+
history.push(`/@${username}`)} className={classNames((mode === 'night' || mode === 'gray')?'text-white':'text-black',classes.width100,classes.lineHeight24,classes.fontsize20,classes.displayFlex,classes.positionRelative,classes.justifyContentStart, classes.alignItemsCenter)}>

Profile

@@ -1120,7 +1232,7 @@ const MobileAppFrame = (props) => {
- + {/*
{
*/}
-
Pockets
+
Bookmarks
- setShowSideBarNavigation(false)} className={classNames(classes.displayFlex,classes.positionRelative)}> +
showComingSoon()} className={classNames(classes.displayFlex,classes.positionRelative)}>
- + {/*
- + - + -
*/} +
*/}
-
Wallet
+
Communities
- -
showNotificationForMessage()} className={classNames(classes.displayFlex,classes.positionRelative)}> +
+
+
+
+
+
+ + + + {showProfessionalTools ? : } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {showSettingsAndSupport ? : } + + + + + + + + + + + + + + + + + + + + + + +
+ {/* setShowSideBarNavigation(false)} className={classNames(classes.displayFlex,classes.positionRelative)}>
- - {/*
- + +
+ - + -
*/} +
-
Message
+
Wallet
-
-
-
-
-
-
setOpenSettingsModal(true)} className={classNames(classes.displayFlex,classes.positionRelative)}> + */} + + {/*
setOpenSettingsModal(true)} className={classNames(classes.displayFlex,classes.positionRelative)}>
@@ -1198,8 +1370,8 @@ const MobileAppFrame = (props) => {
Settings
-
-
+
*/} + {/*
@@ -1213,7 +1385,7 @@ const MobileAppFrame = (props) => {
{(mode === 'night' || mode === 'gray')? 'Dark':'Light'}
-
+
*/} {/*
@@ -1348,7 +1520,7 @@ const MobileAppFrame = (props) => { onClick={handleClearNotification} /> )} - {title === 'Notifications' && } + {/*{title === 'Notifications' && }*/} {title === 'Search' && ( @@ -1377,7 +1549,7 @@ const MobileAppFrame = (props) => { )} -
+
{renderRoutes(route.routes)}
@@ -1393,29 +1565,7 @@ const MobileAppFrame = (props) => { - + ) } diff --git a/src/components/layout/OrganizationAppFrame/index.js b/src/components/layout/OrganizationAppFrame/index.js index 8dd754c6..ff63e1d6 100644 --- a/src/components/layout/OrganizationAppFrame/index.js +++ b/src/components/layout/OrganizationAppFrame/index.js @@ -69,7 +69,7 @@ const OrganizationAppBar = () => { const classes = useStyles() const { pathname } = useLocation() - const termsOfService = (pathname.match(/^\/\/tos/)) + const termsOfService = (pathname.match(/^\/tos/)) const privacyPolicy = (pathname.match(/^\/privacy/)) const disclaimer = (pathname.match(/^\/disclaimer/)) const getStarted = (pathname.match(/^\/getstarted/)) @@ -91,7 +91,6 @@ const OrganizationAppBar = () => { title = 'Leaderboard' } - return ( diff --git a/src/components/layout/SideBarLeft/index.js b/src/components/layout/SideBarLeft/index.js index 97721207..85bd1f98 100644 --- a/src/components/layout/SideBarLeft/index.js +++ b/src/components/layout/SideBarLeft/index.js @@ -19,6 +19,9 @@ import { CircularBrandIcon, // BuzzIcon, WalletIcon, + MessageIcon, + BookmarkIcon, + CommunityIcon, } from 'components/elements' import IconButton from '@material-ui/core/IconButton' import { @@ -33,7 +36,7 @@ import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import { pending } from 'redux-saga-thunk' import { signoutUserRequest, subscribeRequest } from 'store/auth/actions' -import { setBuzzModalStatus, setRefreshRouteStatus } from 'store/interface/actions' +import { broadcastNotification, setBuzzModalStatus, setRefreshRouteStatus } from 'store/interface/actions' import { generateStyles } from 'store/settings/actions' import { pollNotifRequest } from 'store/polling/actions' import moment from 'moment' @@ -120,11 +123,14 @@ const useStyles = createUseStyles(theme => ({ display: 'flex', flexDirection: 'column', justifyContent: 'start', - fontSize: 14, + fontSize: '20px', overflow: 'auto', overflowX: 'hidden', width: '100%', height: 'fit-content', + '& *': { + fontSize: '20px', + }, }, bottom: { position: 'absolute', @@ -200,20 +206,38 @@ const useStyles = createUseStyles(theme => ({ menu: { '& .MuiPaper-root': { background: theme.background.primary, + minWidth: '284px !important', }, '& ul':{ background: theme.background.primary, + '& nav':{ + background: theme.background.primary, + '& div':{ + background: theme.background.primary, + color: theme.font.color, + '& div':{ + '& div':{ + paddingLeft: '5%', + }, + }, + }, + }, }, '& li': { - fontSize: 18, - fontWeight: '500 !important', background: theme.background.primary, color: theme.font.color, - '&:hover': { ...theme.context.view, }, }, + '& *': { + fontSize: '15px !important', + fontWeight: '700 !important', + }, + '& #advanced-subheader': { + fontSize: '20px !important', + fontWeight: '700 !important', + }, }, moreButton: { display: 'flex', @@ -327,6 +351,7 @@ const SideBarLeft = (props) => { fromIntentBuzz, setRefreshRouteStatus, generateStyles, + broadcastNotification, } = props const { username, is_subscribe } = user || '' const [open, setOpen] = useState(false) @@ -347,6 +372,8 @@ const SideBarLeft = (props) => { const [ceramicUser, setCeramicUser] = useState(null) const [userAvatarUrl, setUserAvatarUrl] = useState('') const [fetchingUser, setFetchingUser] = useState(false) + const [showProfessionalTools, setShowProfessionalTools] = useState(false) + const [showSettingsAndSupport, setShowSettingsAndSupport] = useState(false) useEffect(() => { if(checkCeramicLogin(username)) { @@ -459,10 +486,32 @@ const SideBarLeft = (props) => { setOpenMoreMenu(true) } + const showComingSoon = () => { + broadcastNotification('success', `Coming soon`) + } + const handleClickCloseOpenMoreMenu = () => { setOpenMoreMenu(false) } + const toggleProfessionTools = () => { + setShowProfessionalTools(!showProfessionalTools) + } + + const toggleSettingsAndSupport = () => { + setShowSettingsAndSupport(!showSettingsAndSupport) + } + + const handelClickWallet = () => { + history.push(`/@${username}/wallet/balances`) + handelClickItem('wallet') + } + + const handleClickMessages = () => { + window.open("https://chat.d.buzz/") + handelClickItem('messages') + } + const handelClickItem = (name) => { switch(name) { case 'home': @@ -481,6 +530,14 @@ const SideBarLeft = (props) => { setActiveView(name) handleClickOpenMoreMenu() break + case 'messages': + setActiveView(name) + refreshTrendingRouteData() + break + case 'communities': + setActiveView(name) + showComingSoon() + break default: setActiveView(name) return @@ -488,29 +545,34 @@ const SideBarLeft = (props) => { } useEffect(() => { - switch(location.pathname) { - case '/': + switch (true) { + case location.pathname === '/': setActiveView('trending') break - case '/home': + case location.pathname === '/home': setActiveView('home') break - case '/latest': + case location.pathname === '/latest': setActiveView('latest') break - case '/notifications': + case location.pathname === '/communities': + setActiveView('communities') + break + case location.pathname === '/notifications': setActiveView('notifications') break - case '/profile': + case location.pathname === '/profile': setActiveView('profile') break - case `/@${username}/wallet/balances`: + case location.pathname === `/@${username}/wallet/balances`: setActiveView('wallet') break + case location.pathname.startsWith(`/@${username}/t/pockets/`): + setActiveView('pockets') + break default: return } - // eslint-disable-next-line }, [activeView]) @@ -547,18 +609,37 @@ const SideBarLeft = (props) => { icon: activeView === 'notifications' ? : , onClick: () => handelClickItem('notifications'), }, + { + name: 'Messages', + icon: activeView === 'messages' ? : , + path: '#', + preventDefault: true, + onClick: handleClickMessages, + }, + { + name: 'Bookmarks', + path: `/@${username}/t/pockets/`, + icon: activeView === 'pockets' ? : , + onClick: () => handelClickItem('pockets'), + }, + { + name: 'Communities', + path: `/communities`, + icon: activeView === 'communities' ? : , + onClick: () => handelClickItem('communities'), + }, { name: 'Profile', path: `/@${username}/t/buzz?from=nav`, icon: activeView === 'profile' ? : , onClick: () => handelClickItem('profile'), }, - { - name: 'Wallet', - icon: activeView === 'wallet' ? : , - path: `/@${username}/wallet/balances`, - onClick: () => handelClickItem('wallet'), - }, + // { + // name: 'Wallet', + // icon: activeView === 'wallet' ? : , + // path: `/@${username}/wallet/balances`, + // onClick: () => handelClickItem('wallet'), + // }, { name: 'More' , icon:
, @@ -590,6 +671,30 @@ const SideBarLeft = (props) => { preventDefault: false, onClick: () => handelClickItem('latest'), }, + { + name: 'Notifications', + path: `/notifications`, + icon: activeView === 'notifications' ? : , + onClick: () => handelClickItem('notifications'), + }, + { + name: 'Messages', + path: 'https://chat.d.buzz/', + icon: activeView === 'messages' ? : , + onClick: () => handelClickItem('messages'), + }, + { + name: 'Bookmarks', + path: `/@${username}/t/pockets/`, + icon: activeView === 'pockets' ? : , + onClick: () => handelClickItem('pockets'), + }, + { + name: 'Communities', + path: `/communities`, + icon: activeView === 'communities' ? : , + onClick: () => handelClickItem('communities'), + }, { name: 'Profile', path: `/@${username}/t/buzz?from=nav`, @@ -735,21 +840,110 @@ const SideBarLeft = (props) => { className={classes.menu} open={openMoreMenu} onClose={handleClickCloseOpenMoreMenu} + menuTitle='Advanced' + toggleProfessionTools={toggleProfessionTools} + toggleSettingsAndSupport={toggleSettingsAndSupport} items={[ + // { + // onClick: showThemeModal, + // text: 'Theme', + // visible: true, + // }, + // { + // onClick: showSwitchModal, + // text: 'Switch Account', + // visible: !ceramicUser ? true : false, + // }, + // { + // onClick: showSettingsModal, + // text: 'Settings', + // visible: true, + // }, { - onClick: showThemeModal, - text: 'Theme', + text: 'Professional Tools', visible: true, + collapse: showProfessionalTools, + onClick: toggleProfessionTools, + subItems: [ + { + subtext: 'Auto.Vote', + // icon: activeView === 'wallet' ? : , + subvisible: true, + subhref: 'https://auto.vote', + subonClick: '', + }, + { + subtext: 'Blog', + // icon: activeView === 'wallet' ? : , + subvisible: true, + subhref: 'http://blog.d.buzz/#/@'+username, + subonClick: '', + }, + { + subtext: 'DEX', + // icon: activeView === 'wallet' ? : , + subvisible: true, + subhref: 'https://dex.d.buzz', + subonClick: '', + }, + { + subtext: 'Leaderboard', + // icon: activeView === 'wallet' ? : , + subvisible: true, + subhref: 'https://d.buzz/leaderboard', + subonClick: '', + }, + { + subtext: 'Hive dApps', + // icon: activeView === 'wallet' ? : , + subvisible: true, + subhref: '', + subonClick: showComingSoon, + }, + ], }, { - onClick: showSwitchModal, - text: 'Switch Account', - visible: !ceramicUser ? true : false, + text: 'Settings & Support', + visible: true, + collapse: showSettingsAndSupport, + onClick: toggleSettingsAndSupport, + subItems: [ + { + subtext: 'Display', + // icon: activeView === 'wallet' ? : , + subvisible: true, + subhref: '', + subonClick: showThemeModal, + }, + { + subtext: 'Switch Account', + // icon: activeView === 'wallet' ? : , + subvisible: true, + subhref: '', + subonClick: showSwitchModal, + }, + { + subtext: 'Settings', + // icon: activeView === 'wallet' ? : , + subvisible: true, + subhref: '', + subonClick: showSettingsModal, + }, + { + subtext: 'Messages', + // icon: activeView === 'wallet' ? : , + subvisible: true, + subhref: 'https://chat.d.buzz/', + subonClick: '', + }, + ], }, { - onClick: showSettingsModal, - text: 'Settings', + text: 'Wallet', + icon: activeView === 'wallet' ? : , visible: true, + onClick: handelClickWallet, + subItems: [], }, ]} /> @@ -774,6 +968,7 @@ const mapDispatchToProps = (dispatch) => ({ setBuzzModalStatus, setRefreshRouteStatus, generateStyles, + broadcastNotification, }, dispatch), }) diff --git a/src/components/modals/EditProfileModal/index.js b/src/components/modals/EditProfileModal/index.js index 86c8e47c..7e1881de 100644 --- a/src/components/modals/EditProfileModal/index.js +++ b/src/components/modals/EditProfileModal/index.js @@ -190,6 +190,7 @@ const EditProfileModal = (props) => { useEffect(() => { const { + name, cover_image, profile_image, website, @@ -197,7 +198,6 @@ const EditProfileModal = (props) => { location, } = profileMeta || '' - const {name} = postingProfileMeta || '' // get fullname from get_accounts api setProfileName(name || ceramicProfile.name) setProfileAbout(about || ceramicProfile.description) setProfileWebsite(website || ceramicProfile.url) @@ -259,7 +259,7 @@ const EditProfileModal = (props) => { byteArrays.push(byteArray) } - return new Blob(byteArrays, { type: contentType }) + return new Blob(byteArrays, {type: contentType}) } const handleError = (error) => { @@ -269,15 +269,15 @@ const EditProfileModal = (props) => { setImageUploadProgress(0) } - const handleInputType = (input) => { + const handleInputType = (input, username, type = 'profile') => { let originalFiles = [] if (input instanceof Blob) { - const croppedImageFile = new File([input], "cropped-image.png", { type: "image/png" }) + const croppedImageFile = new File([input], `${username}-${type}-image.png`, {type: "image/png"}) originalFiles = [croppedImageFile] } else if (typeof input === 'string' && input.startsWith('data:image/')) { const blob = base64ToBlob(input, 'image/png') - const croppedImageFile = new File([blob], "cropped-image.png", { type: "image/png" }) + const croppedImageFile = new File([blob], `${username}-${type}-image.png`, {type: "image/png"}) originalFiles = [croppedImageFile] } else if (input instanceof Event && input.target && input.target.files) { originalFiles = Array.from(input.target.files) @@ -290,8 +290,8 @@ const EditProfileModal = (props) => { } - const handleChangeProfileImage = async (input) => { - const originalFiles = handleInputType(input) + const handleChangeProfileImage = async (input, username) => { + const originalFiles = handleInputType(input, username) if (!originalFiles) return const allImages = [...originalFiles.filter(image => image.type !== 'image/heic')] @@ -305,7 +305,7 @@ const EditProfileModal = (props) => { quality: 1, }) allImages.push( - new File([pngBlob], image.name.replace('.heic', '.png'), { type: 'image/png', size: pngBlob.size }), + new File([pngBlob], image.name.replace('.heic', '.png'), {type: 'image/png', size: pngBlob.size}), ) }), ) @@ -333,8 +333,8 @@ const EditProfileModal = (props) => { } } - const handleChangeCoverImage = async (input) => { - const originalFiles = handleInputType(input) + const handleChangeCoverImage = async (input, username) => { + const originalFiles = handleInputType(input, username, 'cover') if (!originalFiles) return const allImages = originalFiles.filter(image => image.type !== 'image/heic') @@ -348,7 +348,7 @@ const EditProfileModal = (props) => { quality: 1, }) allImages.push( - new File([pngBlob], image.name.replace('.heic', '.png'), { type: 'image/png', size: pngBlob.size }), + new File([pngBlob], image.name.replace('.heic', '.png'), {type: 'image/png', size: pngBlob.size}), ) }), ) @@ -391,13 +391,13 @@ const EditProfileModal = (props) => { } - const handleCropComplete = (croppedImage) => { + const handleCropComplete = (croppedImage, username) => { switch (currentImageType) { case 'avatar': - handleChangeProfileImage(croppedImage) // Set the cropped image as the new profile image + handleChangeProfileImage(croppedImage, username) // Set the cropped image as the new profile image break case 'cover': - handleChangeCoverImage(croppedImage) + handleChangeCoverImage(croppedImage, username) break default: console.warn(`Unexpected image type: ${currentImageType}`) @@ -599,6 +599,7 @@ const EditProfileModal = (props) => { isOpen={isCropperOpen} onClose={() => setIsCropperOpen(false)} src={selectedImage} + username={username} onCropComplete={handleCropComplete} />
@@ -143,12 +147,12 @@ const FAQs = () => {
-

What is decentralization?

+

What is Decentralization?


- Decentralization is a system or process that's controlled by a number of people, not just a centralized authority. + Decentralization refers to the distribution of control and decision-making across multiple entities or individuals, rather than being centralized in a single authority. This approach promotes greater transparency and inclusivity in systems and processes.

@@ -156,12 +160,12 @@ const FAQs = () => {
-

What is blockchain technology?

+

What is Blockchain Technology?


- A chain of records that's generally impossible to erase. + Blockchain technology is a revolutionary system of recording information in a way that makes it nearly impossible to change or hack. It's a digital ledger of transactions that is duplicated and distributed across the entire network of computer systems.

@@ -174,7 +178,7 @@ const FAQs = () => {

- DBuzz is a decentralized social media platform built on the Hive blockchain. + DBuzz is a decentralized social media platform, leveraging the Hive blockchain. It offers a new way of social interaction, free from central control, emphasizing user-generated content and community governance.

@@ -187,12 +191,25 @@ const FAQs = () => {

- Web1 is about reading, Web2 is about reading & writing (liking, commenting, etc) and Web3 is about reading, writing & owning (D.Buzz, Splinterlands, etc) a piece of the Internet. + Web3 represents the third generation of internet services. Unlike Web1, focused on reading, and Web2, on interaction (reading & writing), Web3 incorporates ownership elements (like in D.Buzz, Splinterlands), giving users a stake in the digital landscape.

+
+
+

Do DBuzz posts include a title section?

+
+
+
+

+ The first 82 characters of each Buzz are used to form a title section at the blockchain level to display properly on front-ends that use titles (PeakD.com, Ecency.com, etc.). +

+
+
+
+

TALKING POINTS :

@@ -211,7 +228,6 @@ const FAQs = () => {
-
diff --git a/src/components/pages/Feeds/index.js b/src/components/pages/Feeds/index.js index cd3a12ea..ef8bf4b7 100644 --- a/src/components/pages/Feeds/index.js +++ b/src/components/pages/Feeds/index.js @@ -43,7 +43,10 @@ const useStyles = createUseStyles(theme => ({ const Feeds = React.memo((props) => { const { - last, + last = { + permalink: '', + author: '', + }, loading, items, isHomeVisited, @@ -104,7 +107,6 @@ const Feeds = React.memo((props) => { clearScrollIndex() clearHomePosts() getHomePostsRequest() - setFeedPostsLoad(true) clearRefreshRouteStatus() } // eslint-disable-next-line @@ -113,31 +115,43 @@ const Feeds = React.memo((props) => { const loadMorePosts = useCallback(() => { if (!loading) { - const { permlink, author } = last - getHomePostsRequest(permlink, author) + if(items.length>0) { + const { permlink, author } = last + getHomePostsRequest(permlink, author) + } } // eslint-disable-next-line }, [last, loading]) useEffect(() => { - if (items.length === 0 && !loading && isFeedPostsLoaded) { - loadMorePosts() + if(items.length>0) { + const { permlink } = last + + if (items.length < 3 && !loading && isFeedPostsLoaded) { + if (permlink !== undefined ) { + loadMorePosts() + } else { + setFeedPostsLoad(true) + } + } else { + setFeedPostsLoad(true) + } } - }, [isFeedPostsLoaded, items.length, loadMorePosts, loading]) + }, [isFeedPostsLoaded, items.length, loadMorePosts, loading , last]) return ( {!isMobile && !buzzModalStatus && ()} - {(items.length === 0) && !loading && ( + {(items.length === 0 && isFeedPostsLoaded) && !loading && (
- Hi there! it looks like you haven't followed anyone yet,
- you may start following people by reading the  + Hi there! it looks like you haven't followed anyone yet,
+ you may start following people by reading the  latest
or trending  - buzzes on d.buzz today. + buzzes on d.buzz today.
diff --git a/src/components/pages/Latest/index.js b/src/components/pages/Latest/index.js index 88bab890..d3d976a1 100644 --- a/src/components/pages/Latest/index.js +++ b/src/components/pages/Latest/index.js @@ -143,14 +143,18 @@ const Latest = (props) => { }, [refreshRouteStatus]) const loadMorePosts = useCallback(() => { - const { permlink, author } = last - getLatestPostsRequest(permlink, author) + if(items.length>0) { + const { permlink, author } = last + getLatestPostsRequest(permlink, author) + } // eslint-disable-next-line }, [last]) useEffect(() => { - if (items.length === 0 && !loading && isLatestPostsLoaded) { + if (items.length < 3 && !loading && isLatestPostsLoaded) { loadMorePosts() + } else { + setLatestPostsLoaded(true) } }, [isLatestPostsLoaded, items.length, loadMorePosts, loading]) diff --git a/src/components/pages/Notification/index.js b/src/components/pages/Notification/index.js index bede68fa..0851b8cc 100644 --- a/src/components/pages/Notification/index.js +++ b/src/components/pages/Notification/index.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react' +import React, {useEffect, useState} from 'react' import Row from 'react-bootstrap/Row' import Col from 'react-bootstrap/Col' import moment from 'moment' @@ -13,6 +13,10 @@ import { bindActionCreators } from 'redux' import { anchorTop } from 'services/helper' import { isMobile } from 'react-device-detect' import { ContainedButton } from 'components/elements' +import { filterNotificationRequest } from 'store/polling/actions' +import {Tab, Tabs} from "@material-ui/core" +import Menu from "@material-ui/core/Menu" +import MenuItem from "@material-ui/core/MenuItem" const addHover = (theme) => { let style = { @@ -29,6 +33,55 @@ const addHover = (theme) => { } const useStyle = createUseStyles(theme => ({ + tabs: { + textTransform: 'none !important', + '&:hover': { + backgroundColor: { + ...theme.textArea, + }, + '& span': { + color: '#e53935', + }, + }, + '&.MuiTabs-indicator': { + backgroundColor: '#ffebee', + }, + '& span': { + fontFamily: 'Segoe-Bold', + fontWeight: 'bold', + ...theme.font, + }, + '& .MuiTab-root span.MuiTab-label': { + fontFamily: 'Segoe-Bold', + fontWeight: 'bold', + ...theme.font, + }, + '&.Mui-selected': { + '& span': { + color: '#e53935', + }, + }, + }, + tabsContainer: { + justifyContent: 'center', + }, + + topContainer: { + borderBottom: theme.border.primary, + '& label': { + fontFamily: 'Segoe-Bold', + paddingTop: 5, + '& span': { + color: '#d32f2f', + fontWeight: 400, + }, + }, + }, + tabContainer: { + '& span.MuiTabs-indicator': { + backgroundColor: '#e53935 !important', + }, + }, row: { width: '98%', margin: '0 auto', @@ -138,6 +191,9 @@ const useStyle = createUseStyles(theme => ({ fontWeight: 'bold', fontSize: 15, ...theme.font, + textAlign: 'center', + display: 'block', + margin: '0 auto', }, button: { '&:hover': { @@ -160,7 +216,7 @@ const Notification = (props) => { notifFilter, } = props - const classes = useStyle() + const { filterNotificationRequest } = props useEffect(() => { anchorTop() @@ -188,13 +244,12 @@ const Notification = (props) => { } const generateFilterDescription = () => { - let verb = `${notifFilter.toLowerCase().charAt(0).toUpperCase()+notifFilter.toLowerCase().slice(1)}s` + let verb = `${notifFilter.toLowerCase().charAt(0).toUpperCase()+notifFilter.toLowerCase().slice(1)}S` if(verb === 'Replys') { verb = 'Replies' } - - return `Showing ${verb}` + // return `Showing ${verb}` } const handleClickViewProfile = (username) => (e) => { @@ -202,63 +257,101 @@ const Notification = (props) => { window.open(`https://d.buzz/@${username}`, '_blank') } + const classes = useStyle() + const [selectedTab, setSelectedTab] = useState(0) + const [anchorEl, setAnchorEl] = useState(null) + + useEffect(() => { + anchorTop() + setPageFrom(null) + }, [setPageFrom]) + + const handleTabChange = (event, newValue) => { + setSelectedTab(newValue) + + switch (newValue) { + case 0: + filterNotificationRequest('ALL') + generateFilterDescription() + break + case 1: + filterNotificationRequest('MENTION') + generateFilterDescription() + break + default: + break + } + } + + const openMenu = (e) => { + setAnchorEl(e.currentTarget) + } + + const closeMenu = () => { + setAnchorEl(null) + } + + const handleFilterChange = (filter) => () => { + filterNotificationRequest(filter) + setAnchorEl(null) + } + return ( - {notifFilter !== 'ALL' && ( -
-
- - {generateFilterDescription()} - -
- )} +
+ + + + + + + + Votes + Follows + Replies + Reblogs + Transfers + + +
+ {/*{notifFilter !== 'ALL' &&
Showing {notifFilter}S
}*/} + {notifications.map((item, index) => ( - -
-
- - - -
- -
- - -
-
-
-
-
-
- - +
+
+ + + +
+ +
+ + +
- + +
+ +
- - - -
+
+ + +
+ +
+ + +
- +
))} - {(!loading && notifications.length === 0) && - (

You have no notifications
)} + {(!loading && notifications.length === 0) &&
You have no notifications
} ) @@ -274,6 +367,7 @@ const mapStateToProps = (state) => ({ const mapDispatchToProps = (dispatch) => ({ ...bindActionCreators({ setPageFrom, + filterNotificationRequest, }, dispatch), }) diff --git a/src/components/pages/Profile/index.js b/src/components/pages/Profile/index.js index 4daa7f81..ae1caa3f 100644 --- a/src/components/pages/Profile/index.js +++ b/src/components/pages/Profile/index.js @@ -140,12 +140,14 @@ const useStyles = createUseStyles(theme => ({ width: 'inherit', ...theme.font, // fontWeight: 'bold', - fontFamily: 'Segoe', + fontFamily: 'Segoe-Bold', + fontSize: '14px', }, '&.Mui-selected': { '& span': { color: '#e53935', fontFamily: 'Segoe-Bold', + fontSize: '16px', }, }, }, @@ -287,6 +289,7 @@ const Profile = (props) => { const [followsYou, setFollowsYou] = useState(false) useEffect(() => { + if (profile.ceramic) { setCeramicProfile(profile.basic_profile) setCeramicUser(true) @@ -354,11 +357,10 @@ const Profile = (props) => { if (index === 1) { tab = 'replies' } else if (index === 2) { - broadcastNotification('success', `Coming soon`) tab = 'media' } else if (index === 3) { tab = 'pockets' - }else if (index === 4) { + } else if (index === 4) { broadcastNotification('success', `Coming soon`) tab = 'likes' } @@ -397,6 +399,7 @@ const Profile = (props) => { getAccountRepliesRequest(username) getFollowersRequest(username) getFollowingRequest(username) + reloadProfile() } setMoreButtonOptions() @@ -935,7 +938,7 @@ const Profile = (props) => { {!loading && !ceramic && } {!loading && !ceramic && - } + } {!loading && !ceramic && } diff --git a/src/components/pages/TermsConditions/index.js b/src/components/pages/TermsConditions/index.js index d8c5f6d0..8b10e4f3 100644 --- a/src/components/pages/TermsConditions/index.js +++ b/src/components/pages/TermsConditions/index.js @@ -121,7 +121,7 @@ const TermsConditions = () => {
  • For European Union (EU) Users
  • United States Legal Compliance
  • Severability and Waiver
  • -
  • Translation InterpretationS
  • +
  • Translation Interpretations
  • Changes to These Terms and Conditions
  • Contact Us
  • @@ -518,7 +518,7 @@ const TermsConditions = () => {
    -

    Translation Interpretation

    +

    Translation Interpretations


    diff --git a/src/components/pages/Trending/index.js b/src/components/pages/Trending/index.js index a9eaba00..7ff81bf0 100644 --- a/src/components/pages/Trending/index.js +++ b/src/components/pages/Trending/index.js @@ -24,46 +24,46 @@ import { import { anchorTop } from 'services/helper' import { InfiniteList, HelmetGenerator } from 'components' import { clearScrollIndex, clearRefreshRouteStatus } from 'store/interface/actions' -import { createUseStyles } from 'react-jss' +// import { createUseStyles } from 'react-jss' import { isUserAlreadyVotedForProposal } from 'services/api' -import IconButton from '@material-ui/core/IconButton' -import { CloseIcon } from 'components/elements' +// import IconButton from '@material-ui/core/IconButton' +// import { CloseIcon } from 'components/elements' import Cookies from 'js-cookie' -const useStyles = createUseStyles(theme => ({ - opensourceWrapper: { - position: 'relative', - padding: '25px 0px 25px 0px', - width: '100%', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - backgroundColor: '#e6ecf0', - - '& .title': { - width: 'fit-content', - fontSize: 20, - fontWeight: 'bold', - }, - - '& .button': { - marginTop: 15, - borderRadius: 15, - width: 'fit-content', - padding: '5px 15px 5px 15px', - fontSize: 18, - fontWeight: 'bold', - background: '#E61C34', - color: '#FFFFFF', - cursor: 'pointer', - userSelect: 'none', - - '&:hover': { - opacity: 0.85, - }, - }, - }, -})) +// const useStyles = createUseStyles(theme => ({ +// opensourceWrapper: { +// position: 'relative', +// padding: '25px 0px 25px 0px', +// width: '100%', +// display: 'flex', +// flexDirection: 'column', +// alignItems: 'center', +// backgroundColor: '#e6ecf0', + +// '& .title': { +// width: 'fit-content', +// fontSize: 20, +// fontWeight: 'bold', +// }, + +// '& .button': { +// marginTop: 15, +// borderRadius: 15, +// width: 'fit-content', +// padding: '5px 15px 5px 15px', +// fontSize: 18, +// fontWeight: 'bold', +// background: '#E61C34', +// color: '#FFFFFF', +// cursor: 'pointer', +// userSelect: 'none', + +// '&:hover': { +// opacity: 0.85, +// }, +// }, +// }, +// })) const Trending = (props) => { const { @@ -93,9 +93,9 @@ const Trending = (props) => { clearRefreshRouteStatus, } = props - const classes = useStyles() + // const classes = useStyles() - const [isUserVotedForProposal, setIsUserVotedForProposal] = useState(true) + const [, setIsUserVotedForProposal] = useState(true) useEffect(() => { setPageFrom('trending') @@ -136,8 +136,10 @@ const Trending = (props) => { }, [refreshRouteStatus]) const loadMorePosts = useCallback(() => { - const { permlink, author } = last - getTrendingPostsRequest(permlink, author) + if(items.length>0) { + const { permlink, author } = last + getTrendingPostsRequest(permlink, author) + } // eslint-disable-next-line }, [last]) @@ -178,30 +180,30 @@ const Trending = (props) => { } }, [user]) - const handleHideProposalBanner = () => { - const showProposalBanner = { - visibility: false, - } + // const handleHideProposalBanner = () => { + // const showProposalBanner = { + // visibility: false, + // } - const showProposalBannerString = JSON.stringify(showProposalBanner) + // const showProposalBannerString = JSON.stringify(showProposalBanner) - Cookies.set('showProposalBanner', showProposalBannerString, { expires: 10 }) + // Cookies.set('showProposalBanner', showProposalBannerString, { expires: 10 }) - setIsUserVotedForProposal(true) - } + // setIsUserVotedForProposal(true) + // } return ( {/* disable banner */} - {!isUserVotedForProposal && + {/* {!isUserVotedForProposal &&
    {Vote for DBuzz - Proposal #2} Vote for DBuzz Proposal -
    } +
    } */} ) diff --git a/src/components/sections/AccountComments/index.js b/src/components/sections/AccountComments/index.js index 8bb144bc..19b5d773 100644 --- a/src/components/sections/AccountComments/index.js +++ b/src/components/sections/AccountComments/index.js @@ -28,8 +28,10 @@ const AccountComments = (props) => { const classes = useStyle() const loadMorePosts = useCallback(() => { try { - const { permlink, author: start_author } = last - getAccountCommentsRequest(author, permlink, start_author) + if(items.length>0) { + const { permlink, author: start_author } = last + getAccountCommentsRequest(author, permlink, start_author) + } } catch(e) { } // eslint-disable-next-line }, [last]) diff --git a/src/components/sections/AccountFollowers/index.js b/src/components/sections/AccountFollowers/index.js index c7635206..c8039ade 100644 --- a/src/components/sections/AccountFollowers/index.js +++ b/src/components/sections/AccountFollowers/index.js @@ -145,8 +145,10 @@ const AccountFollowers = (props) => { } const loadMorePosts = () => { - const { follower } = last || '' - getFollowersRequest(author, follower) + if(items.length>0) { + const { follower } = last || '' + getFollowersRequest(author, follower) + } } return ( diff --git a/src/components/sections/AccountFollowing/index.js b/src/components/sections/AccountFollowing/index.js index 59999519..8e8e1cdd 100644 --- a/src/components/sections/AccountFollowing/index.js +++ b/src/components/sections/AccountFollowing/index.js @@ -146,8 +146,10 @@ const AccountFollowing = (props) => { } const loadMorePosts = () => { - const { following } = last - getFollowingRequest(author, following) + if(items.length>0) { + const { following } = last + getFollowingRequest(author, following) + } } return ( diff --git a/src/components/sections/AccountMedia/index.js b/src/components/sections/AccountMedia/index.js new file mode 100644 index 00000000..bb496fbd --- /dev/null +++ b/src/components/sections/AccountMedia/index.js @@ -0,0 +1,88 @@ +import React, { useEffect, useState } from 'react' +import { connect } from 'react-redux' +import { createUseStyles } from 'react-jss' +import { bindActionCreators } from 'redux' +import { setViewImageModal } from 'store/interface/actions' +import ViewImageModal from 'components/modals/ViewImageModal' + +const useStyles = createUseStyles(theme => ({ + grid: { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))', + gap: 2, + '& img': { + width: '100%', + height: '100%', + objectFit: 'cover', + cursor: 'pointer', // Add cursor pointer to indicate clickable + }, + }, +})) + +const AccountMedia = (props) => { + const { items = [], author, viewImageModal, setViewImageModal } = props + const classes = useStyles() + + // State to store the extracted image links + const [imageLinks, setImageLinks] = useState([]) + + // Function to extract image links from items + const extractImageLinks = () => { + const links = [] + items.forEach(item => { + const regex = /!\[(?:[^\]]*?)\]\((.+?)\)|(https:\/\/(storageapi\.fleek\.co)?(media\.d\.buzz)?\/[a-z-]+\/dbuzz-images\/(dbuzz-image-[0-9]+\.(?:png|jpg|gif|jpeg|webp|bmp)))|(https?:\/\/[a-zA-Z0-9=+-?_]+\.(?:png|jpg|gif|jpeg|webp|bmp|HEIC))|(?:https?:\/\/(?:ipfs\.io\/ipfs\/[a-zA-Z0-9=+-?]+))/gi + const matches = item.body.match(regex) + if (matches) { + links.push(...matches) + } + }) + + setImageLinks(links) + } + + useEffect(() => { + extractImageLinks() + // eslint-disable-next-line + }, [items]) + + + const handleImageClick = (imageSrc) => { + setViewImageModal({ + selectedImage: imageSrc, + images: imageLinks, // Optionally pass all images for a gallery view or navigation + }) + } + + return ( + +
    + {( + imageLinks.map((link, index) => ( + {`Media handleImageClick(link)} /> + )) + )} +
    + {imageLinks.length === 0 &&
    No media from @{author}
    } + setViewImageModal({selectedImage: '', images: []})} + /> +
    + ) +} + +const mapStateToProps = (state) => ({ + items: state.profile.get('posts'), + user: state.auth.get('user'), + viewImageModal: state.interfaces.get('viewImageModal'), + +}) + +const mapDispatchToProps = (dispatch) => ({ + ...bindActionCreators({ + setViewImageModal, + }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(AccountMedia) diff --git a/src/components/sections/AccountPockets/index.js b/src/components/sections/AccountPockets/index.js index 3f8e1a70..c9794f43 100644 --- a/src/components/sections/AccountPockets/index.js +++ b/src/components/sections/AccountPockets/index.js @@ -132,12 +132,14 @@ const useStyle = createUseStyles(theme => ({ '& span': { ...theme.font, // fontWeight: 'bold', - fontFamily: 'Segoe', + fontFamily: 'Segoe-Bold', + fontSize: '14px', }, '&.Mui-selected': { '& span': { color: '#e53935', fontFamily: 'Segoe-Bold', + fontSize: '16px', }, }, }, diff --git a/src/components/sections/AccountPosts/index.js b/src/components/sections/AccountPosts/index.js index 54652376..b961024b 100644 --- a/src/components/sections/AccountPosts/index.js +++ b/src/components/sections/AccountPosts/index.js @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react' +import React, {useCallback, useEffect, useState} from 'react' import { connect } from 'react-redux' import { pending } from 'redux-saga-thunk' @@ -26,13 +26,35 @@ const AccountPosts = (props) => { mutelist, } = props const classes = useStyle() - const loadMorePosts = useCallback(() => { - try { - const { permlink, author: start_author } = last - getAccountPostsRequest(author, permlink, start_author) - } catch(e) { } + + const [isFeedPostsLoaded , setFeedPostsLoad] = useState(false) + + const loadMorePosts = useCallback(() => { + if (!loading) { + if(items.length>0) { + const { permlink, author: start_author } = last + getAccountPostsRequest(author, permlink, start_author) + } + } // eslint-disable-next-line - }, [last]) + }, [last, loading]) + + useEffect(() => { + if (items.length < 3 && !loading && isFeedPostsLoaded) { + loadMorePosts() + } else { + setFeedPostsLoad(true) + } + }, [isFeedPostsLoaded, items.length, loadMorePosts, loading]) + + + // const loadMorePosts = useCallback(() => { + // try { + // const { permlink, author: start_author } = last + // getAccountPostsRequest(author, permlink, start_author) + // } catch(e) { } + // // eslint-disable-next-line + // }, [last]) const muted = mutelist.includes(author) @@ -42,8 +64,7 @@ const AccountPosts = (props) => { {!muted && ( - {(!loading && items.length === 0) && - (

    No Buzz's from @{author}
    )} + {(!loading && items.length === 0) && (

    No Buzz's from @{author}
    )}
    )} {muted &&

    This user is on your mutelist, unmute this user to view their buzzes
    } @@ -66,4 +87,4 @@ const mapDispatchToProps = (dispatch) => ({ }, dispatch), }) -export default connect(mapStateToProps, mapDispatchToProps)(AccountPosts) +export default connect(mapStateToProps, mapDispatchToProps)(AccountPosts) \ No newline at end of file diff --git a/src/components/sections/AccountReplies/index.js b/src/components/sections/AccountReplies/index.js index b63fb82f..2a9da69e 100644 --- a/src/components/sections/AccountReplies/index.js +++ b/src/components/sections/AccountReplies/index.js @@ -27,8 +27,10 @@ const AccountReplies = (props) => { const classes = useStyle() const loadMorePosts = useCallback(() => { - const { author: start_author, permlink } = last - getAccountRepliesRequest(author, permlink, start_author) + if(items.length>0) { + const { author: start_author, permlink } = last + getAccountRepliesRequest(author, permlink, start_author) + } // eslint-disable-next-line }, [last]) diff --git a/src/components/sections/CreateBuzzForm/index.js b/src/components/sections/CreateBuzzForm/index.js index 35b729a6..c885c5d6 100644 --- a/src/components/sections/CreateBuzzForm/index.js +++ b/src/components/sections/CreateBuzzForm/index.js @@ -49,7 +49,7 @@ import SaveDraftModal from 'components/modals/SaveDraftModal' import {LinearProgress} from '@material-ui/core' import {styled} from '@material-ui/styles' import {checkForCeramicAccount, createPostRequest, getBasicProfile, getIpfsLink} from 'services/ceramic' -import {createPermlink, publishPostWithHAS} from 'services/api' +import {createPermlink} from 'services/api' import heic2any from 'heic2any' import IconButton from '@material-ui/core/IconButton' import CircularProgress from '@material-ui/core/CircularProgress' @@ -415,9 +415,9 @@ const useStyles = createUseStyles(theme => ({ buzzToTwitterToggle: { display: 'grid', placeItems: 'center', - height: 28, - width: 28, - background: '#E65768', + height: 32, + width: 32, + background: '#ffffff', borderRadius: '50%', cursor: 'pointer', @@ -747,7 +747,6 @@ const CreateBuzzForm = (props) => { buzzThreads = {1: {id: 1, content: '', images: []}}, updateBuzzThreads, publishReplyRequest, - setContentRedirect, viewImageModal, setViewImageModal, } = props @@ -1247,87 +1246,35 @@ const CreateBuzzForm = (props) => { broadcastNotification('error', `${origin_app_name} requires to buzz a minimum of ${parseInt(min_chars)} characters.`) } else { if (!ceramicUser) { - setBuzzLoading(true) - setBuzzing(true) + publishPostRequest(buzzContent, tags, payout, buzzPermlink) + .then((data) => { + if (data.success) { + setPageFrom(null) + const {author, permlink} = data + // hideModalCallback() + clearIntentBuzz() + removeAutoSavedDraft() + broadcastNotification('success', 'You successfully published a post') + setPublishedBuzzes(1) + setNextBuzz(2) + setBuzzData({author: author, permlink: permlink}) + setBuzzing(false) - if (user.useHAS) { - publishPostWithHAS(user, buzzContent, tags, payout, buzzPermlink) - .then((data) => { - setContentRedirect(data.content) - - import('@mintrawa/hive-auth-client').then((HiveAuth) => { - HiveAuth.hacMsg.subscribe(m => { - if (isMobile) { - broadcastNotification('warning', 'Tap on this link to open Hive Keychain app and confirm the transaction.', 600000, `has://sign_req/${m.msg}`) - } else { - broadcastNotification('warning', 'Please open Hive Keychain app on your phone and confirm the transaction.', 600000) - } - if (m.type === 'sign_wait') { - console.log('%c[HAC Sign wait]', 'color: goldenrod', m.msg ? m.msg.uuid : null) - } - if (m.type === 'tx_result') { - console.log('%c[HAC Sign result]', 'color: goldenrod', m.msg ? m.msg : null) - if (m.msg?.status === 'accepted') { - const status = m.msg?.status - console.log(status) - // success - const {author, permlink} = data - removeAutoSavedDraft() - broadcastNotification('success', 'You successfully published a post') - setBuzzLoading(false) - setBuzzing(false) - clearIntentBuzz() - resetBuzzForm() - hideModalCallback() - history.push(`/@${author}/${permlink}`) - } else if (m.msg?.status === 'rejected') { - const status = m.msg?.status - console.log(status) - // error - broadcastNotification('error', 'Your HiveAuth post transaction is rejected.') - setBuzzLoading(false) - } else if (m.msg?.status === 'error') { - const error = m.msg?.status.error - console.log(error) - broadcastNotification('error', 'Unknown error occurred, please try again in some time.') - } - } - }) - }) - }) - } else { - publishPostRequest(buzzContent, tags, payout, buzzPermlink) - .then((data) => { - if (data.success) { - setPageFrom(null) - const {author, permlink} = data - // hideModalCallback() - clearIntentBuzz() - removeAutoSavedDraft() - broadcastNotification('success', 'You successfully published a post') - setPublishedBuzzes(1) - setNextBuzz(2) - setBuzzData({author: author, permlink: permlink}) - setBuzzing(false) - - if (!isThread) { - hideModalCallback() - resetBuzzForm() - history.push(`/@${author}/${permlink}`) - } - } else { - broadcastNotification('error', data.errorMessage) - setBuzzLoading(false) + if (!isThread) { + hideModalCallback() + resetBuzzForm() + history.push(`/@${author}/${permlink}`) } - }) - } + } else { + broadcastNotification('error', data.errorMessage) + setBuzzLoading(false) + } + }) } else { - // alert('ceramic!!!') setBuzzLoading(true) setBuzzing(true) createPostRequest(user.username, '', buzzContent) .then((data) => { - // console.log(data) if (data) { setPageFrom(null) const {creatorId, streamId} = data @@ -1537,10 +1484,11 @@ const CreateBuzzForm = (props) => { // genarate buzz permlink if video is attached useEffect(() => { if (videoLimit) { - setBuzzPermlink(createPermlink()) + setBuzzPermlink(createPermlink(buzzContent)) } else { setBuzzPermlink(null) } + // eslint-disable-next-line }, [videoLimit]) // update content based on the buzz's content @@ -1780,7 +1728,7 @@ const CreateBuzzForm = (props) => { style={{...BuzzToTwitterToggleStyle}} onClick={() => setBuzzToTwitter(!buzzToTwitter)} > - twitter-icon {/*
    Buzz to Twitter
    */}
    diff --git a/src/components/sections/PostList/index.js b/src/components/sections/PostList/index.js index 6ed00c98..58b98113 100644 --- a/src/components/sections/PostList/index.js +++ b/src/components/sections/PostList/index.js @@ -9,6 +9,7 @@ import { // PostTags, LoginModal, PostActions, + DeleteBuzzModal, } from 'components' import { openUserDialog, @@ -310,6 +311,7 @@ const PostList = React.memo((props) => { const [seletedRemoveFromPocketBuzz, setSeletedRemoveFromPocketBuzz] = useState(null) const [pockets, setPockets] = useState([]) const [openLoginModal, setOpenLoginModal] = useState(false) + const [deleteBuzzModal, setDeleteBuzzModal] = useState(false) const buzzRowRef = useRef(null) @@ -558,6 +560,11 @@ const PostList = React.memo((props) => { // eslint-disable-next-line }, []) + const handleClickDeleteBuzz = () => { + setAnchorEl(null) + setDeleteBuzzModal(true) + } + return (
    @@ -635,6 +642,14 @@ const PostList = React.memo((props) => { onClose={closeMenu} className={classes.menu} > + {isAuthor() && replyCount===0 && upvotes===0 && + + Delete + + } {Add to Pocket} {(pockets && pockets.find(pocket => pocket.pocketBuzzes.find((b) => b.permlink === permlink) !== undefined) && Remove from {selectedPocket.name || getPocket().pocketName})} {!isAuthor() && (Tip)} @@ -650,6 +665,7 @@ const PostList = React.memo((props) => { + ) }) diff --git a/src/components/sections/WalletBalances/index.js b/src/components/sections/WalletBalances/index.js index 6ac73e6d..0242e211 100644 --- a/src/components/sections/WalletBalances/index.js +++ b/src/components/sections/WalletBalances/index.js @@ -61,14 +61,14 @@ const useStyles = createUseStyles(theme => ({ fontSize: 14, }, value : { - fontWeight: 'bold', + fontWeight: 'medium', paddingRight: 5, marginTop: 0, marginBottom: 0, paddingTop: 0, paddingBottom: 0, - color: '#e61c34', textAlign: 'end', + ...theme.hiveValuesFont, }, circle: { strokeLinecap: 'round', diff --git a/src/components/wrappers/Init/index.js b/src/components/wrappers/Init/index.js index c11d111e..f11b7030 100644 --- a/src/components/wrappers/Init/index.js +++ b/src/components/wrappers/Init/index.js @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react' import { getTrendingTagsRequest } from 'store/posts/actions' -import { getSavedUserRequest, initWSHASConnectionRequest, initCeremicLoginRequest } from 'store/auth/actions' -import { getBestRpcNode, checkVersionRequest, setDefaultVotingWeightRequest, getWSNodeHAS } from 'store/settings/actions' +import { getSavedUserRequest, initCeremicLoginRequest } from 'store/auth/actions' +import { getRpcNode, checkVersionRequest, setDefaultVotingWeightRequest } from 'store/settings/actions' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import { BrandIcon, Spinner } from 'components/elements' @@ -10,7 +10,6 @@ import { createUseStyles } from 'react-jss' import config from 'config' import { getTheme } from 'services/helper' import BrandIconDark from 'components/elements/Icons/BrandIconDark' -// import { getBestCeramicHost } from 'services/ceramic' import Paper from '@material-ui/core/Paper' import classNames from 'classnames' @@ -156,10 +155,7 @@ const Init = (props) => { const { getSavedUserRequest, getTrendingTagsRequest, - getBestRpcNode, - getWSNodeHAS, - initWSHASConnectionRequest, - // initCeremicLoginRequest, + getRpcNode, checkVersionRequest, getCensorTypesRequest, children, @@ -207,13 +203,7 @@ const Init = (props) => { if(!isStaging) { checkVersionRequest().then((isLatest) => { setIsLatest(isLatest) - getBestRpcNode().then(() => { - getWSNodeHAS() - initWSHASConnectionRequest() - // getBestCeramicHost().then((host) => { - // initCeremicLoginRequest() - // localStorage.setItem('ceramic', host) - // }) + getRpcNode().then(() => { const defaultUpvoteWeight = localStorage.getItem('voteWeight') || 1 setDefaultVotingWeightRequest(defaultUpvoteWeight).then(() => { getSavedUserRequest().then(() => { @@ -226,13 +216,7 @@ const Init = (props) => { }) } else { setIsLatest(isLatest) - getBestRpcNode().then(() => { - getWSNodeHAS() - initWSHASConnectionRequest() - // getBestCeramicHost().then((host) => { - // initCeremicLoginRequest() - // localStorage.setItem('ceramic', host) - // }) + getRpcNode().then(() => { const defaultUpvoteWeight = localStorage.getItem('voteWeight') || 1 setDefaultVotingWeightRequest(defaultUpvoteWeight).then(() => { getSavedUserRequest().then(() => { @@ -275,9 +259,7 @@ const mapDispatchToProps = (dispatch) => ({ ...bindActionCreators({ getTrendingTagsRequest, getSavedUserRequest, - getBestRpcNode, - getWSNodeHAS, - initWSHASConnectionRequest, + getRpcNode, initCeremicLoginRequest, checkVersionRequest, getCensorTypesRequest, diff --git a/src/config.js b/src/config.js index 61b9931e..97dee144 100644 --- a/src/config.js +++ b/src/config.js @@ -11,8 +11,7 @@ const config = { GIPHY_API_KEY: 'ecohRlzr8FrMGrTfX8JJ4uoilgdIiZI5', DISABLE_MOBILE: false, BRANCH: process.env.REACT_APP_ENV, - HAS_WS: 'wss://hive-auth.arcange.eu', - HIVE_API_NODE: process.env.REACT_APP_HIVE_API_NODE, + DEFAULT_RPC_NODE: process.env.REACT_APP_DEFAULT_RPC_NODE, APP_NAME: 'D.Buzz', APP_DESCRIPTION: 'Micro-blogging social media Dapp on the HIVE blockchain.', APP_ICON: 'https://images.hive.blog/p/D5zH9SyxCKdAD9rYwjD1VDFVfes4J8WBmiaYPdeZndqBcsWCe3xdjZ9FWukYQcKxKMUaJu2FLm66h23z4KvAS7BxXaBpNLPzMVDqas4kKbBvZPf2zny6g2ePMPCmbC44pcvGUN?width=128&height=128', diff --git a/src/index.js b/src/index.js index 97bb33cd..a3cc4021 100644 --- a/src/index.js +++ b/src/index.js @@ -1,16 +1,14 @@ -import React from 'react' -import ReactDOM from 'react-dom/' -import App from './App' -import store from 'store/store' -import * as serviceWorker from './serviceWorker' -import { Provider } from 'react-redux' -import { - BrowserRouter as Router, -} from 'react-router-dom' -import initReactFastclick from 'react-fastclick' -import 'bootstrap/dist/css/bootstrap.min.css' -import './override.css' -import HttpsRedirect from 'react-https-redirect' +import React from "react" +import ReactDOM from "react-dom/" +import App from "./App" +import store from "store/store" +import * as serviceWorker from "./serviceWorker" +import { Provider } from "react-redux" +import { BrowserRouter as Router } from "react-router-dom" +import initReactFastclick from "react-fastclick" +import "bootstrap/dist/css/bootstrap.min.css" +import "./override.css" +import HttpsRedirect from "react-https-redirect" initReactFastclick() @@ -22,10 +20,22 @@ ReactDOM.render( , - document.getElementById('root'), + document.getElementById("root") ) -// If you want your app to work offline and load faster, you can change -// unregister() to register() below. Note this comes with some pitfalls. -// Learn more about service workers: https://bit.ly/CRA-PWA -serviceWorker.register() +serviceWorker.register({ + onUpdate: (registration) => { + const waitingServiceWorker = registration.waiting + + console.log("service worker event") + if (waitingServiceWorker) { + console.log("waiting service worker") + waitingServiceWorker.addEventListener("statechange", (event) => { + if (event.target.state === "activated") { + window.location.reload() + } + }) + waitingServiceWorker.postMessage({ type: "SKIP_WAITING" }) + } + }, +}) diff --git a/src/override.css b/src/override.css index 1972707a..6d03a6f2 100644 --- a/src/override.css +++ b/src/override.css @@ -114,8 +114,8 @@ div.ReactTags__selected a.ReactTags__remove { transition-property:'background-color'; transition-timing-function:ease; transition-duration: 250ms; - } + .navigationsmallWidth{ height: 100%; position: fixed; diff --git a/src/routes.js b/src/routes.js index 8b145499..9446af07 100644 --- a/src/routes.js +++ b/src/routes.js @@ -15,6 +15,7 @@ const AccountFollowers = React.lazy(() => import('./components/sections/AccountF const AccountFollowing = React.lazy(() => import('./components/sections/AccountFollowing')) const AccountComments = React.lazy(() => import('./components/sections/AccountComments')) const AccountPockets = React.lazy(() => import('./components/sections/AccountPockets')) +const AccountMedia = React.lazy(() => import('./components/sections/AccountMedia')) const AccountFollow = React.lazy(() => import('./components/sections/AccountFollow')) const AccountMuted = React.lazy(() => import('./components/sections/AccountMuted')) const AccountMutedUsers = React.lazy(() => import('./components/sections/AccountMutedFollowed')) @@ -83,6 +84,11 @@ const routes = [ exact: true, component: Trending, }, + { + path: '/messages', + exact: true, + component: Trending, + }, { path: '/latest', exact: true, @@ -281,6 +287,11 @@ const routes = [ exact: true, component: AccountPockets, }, + { + path: '/@:username/t/media', + exact: true, + component: AccountMedia, + }, { path: '/@:username/t/pockets/:pocketId', exact: true, diff --git a/src/service-worker.js b/src/service-worker.js index ea92837e..678f7f61 100644 --- a/src/service-worker.js +++ b/src/service-worker.js @@ -7,14 +7,18 @@ // You can also remove this file if you'd prefer not to use a // service worker, and the Workbox build step will be skipped. -import { clientsClaim } from 'workbox-core' -import { ExpirationPlugin } from 'workbox-expiration' -import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching' -import { registerRoute } from 'workbox-routing' -import { StaleWhileRevalidate } from 'workbox-strategies' +import { clientsClaim } from "workbox-core" +import { ExpirationPlugin } from "workbox-expiration" +import { precacheAndRoute, createHandlerBoundToURL } from "workbox-precaching" +import { registerRoute } from "workbox-routing" +import { StaleWhileRevalidate } from "workbox-strategies" clientsClaim() +self.addEventListener("install", (event) => { + self.skipWaiting() +}) + // Precache all of the assets generated by your build process. // Their URLs are injected into the manifest variable below. // This variable must be present somewhere in your service worker file, @@ -24,16 +28,16 @@ precacheAndRoute(self.__WB_MANIFEST) // Set up App Shell-style routing, so that all navigation requests // are fulfilled with your index.html shell. Learn more at // https://developers.google.com/web/fundamentals/architecture/app-shell -const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$') +const fileExtensionRegexp = new RegExp("/[^/?]+\\.[^/]+$") registerRoute( // Return false to exempt requests from being fulfilled by index.html. ({ request, url }) => { // If this isn't a navigation, skip. - if (request.mode !== 'navigate') { + if (request.mode !== "navigate") { return false } // If this is a URL that starts with /_, skip. - if (url.pathname.startsWith('/_')) { + if (url.pathname.startsWith("/_")) { return false } // If this looks like a URL for a resource, because it contains // a file extension, skip. @@ -43,16 +47,17 @@ registerRoute( return true }, - createHandlerBoundToURL(process?.env.PUBLIC_URL + '/index.html'), + createHandlerBoundToURL(process?.env.PUBLIC_URL + "/index.html"), ) // An example runtime caching route for requests that aren't handled by the // precache, in this case same-origin .png requests like those from in public/ registerRoute( // Add in any other file extensions or routing criteria as needed. - ({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'), // Customize this strategy as needed, e.g., by changing to CacheFirst. + ({ url }) => + url.origin === self.location.origin && url.pathname.endsWith(".png"), // Customize this strategy as needed, e.g., by changing to CacheFirst. new StaleWhileRevalidate({ - cacheName: 'images', + cacheName: "images", plugins: [ // Ensure that once this runtime cache reaches a maximum size the // least-recently used images are removed. @@ -63,10 +68,20 @@ registerRoute( // This allows the web app to trigger skipWaiting via // registration.waiting.postMessage({type: 'SKIP_WAITING'}) -self.addEventListener('message', (event) => { - if (event.data && event.data.type === 'SKIP_WAITING') { +self.addEventListener("message", (event) => { + if (event.data && event.data.type === "SKIP_WAITING") { self.skipWaiting() + window.location.reload() } }) +self.addEventListener("activate", async () => { + // after we've taken over, iterate over all the current clients (windows) + const tabs = await self.clients.matchAll({ type: "window" }) + tabs.forEach((tab) => { + // ...and refresh each one of them + tab.navigate(tab.url) + }) +}) + // Any other custom service worker logic can go here. diff --git a/src/serviceWorker.js b/src/serviceWorker.js index d482dfa0..b5b16593 100644 --- a/src/serviceWorker.js +++ b/src/serviceWorker.js @@ -11,9 +11,9 @@ // opt-in, read https://bit.ly/CRA-PWA const isLocalhost = Boolean( - window.location.hostname === 'localhost' || + window.location.hostname === "localhost" || // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || + window.location.hostname === "[::1]" || // 127.0.0.0/8 are considered localhost for IPv4. window.location.hostname.match( /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/, @@ -21,7 +21,7 @@ const isLocalhost = Boolean( ) export function register(config) { - if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + if ("serviceWorker" in navigator) { // The URL constructor is available in all browsers that support SW. const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href) if (publicUrl.origin !== window.location.origin) { @@ -31,58 +31,76 @@ export function register(config) { return } - window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` - - if (isLocalhost) { - // This is running on localhost. Let's check if a service worker still exists or not. - checkValidServiceWorker(swUrl, config) - - // Add some additional logging to localhost, pointing developers to the - // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit https://bit.ly/CRA-PWA', - ) - }) - } else { - // Is not localhost. Just register service worker - registerValidSW(swUrl, config) - } - }) + // window.addEventListener("load", () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config) + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + "This web app is being served cache-first by a service " + + "worker. To learn more, visit https://bit.ly/CRA-PWA", + ) + }) + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config) + } + // }) } } function registerValidSW(swUrl, config) { + navigator.serviceWorker.addEventListener("controllerchange", () => { + window.location.reload() + }) + navigator.serviceWorker .register(swUrl) - .then(registration => { + .then((registration) => { registration.onupdatefound = () => { + const waitingWorker = registration.waiting + + if (waitingWorker && waitingWorker.state === "waiting") { + waitingWorker.postMessage({ type: "SKIP_WAITING" }) + } + + waitingWorker.onstatechange = () => { + if (waitingWorker.state === "activated") { + window.location.reload() + } + } + const installingWorker = registration.installing if (installingWorker == null) { return } + installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { + if (installingWorker.state === "installed") { if (navigator.serviceWorker.controller) { // At this point, the updated precached content has been fetched, // but the previous service worker will still serve the older // content until all client tabs are closed. console.log( - 'New content is available and will be used when all ' + - 'tabs for this page are closed. See https://bit.ly/CRA-PWA.', + "New content is available and will be used when all " + + "tabs for this page are closed. See https://bit.ly/CRA-PWA.", ) // Execute callback if (config && config.onUpdate) { + window.location.reload() config.onUpdate(registration) } } else { // At this point, everything has been precached. // It's the perfect time to display a // "Content is cached for offline use." message. - console.log('Content is cached for offline use.') + console.log("Content is cached for offline use.") // Execute callback if (config && config.onSuccess) { @@ -93,29 +111,35 @@ function registerValidSW(swUrl, config) { } } }) - .catch(error => { - console.error('Error during service worker registration:', error) + .catch((error) => { + alert("error") + console.error("Error during service worker registration:", error) }) } function checkValidServiceWorker(swUrl, config) { // Check if the service worker can be found. If it can't reload the page. fetch(swUrl, { - headers: { 'Service-Worker': 'script' }, + headers: { "Service-Worker": "script" }, }) - .then(response => { + .then((response) => { // Ensure service worker exists, and that we really are getting a JS file. - const contentType = response.headers.get('content-type') + const contentType = response.headers.get("content-type") if ( response.status === 404 || - (contentType != null && contentType.indexOf('javascript') === -1) + (contentType != null && contentType.indexOf("javascript") === -1) ) { // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { - registration.unregister().then(() => { - window.location.reload() + navigator.serviceWorker.ready + .then((registration) => { + registration + .unregister() + .then(() => { + window.location.reload() + }) + .catch((errors) => alert("error sericeworkers" + errors)) }) - }) + .catch((errors) => console.log(errors)) } else { // Service worker found. Proceed as normal. registerValidSW(swUrl, config) @@ -123,18 +147,18 @@ function checkValidServiceWorker(swUrl, config) { }) .catch(() => { console.log( - 'No internet connection found. App is running in offline mode.', + "No internet connection found. App is running in offline mode.", ) }) } export function unregister() { - if ('serviceWorker' in navigator) { + if ("serviceWorker" in navigator) { navigator.serviceWorker.ready - .then(registration => { + .then((registration) => { registration.unregister() }) - .catch(error => { + .catch((error) => { console.error(error.message) }) } diff --git a/src/services/api.js b/src/services/api.js index b3da8a64..c5adb1e7 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -1,16 +1,20 @@ -import {api, auth, broadcast, formatter} from '@hiveio/hive-js' +import { + api, + auth, + broadcast, + formatter, +} from '@hiveio/hive-js' import {hash} from '@hiveio/hive-js/lib/auth/ecc' import {Promise, reject} from 'bluebird' import {v4 as uuidv4} from 'uuid' import appConfig from 'config' -import config from 'config' import axios from 'axios' import getSlug from 'speakingurl' import moment from 'moment' import {ChainTypes, makeBitMaskFilter} from '@hiveio/hive-js/lib/auth/serializer' import 'react-app-polyfill/stable' import {calculateOverhead, stripHtml} from 'services/helper' -import {hacManualTransaction, hacUserAuth, hacVote} from "@mintrawa/hive-auth-client" +import { hiveAPIUrls } from './helper' // Adjust the path as necessary const searchUrl = `${appConfig.SEARCH_API}/search` const scrapeUrl = `${appConfig.SCRAPE_API}/scrape` @@ -18,42 +22,38 @@ const imageUrl = `${appConfig.IMAGE_API}` const videoUrl = `${appConfig.VIDEO_API}` const censorUrl = `${appConfig.CENSOR_API}` const priceChartURL = `${appConfig.PRICE_API}` -const hiveAPINODE = `${appConfig.HIVE_API_NODE}` - -const APP_META = { - name: config.APP_NAME, - description: config.APP_DESCRIPTION, - icon: config.APP_ICON, -} - const visited = [] -const defaultNode = process.env.REACT_APP_DEFAULT_RPC_NODE +const defaultNode = appConfig.DEFAULT_RPC_NODE -export const geRPCNode = () => { - return new Promise((resolve) => { - if (localStorage.getItem('rpc-setting')) { - if (localStorage.getItem('rpc-setting') !== 'default') { - const node = localStorage.getItem('rpc-setting') - resolve(node) - } else { - resolve(defaultNode) - } +export const getActiveRPCNode = () => { + const rpcSetting = localStorage.getItem('rpc-setting') + // Check if the rpcSetting is not 'default' and is included in the hiveAPIUrls list + if (rpcSetting && rpcSetting !== 'default' && hiveAPIUrls.includes(rpcSetting)) { + return rpcSetting + } + // Return defaultNode if the condition above is not met + return defaultNode +} +export const setRPCNode = async () => { + try { + const node = getActiveRPCNode() + if (api && typeof api.setOptions === 'function') { + api.setOptions({ url: node }) } else { - resolve(defaultNode) + throw new Error('API object or setOptions method is not available') } - }) -} - -export const setRPCNode = () => { - geRPCNode() - .then((node) => { - api.setOptions({url: node}) - }) + } catch (error) { + console.error('Error setting RPC node, reverting to default:', error) + if (api && typeof api.setOptions === 'function') { + api.setOptions({ url: defaultNode }) + } else { + console.error('Failed to revert to default RPC node') + } + } } - export const invokeMuteFilter = (items, mutelist, opacityUsers = []) => { return items.filter((item) => !mutelist.includes(item.author) || opacityUsers.includes(item.author)) } @@ -71,7 +71,7 @@ export const invokeFilter = (item) => { } export const removeFootNote = (data) => { - if (typeof data !== 'string') { + if(typeof data !== 'string') { return data.forEach((item) => { item.body = item.body.replace('

    Posted via D.Buzz', '') item.body = item.body.replace('

    Posted via D.Buzz', '') @@ -128,7 +128,6 @@ export const searchPeople = (username) => { }) } - export const fetchDiscussions = (author, permlink) => { return new Promise((resolve, reject) => { const params = {"author": `${author}`, "permlink": `${permlink}`} @@ -275,18 +274,16 @@ export const fetchAccountPosts = (account, start_permlink = null, start_author = }) } - -export const fetchTrendingTags = () => { +export const fetchTrendingTags = (tag = "hive-193084", limit = 100) => { return new Promise((resolve, reject) => { - // set RPC node here because this is the first API call to execute - setRPCNode() - api.getTrendingTagsAsync("dbuzz", 100) - .then((result) => { - resolve(result) - }) - .catch((error) => { - reject(error) - }) + api.call('condenser_api.get_trending_tags', [tag, limit], (err, data) => { + if (err) { + console.error('Error fetching trending tags:', err) + reject(err) + } else { + resolve(data) + } + }) }) } @@ -307,7 +304,6 @@ export const fetchContent = (author, permlink) => { } export const fetchReplies = (author, permlink) => { - api.setOptions({url: hiveAPINODE}) return api.getContentRepliesAsync(author, permlink) .then(async (replies) => { if (replies.length !== 0) { @@ -415,7 +411,7 @@ export const fetchProfile = (username, checkFollow = false) => { return new Promise((resolve, reject) => { api.getAccountsAsync(username) .then(async (result) => { - if (result.length === 0) resolve(result) + if(result.length === 0) resolve(result) result.forEach(async (item, index) => { const repscore = item.reputation let score = formatter.reputation(repscore) @@ -565,6 +561,8 @@ export const fetchFollowCount = (username) => { } export const fetchMuteList = (user) => { + const activeRPCNode = getActiveRPCNode() + api.setOptions({url: activeRPCNode}) return new Promise((resolve, reject) => { api.call('condenser_api.get_following', [user, null, 'ignore', 1000], async (err, data) => { if (err) { @@ -673,246 +671,6 @@ export const checkAccountIsFollowingLists = (observer) => { }) } - -// has apis - -export const hiveAuthenticationService = (username) => { - const challenge = JSON.stringify({token: uuidv4()}) - const hacModule = "has" - const hacPwd = sessionStorage.getItem('hacPwd') - - hacUserAuth(username, APP_META, hacPwd, {key_type: 'posting', value: challenge}, hacModule) - -} - -export const hasFollowService = (username, following) => { - hacManualTransaction("posting", ["custom_json", { - "required_auths": [], - "required_posting_auths": [`${username}`], - "id": "follow", - "json": JSON.stringify(["follow", {"follower": `${username}`, "following": `${following}`, "what": ["blog"]}]), - }]) -} - -export const hasUnFollowService = (username, following) => { - hacManualTransaction("posting", ["custom_json", { - "required_auths": [], - "required_posting_auths": [`${username}`], - "id": "follow", - "json": JSON.stringify(["follow", {"follower": `${username}`, "following": `${following}`, "what": []}]), - }]) -} - -export const hasUpvoteService = (author, permlink, weight) => { - return hacVote(author, permlink, parseInt(weight)) -} - -export const hasReplyService = (username, body, parent_author, parent_permlink, json_metadata, permlink) => { - hacManualTransaction("posting", ["comment", { - "author": username, - "title": '', - "body": `${body.trim()}`, - parent_author, - parent_permlink, - permlink, - json_metadata, - }]) -} - -export const hasClearNotificationService = (username, lastNotification) => { - let date = moment().utc().format() - date = `${date}`.replace('Z', '') - - const json = JSON.stringify(["setLastRead", {date}]) - hacManualTransaction("posting", ["custom_json", { - 'required_auths': [], - 'required_posting_auths': [username], - 'id': 'notify', - json, - }]) -} - - -export const hasPostService = (operations) => { - console.log('wrdd') - hacManualTransaction("posting", operations) -} - -export const hasGeneratePostService = (account, title, tags, body, payout, permlink) => { - const json_metadata = createMeta(tags) - - const operations = [] - - return new Promise((resolve) => { - const op_comment = [ - 'comment', - { - 'author': account, - 'title': stripHtml(title), - 'body': `${body.trim()}`, - 'parent_author': '', - 'parent_permlink': `${appConfig.TAG}`, - permlink, - json_metadata, - }, - ] - - operations.push(op_comment) - - const max_accepted_payout = `${payout.toFixed(3)} HBD` - const extensions = [] - - - if (payout === 0) { - extensions.push([ - 0, - { - beneficiaries: - [ - {account: 'null', weight: 10000}, - ], - }, - ]) - } - - - const op_comment_options = [ - 'comment_options', - { - 'author': account, - permlink, - max_accepted_payout, - 'percent_hbd': 10000, - 'allow_votes': true, - 'allow_curation_rewards': true, - extensions, - }, - ] - - operations.push(op_comment_options) - - resolve(operations) - }) - -} - -const footnote = (body) => { - const footnoteAppend = '

    Posted via D.Buzz' - body = `${body} ${footnoteAppend}` - - return body -} - -export const publishPostWithHAS = async (user, body, tags, payout, perm) => { - - let title = stripHtml(body) - title = `${title}`.replace(/(?:https?|ftp):\/\/[\n\S]+/g, '') - title = `${title}`.trim() - - if (title.length > 70) { - title = `${title.substr(0, 70)} ...` - } - - body = footnote(body) - - const permlink = perm ? perm : createPermlink(title) - - const operations = await hasGeneratePostService(user.username, title, tags, body, payout, permlink) - console.log(operations) - hasPostService(operations[0]) - const comment = operations[0] - const json_metadata = comment[1].json_metadata - - let currentDatetime = moment().toISOString() - currentDatetime = currentDatetime.replace('Z', '') - - let cashout_time = moment().add(7, 'days').toISOString() - cashout_time = cashout_time.replace('Z', '') - - let bodyOperation = comment[1].body - bodyOperation = bodyOperation.replace('

    Posted via D.Buzz', '') - - - const content = { - author: user.username, - category: 'hive-193084', - permlink, - title: comment[1].title, - body: bodyOperation, - replies: [], - total_payout_value: '0.000 HBD', - curator_payout_value: '0.000 HBD', - pending_payout_value: '0.000 HBD', - active_votes: [], - root_author: "", - parent_author: null, - parent_permlink: "hive-190384", - root_permlink: permlink, - root_title: title, - json_metadata, - children: 0, - created: currentDatetime, - cashout_time, - max_accepted_payout: `${payout.toFixed(3)} HBD`, - } - - const data = { - author: user.username, - permlink, - content, - } - - return data -} - -export const publishReplyWithHAS = async (username, body, parent_author, parent_permlink, ref, treeHistory) => { - body = footnote(body) - let replyData = {} - const json_metadata = createMeta() - let permlink = createPermlink(body.substring(0, 100)) - permlink = `re-${permlink}` - hasReplyService(username, body, parent_author, parent_permlink, json_metadata, permlink) - let currentDatetime = moment().toISOString() - currentDatetime = currentDatetime.replace('Z', '') - - const reply = { - author: username, - category: 'hive-193084', - permlink: permlink, - title: '', - body: `${body.trim()}`, - replies: [], - total_payout_value: '0.000 HBD', - curator_payout_value: '0.000 HBD', - pending_payout_value: '0.000 HBD', - active_votes: [], - parent_author, - parent_permlink, - root_author: parent_author, - root_permlink: parent_permlink, - children: 0, - created: currentDatetime, - } - - reply.body = reply.body.replace('

    Posted via D.Buzz', '') - - reply.refMeta = { - ref, - author: parent_author, - permlink: parent_permlink, - treeHistory, - } - - replyData = reply - - - const data = { - reply: replyData, - } - - return data -} - // keychain apis export const keychainSignIn = (username) => { @@ -1318,8 +1076,8 @@ export const generateUpdateOperation = (parent_author, parent_permlink, author, export const generateReplyOperation = (account, body, parent_author, parent_permlink) => { const json_metadata = createMeta() - let permlink = createPermlink(body.substring(0, 100)) - permlink = `re-${permlink}` + // let permlink = createPermlink(body.substring(0, 100)) + const permlink = `re-${parent_permlink}` return new Promise((resolve) => { const op_comment = [[ @@ -1536,13 +1294,13 @@ export const searchPostAuthor = (author) => { export const searchPostGeneral = (query) => { return new Promise(async (resolve, reject) => { // const body = {query} - const {tag, sort} = query + const { tag , sort } = query axios({ method: 'POST', url: `${searchUrl}/query`, data: { - query: tag, - sort: sort, + query : tag, + sort : sort, }, }).then(async (result) => { const data = result.data @@ -1550,8 +1308,12 @@ export const searchPostGeneral = (query) => { if (data.results.length !== 0) { const getProfiledata = mapFetchProfile(data.results, false) await Promise.all([getProfiledata]) + data.results = data.results.filter((item) => + item.body.length <= 280 && !item.permlink.startsWith('re-'), + ) + removeFootNote(data.results) - data.results = data.results.filter((item) => item.body.length <= 280) + } resolve(data) @@ -1575,8 +1337,9 @@ export const checkIfImage = (links) => { export const uploadImage = async (data, progress) => { const formData = new FormData() - formData.append('file', data) - + const customImageName = data.name.replace(/ /g, "-") + formData.append('file', data, data.name) + formData.append('customFileName', customImageName) return new Promise(async (resolve, reject) => { try { const response = await axios({ @@ -1597,9 +1360,9 @@ export const uploadImage = async (data, progress) => { reject(error) } }) - } + export const uploadVideo = async (data, username, progress) => { const formData = new FormData() formData.append('username', username) @@ -1640,18 +1403,6 @@ export const getLinkMeta = (url) => { }) } -export const getBestRpcNode = () => { - return new Promise((resolve) => { - axios.get('https://beacon.peakd.com/api/best') - .then(function (result) { - resolve(result.data[0].endpoint) - }) - .catch(function (error) { - resolve(hiveAPINODE) - }) - }) -} - export const checkVersion = () => { return new Promise((resolve) => { axios.get('https://endpoint.d.buzz/version.json') @@ -1746,7 +1497,6 @@ export const generateClaimRewardOperation = (account, reward_hive, reward_hbd, r export const getEstimateAccountValue = (account) => { return new Promise(async (resolve) => { - console.log(account) await formatter.estimateAccountValue(account) .catch(function (err) { console.log(err) diff --git a/src/services/ceramic.js b/src/services/ceramic.js index 18433eca..4ec59cd3 100644 --- a/src/services/ceramic.js +++ b/src/services/ceramic.js @@ -1,36 +1,31 @@ +import { ApolloClient, InMemoryCache } from '@apollo/client' +import { isMobile } from 'react-device-detect' +import { EthereumProvider } from '@walletconnect/ethereum-provider' +import { DID } from 'dids' +import { Ed25519Provider } from 'key-did-provider-ed25519' +import { getResolver } from 'key-did-resolver' +import Web3 from 'web3' +import * as Web3Modal from 'web3modal' +import * as SpkNetwork from '@spknetwork/graph-client' +import axios from "axios" import { CeramicClient } from '@ceramicnetwork/http-client' - -let axios - -import('axios').then((Axios) => { - axios = Axios +import { IDX } from '@ceramicstudio/idx' +import { fromString } from 'uint8arrays' +import { hash } from '@stablelib/sha256' + +// const hosts = [ +// 'https://ceramic.3speak.tv', +// 'https://ceramic.web3telekom.xyz', +// 'https://ceramic-node.vitalpointai.com', +// ] + +export const unionIndexerClient = new ApolloClient({ + uri: 'https://union.us-02.infra.3speak.tv/api/v2/graphql', + cache: new InMemoryCache(), }) -const hosts = [ - 'https://ceramic.3speak.tv', - 'https://ceramic.web3telekom.xyz', - 'https://ceramic-node.vitalpointai.com', -] - export const getBestCeramicHost = async() => { - const hostsWithTimes = [] - const times = [] - let fastestHost = 'https://ceramic.3speak.tv' - await Promise.all( - hosts.map(async (host) => { - try { - const start = Date.now() - await axios.get(`${host}/api/v0/streams/kjzl6cwe1jw149xy2w2qycwts4xjpvyzrkptdw20iui7r486bd6sasqb9tgglzp`) - .then(() => { - const finish = Date.now() - hostsWithTimes.push({host, time: (finish - start) / 1000}) - times.push((finish - start) / 1000) - }) - } catch(err) { return } - })) - .then(() => { - fastestHost = (hostsWithTimes.find(h => h.time === Math.min(...times))).host - }) + const fastestHost = 'https://ceramic.3speak.tv' return fastestHost } @@ -42,48 +37,26 @@ const normalizeAuthSecret = (authSecret64) => { return authSecret } -const providerOptions = { - /* See Provider Options Section */ -} - -let web3Modal -let web3 - -// dynamic imports - -import('web3modal').then((Web3Modal) => { - web3Modal = new Web3Modal.default({ - network: "mainnet", // optional - cacheProvider: true, // optional - providerOptions, // required - }) +const web3Modal = new Web3Modal.default({ + network: "mainnet", + cacheProvider: true, + providerOptions: {}, }) -import('web3').then((Web3) => { - web3 = new Web3.default() -}) +const web3 = new Web3() const idxAliases = { rootPosts: 'ceramic://kjzl6cwe1jw149xy2w2qycwts4xjpvyzrkptdw20iui7r486bd6sasqb9tgglzp', socialConnectionIndex: 'ceramic://kjzl6cwe1jw145f1327br2k7lkd5acrn6d2omh88xjt70ovnju491moahrxddns', } -export const API_NODE = 'https://us-01.infra.3speak.tv' - -let idx -let spk +export const SPK_INDEXER_HOST = 'https://offchain.us-02.infra.3speak.tv' -const Ceramic = new CeramicClient(localStorage.getItem('ceramic') || hosts[0]) - -// dynamic imports -import('@ceramicstudio/idx').then((CeramicStudio) => { - idx = new CeramicStudio.IDX({ceramic: Ceramic, aliases: idxAliases}) -}) -import('@spknetwork/graph-client').then((SpkNetwork) => { - spk = new SpkNetwork.SpkClient(API_NODE, Ceramic) -}) +const ceramicClient = new CeramicClient('https://ceramic.us-02.infra.3speak.tv') +const spk = new SpkNetwork.SpkClient(SPK_INDEXER_HOST, ceramicClient) +const idx = new IDX({ceramic: ceramicClient, aliases: idxAliases}) -window.ceramicclient = Ceramic +window.ceramicclient = ceramicClient window.idxclient = idx window.spkclient = spk @@ -101,23 +74,20 @@ const connectPrompt = async() => { return firstAccount } -const resloveEthDid = async(did) => { - let proof - let verified - const account = await web3.eth.getAccounts().then(data=>data[0]) - await import('3id-blockchain-utils').then(async(utils) => { - proof = await utils.createLink(did, `${account}@eip155:1`, web3.eth.currentProvider) - verified = await utils.validateLink(proof) - }) +// const resolveEthDid = async(did, provider) => { +// const account = await provider.getAccounts().then(data=>data[0]) - // await authenticate(proof.message, `${account}@eip155:1`, web3.eth.currentProvider) +// const proof = await utils.createLink(did, `${account}@eip155:1`, provider.currentProvider) +// const verified = await utils.validateLink(proof) - return verified -} +// await authenticate(proof.message, `${account}@eip155:1`, provider.currentProvider) + +// return verified +// } export const authenticateWithCeramic = (did, secret) => { - Ceramic.setDID(did) + ceramicClient.setDID(did) const ceramicAuth = {authDID: did.id, authSecret: secret} localStorage.setItem('ceramic.auth', JSON.stringify(ceramicAuth)) } @@ -126,7 +96,7 @@ export const reauthenticateWithCeramic = async() => { const auth = JSON.parse(localStorage.getItem('ceramic.auth')) const secret = Object.values(auth.authSecret) const did = await createIdentity(secret) - Ceramic.setDID(did) + ceramicClient.setDID(did) } const getCeramicAuth = () => { @@ -140,62 +110,83 @@ export const checkCeramicLogin = () => { return ceramicAuth } -const createIdentity = async(authSecret) => { - let provider - let resolver - import('key-did-provider-ed25519').then((keyDidProvider) => { - provider = new keyDidProvider.Ed25519Provider(authSecret) - }) - import('key-did-resolver').then((keyDidResolver) => { - resolver = keyDidResolver.getResolver() - }) - let did - await import('dids').then((dids) => { - did = new dids.DID({ provider, resolver }) - }) - - await did.authenticate() - - return did +const createIdentity = async(seed) => { + try { + const provider = new Ed25519Provider(seed) + const did = new DID({ provider, resolver: getResolver() }) + await did.authenticate() + return did + } + catch(err) { + console.log(err) + } } -let hash -let fromString -import('@stablelib/sha256').then((SHA) => { - hash = SHA.hash -}) -import('uint8arrays').then((Unit8Arrays) => { - fromString = Unit8Arrays.fromString -}) - export const loginWithMetaMask = async() => { - const account = await connectPrompt() - const info = await web3.eth.personal.sign('Allow this account to control your identity', account) - const authSecret = normalizeAuthSecret(hash(fromString(info.slice(2)))) + let signedMessage + + const walletConnectProvider = await EthereumProvider.init({ + projectId: '686fee168a35f3cd368400c22a86860a', + chains: [1], + showQrModal: true, + methods: ['personal_sign', 'eth_requestAccounts'], + events: [], + qrModalOptions: { + themeMode: 'light', + themeVariables: { + "--wcm-z-index": 9999, + }, + }, + }) + + if (!isMobile) { + const account = await connectPrompt() + if (account) { + signedMessage = await web3.eth.personal.sign('Allow this account to control your identity', account) + } + } else { + try { + await walletConnectProvider.connect() + const accounts = await walletConnectProvider.request({ method: 'eth_requestAccounts' }) + const account= accounts[0] + signedMessage = await walletConnectProvider.request({ method: 'personal_sign', params: ['Allow this account to control your identity', account] }) + } catch (error) { + console.error("Failed to connect:", error) + } + } - const did = await createIdentity(authSecret) - const proof = await resloveEthDid(did.id) + const authSecret = normalizeAuthSecret(hash(fromString(signedMessage.slice(2)))) + const did = await createIdentity(authSecret) - if(proof) { + if(did) { authenticateWithCeramic(did, authSecret) } - return proof ? did : null + return did ? did : 0 } export const checkForCeramicAccount = (account) => { return (account || '').startsWith('did:key:') } -export const createPostRequest = async(did, title, body) => { - return await spk.createDocument({ - app: 'dBuzz', - title: title, - body: body, - debug_metadata: { - user_id: did, - }, - }) + +export const createPostRequest = async(did, body) => { + try { + return await spk.createDocument({ + title: '', + body: body, + json_metadata: { + app: 'dBuzz', + }, + debug_metadata: { + did: did, + }, + app: 'dBuzz', + }, null) + } + catch(err) { + console.log(err.message) + } } export const updatePostRequest = async(parentId, body) => { @@ -214,11 +205,18 @@ export const replyRequest = async(parentId, did, body) => { }, parentId) } +export const generateHiveCeramicParentId = async (author, permlink) => { + return (await axios.post("https://union.us-02.infra.3speak.tv/api/v1/create_stream_id", { + author, + permlink, + })).data?.stream_id +} + export const getUserPostRequest = async(did) => { const posts = [] if(did) { - const { data } = await axios.post(`${API_NODE}/v1/graphql`, { + const { data } = await axios.post(`${SPK_INDEXER_HOST}/v1/graphql`, { query: ` { publicFeed(parent_id:null, creator_id:"${did}") { @@ -285,7 +283,7 @@ export const getUserPostRequest = async(did) => { export const getChildPostsRequest = async(parentId) => { const posts = [] if(parentId) { - const { data } = await axios.post(`${API_NODE}/v1/graphql`, { + const { data } = await axios.post(`${SPK_INDEXER_HOST}/v1/graphql`, { query: ` { publicFeed(parent_id:"${parentId}") { @@ -392,7 +390,7 @@ export const getSinglePost = async(streamId) => { export const getBasicProfile = async(did) => { let profileData if(did) { - const { data } = await axios.post(`${API_NODE}/v1/graphql`, { + const { data } = await axios.post(`${SPK_INDEXER_HOST}/v1/graphql`, { query: ` { ceramicProfile(userId: "${did}") { @@ -446,15 +444,21 @@ export const setBasicProfile = async(profile) => { }) } -export const getIpfsLink = (hash) => { - return `https://ipfs.io/ipfs/${hash.replace('ipfs://', '')}` +export const getIpfsLink = (hash='') => { + if(!!hash) { + if(hash.startsWith('ipfs://')) { + return `https://ipfs.io/ipfs/${hash.replace('ipfs://', '')}` + } else { + return hash + } + } } export const getFollowingList = async(did) => { let following if(did) { - const { data } = await axios.post(`${API_NODE}/v1/graphql`, { + const { data } = await axios.post(`${SPK_INDEXER_HOST}/v1/graphql`, { query: ` { following(did: "${did}") { @@ -484,7 +488,7 @@ export const getFollowersList = async(did) => { let followers if(did) { - const { data } = await axios.post(`${API_NODE}/v1/graphql`, { + const { data } = await axios.post(`${SPK_INDEXER_HOST}/v1/graphql`, { query: ` { followers(did: "${did}") { @@ -545,7 +549,7 @@ export const getFollowingFeed = async (did) => { const feed = [] if(did) { - const { data } = await axios.post(`${API_NODE}/v1/graphql`, { + const { data } = await axios.post(`${SPK_INDEXER_HOST}/v1/graphql`, { query: ` { followingFeed(did: "${did}") { diff --git a/src/services/helper.js b/src/services/helper.js index 9868f125..7ff1a54a 100644 --- a/src/services/helper.js +++ b/src/services/helper.js @@ -599,16 +599,11 @@ export const calculateAverageRanking = (users) => { export const hiveAPIUrls = [ "https://api.hive.blog", - "https://rpc.ecency.com", "https://hive-api.3speak.tv", - "https://rpc.ausbit.dev", - "https://hived.privex.io", "https://anyx.io", "https://api.deathwing.me", "https://hived.emre.sh", - "https://hive-api.arcange.eu", "https://api.openhive.network", "https://techcoderx.com", - "https://hive.roelandp.nl", "https://api.c0ff33a.uk", ] \ No newline at end of file diff --git a/src/services/theme.js b/src/services/theme.js index 00fca101..44f7f9a5 100644 --- a/src/services/theme.js +++ b/src/services/theme.js @@ -67,6 +67,9 @@ const night = { font: { color: 'rgb(255, 255, 255) !important', }, + hiveValuesFont: { + color: 'white !important', + }, textIcon : { color: 'rgb(136, 153, 166)', }, @@ -248,6 +251,9 @@ const light = { font: { color: 'black !important', }, + hiveValuesFont: { + color: 'black !important', + }, border: { primary: '1px solid #e6ecf0', thick: '10px solid #e6ecf0', diff --git a/src/store/auth/actions.js b/src/store/auth/actions.js index 5c1a45d7..0646d6b0 100644 --- a/src/store/auth/actions.js +++ b/src/store/auth/actions.js @@ -2,9 +2,9 @@ export const AUTHENTICATE_USER_REQUEST = 'AUTHENTICATE_USER_REQUEST' export const AUTHENTICATE_USER_SUCCESS = 'AUTHENTICATE_USER_SUCCESS' export const AUTHENTICATE_USER_FAILURE = 'AUTHENTICATE_USER_FAILURE' -export const authenticateUserRequest = (username, password, useKeychain, useHAS, useCeramic) => ({ +export const authenticateUserRequest = (username, password, useKeychain, useCeramic) => ({ type: AUTHENTICATE_USER_REQUEST, - payload: { username, password, useKeychain, useHAS, useCeramic }, + payload: { username, password, useKeychain, useCeramic }, meta: { thunk: true, }, @@ -45,29 +45,6 @@ export const signoutUserFailure = (error, meta) => ({ meta, }) -export const INIT_WS_HAS_CONNECTION_REQUEST = 'INIT_WS_HAS_CONNECTION_REQUEST' -export const INIT_WS_HAS_CONNECTION_SUCCESS = 'INIT_WS_HAS_CONNECTION_SUCCESS' -export const INIT_WS_HAS_CONNECTION_FAILURE = 'INIT_WS_HAS_CONNECTION_FAILURE' - -export const initWSHASConnectionRequest = () => ({ - type: INIT_WS_HAS_CONNECTION_REQUEST, - meta: { - thunk: true, - }, -}) - -export const initWSHASConnectionSuccess = (response, meta) => ({ - type: INIT_WS_HAS_CONNECTION_SUCCESS, - payload: response, - meta, -}) - -export const initWSHASConnectionFailure = (error, meta) => ({ - type: INIT_WS_HAS_CONNECTION_FAILURE, - payload: error, - meta, -}) - export const INIT_CERAMIC_LOGIN_REQUEST = 'INIT_CERAMIC_LOGIN_REQUEST' export const INIT_CERAMIC_LOGIN_SUCCESS = 'INIT_CERAMIC_LOGIN_SUCCESS' export const INIT_CERAMIC_LOGIN_FAILURE = 'INIT_CERAMIC_LOGIN_FAILURE' @@ -89,13 +66,6 @@ export const initCeremicLoginFailure = (error) => ({ payload: error, }) -export const SET_HAS_QR_CODE = 'SET_HAS_QR_CODE' - -export const setHASQRCode = (qrCode) => ({ - type: SET_HAS_QR_CODE, - payload: qrCode, -}) - export const GET_SAVED_USER_REQUEST = 'GET_SAVED_USER_REQUEST' export const GET_SAVED_USER_SUCCESS = 'GET_SAVED_USER_SUCCESS' diff --git a/src/store/auth/reducers.js b/src/store/auth/reducers.js index d8f3b7a9..c24b1564 100644 --- a/src/store/auth/reducers.js +++ b/src/store/auth/reducers.js @@ -13,7 +13,6 @@ import { SET_ACCOUNT_LIST, SET_HIDDEN_BUZZES, SET_CENSOR_LIST, - SET_HAS_QR_CODE, NEW_CERAMIC_USER_NAME, CERAMIC_USER_NAME_MODAL, } from './actions' @@ -30,7 +29,6 @@ const defaultState = fromJS({ accounts: [], hiddenBuzzes: [], censorList: [], - hasQRCode: {}, ceramicUserNameModal: true, newCeramicUserName: null, }) @@ -65,8 +63,6 @@ export const auth = (state = defaultState, { type, payload }) => { return state.set('hiddenBuzzes', payload) case SET_CENSOR_LIST: return state.set('censorList', payload) - case SET_HAS_QR_CODE: - return state.set('hasQRCode', payload) case CERAMIC_USER_NAME_MODAL: return state.set('ceramicUserNameModal', payload) case NEW_CERAMIC_USER_NAME: diff --git a/src/store/auth/sagas.js b/src/store/auth/sagas.js index d21e065d..ec01d3fa 100644 --- a/src/store/auth/sagas.js +++ b/src/store/auth/sagas.js @@ -68,9 +68,6 @@ import { unfollowBlacklistsSuccess, unfollowBlacklistsFailure, - INIT_WS_HAS_CONNECTION_REQUEST, - initWSHASConnectionSuccess, - initWSHASConnectionFailure, INIT_CERAMIC_LOGIN_REQUEST, initCeremicLoginFailure, initCeremicLoginSuccess, @@ -95,22 +92,19 @@ import { generateUnblacklistOperation, generateFollowBlacklistsOperation, generateUnfollowBlacklistsOperation, - hiveAuthenticationService, } from 'services/api' import { generateSession, readSession, errorMessageComposer} from 'services/helper' import { checkCeramicLogin, getBasicProfile, loginWithMetaMask, reauthenticateWithCeramic } from 'services/ceramic' -import FingerprintJS from '@fingerprintjs/fingerprintjs' - function* authenticateUserRequest(payload, meta) { - const { password, useKeychain, useHAS, useCeramic } = payload + const { password, useKeychain, useCeramic } = payload let { username } = payload username = `${username}`.toLowerCase() // console.log('AUTHENTICATING') - const user = { username, useKeychain, useHAS, useCeramic, is_authenticated: false, is_subscribe: false } + const user = { username, useKeychain, useCeramic, is_authenticated: false, is_subscribe: false } let users = yield call([localStorage, localStorage.getItem], 'user') let accounts = yield call([localStorage, localStorage.getItem], 'accounts') @@ -136,69 +130,6 @@ function* authenticateUserRequest(payload, meta) { if(data.success) { user.is_authenticated = true } - } else if(useHAS) { - yield call(hiveAuthenticationService, username) - import('@mintrawa/hive-auth-client').then(({hacMsg}) => { - hacMsg.subscribe((m) => { - /** generate QR Code */ - if (m.type === 'qr_code') { - const hasQRCode = "has://auth_req/" + (m).msg - localStorage.setItem('hasQRcode', hasQRCode) - } - - /** recieved authentication msg */ - if (m.type === 'authentication') { - - console.log('%c[HAC authentication msg]', 'color: goldenrod', m) - - /** Authentication approved */ - if (m.msg?.status === "authentified") { - user.is_authenticated = true - - const is_subscribe = getCommunityRole(username) - user.is_subscribe = is_subscribe - user.active = true - - // let mutelist = fetchMuteList(username) - - // mutelist = [...new Set(mutelist.map(item => item.following))] - - // setMuteList(mutelist) - - const session = generateSession(user) - - const accountIndex = accounts.findIndex(item => item.username === username) - - if(accountIndex === -1) { - accounts.push({ username, keychain: useKeychain, has: useHAS, cermic: useCeramic }) - } else { - accounts[accountIndex].keychain = useKeychain - } - - users.push(session) - localStorage.removeItem('hasQRcode') - localStorage.setItem('current', username) - localStorage.setItem('user', JSON.stringify(users)) - localStorage.setItem('active', username) - localStorage.setItem('accounts', JSON.stringify(accounts)) - localStorage.setItem('drafts', JSON.stringify(JSON.parse(drafts))) - setAccountList(accounts) - - authenticateUserSuccess(user, meta) - const origin = window.location.origin - window.location.href = origin + '#/trending' - - /** Authentication rejected */ - } else if (m.msg?.status === "rejected") { - window.location.reload() - - /** Authentication error */ - } else { - window.location.reload() - } - } - }) - }) } else if(useCeramic) { // sign in with meta mask console.log('logging in with ceramic + meta mask!') @@ -229,7 +160,7 @@ function* authenticateUserRequest(payload, meta) { const accountIndex = accounts.findIndex(item => item.username === username) if(accountIndex === -1) { - accounts.push({ username: did.id, keychain: useKeychain, has: useHAS, cermic: useCeramic }) + accounts.push({ username: did.id, keychain: useKeychain, cermic: useCeramic }) } else { accounts[accountIndex].keychain = useKeychain } @@ -282,7 +213,7 @@ function* authenticateUserRequest(payload, meta) { const accountIndex = accounts.findIndex(item => item.username === username) if(accountIndex === -1) { - accounts.push({ username, keychain: useKeychain, has: useHAS, ceramic: useCeramic }) + accounts.push({ username, keychain: useKeychain, ceramic: useCeramic }) } else { accounts[accountIndex].keychain = useKeychain } @@ -312,7 +243,7 @@ function* authenticateUserRequest(payload, meta) { const accountIndex = accounts.findIndex(item => item.username === username) if(accountIndex === -1) { - accounts.push({ username, keychain: useKeychain, has: useHAS, ceramic: useCeramic }) + accounts.push({ username, keychain: useKeychain, ceramic: useCeramic }) } else { accounts[accountIndex].keychain = useKeychain } @@ -340,7 +271,7 @@ function* authenticateUserRequest(payload, meta) { } function* getSavedUserRequest (meta) { - let user = { username: '', useKeychain: false, useHAS: false, is_authenticated: false, useCeramic: false } + let user = { username: '', useKeychain: false, is_authenticated: false, useCeramic: false } try { let saved = yield call([localStorage, localStorage.getItem], 'user') let active = yield call([localStorage, localStorage.getItem], 'active') @@ -383,16 +314,6 @@ function* getSavedUserRequest (meta) { reauthenticateWithCeramic() } } - - if(user.useHAS) { - import('@mintrawa/hive-auth-client').then((HiveAuth) => { - if(HiveAuth.hacGetAccounts().length === 0) { - alert('Your HiveAuth session has been expired. Please login again.') - signoutUserRequest() - localStorage.clear() - } - }) - } if(user.useCeramic) { const auth = JSON.parse(localStorage.getItem('ceramic.auth')) @@ -425,67 +346,6 @@ function* getSavedUserRequest (meta) { } } -function* initWSHASConnectionRequest(meta) { - const ceramicAuth = checkCeramicLogin() - const active = localStorage.getItem('active') - const accounts = JSON.parse(localStorage.getItem('accounts')) - - if(!ceramicAuth) { - try { - const fingerPrintRequest = FingerprintJS.load({ monitoring: false }) - - fingerPrintRequest.then(fingerPrint => fingerPrint.get()) - .then(async(result) => { - sessionStorage.setItem('hacPwd', result.visitorId) - const current = localStorage.getItem('active') - let HiveAuthClient - let hacGetAccounts - let hacGetConnectionStatus - await import('@mintrawa/hive-auth-client').then((HiveAuth) => { - HiveAuthClient = HiveAuth.HiveAuthClient - hacGetAccounts = HiveAuth.hacGetAccounts - hacGetConnectionStatus = HiveAuth.hacGetConnectionStatus - }) - - if (current && accounts[0].has) { - const hacAccount = hacGetAccounts(current, result.visitorId) - - if (hacAccount[0]) { - const has_expire = hacAccount[0].has?.has_expire - const expire = has_expire ? new Date(has_expire) : 1 - - console.log('expire', expire) - - hacGetConnectionStatus() - const result = HiveAuthClient(hacAccount[0].has ? [hacAccount[0].has.has_server] : undefined, { debug: true, delay: 3000 }) - initWSHASConnectionSuccess(result, meta) - // window.location.href('') - } else { - hacGetConnectionStatus() - /** clear hac value and localstorage */ - if(active === 'null') { - localStorage.clear() - } - const result = HiveAuthClient(undefined, { debug: true, delay: 3000 }) - initWSHASConnectionSuccess(result, meta) - } - } else { - hacGetConnectionStatus() - if(active === 'null') { - localStorage.clear() - } - const result = HiveAuthClient(undefined, { debug: true, delay: 3000 }) - initWSHASConnectionSuccess(result, meta) - } - - }).catch( error => console.log(error)) - - } catch(error) { - yield put(initWSHASConnectionFailure(error, meta)) - } - } -} - function* initCeramicLoginRequest() { const current = localStorage.getItem('active') const ceramicAuth = checkCeramicLogin() @@ -510,7 +370,7 @@ function* initCeramicLoginRequest() { function* signoutUserRequest(meta) { try { - const user = { username: '', useKeychain: false, useHAS: false, is_authenticated: false, useCeramic: false } + const user = { username: '', useKeychain: false, is_authenticated: false, useCeramic: false } const lastUser = yield call([localStorage, localStorage.getItem], 'current') const voteWeight = yield call([localStorage, localStorage.getItem], 'voteWeight') @@ -886,10 +746,6 @@ function* watchGetSavedUserRequest({ meta }) { yield call(getSavedUserRequest, meta) } -function* watchInitWSHASConnectionRequest({ meta }) { - yield call(initWSHASConnectionRequest, meta) -} - function* watchInitCeramicLoginRequest() { yield call(initCeramicLoginRequest) } @@ -946,7 +802,6 @@ export default function* sagas() { yield takeEvery(AUTHENTICATE_USER_REQUEST, watchAuthenticateUserRequest) yield takeEvery(SIGNOUT_USER_REQUEST, watchSignoutUserRequest) yield takeEvery(GET_SAVED_USER_REQUEST, watchGetSavedUserRequest) - yield takeEvery(INIT_WS_HAS_CONNECTION_REQUEST, watchInitWSHASConnectionRequest) yield takeEvery(INIT_CERAMIC_LOGIN_REQUEST, watchInitCeramicLoginRequest) yield takeEvery(SUBSCRIBE_REQUEST, watchSubscribeRequest) yield takeEvery(CHECK_HAS_UPDATE_AUTHORITY_REQUEST, watchCheckHasUpdateAuthorityRequest) diff --git a/src/store/polling/actions.js b/src/store/polling/actions.js index f4975f66..ad69f405 100644 --- a/src/store/polling/actions.js +++ b/src/store/polling/actions.js @@ -44,22 +44,4 @@ export const filterNotificationsFailure = (error, meta) => ({ type: FILTER_NOTIFICATIONS_FAILURE, payload: error, meta, -}) - -export const HAS_CONNECTION_REQUEST = 'HAS_CONNECTION_REQUEST' -export const HAS_CONNECTION_SUCCESS = 'HAS_CONNECTION_SUCCESS' -export const HAS_CONNECTION_FAILURE = 'HAS_CONNECTION_FAILURE' - -export const hasConnectionRequest = () => ({ - type: HAS_CONNECTION_REQUEST, -}) - -export const hasConnectionSuccess = (response) => ({ - type: HAS_CONNECTION_SUCCESS, - payload: response, -}) - -export const hasConnectionFailure = (error) => ({ - type: HAS_CONNECTION_FAILURE, - payload: error, }) \ No newline at end of file diff --git a/src/store/posts/sagas.js b/src/store/posts/sagas.js index 5adb04ce..89ee7a50 100644 --- a/src/store/posts/sagas.js +++ b/src/store/posts/sagas.js @@ -108,8 +108,6 @@ import { invokeMuteFilter, getMutePattern, uploadVideo, - hasFollowService, - hasUnFollowService, fetchSingleProfile, searchHiveTags, searchPostGeneral, @@ -124,7 +122,6 @@ import { getFollowingFeed, getSinglePost, } from "services/ceramic" -import {broadcastNotification} from "store/interface/actions" const footnote = (body) => { const footnoteAppend = '

    Posted via D.Buzz' @@ -311,7 +308,7 @@ function* getHomePostsRequest(payload, meta) { const user = yield select(state => state.auth.get('user')) const {username: account} = user - const params = {sort: 'feed', account, limit: 20, start_permlink, start_author} + const params = {sort: 'feed', account, limit: 50, start_permlink, start_author} const method = 'get_account_posts' try { @@ -356,7 +353,8 @@ function* getLatestPostsRequest(payload, meta) { const censoredList = yield select(state => state.auth.get('censorList')) const {start_permlink, start_author} = payload - const params = {sort: 'created', start_permlink, start_author, limit: 50} + + const params = {sort: 'created', start_permlink, start_author, limit: 20} const method = 'get_ranked_posts' try { @@ -707,206 +705,98 @@ function* getSearchTags(payload, meta) { function* followRequest(payload, meta) { const {following} = payload const user = yield select(state => state.auth.get('user')) - const {username, useKeychain, is_authenticated, useHAS} = user + const {username, useKeychain, is_authenticated} = user const operation = yield call(generateFollowOperation, username, following) let success = false + try { - if (useHAS && is_authenticated) { - let recentFollows = yield select(state => state.posts.get('hasBeenRecentlyFollowed')) - let recentUnfollows = yield select(state => state.posts.get('hasBeenRecentlyUnfollowed')) - - yield call(hasFollowService, username, following) - - import('@mintrawa/hive-auth-client').then((HiveAuth) => { - HiveAuth.hacMsg.subscribe(m => { - broadcastNotification('warning', 'Please open Hive Keychain app on your phone and confirm the transaction.', 600000) - if (m.type === 'sign_wait') { - console.log('%c[HAC Sign wait]', 'color: goldenrod', m.msg ? m.msg.uuid : null) - } - - if (m.type === 'tx_result') { - console.log('%c[HAC Sign result]', 'color: goldenrod', m.msg ? m.msg : null) - if (m.msg?.status === 'accepted') { - if (!Array.isArray(recentUnfollows)) { - recentUnfollows = [] - } else { - const index = recentUnfollows.findIndex((item) => item === following) - if (index) { - recentUnfollows.splice(index, 1) - } - } - - if (!Array.isArray(recentFollows)) { - recentFollows = [] - } - recentFollows.push(following) - setHasBeenFollowedRecently(recentFollows) - setHasBeenUnfollowedRecently(recentUnfollows) - - } else if (m.msg?.status === 'error') { - const error = m.msg?.status.error - - followFailure(error, meta) - } - - } - - }) - }) - - - if (success) { - console.log('succe', success) - yield put(followSuccess(success, meta)) + if (useKeychain && is_authenticated) { + const result = yield call(broadcastKeychainOperation, username, operation) + success = result.success } else { - const error = 'transaction error' - yield put(followFailure(error, meta)) + let {login_data} = user + login_data = extractLoginData(login_data) + + const wif = login_data[1] + const result = yield call(broadcastOperation, operation, [wif]) + success = result.success } - } else { - try { + if (success) { + let recentFollows = yield select(state => state.posts.get('hasBeenRecentlyFollowed')) + let recentUnfollows = yield select(state => state.posts.get('hasBeenRecentlyUnfollowed')) - if (useKeychain) { - const result = yield call(broadcastKeychainOperation, username, operation) - success = result.success + if (!Array.isArray(recentUnfollows)) { + recentUnfollows = [] } else { - let {login_data} = user - login_data = extractLoginData(login_data) - - const wif = login_data[1] - const result = yield call(broadcastOperation, operation, [wif]) - success = result.success - } - - if (success) { - let recentFollows = yield select(state => state.posts.get('hasBeenRecentlyFollowed')) - let recentUnfollows = yield select(state => state.posts.get('hasBeenRecentlyUnfollowed')) - - if (!Array.isArray(recentUnfollows)) { - recentUnfollows = [] - } else { - const index = recentUnfollows.findIndex((item) => item === following) - if (index) { - recentUnfollows.splice(index, 1) - } - } - - if (!Array.isArray(recentFollows)) { - recentFollows = [] + const index = recentUnfollows.findIndex((item) => item === following) + if (index) { + recentUnfollows.splice(index, 1) } - recentFollows.push(following) - yield put(setHasBeenFollowedRecently(recentFollows)) - yield put(setHasBeenUnfollowedRecently(recentUnfollows)) } - yield put(followSuccess(success, meta)) - } catch (error) { - yield put(followFailure(error, meta)) + if (!Array.isArray(recentFollows)) { + recentFollows = [] + } + recentFollows.push(following) + yield put(setHasBeenFollowedRecently(recentFollows)) + yield put(setHasBeenUnfollowedRecently(recentUnfollows)) } + + yield put(followSuccess(success, meta)) + } catch (error) { + yield put(followFailure(error, meta)) } } function* unfollowRequest(payload, meta) { const {following} = payload const user = yield select(state => state.auth.get('user')) - const {username, useKeychain, useHAS, is_authenticated} = user + const {username, useKeychain, is_authenticated} = user const operation = yield call(generateUnfollowOperation, username, following) let success = false - if (useHAS && is_authenticated) { - let recentFollows = yield select(state => state.posts.get('hasBeenRecentlyFollowed')) - let recentUnfollows = yield select(state => state.posts.get('hasBeenRecentlyUnfollowed')) - yield call(hasUnFollowService, username, following) - - import('@mintrawa/hive-auth-client').then((HiveAuth) => { - HiveAuth.hacMsg.subscribe(m => { - broadcastNotification('warning', 'Please open Hive Keychain app on your phone and confirm the transaction.', 600000) - if (m.type === 'sign_wait') { - console.log('%c[HAC Sign wait]', 'color: goldenrod', m.msg ? m.msg.uuid : null) - } - - if (m.type === 'tx_result') { - console.log('%c[HAC Sign result]', 'color: goldenrod', m.msg ? m.msg : null) - if (m.msg?.status === 'accepted') { - if (!Array.isArray(recentFollows)) { - recentFollows = [] - } else { - const index = recentFollows.findIndex((item) => item === following) - if (index) { - recentFollows.splice(index, 1) - } - } - - if (!Array.isArray(recentUnfollows)) { - recentUnfollows = [] - } - recentUnfollows.push(following) - - setHasBeenFollowedRecently(recentFollows) - setHasBeenUnfollowedRecently(recentUnfollows) - - } else if (m.msg?.status === 'error') { - const error = m.msg?.status.error - - followFailure(error, meta) - } - - } + try { + if (useKeychain && is_authenticated) { + const result = yield call(broadcastKeychainOperation, username, operation) + success = result.success + } else { + let {login_data} = user + login_data = extractLoginData(login_data) - }) - }) + const wif = login_data[1] + const result = yield call(broadcastOperation, operation, [wif]) + success = result.success + } if (success) { - console.log('success', success) - yield put(unfollowSuccess(success, meta)) - } else { - const error = 'transaction error' - yield put(unfollowFailure(error, meta)) - } + let recentFollows = yield select(state => state.posts.get('hasBeenRecentlyFollowed')) + let recentUnfollows = yield select(state => state.posts.get('hasBeenRecentlyUnfollowed')) - } else { - try { - if (useKeychain) { - const result = yield call(broadcastKeychainOperation, username, operation) - success = result.success + if (!Array.isArray(recentFollows)) { + recentFollows = [] } else { - let {login_data} = user - login_data = extractLoginData(login_data) - - const wif = login_data[1] - const result = yield call(broadcastOperation, operation, [wif]) - success = result.success - } - - if (success) { - let recentFollows = yield select(state => state.posts.get('hasBeenRecentlyFollowed')) - let recentUnfollows = yield select(state => state.posts.get('hasBeenRecentlyUnfollowed')) - - if (!Array.isArray(recentFollows)) { - recentFollows = [] - } else { - const index = recentFollows.findIndex((item) => item === following) - if (index) { - recentFollows.splice(index, 1) - } - } - - if (!Array.isArray(recentUnfollows)) { - recentUnfollows = [] + const index = recentFollows.findIndex((item) => item === following) + if (index) { + recentFollows.splice(index, 1) } - recentUnfollows.push(following) + } - yield put(setHasBeenFollowedRecently(recentFollows)) - yield put(setHasBeenUnfollowedRecently(recentUnfollows)) + if (!Array.isArray(recentUnfollows)) { + recentUnfollows = [] } + recentUnfollows.push(following) - yield put(unfollowSuccess(success, meta)) - } catch (error) { - yield put(unfollowFailure(error, meta)) + yield put(setHasBeenFollowedRecently(recentFollows)) + yield put(setHasBeenUnfollowedRecently(recentUnfollows)) } + + yield put(unfollowSuccess(success, meta)) + } catch (error) { + yield put(unfollowFailure(error, meta)) } } diff --git a/src/store/profile/sagas.js b/src/store/profile/sagas.js index 9b3cd4f1..a446024a 100644 --- a/src/store/profile/sagas.js +++ b/src/store/profile/sagas.js @@ -80,7 +80,6 @@ import { getAccountLists, checkAccountIsFollowingLists, generateUpdateAccountOperation, - hasClearNotificationService, } from 'services/api' import {censorLinks, errorMessageComposer} from "services/helper" @@ -188,6 +187,7 @@ function* getAccountPostRequest(payload, meta) { return arr.map(mapObj => mapObj['post_id']).indexOf(obj['post_id']) === pos }) + data = data.filter(item => invokeFilter(item)) const censoredList = yield select(state => state.auth.get('censorList')) @@ -299,87 +299,44 @@ function* getFollowingRequest(payload, meta) { function* clearNotificationRequest(meta) { const user = yield select(state => state.auth.get('user')) const notifications = yield select(state => state.polling.get('notifications')) - const {username, useKeychain, useHAS, is_authenticated} = user + const {username, useKeychain} = user const lastNotification = notifications[0] let success = false - if (useHAS && is_authenticated) { - yield call(hasClearNotificationService, username, lastNotification) - success = true - - import('@mintrawa/hive-auth-client').then((HiveAuth) => { - HiveAuth.hacMsg.subscribe(m => { - - if (m.type === 'sign_wait') { - console.log('%c[HAC Sign wait]', 'color: goldenrod', m.msg ? m.msg.uuid : null) - } - - if (m.type === 'tx_result') { - console.log('%c[HAC Sign result]', 'color: goldenrod', m.msg ? m.msg : null) - if (m.msg?.status === 'accepted') { - success = true - - } else if (m.msg?.status === 'error') { - const error = m.msg?.status.error - - clearNotificationsFailure(error, meta) - } + try { - } + const operation = yield call(generateClearNotificationOperation, username, lastNotification) - }) - }) - - let old = yield select(state => state.polling.get('count')) + if (lastNotification.length !== 0) { + if (useKeychain) { + const result = yield call(broadcastKeychainOperation, username, operation) + success = result.success + } else { + let {login_data} = user + login_data = extractLoginData(login_data) - if (success) { - old = { - success: true, - lastread: '', - unread: 0, + const wif = login_data[1] + const result = yield call(broadcastOperation, operation, [wif]) + success = result.success } - } else { - old.success = success - } - - yield put(clearNotificationsSuccess(old, meta)) - } else { - try { - - const operation = yield call(generateClearNotificationOperation, username, lastNotification) - - if (lastNotification.length !== 0) { - if (useKeychain) { - const result = yield call(broadcastKeychainOperation, username, operation) - success = result.success - } else { - let {login_data} = user - login_data = extractLoginData(login_data) + let old = yield select(state => state.polling.get('count')) - const wif = login_data[1] - const result = yield call(broadcastOperation, operation, [wif]) - success = result.success + if (success) { + old = { + success: true, + lastread: '', + unread: 0, } - - let old = yield select(state => state.polling.get('count')) - - if (success) { - old = { - success: true, - lastread: '', - unread: 0, - } - } else { - old.success = success - } - yield put(clearNotificationsSuccess(old, meta)) } else { - yield put(clearNotificationsFailure('failed to clear notification', meta)) + old.success = success } - } catch (error) { - yield put(clearNotificationsFailure(error, meta)) + yield put(clearNotificationsSuccess(old, meta)) + } else { + yield put(clearNotificationsFailure('failed to clear notification', meta)) } + } catch (error) { + yield put(clearNotificationsFailure(error, meta)) } } diff --git a/src/store/settings/actions.js b/src/store/settings/actions.js index 42ee3c8f..5229b0a3 100644 --- a/src/store/settings/actions.js +++ b/src/store/settings/actions.js @@ -52,28 +52,12 @@ export const generateStyles = (theme) => ({ payload: theme, }) -export const GET_WS_NODE_HAS = 'GET_WS_NODE_HAS' -export const SET_WS_NODE_HAS = 'SET_WS_NODE_HAS' - -export const getWSNodeHAS = () => ({ - type: GET_WS_NODE_HAS, - meta: { - thunk: true, - }, -}) - -export const setWSNodeHAS = (response, meta) => ({ - type: SET_WS_NODE_HAS, - payload: response, - meta, -}) - -export const GET_BEST_RPC_NODE = 'GET_BEST_RPC_NODE' +export const GET_RPC_NODE = 'GET_RPC_NODE' export const SET_RPC_NODE = 'SET_RPC_NODE' -export const getBestRpcNode = () => ({ - type: GET_BEST_RPC_NODE, +export const getRpcNode = () => ({ + type: GET_RPC_NODE, meta: { thunk: true, }, diff --git a/src/store/settings/reducers.js b/src/store/settings/reducers.js index 3f6df945..99d9f96e 100644 --- a/src/store/settings/reducers.js +++ b/src/store/settings/reducers.js @@ -12,7 +12,7 @@ import config from 'config' const defaultState = fromJS({ theme: {}, themeStyles: {}, - rpcNode: config.HIVE_API_NODE, + rpcNode: config.DEFAULT_RPC_NODE, censorTypes: [], defaultVoteWeight: 1, }) diff --git a/src/store/settings/sagas.js b/src/store/settings/sagas.js index 16219134..c57892b6 100644 --- a/src/store/settings/sagas.js +++ b/src/store/settings/sagas.js @@ -9,7 +9,7 @@ import { setThemeSuccess, setThemeFailure, - GET_BEST_RPC_NODE, + GET_RPC_NODE, setRpcNode, CHECK_VERSION_REQUEST, @@ -24,16 +24,13 @@ import { SET_DEFAULT_VOTING_WEIGHT_REQUEST, setDefaultVotingWeightSuccess, - - GET_WS_NODE_HAS, - setWSNodeHAS, } from './actions' import { checkVersion, getCensorTypes, censorBuzz, - geRPCNode, + getActiveRPCNode, } from 'services/api' import config from 'config' @@ -78,21 +75,14 @@ function* checkVersionRequest(meta) { yield put(checkVersionSuccess(latest, meta)) } -function* getBestRPCNode(meta) { - const node = yield call(geRPCNode) +function* getRPCNode(meta) { + const node = yield call(getActiveRPCNode) yield call([localStorage, localStorage.setItem], 'rpc-node', node) yield put(setRpcNode(node, meta)) } -function* getWSNodeHASRequest(meta) { - const hasServer = config.HAS_WS - yield call([localStorage, localStorage.setItem], 'websocketHAS', hasServer) - - yield put(setWSNodeHAS(hasServer, meta)) -} - function* getCensorTypesRequest(meta) { const types = yield call(getCensorTypes) yield put(getCensorTypesSuccess(types, meta)) @@ -129,12 +119,8 @@ function* watchSetThemeRequest({ payload, meta }) { yield call(setThemeRequest, payload ,meta) } -function* watchGetBestRPCNode({ meta }) { - yield call(getBestRPCNode, meta) -} - -function* watchGetWSNodeHAS({ meta }) { - yield call(getWSNodeHASRequest, meta) +function* watchGetRPCNode({ meta }) { + yield call(getRPCNode, meta) } function* watchCheckVersionRequest({ meta }) { @@ -156,8 +142,7 @@ function* watchSetDefaultVotingWeightRequest({ payload, meta }) { export default function* sagas() { yield takeEvery(GET_SAVED_THEME_REQUEST, watchGetSavedThemeRequest) yield takeEvery(SET_THEME_REQUEST, watchSetThemeRequest) - yield takeEvery(GET_BEST_RPC_NODE, watchGetBestRPCNode) - yield takeEvery(GET_WS_NODE_HAS, watchGetWSNodeHAS) + yield takeEvery(GET_RPC_NODE, watchGetRPCNode) yield takeEvery(CHECK_VERSION_REQUEST, watchCheckVersionRequest) yield takeEvery(GET_CENSOR_TYPES_REQUEST, watchGetCensorTypesRequest) yield takeEvery(CENSOR_BUZZ_REQUEST, watchCensorBuzzRequest) diff --git a/yarn.lock b/yarn.lock index e6979561..a158419b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2621,16 +2621,6 @@ prop-types "^15.7.2" react-is "^16.8.0 || ^17.0.0" -"@mintrawa/hive-auth-client@^0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@mintrawa/hive-auth-client/-/hive-auth-client-0.0.8.tgz#f9c624a0db5d9640372d9c8bbaf43f9a9a948846" - integrity sha512-UxAWSKLV43wFjAuI1FKZ3qgR/OV8tAm+qa/9Xl16RgE/fcWzxN2w3cK68rPdFoamz45YhsbZJ+5n28beGjknLQ== - dependencies: - buffer "^6.0.3" - crypto-js "^4.1.1" - rxjs "^7.5.1" - uuid "^8.3.2" - "@motionone/animation@^10.15.1": version "10.15.1" resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.15.1.tgz#4a85596c31cbc5100ae8eb8b34c459fb0ccf6807" @@ -2987,6 +2977,95 @@ "@noble/hashes" "~1.3.0" "@scure/base" "~1.1.0" +"@sentry-internal/feedback@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.102.1.tgz#747f88c2881c76fddd16bce57cc4bc17b4c2af93" + integrity sha512-vY4hpLLMNLjICtWiizc7KeGbWOTUMGrF7C+9dPCztZww3CLgzWy9A7DvPj5hodRiYzpdRnAMl8yQnMFbYXh7bA== + dependencies: + "@sentry/core" "7.102.1" + "@sentry/types" "7.102.1" + "@sentry/utils" "7.102.1" + +"@sentry-internal/replay-canvas@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-7.102.1.tgz#f098814ce21fdf95ef6d440d7ff8a6d3bfe73054" + integrity sha512-GUX4RWI10uRjdjeyvCLtAAhWRVqnAnG6+yNxWfqUQ3qMA7B7XxG43KT2UhSnulmErNzODQ6hA68rGPwwYeRIww== + dependencies: + "@sentry/core" "7.102.1" + "@sentry/replay" "7.102.1" + "@sentry/types" "7.102.1" + "@sentry/utils" "7.102.1" + +"@sentry-internal/tracing@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.102.1.tgz#5c39c8f04a4a1a665fb6d368e1cd13605152f18b" + integrity sha512-RkFlFyAC0fQOvBbBqnq0CLmFW5m3JJz9pKbZd5vXPraWAlniKSb1bC/4DF9SlNx0FN1LWG+IU3ISdpzwwTeAGg== + dependencies: + "@sentry/core" "7.102.1" + "@sentry/types" "7.102.1" + "@sentry/utils" "7.102.1" + +"@sentry/browser@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.102.1.tgz#30d3da587b2b6542b3d9e39d923ed28a2704d454" + integrity sha512-7BOfPBiM7Kp6q/iy0JIbsBTxIASV+zWXByqqjuEMWGj3X2u4oRIfm3gv4erPU/l+CORQUVQZLSPGoIoM1gbB/A== + dependencies: + "@sentry-internal/feedback" "7.102.1" + "@sentry-internal/replay-canvas" "7.102.1" + "@sentry-internal/tracing" "7.102.1" + "@sentry/core" "7.102.1" + "@sentry/replay" "7.102.1" + "@sentry/types" "7.102.1" + "@sentry/utils" "7.102.1" + +"@sentry/core@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.102.1.tgz#855d37b6bba9986a9380864c823e696d3fc5aa01" + integrity sha512-QjY+LSP3du3J/C8x/FfEbRxgZgsWd0jfTJ4P7s9f219I1csK4OeBMC3UA1HwEa0pY/9OF6H/egW2CjOcMM5Pdg== + dependencies: + "@sentry/types" "7.102.1" + "@sentry/utils" "7.102.1" + +"@sentry/react@^7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.102.1.tgz#c4ef94be7ee7ee4267d513ddccd29ce63f16e48f" + integrity sha512-X4j2DgbktlEifnd21YJKCayAmff5hnaS+9MNz9OonEwD0ARi0ks7bo0wtWHMjPK20992MO+JwczVg/1BXJYDdQ== + dependencies: + "@sentry/browser" "7.102.1" + "@sentry/core" "7.102.1" + "@sentry/types" "7.102.1" + "@sentry/utils" "7.102.1" + hoist-non-react-statics "^3.3.2" + +"@sentry/replay@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.102.1.tgz#d6c17332d14dc312b124bbbda8f35d6a982b893c" + integrity sha512-HR/j9dGIvbrId8fh8mQlODx7JrhRmawEd9e9P3laPtogWCg/5TI+XPb2VGSaXOX9VWtb/6Z2UjHsaGjgg6YcuA== + dependencies: + "@sentry-internal/tracing" "7.102.1" + "@sentry/core" "7.102.1" + "@sentry/types" "7.102.1" + "@sentry/utils" "7.102.1" + +"@sentry/tracing@^7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.102.1.tgz#6594b1b01e0fd1201dca2a12f32919bd9b5d8a4d" + integrity sha512-9VQEox0R7ouhhUVHtBwlGlXG5beDCM/Uo0BY+G0M1H03aFJsLAwnxPNeWnK3WvPejxf94EgdimKMjDjv9l2Sbg== + dependencies: + "@sentry-internal/tracing" "7.102.1" + +"@sentry/types@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.102.1.tgz#18c35f32ecbd12afb9860ca2de7bfff542d10b27" + integrity sha512-htKorf3t/D0XYtM7foTcmG+rM47rDP6XdbvCcX5gBCuCYlzpM1vqCt2rl3FLktZC6TaIpFRJw1TLfx6m+x5jdA== + +"@sentry/utils@7.102.1": + version "7.102.1" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.102.1.tgz#45ddcdf2e700d40160347bbdf4233aff3179d398" + integrity sha512-+8WcFjHVV/HROXSAwMuUzveElBFC43EiTG7SNEBNgOUeQzQVTmbUZXyTVgLrUmtoWqvnIxCacoLxtZo1o67kdg== + dependencies: + "@sentry/types" "7.102.1" + "@sinclair/typebox@^0.24.1": version "0.24.51" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" @@ -6305,7 +6384,7 @@ crypto-browserify@^3.12.0: randombytes "^2.0.0" randomfill "^1.0.3" -crypto-js@4.2.0, crypto-js@^4.1.1, crypto-js@^4.2.0: +crypto-js@4.2.0, crypto-js@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== @@ -12924,20 +13003,6 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qr.js@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f" - integrity sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ== - -qrcode.react@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-1.0.1.tgz#2834bb50e5e275ffe5af6906eff15391fe9e38a5" - integrity sha512-8d3Tackk8IRLXTo67Y+c1rpaiXjoz/Dd2HpcMdW//62/x8J1Nbho14Kh8x974t9prsLHN6XqVgcnRiBGFptQmg== - dependencies: - loose-envify "^1.4.0" - prop-types "^15.6.0" - qr.js "0.0.0" - qrcode@1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.3.tgz#03afa80912c0dccf12bc93f615a535aad1066170" @@ -14025,7 +14090,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^7.0.0, rxjs@^7.5.1, rxjs@^7.5.2, rxjs@^7.5.4: +rxjs@^7.0.0, rxjs@^7.5.2, rxjs@^7.5.4: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==