From f05461847eff32b1252854690664d74e4d294fd7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Wed, 6 Jul 2022 18:14:37 +0200
Subject: [PATCH 01/20] basic sort and filter algorithm
---
contracts/PointSocial.sol | 220 ++++++++---
src/components/feed/Feed.jsx | 301 +++++++-------
tests/unit/smartcontracts/PointSocial.js | 475 +++++++++++------------
3 files changed, 546 insertions(+), 450 deletions(-)
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index 04868e2..8f41a50 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -64,6 +64,14 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
event ProfileChange(address indexed from, uint256 indexed date);
+ event MultiplersChanged(
+ address indexed from,
+ uint256 timestamp,
+ uint256 likesWeightMultiplier,
+ uint256 dislikesWeightWultiplier,
+ uint256 newWeightMultiplier
+ );
+
address private _identityContractAddr;
string private _identityHandle;
@@ -120,13 +128,48 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 dislikesCount;
bool liked;
bool disliked;
+ int256 weight;
}
+ uint256 public likesWeightMultiplier;
+ uint256 public dislikesWeightWultiplier;
+ uint256 public newWeightMultiplier;
+ uint256 public weightThreshold;
+
modifier postExists(uint256 _postId) {
require(postById[_postId].from != address(0), "Post does not exist");
_;
}
+ modifier onlyDeployer() {
+ require(
+ IIdentity(_identityContractAddr).isIdentityDeployer(
+ _identityHandle,
+ msg.sender
+ ),
+ "Not a deployer"
+ );
+ _;
+ }
+
+ function setWeights(
+ uint256 _likesWeightMultiplier,
+ uint256 _dislikesWeightWultiplier,
+ uint256 _newWeightMultiplier
+ ) external onlyDeployer {
+ likesWeightMultiplier = _likesWeightMultiplier;
+ dislikesWeightWultiplier = _dislikesWeightWultiplier;
+ newWeightMultiplier = _newWeightMultiplier;
+
+ emit MultiplersChanged(
+ msg.sender,
+ block.timestamp,
+ likesWeightMultiplier,
+ dislikesWeightWultiplier,
+ newWeightMultiplier
+ );
+ }
+
function initialize(
address identityContractAddr,
string calldata identityHandle
@@ -137,15 +180,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
_identityHandle = identityHandle;
}
- function _authorizeUpgrade(address) internal view override {
- require(
- IIdentity(_identityContractAddr).isIdentityDeployer(
- _identityHandle,
- msg.sender
- ),
- "You are not a deployer of this identity"
- );
- }
+ function _authorizeUpgrade(address) internal view override onlyDeployer {}
function addMigrator(address migrator) public onlyOwner {
require(_migrator == address(0), "Access Denied");
@@ -230,51 +265,116 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
);
}
- function getAllPosts() public view returns (PostWithMetadata[] memory) {
- PostWithMetadata[] memory postsWithMetadata = new PostWithMetadata[](postIds.length);
- for (uint256 i = 0; i < postIds.length; i++) {
- postsWithMetadata[i] = _getPostWithMetadata(postIds[i]);
+ function _inArray(uint256 _number, uint256[] memory _array)
+ internal
+ pure
+ returns (bool)
+ {
+ uint256 length = _array.length;
+ for (uint256 i = 0; i < length; ) {
+ if (_array[i] == _number) {
+ return true;
+ }
+ unchecked {
+ i++;
+ }
}
- return postsWithMetadata;
+ return false;
}
- function getAllPostsLength() public view returns (uint256) {
- uint256 length = 0;
- for (uint256 i = 0; i < postIds.length; i++) {
- if (postById[postIds[i]].createdAt > 0) {
- length++;
+ function _validPostToBeShown(
+ PostWithMetadata memory _post,
+ uint256[] memory _postIdsToFilter
+ ) public view returns (bool) {
+ // not deleted posts
+ // posts with enough weight
+ // posts not viewed already
+ return
+ _post.createdAt != 0 &&
+ _post.weight >= int256(weightThreshold) &&
+ !_inArray(_post.id, _postIdsToFilter);
+ }
+
+ function _filterPosts(
+ uint256[] memory _idsToFilter,
+ uint256[] memory _ids,
+ uint256 _maxQty
+ ) internal view returns (PostWithMetadata[] memory) {
+ uint256 length = _ids.length;
+
+ PostWithMetadata[] memory _filteredArray = new PostWithMetadata[](
+ _maxQty
+ );
+
+ uint256 insertedLength = 0;
+ for (uint256 i = 0; i < length; ) {
+ if (insertedLength >= _maxQty) {
+ break;
+ }
+
+ PostWithMetadata memory _post = _getPostWithMetadata(_ids[i]);
+
+ if (_validPostToBeShown(_post, _idsToFilter)) {
+ _filteredArray[insertedLength] = _post;
+ unchecked {
+ insertedLength++;
+ }
+ }
+ unchecked {
+ i++;
}
}
- return length;
+
+ PostWithMetadata[] memory _toReturnArray = new PostWithMetadata[](
+ insertedLength
+ );
+
+ for (uint256 j = 0; j < insertedLength; ) {
+ _toReturnArray[j] = _filteredArray[j];
+ unchecked {
+ j++;
+ }
+ }
+
+ return _toReturnArray;
}
- function getPaginatedPosts(uint256 cursor, uint256 howMany)
+ function getAllPosts(uint256[] memory _viewedPostsIds)
public
view
returns (PostWithMetadata[] memory)
{
- uint256 length = howMany;
- if (length > postIds.length - cursor) {
- length = postIds.length - cursor;
- }
+ return _filterPosts(_viewedPostsIds, postIds, postIds.length);
+ }
- PostWithMetadata[] memory postsWithMetadata = new PostWithMetadata[](length);
- for (uint256 i = length; i > 0; i--) {
- postsWithMetadata[length - i] = _getPostWithMetadata(postIds[postIds.length - cursor - i]);
+ function getAllPostsLength() public view returns (uint256) {
+ uint256 length = 0;
+ for (uint256 i = 0; i < postIds.length; i++) {
+ if (postById[postIds[i]].createdAt > 0) {
+ length++;
+ }
}
- return postsWithMetadata;
+ return length;
}
- function getAllPostsByOwner(address owner)
+ function getPaginatedPosts(
+ uint256 howMany,
+ uint256[] memory _viewedPostsIds
+ ) public view returns (PostWithMetadata[] memory) {
+ return _filterPosts(_viewedPostsIds, postIds, howMany);
+ }
+
+ function getAllPostsByOwner(address owner, uint256[] memory _viewedPostsIds)
public
view
returns (PostWithMetadata[] memory)
{
- PostWithMetadata[] memory postsWithMetadata = new PostWithMetadata[](postIdsByOwner[owner].length);
- for (uint256 i = 0; i < postIdsByOwner[owner].length; i++) {
- postsWithMetadata[i] = _getPostWithMetadata(postIdsByOwner[owner][i]);
- }
- return postsWithMetadata;
+ return
+ _filterPosts(
+ _viewedPostsIds,
+ postIdsByOwner[owner],
+ postIdsByOwner[owner].length
+ );
}
function getAllPostsByOwnerLength(address owner)
@@ -291,8 +391,18 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
return length;
}
- function _getPostWithMetadata(uint256 _postId) internal view returns (PostWithMetadata memory) {
+ function _getPostWithMetadata(uint256 _postId)
+ internal
+ view
+ returns (PostWithMetadata memory)
+ {
Post memory post = postById[_postId];
+ uint256 dislikesCount = getPostDislikesQty(post.id);
+ uint256 likesWeight = post.likesCount * likesWeightMultiplier;
+ uint256 weightPunishment = (dislikesCount * dislikesWeightWultiplier) +
+ (block.timestamp - post.createdAt) *
+ newWeightMultiplier;
+ int256 weight = int256(likesWeight) - int256(weightPunishment);
PostWithMetadata memory postWithMetadata = PostWithMetadata(
post.id,
post.from,
@@ -301,9 +411,10 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
post.createdAt,
post.likesCount,
post.commentsCount,
- getPostDislikesQty(post.id),
+ dislikesCount,
checkLikeToPost(post.id),
- checkDislikeToPost(_postId)
+ checkDislikeToPost(_postId),
+ weight
);
return postWithMetadata;
}
@@ -320,24 +431,17 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
function getPaginatedPostsByOwner(
address owner,
- uint256 cursor,
- uint256 howMany
+ uint256 howMany,
+ uint256[] memory _viewedPostsIds
) public view returns (PostWithMetadata[] memory) {
- uint256 _ownerPostLength = postIdsByOwner[owner].length;
-
- uint256 length = howMany;
- if (length > _ownerPostLength - cursor) {
- length = _ownerPostLength - cursor;
- }
-
- PostWithMetadata[] memory postsWithMetadata = new PostWithMetadata[](length);
- for (uint256 i = length; i > 0; i--) {
- postsWithMetadata[length - i] = _getPostWithMetadata(postIdsByOwner[owner][_ownerPostLength - cursor - i]);
- }
- return postsWithMetadata;
+ return _filterPosts(_viewedPostsIds, postIdsByOwner[owner], howMany);
}
- function getPostById(uint256 id) public view returns (PostWithMetadata memory) {
+ function getPostById(uint256 id)
+ public
+ view
+ returns (PostWithMetadata memory)
+ {
return _getPostWithMetadata(id);
}
@@ -556,8 +660,8 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
}
function _removeDislikeFromPost(uint256 _postId) internal {
- uint256 dislikeId = dislikeIdByUserAndPost[msg.sender][_postId];
- dislikeById[dislikeId].active = false;
+ uint256 dislikeId = dislikeIdByUserAndPost[msg.sender][_postId];
+ dislikeById[dislikeId].active = false;
}
/**
@@ -566,11 +670,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
* @param _postId - The post id
* @return Post's dislikes qty
*/
- function getPostDislikesQty(uint256 _postId)
- public
- view
- returns (uint256)
- {
+ function getPostDislikesQty(uint256 _postId) public view returns (uint256) {
Dislike[] memory postDislikes = _getPostDislikes(_postId);
return postDislikes.length;
}
diff --git a/src/components/feed/Feed.jsx b/src/components/feed/Feed.jsx
index 75c6134..c02229d 100644
--- a/src/components/feed/Feed.jsx
+++ b/src/components/feed/Feed.jsx
@@ -1,71 +1,71 @@
-import "./feed.css";
-import { useState, useEffect } from "react";
+import './feed.css';
+import { useState, useEffect } from 'react';
import { useAppContext } from '../../context/AppContext';
-import useInView from 'react-cool-inview'
+import useInView from 'react-cool-inview';
import { makeStyles } from '@material-ui/core/styles';
-import unionWith from "lodash/unionWith";
-import isEqual from "lodash/isEqual";
+import unionWith from 'lodash/unionWith';
+import isEqual from 'lodash/isEqual';
import { Box, Button, Snackbar, SnackbarContent, Typography } from '@material-ui/core';
import HourglassEmptyOutlinedIcon from '@material-ui/icons/HourglassEmptyOutlined';
import CircularProgressWithIcon from '../generic/CircularProgressWithIcon';
import InboxOutlinedIcon from '@material-ui/icons/InboxOutlined';
-import PostCard from "../post/PostCard";
+import PostCard from '../post/PostCard';
-import EventConstants from "../../events";
+import EventConstants from '../../events';
import PostManager from '../../services/PostManager';
const NUM_POSTS_PER_CALL = 5;
const useStyles = makeStyles((theme) => ({
root: {
- padding: 0,
- margin: 0,
+ padding: 0,
+ margin: 0,
marginTop: '10px',
- maxWidth: '900px'
+ maxWidth: '900px',
},
observer: {
display: 'flex',
- justifyContent: 'center'
+ justifyContent: 'center',
},
empty: {
padding: theme.spacing(2, 2),
- display: "flex",
- flexDirection: "column",
- alignItems:"center",
- justifyContent: "center"
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'center',
},
backdrop: {
zIndex: theme.zIndex.drawer + 1,
},
container: {
- display: "flex",
- height: "100%",
- minHeight: "50vh",
- flexDirection: "column",
+ display: 'flex',
+ height: '100%',
+ minHeight: '50vh',
+ flexDirection: 'column',
},
separator: {
- marginTop: "20px",
- marginBottom: "20px",
+ marginTop: '20px',
+ marginBottom: '20px',
},
extendedIcon: {
marginRight: theme.spacing(1),
- },
+ },
}));
-const Feed = ({ account, setAlert, setUpperLoading, canPost=false }) => {
- const {observe} = useInView({
- onEnter: async({observe,unobserve}) => {
- if(length === posts.length) return;
+const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
+ const { observe } = useInView({
+ onEnter: async ({ observe, unobserve }) => {
+ if (length === posts.length) return;
unobserve();
await getPosts();
observe();
- }
+ },
});
const styles = useStyles();
- const [posts, setPosts] = useState([])
+ const [posts, setPosts] = useState([]);
const [length, setLength] = useState(0);
const [loading, setLoading] = useState(false);
const [reload, setReload] = useState(false);
@@ -73,193 +73,226 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost=false }) => {
const { walletAddress, events } = useAppContext();
// sorts accending (newest first)
- const compareByTimestamp = ( post1, post2 ) => {
- if ( post1.createdAt < post2.createdAt ){
+ const compareByTimestamp = (post1, post2) => {
+ if (post1.createdAt < post2.createdAt) {
return 1;
}
- if ( post1.createdAt > post2.createdAt ){
+ if (post1.createdAt > post2.createdAt) {
return -1;
}
return 0;
- }
+ };
- useEffect(()=>{
+ useEffect(() => {
reloadPosts();
}, []);
useEffect(() => {
getEvents();
return () => {
- events.listeners["PointSocial"]["StateChange"].removeListener("StateChange", handleEvents, { type: 'feed'});
- events.unsubscribe("PointSocial", "StateChange");
+ events.listeners['PointSocial']['StateChange'].removeListener('StateChange', handleEvents, {
+ type: 'feed',
+ });
+ events.unsubscribe('PointSocial', 'StateChange');
};
}, []);
- const getEvents = async() => {
+ const getEvents = async () => {
try {
- (await events.subscribe("PointSocial", "StateChange")).on("StateChange", handleEvents, { type: 'feed'});
- }
- catch(error) {
+ (await events.subscribe('PointSocial', 'StateChange')).on('StateChange', handleEvents, {
+ type: 'feed',
+ });
+ } catch (error) {
console.log(error.message);
}
- }
+ };
- const handleEvents = async(event) => {
+ const handleEvents = async (event) => {
if (event) {
if (event.component === EventConstants.Component.Feed) {
- switch(event.action) {
+ switch (event.action) {
case EventConstants.Action.Create:
- if (event.from.toString().toLowerCase() === walletAddress.toLowerCase()) {
- // Autoload own posts
- await reloadPosts();
- }
- else {
- setReload(true);
- }
- break;
+ if (event.from.toString().toLowerCase() === walletAddress.toLowerCase()) {
+ // Autoload own posts
+ await reloadPosts();
+ } else {
+ setReload(true);
+ }
+ break;
default:
- break;
+ break;
}
- }
- else if (event.component === EventConstants.Component.Post) {
- switch(event.action) {
+ } else if (event.component === EventConstants.Component.Post) {
+ switch (event.action) {
case EventConstants.Action.Delete:
deletePost(event.id);
- break;
+ break;
default:
- break;
+ break;
}
}
}
- }
+ };
- const getPostsLength = async() => {
+ const getPostsLength = async () => {
try {
setLoading(true);
- const data = await (account?
- PostManager.getAllPostsByOwnerLength(account) :
- PostManager.getAllPostsLength());
+ const data = await (account
+ ? PostManager.getAllPostsByOwnerLength(account)
+ : PostManager.getAllPostsLength());
setLength(Number(data));
- }
- catch(error) {
+ } catch (error) {
console.log(error.message);
setAlert(error.message);
}
setLoading(false);
- }
+ };
const fetchPosts = async (onlyNew = false) => {
+ const viewedPostIds = posts.map(({ id }) => id);
+
try {
setLoading(true);
-
- const data = await (account?
- PostManager.getPaginatedPostsByOwner(account,onlyNew?0:posts.length,NUM_POSTS_PER_CALL) :
- PostManager.getPaginatedPosts(onlyNew?0:posts.length,NUM_POSTS_PER_CALL));
- const newPosts = data.filter(r => (parseInt(r[4]) !== 0))
- .map(([id, from, contents, image, createdAt, likesCount, commentsCount, dislikesCount, liked, disliked]) => (
- {
- id,
- from,
- contents,
- image,
- createdAt: createdAt*1000,
- likesCount: parseInt(likesCount, 10),
- dislikesCount: parseInt(dislikesCount, 10),
- commentsCount: parseInt(commentsCount, 10),
- liked,
- disliked,
- }
- )
+ const data = await (account
+ ? PostManager.getPaginatedPostsByOwner(account, NUM_POSTS_PER_CALL, viewedPostIds)
+ : PostManager.getPaginatedPosts(NUM_POSTS_PER_CALL, viewedPostIds));
+
+ const newPosts = data.map(
+ ([
+ id,
+ from,
+ contents,
+ image,
+ createdAt,
+ likesCount,
+ commentsCount,
+ dislikesCount,
+ liked,
+ disliked,
+ ]) => ({
+ id,
+ from,
+ contents,
+ image,
+ createdAt: createdAt * 1000,
+ likesCount: parseInt(likesCount, 10),
+ dislikesCount: parseInt(dislikesCount, 10),
+ commentsCount: parseInt(commentsCount, 10),
+ liked,
+ disliked,
+ })
);
return newPosts;
- } catch(error) {
+ } catch (error) {
console.log(error.message);
setAlert(error.message);
- }
- finally {
+ } finally {
setLoading(false);
}
- }
+ };
const getPosts = async (loadNew = false) => {
try {
setLoading(true);
const posts = await fetchPosts(loadNew);
- setPosts(prev => {
+ setPosts((prev) => {
const result = unionWith(prev, posts, isEqual);
result.sort(compareByTimestamp);
return result;
});
- }
- catch(error) {
+ } catch (error) {
console.log(error);
setAlert(error.message);
- }
- finally {
+ } finally {
setLoading(false);
}
- }
+ };
const reloadPosts = async () => {
await getPostsLength();
await getPosts(true);
setReload(false);
- }
+ };
const deletePost = async (postId) => {
await getPostsLength();
- setPosts((posts) => posts.filter(post => post.id !== postId));
- }
+ setPosts((posts) => posts.filter((post) => post.id !== postId));
+ };
return (
<>
-
-
+ >}
+ action={
+ <>
+
+
+ >
+ }
/>
-
-
+
+
- {
- (length === 0)?
-
-
-
- {`No posts yet.${ canPost? " Be the first!" : "" }`}
-
-
-
- :
- posts.filter(post => post.createdAt > 0).map((post) => (
-
+ {length === 0 ? (
+
+
+
+
+ {`No posts yet.${canPost ? ' Be the first!' : ''}`}
+
+
+
+ ) : (
+ posts
+ .filter((post) => post.createdAt > 0)
+ .map((post) => (
+
))
- }
+ )}
- {
- loading &&
- } props={{color : "inherit"}} />
- }
+ {loading && (
+ }
+ props={{ color: 'inherit' }}
+ />
+ )}
-
+
>
);
-
-}
-export default Feed
+};
+export default Feed;
diff --git a/tests/unit/smartcontracts/PointSocial.js b/tests/unit/smartcontracts/PointSocial.js
index 8c41a07..c489497 100644
--- a/tests/unit/smartcontracts/PointSocial.js
+++ b/tests/unit/smartcontracts/PointSocial.js
@@ -1,63 +1,64 @@
-const { expect } = require("chai");
-const { ethers, upgrades } = require("hardhat");
-
-describe("PointSocial contract", function () {
-
- let pointSocial;
- let identityContract;
- let owner;
- let addr1;
- let addr2;
- let addr3;
- let addrs;
- let handle = "social";
- let postContent = "0x9d6b0f937680809a01639ad1ae4770241c7c8a0ded490d2f023669f18c6d744b";
- let postimage = '0x5f04837d78fa7a656419f98d73fc1ddaac1bfdfca9a244a2ee128737a186da6e';
-
- beforeEach(async function () {
- [owner, addr1, addr2, addr3, ...addrs] = await ethers.getSigners();
- const identityFactory = await ethers.getContractFactory("Identity");
- identityContract = await upgrades.deployProxy(identityFactory, [], {kind: 'uups'});
- await identityContract.deployed();
- const factory = await ethers.getContractFactory("PointSocial");
- pointSocial = await upgrades.deployProxy(factory, [identityContract.address, handle], {kind: 'uups'});
- await pointSocial.deployed();
+const { expect } = require('chai');
+const { ethers, upgrades } = require('hardhat');
+
+describe('PointSocial contract', function () {
+ let pointSocial;
+ let identityContract;
+ let owner;
+ let addr1;
+ let addr2;
+ let addr3;
+ let addrs;
+ let handle = 'social';
+ let postContent = '0x9d6b0f937680809a01639ad1ae4770241c7c8a0ded490d2f023669f18c6d744b';
+ let postimage = '0x5f04837d78fa7a656419f98d73fc1ddaac1bfdfca9a244a2ee128737a186da6e';
+
+ beforeEach(async function () {
+ [owner, addr1, addr2, addr3, ...addrs] = await ethers.getSigners();
+ const identityFactory = await ethers.getContractFactory('Identity');
+ identityContract = await upgrades.deployProxy(identityFactory, [], { kind: 'uups' });
+ await identityContract.deployed();
+ const factory = await ethers.getContractFactory('PointSocial');
+ pointSocial = await upgrades.deployProxy(factory, [identityContract.address, handle], {
+ kind: 'uups',
+ });
+ await pointSocial.deployed();
+ });
+
+ describe('Testing deployment functions', function () {
+ it('Should upgrade the proxy by a deployer', async function () {
+ await identityContract.setDevMode(true);
+ await identityContract.register(
+ handle,
+ owner.address,
+ '0xed17268897bbcb67127ed550cee2068a15fdb6f69097eebeb6e2ace46305d1ce',
+ '0xe1e032c91d4c8fe6bab1f198871dbafb8842f073acff8ee9b822f748b180d7eb'
+ );
+ await identityContract.addIdentityDeployer(handle, addr1.address);
+ const factory = await ethers.getContractFactory('PointSocial');
+ let socialFactoryDeployer = factory.connect(addr1);
+
+ await upgrades.upgradeProxy(pointSocial.address, socialFactoryDeployer);
});
- describe("Testing deployment functions", function () {
-
- it("Should upgrade the proxy by a deployer", async function () {
- await identityContract.setDevMode(true);
- await identityContract.register(
- handle,
- owner.address,
- '0xed17268897bbcb67127ed550cee2068a15fdb6f69097eebeb6e2ace46305d1ce',
- '0xe1e032c91d4c8fe6bab1f198871dbafb8842f073acff8ee9b822f748b180d7eb');
- await identityContract.addIdentityDeployer(handle, addr1.address);
- const factory = await ethers.getContractFactory("PointSocial");
- let socialFactoryDeployer = factory.connect(addr1);
-
- await upgrades.upgradeProxy(pointSocial.address, socialFactoryDeployer);
- });
-
- it("Should not upgrade the proxy by a non-deployer", async function () {
- await identityContract.setDevMode(true);
- await identityContract.register(
- handle,
- owner.address,
- '0xed17268897bbcb67127ed550cee2068a15fdb6f69097eebeb6e2ace46305d1ce',
- '0xe1e032c91d4c8fe6bab1f198871dbafb8842f073acff8ee9b822f748b180d7eb');
-
- const factory = await ethers.getContractFactory("PointSocial");
- let socialFactoryDeployer = factory.connect(addr1);
- await expect(
- upgrades.upgradeProxy(pointSocial.address, socialFactoryDeployer)
- ).to.be.revertedWith('You are not a deployer of this identity');
- });
-
+ it('Should not upgrade the proxy by a non-deployer', async function () {
+ await identityContract.setDevMode(true);
+ await identityContract.register(
+ handle,
+ owner.address,
+ '0xed17268897bbcb67127ed550cee2068a15fdb6f69097eebeb6e2ace46305d1ce',
+ '0xe1e032c91d4c8fe6bab1f198871dbafb8842f073acff8ee9b822f748b180d7eb'
+ );
+
+ const factory = await ethers.getContractFactory('PointSocial');
+ let socialFactoryDeployer = factory.connect(addr1);
+ await expect(
+ upgrades.upgradeProxy(pointSocial.address, socialFactoryDeployer)
+ ).to.be.revertedWith('You are not a deployer of this identity');
});
-
- /*describe("Testing migrator functions", function () {
+ });
+
+ /*describe("Testing migrator functions", function () {
it("User can add migrator", async function () {
await pointSocial.addMigrator(
addr1.address
@@ -162,245 +163,207 @@ describe("PointSocial contract", function () {
});
});*/
- describe("Testing user functions", function () {
- it("User can create post", async function () {
- await pointSocial.addPost(
- postContent,
- postimage
- )
- const post = await pointSocial.getPostById(1);
+ describe('Testing user functions', function () {
+ it('User can create post', async function () {
+ await pointSocial.addPost(postContent, postimage);
+ const post = await pointSocial.getPostById(1);
- expect(post.contents).to.be.equal(postContent);
- expect(post.image).to.be.equal(postimage);
- });
+ expect(post.contents).to.be.equal(postContent);
+ expect(post.image).to.be.equal(postimage);
+ });
- it("Get posts by owner", async function () {
- await pointSocial.addPost(
- postContent,
- postimage
- )
- const posts = await pointSocial.getAllPostsByOwner(owner.address);
+ it('Get posts by owner', async function () {
+ await pointSocial.addPost(postContent, postimage);
+ const posts = await pointSocial.getAllPostsByOwner(owner.address, []);
- expect(posts[0].contents).to.be.equal(postContent);
- expect(posts[0].image).to.be.equal(postimage);
- });
-
- it("Get more then one post by owner", async function () {
- await pointSocial.addPost(
- postContent,
- postimage
- )
+ expect(posts[0].contents).to.be.equal(postContent);
+ expect(posts[0].image).to.be.equal(postimage);
+ });
- await pointSocial.addPost(
- "0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55",
- "0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe"
- )
+ it('Get more then one post by owner', async function () {
+ await pointSocial.addPost(postContent, postimage);
- const postsLength = await pointSocial.getAllPostsLength();
+ await pointSocial.addPost(
+ '0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55',
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
- expect(postsLength.toString()).to.be.equal("2");
- });
-
- it("Get paginated posts", async function () {
- await pointSocial.addPost(
- postContent,
- postimage
- )
+ const postsLength = await pointSocial.getAllPostsLength();
- await pointSocial.addPost(
- "0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55",
- "0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe"
- )
+ expect(postsLength.toString()).to.be.equal('2');
+ });
- const paginatedPosts = await pointSocial.getPaginatedPostsByOwner(owner.address, "1", "10");
+ it('Get paginated posts', async function () {
+ await pointSocial.addPost(postContent, postimage);
- expect(paginatedPosts[0].contents).to.be.equal(postContent);
- expect(paginatedPosts[0].image).to.be.equal(postimage);
+ await pointSocial.addPost(
+ '0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55',
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
- });
-
-
- it("Get paginated posts more pages", async function () {
- await pointSocial.addPost(
- postContent,
- postimage
- )
+ const paginatedPosts = await pointSocial.getPaginatedPostsByOwner(owner.address, '10', []);
- await pointSocial.addPost(
- "0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55",
- "0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe"
- )
+ expect(paginatedPosts[0].contents).to.be.equal(postContent);
+ expect(paginatedPosts[0].image).to.be.equal(postimage);
+ });
- await pointSocial.connect(addr1).addPost(
- "0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55",
- "0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe"
- )
+ it('Get paginated posts more pages', async function () {
+ await pointSocial.addPost(postContent, postimage);
+
+ await pointSocial.addPost(
+ '0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55',
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
+
+ await pointSocial
+ .connect(addr1)
+ .addPost(
+ '0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55',
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
+
+ await pointSocial
+ .connect(addr1)
+ .addPost(
+ '0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55',
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
+
+ await pointSocial
+ .connect(addr2)
+ .addPost(
+ '0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55',
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
+
+ await pointSocial
+ .connect(addr2)
+ .addPost(
+ '0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55',
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
+
+ const paginatedPosts = await pointSocial.getPaginatedPosts('10', ['1']);
+
+ expect(paginatedPosts.length).to.be.equal(5);
+ });
- await pointSocial.connect(addr1).addPost(
- "0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55",
- "0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe"
- )
+ it('get all posts', async () => {
+ await pointSocial
+ .connect(addr2)
+ .addPost(
+ '0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55',
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
+ const posts = await pointSocial.getAllPosts([]);
+ expect(posts.length).to.be.equal(1);
+ });
- await pointSocial.connect(addr2).addPost(
- "0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55",
- "0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe"
- )
+ it('Add comments to posts', async function () {
+ await pointSocial.addPost(postContent, postimage);
- await pointSocial.connect(addr2).addPost(
- "0xdd5a0873f998fff6b00052b51d1662f2993b603d9837da33cbc281a06b9f3b55",
- "0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe"
- )
+ await pointSocial.addCommentToPost(
+ '1',
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
- const paginatedPosts = await pointSocial.getPaginatedPosts("1", "10");
-
- expect(paginatedPosts.length).to.be.equal(5);
- });
-
- it("Add comments to posts", async function () {
- await pointSocial.addPost(
- postContent,
- postimage
- )
+ const postCommentId = await pointSocial.commentIdsByPost(1, 0);
+ const postComments = await pointSocial.commentById(postCommentId);
- await pointSocial.addCommentToPost(
- "1",
- "0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe"
- )
-
- const postCommentId = await pointSocial.commentIdsByPost(1, 0);
- const postComments = await pointSocial.commentById(postCommentId);
+ expect(postComments.contents).to.be.equal(
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
+ });
- expect(postComments.contents).to.be.equal("0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe");
- });
-
-
- it("Add dislike to posts", async () => {
- await pointSocial.addPost(
- postContent,
- postimage
- )
+ it('Add dislike to posts', async () => {
+ await pointSocial.addPost(postContent, postimage);
- await pointSocial.addDislikeToPost(
- "1"
- )
+ await pointSocial.addDislikeToPost('1');
- await pointSocial.connect(addr2).addDislikeToPost(
- "1"
- )
-
- const [post] = await pointSocial.getAllPosts();
+ await pointSocial.connect(addr2).addDislikeToPost('1');
- expect(post.dislikesCount).to.be.equal(2);
- });
+ const [post] = await pointSocial.getAllPosts([]);
- it("Add a dislike should remove user's like", async () => {
- await pointSocial.addPost(
- postContent,
- postimage
- )
+ expect(post.dislikesCount).to.be.equal(2);
+ });
- await pointSocial.addLikeToPost(
- "1"
- )
+ it("Add a dislike should remove user's like", async () => {
+ await pointSocial.addPost(postContent, postimage);
- await pointSocial.addDislikeToPost(
- "1"
- )
+ await pointSocial.addLikeToPost('1');
- const [post] = await pointSocial.getAllPosts();
+ await pointSocial.addDislikeToPost('1');
- expect(post.likesCount).to.be.equal(0);
- expect(post.dislikesCount).to.be.equal(1);
- });
+ const posts = await pointSocial.getAllPosts([]);
+ const [post] = posts;
- it("Add a like should remove user's dislike", async () => {
- await pointSocial.addPost(
- postContent,
- postimage
- )
+ expect(post.likesCount).to.be.equal(0);
+ expect(post.dislikesCount).to.be.equal(1);
+ });
- await pointSocial.addDislikeToPost(
- "1"
- )
+ it("Add a like should remove user's dislike", async () => {
+ await pointSocial.addPost(postContent, postimage);
- await pointSocial.addLikeToPost(
- "1"
- )
+ await pointSocial.addDislikeToPost('1');
- const [post] = await pointSocial.getAllPosts();
+ await pointSocial.addLikeToPost('1');
- expect(post.likesCount).to.be.equal(1);
- expect(post.dislikesCount).to.be.equal(0);
- });
-
- it("Add like to posts", async function () {
- await pointSocial.addPost(
- postContent,
- postimage
- )
+ const [post] = await pointSocial.getAllPosts([]);
- await pointSocial.addLikeToPost(
- "1"
- )
+ expect(post.likesCount).to.be.equal(1);
+ expect(post.dislikesCount).to.be.equal(0);
+ });
- await pointSocial.connect(addr2).addLikeToPost(
- "1"
- )
-
- const posts = await pointSocial.getAllPosts();
+ it('Add like to posts', async function () {
+ await pointSocial.addPost(postContent, postimage);
- expect(posts[0].likesCount).to.be.equal(2);
- });
-
- it("Add like to posts twice same user", async function () {
- await pointSocial.addPost(
- postContent,
- postimage
- )
+ await pointSocial.addLikeToPost('1');
- await pointSocial.connect(addr1).addLikeToPost(
- 1
- )
+ await pointSocial.connect(addr2).addLikeToPost('1');
- await pointSocial.connect(addr1).addLikeToPost(
- 1
- )
+ const posts = await pointSocial.getAllPosts([]);
- await pointSocial.connect(addr2).addLikeToPost(
- 1
- )
- //remove index?
-
- const postsByOwnerLenght = await pointSocial.getAllPostsByOwnerLength(owner.address);
- const posts = await pointSocial.getAllPosts();
+ expect(posts[0].likesCount).to.be.equal(2);
+ });
- expect(postsByOwnerLenght).to.be.equal(1);
- expect(posts[0].likesCount).to.be.equal(1);
- });
-
-
-
- it("Add comments to posts", async function () {
- await pointSocial.addPost(
- postContent,
- postimage
- )
+ it('Add like to posts twice same user', async function () {
+ await pointSocial.addPost(postContent, postimage);
- await pointSocial.addCommentToPost(
- "1",
- "0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe"
- )
+ await pointSocial.connect(addr1).addLikeToPost(1);
- await pointSocial.addCommentToPost(
- "1",
- "0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe"
- )
-
- const postComments = await pointSocial.getAllCommentsForPost(1);
+ await pointSocial.connect(addr1).addLikeToPost(1);
- expect(postComments[0].contents).to.be.equal("0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe");
- expect(postComments[1].contents).to.be.equal("0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe");
- });
+ await pointSocial.connect(addr2).addLikeToPost(1);
+ //remove index?
+
+ const postsByOwnerLenght = await pointSocial.getAllPostsByOwnerLength(owner.address, []);
+ const posts = await pointSocial.getAllPosts([]);
+
+ expect(postsByOwnerLenght).to.be.equal(1);
+ expect(posts[0].likesCount).to.be.equal(1);
});
-});
\ No newline at end of file
+ it('Add comments to posts', async function () {
+ await pointSocial.addPost(postContent, postimage);
+
+ await pointSocial.addCommentToPost(
+ '1',
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
+
+ await pointSocial.addCommentToPost(
+ '1',
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
+
+ const postComments = await pointSocial.getAllCommentsForPost(1);
+
+ expect(postComments[0].contents).to.be.equal(
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
+ expect(postComments[1].contents).to.be.equal(
+ '0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
+ );
+ });
+ });
+});
From edb379d9db65089bb6f76c31f76ce12e3450ee5c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Wed, 6 Jul 2022 18:55:43 +0200
Subject: [PATCH 02/20] wip
---
contracts/PointSocial.sol | 37 ++++++++++++++----------
src/components/feed/Feed.jsx | 14 +--------
tests/unit/smartcontracts/PointSocial.js | 2 +-
3 files changed, 23 insertions(+), 30 deletions(-)
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index 8f41a50..946c31b 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -69,7 +69,8 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 timestamp,
uint256 likesWeightMultiplier,
uint256 dislikesWeightWultiplier,
- uint256 newWeightMultiplier
+ uint256 oldWeightMultiplier,
+ uint256 initialWeight
);
address private _identityContractAddr;
@@ -133,8 +134,9 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 public likesWeightMultiplier;
uint256 public dislikesWeightWultiplier;
- uint256 public newWeightMultiplier;
+ uint256 public oldWeightMultiplier;
uint256 public weightThreshold;
+ uint256 public initialWeight;
modifier postExists(uint256 _postId) {
require(postById[_postId].from != address(0), "Post does not exist");
@@ -155,18 +157,21 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
function setWeights(
uint256 _likesWeightMultiplier,
uint256 _dislikesWeightWultiplier,
- uint256 _newWeightMultiplier
+ uint256 _oldWeightMultiplier,
+ uint256 _initialWeight
) external onlyDeployer {
likesWeightMultiplier = _likesWeightMultiplier;
dislikesWeightWultiplier = _dislikesWeightWultiplier;
- newWeightMultiplier = _newWeightMultiplier;
+ oldWeightMultiplier = _oldWeightMultiplier;
+ initialWeight = _initialWeight;
emit MultiplersChanged(
msg.sender,
block.timestamp,
likesWeightMultiplier,
dislikesWeightWultiplier,
- newWeightMultiplier
+ oldWeightMultiplier,
+ initialWeight
);
}
@@ -296,8 +301,8 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
}
function _filterPosts(
- uint256[] memory _idsToFilter,
uint256[] memory _ids,
+ uint256[] memory _idsToFilter,
uint256 _maxQty
) internal view returns (PostWithMetadata[] memory) {
uint256 length = _ids.length;
@@ -307,7 +312,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
);
uint256 insertedLength = 0;
- for (uint256 i = 0; i < length; ) {
+ for (uint256 i = length - 1; i >= 0; i--) {
if (insertedLength >= _maxQty) {
break;
}
@@ -320,9 +325,6 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
insertedLength++;
}
}
- unchecked {
- i++;
- }
}
PostWithMetadata[] memory _toReturnArray = new PostWithMetadata[](
@@ -344,7 +346,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
view
returns (PostWithMetadata[] memory)
{
- return _filterPosts(_viewedPostsIds, postIds, postIds.length);
+ return _filterPosts(postIds, _viewedPostsIds, postIds.length);
}
function getAllPostsLength() public view returns (uint256) {
@@ -361,7 +363,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 howMany,
uint256[] memory _viewedPostsIds
) public view returns (PostWithMetadata[] memory) {
- return _filterPosts(_viewedPostsIds, postIds, howMany);
+ return _filterPosts(postIds, _viewedPostsIds, howMany);
}
function getAllPostsByOwner(address owner, uint256[] memory _viewedPostsIds)
@@ -371,8 +373,8 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
{
return
_filterPosts(
- _viewedPostsIds,
postIdsByOwner[owner],
+ _viewedPostsIds,
postIdsByOwner[owner].length
);
}
@@ -401,8 +403,11 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 likesWeight = post.likesCount * likesWeightMultiplier;
uint256 weightPunishment = (dislikesCount * dislikesWeightWultiplier) +
(block.timestamp - post.createdAt) *
- newWeightMultiplier;
- int256 weight = int256(likesWeight) - int256(weightPunishment);
+ oldWeightMultiplier;
+ int256 weight = int256(initialWeight) +
+ int256(likesWeight) -
+ int256(weightPunishment);
+
PostWithMetadata memory postWithMetadata = PostWithMetadata(
post.id,
post.from,
@@ -434,7 +439,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 howMany,
uint256[] memory _viewedPostsIds
) public view returns (PostWithMetadata[] memory) {
- return _filterPosts(_viewedPostsIds, postIdsByOwner[owner], howMany);
+ return _filterPosts(postIdsByOwner[owner], _viewedPostsIds, howMany);
}
function getPostById(uint256 id)
diff --git a/src/components/feed/Feed.jsx b/src/components/feed/Feed.jsx
index c02229d..107fe1d 100644
--- a/src/components/feed/Feed.jsx
+++ b/src/components/feed/Feed.jsx
@@ -72,17 +72,6 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
const { walletAddress, events } = useAppContext();
- // sorts accending (newest first)
- const compareByTimestamp = (post1, post2) => {
- if (post1.createdAt < post2.createdAt) {
- return 1;
- }
- if (post1.createdAt > post2.createdAt) {
- return -1;
- }
- return 0;
- };
-
useEffect(() => {
reloadPosts();
}, []);
@@ -185,7 +174,7 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
})
);
- return newPosts;
+ return newPosts.sort(({ weight: w1 }, { weight: w2 }) => w1 - w2);
} catch (error) {
console.log(error.message);
setAlert(error.message);
@@ -200,7 +189,6 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
const posts = await fetchPosts(loadNew);
setPosts((prev) => {
const result = unionWith(prev, posts, isEqual);
- result.sort(compareByTimestamp);
return result;
});
} catch (error) {
diff --git a/tests/unit/smartcontracts/PointSocial.js b/tests/unit/smartcontracts/PointSocial.js
index c489497..3bb8431 100644
--- a/tests/unit/smartcontracts/PointSocial.js
+++ b/tests/unit/smartcontracts/PointSocial.js
@@ -201,7 +201,7 @@ describe('PointSocial contract', function () {
'0x0090916c0e6846d5dc8d22560e90782ded96e4efdeb53db214f612a54d4f5fbe'
);
- const paginatedPosts = await pointSocial.getPaginatedPostsByOwner(owner.address, '10', []);
+ const paginatedPosts = await pointSocial.getPaginatedPostsByOwner(owner.address, 10, []);
expect(paginatedPosts[0].contents).to.be.equal(postContent);
expect(paginatedPosts[0].image).to.be.equal(postimage);
From 951c41caf358d05931ad3103105d9b17da261c17 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Wed, 6 Jul 2022 19:30:35 +0200
Subject: [PATCH 03/20] return posts order from newest to oldest
---
contracts/PointSocial.sol | 4 ++--
tests/unit/smartcontracts/PointSocial.js | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index 946c31b..1329321 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -312,12 +312,12 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
);
uint256 insertedLength = 0;
- for (uint256 i = length - 1; i >= 0; i--) {
+ for (uint256 i = length; i > 0; i--) {
if (insertedLength >= _maxQty) {
break;
}
- PostWithMetadata memory _post = _getPostWithMetadata(_ids[i]);
+ PostWithMetadata memory _post = _getPostWithMetadata(_ids[i - 1]);
if (_validPostToBeShown(_post, _idsToFilter)) {
_filteredArray[insertedLength] = _post;
diff --git a/tests/unit/smartcontracts/PointSocial.js b/tests/unit/smartcontracts/PointSocial.js
index 3bb8431..f760128 100644
--- a/tests/unit/smartcontracts/PointSocial.js
+++ b/tests/unit/smartcontracts/PointSocial.js
@@ -203,8 +203,8 @@ describe('PointSocial contract', function () {
const paginatedPosts = await pointSocial.getPaginatedPostsByOwner(owner.address, 10, []);
- expect(paginatedPosts[0].contents).to.be.equal(postContent);
- expect(paginatedPosts[0].image).to.be.equal(postimage);
+ expect(paginatedPosts[1].contents).to.be.equal(postContent);
+ expect(paginatedPosts[1].image).to.be.equal(postimage);
});
it('Get paginated posts more pages', async function () {
From 904bbb7c3d2df6315a879df7e491524af16cec63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Wed, 6 Jul 2022 19:40:12 +0200
Subject: [PATCH 04/20] add new post at the top of the list
---
src/components/feed/Feed.jsx | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/components/feed/Feed.jsx b/src/components/feed/Feed.jsx
index 107fe1d..34e9c77 100644
--- a/src/components/feed/Feed.jsx
+++ b/src/components/feed/Feed.jsx
@@ -103,7 +103,7 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
case EventConstants.Action.Create:
if (event.from.toString().toLowerCase() === walletAddress.toLowerCase()) {
// Autoload own posts
- await reloadPosts();
+ await reloadPosts(true);
} else {
setReload(true);
}
@@ -138,7 +138,7 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
setLoading(false);
};
- const fetchPosts = async (onlyNew = false) => {
+ const fetchPosts = async () => {
const viewedPostIds = posts.map(({ id }) => id);
try {
@@ -183,11 +183,14 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
}
};
- const getPosts = async (loadNew = false) => {
+ const getPosts = async (refresh = false) => {
try {
setLoading(true);
- const posts = await fetchPosts(loadNew);
+ const posts = await fetchPosts();
setPosts((prev) => {
+ if (refresh) {
+ return posts;
+ }
const result = unionWith(prev, posts, isEqual);
return result;
});
@@ -199,9 +202,9 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
}
};
- const reloadPosts = async () => {
+ const reloadPosts = async (refresh = false) => {
await getPostsLength();
- await getPosts(true);
+ await getPosts(refresh);
setReload(false);
};
From c0dfdb35ac4ea36c9314bdf5fd1bae698e7ab250 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Fri, 8 Jul 2022 18:40:17 +0200
Subject: [PATCH 05/20] typescript
---
contracts/PointSocial.sol | 2 +
hardhat.config.js | 43 ---
hardhat.config.ts | 74 +++++
package-lock.json | 298 +++++++++++++++++-
package.json | 7 +-
tests/unit/smartcontracts/FeedSortAlgoritm.ts | 161 ++++++++++
tsconfig.json | 12 +
7 files changed, 546 insertions(+), 51 deletions(-)
delete mode 100644 hardhat.config.js
create mode 100644 hardhat.config.ts
create mode 100644 tests/unit/smartcontracts/FeedSortAlgoritm.ts
create mode 100644 tsconfig.json
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index 1329321..6894eb1 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -158,11 +158,13 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 _likesWeightMultiplier,
uint256 _dislikesWeightWultiplier,
uint256 _oldWeightMultiplier,
+ uint256 _weightThreshold,
uint256 _initialWeight
) external onlyDeployer {
likesWeightMultiplier = _likesWeightMultiplier;
dislikesWeightWultiplier = _dislikesWeightWultiplier;
oldWeightMultiplier = _oldWeightMultiplier;
+ weightThreshold = _weightThreshold;
initialWeight = _initialWeight;
emit MultiplersChanged(
diff --git a/hardhat.config.js b/hardhat.config.js
deleted file mode 100644
index 81f1a86..0000000
--- a/hardhat.config.js
+++ /dev/null
@@ -1,43 +0,0 @@
-require("@nomiclabs/hardhat-waffle");
-require('@openzeppelin/hardhat-upgrades');
-require("solidity-coverage");
-
-module.exports = {
- solidity: {
- compilers: [
- {
- version: '0.8.0',
- settings: {
- optimizer: {
- enabled: true,
- runs: 1000
- }
- }
- },
- {
- version: '0.8.4',
- settings: {
- optimizer: {
- enabled: true,
- runs: 1000
- }
- }
- },
- {
- version: '0.8.7',
- settings: {
- optimizer: {
- enabled: true,
- runs: 1000
- }
- }
- },
- ]
- },
- paths: {
- artifacts:'./hardhat/build',
- sources: './contracts',
- tests: './tests/unit/smartcontracts',
- cache: './hardhat/cache'
- }
-};
diff --git a/hardhat.config.ts b/hardhat.config.ts
new file mode 100644
index 0000000..1a55f07
--- /dev/null
+++ b/hardhat.config.ts
@@ -0,0 +1,74 @@
+import '@nomiclabs/hardhat-waffle';
+import '@openzeppelin/hardhat-upgrades';
+import fs from 'fs';
+import os from 'os';
+import { hdkey } from 'ethereumjs-wallet';
+import { mnemonicToSeedSync } from 'bip39';
+
+const keystorePath = `${os.homedir()}/.point/keystore/key.json`;
+
+const networks: Record = {
+ rinkeby: {
+ url: process.env.RINKEBY_URL || '',
+ accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [],
+ },
+};
+
+try {
+ if (fs.existsSync(keystorePath)) {
+ const keystore: any = JSON.parse(
+ fs.readFileSync(`${os.homedir()}/.point/keystore/key.json`).toString()
+ );
+
+ const wallet = hdkey.fromMasterSeed(mnemonicToSeedSync(keystore.phrase)).getWallet();
+ const privateKey = wallet.getPrivateKey().toString('hex');
+
+ networks.ynet = {
+ url: 'http://ynet.point.space:44444',
+ accounts: [privateKey],
+ };
+ }
+} catch (err) {}
+
+export default {
+ solidity: {
+ compilers: [
+ {
+ version: '0.8.0',
+ settings: {
+ optimizer: {
+ enabled: true,
+ runs: 1000,
+ },
+ },
+ },
+ {
+ version: '0.8.4',
+ settings: {
+ optimizer: {
+ enabled: true,
+ runs: 1000,
+ },
+ },
+ },
+ {
+ version: '0.8.7',
+ settings: {
+ optimizer: {
+ enabled: true,
+ runs: 1000,
+ },
+ },
+ },
+ ],
+ },
+
+ networks,
+
+ paths: {
+ artifacts: './hardhat/build',
+ sources: './contracts',
+ tests: './tests/unit/smartcontracts',
+ cache: './hardhat/cache',
+ },
+};
diff --git a/package-lock.json b/package-lock.json
index d698c7b..2e3893c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -53,6 +53,9 @@
"@nomiclabs/hardhat-waffle": "^2.0.3",
"@openzeppelin/hardhat-upgrades": "^1.19.0",
"@parcel/transformer-image": "^2.0.0-rc.0",
+ "@types/chai": "^4.3.1",
+ "@types/mocha": "^9.1.1",
+ "@types/node": "^18.0.3",
"babel-jest": "^28.1.1",
"chai": "^4.3.6",
"ethereum-waffle": "^3.4.4",
@@ -63,7 +66,9 @@
"parcel": "^2.0.0-rc.0",
"react-error-overlay": "^6.0.9",
"react-test-renderer": "^17.0.2",
- "solidity-coverage": "^0.7.21"
+ "solidity-coverage": "^0.7.21",
+ "ts-node": "^10.8.2",
+ "typescript": "^4.7.4"
}
},
"node_modules/@ampproject/remapping": {
@@ -2004,6 +2009,28 @@
"node": ">=0.1.95"
}
},
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "devOptional": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "devOptional": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
"node_modules/@csstools/convert-colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
@@ -6368,6 +6395,30 @@
"node": ">=10.13.0"
}
},
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+ "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+ "devOptional": true
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "devOptional": true
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "devOptional": true
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
+ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+ "devOptional": true
+ },
"node_modules/@typechain/ethers-v5": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-2.0.0.tgz",
@@ -6570,10 +6621,16 @@
"@types/node": "*"
}
},
+ "node_modules/@types/mocha": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz",
+ "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==",
+ "dev": true
+ },
"node_modules/@types/node": {
- "version": "17.0.14",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.14.tgz",
- "integrity": "sha512-SbjLmERksKOGzWzPNuW7fJM7fk3YXVTFiZWB/Hs99gwhk+/dnrQRPBQjPW9aO+fi1tAffi9PrwFvsmOKmDTyng=="
+ "version": "18.0.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.3.tgz",
+ "integrity": "sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ=="
},
"node_modules/@types/node-fetch": {
"version": "2.6.1",
@@ -7485,6 +7542,12 @@
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "devOptional": true
+ },
"node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -10072,6 +10135,12 @@
"sha.js": "^2.4.8"
}
},
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "devOptional": true
+ },
"node_modules/cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -27679,6 +27748,12 @@
"node": ">=6"
}
},
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "devOptional": true
+ },
"node_modules/makeerror": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
@@ -38527,6 +38602,67 @@
"node": ">=4"
}
},
+ "node_modules/ts-node": {
+ "version": "10.8.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.2.tgz",
+ "integrity": "sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==",
+ "devOptional": true,
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ts-node/node_modules/acorn-walk": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ts-node/node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
"node_modules/ts-pnp": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
@@ -38747,6 +38883,18 @@
"is-typedarray": "^1.0.0"
}
},
+ "node_modules/typescript": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
+ "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
"node_modules/typical": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz",
@@ -39206,6 +39354,12 @@
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA=="
},
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "devOptional": true
+ },
"node_modules/v8-to-istanbul": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz",
@@ -42209,6 +42363,15 @@
"node": ">=8"
}
},
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -43537,6 +43700,27 @@
"minimist": "^1.2.0"
}
},
+ "@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "devOptional": true,
+ "requires": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "dependencies": {
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "devOptional": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ }
+ }
+ },
"@csstools/convert-colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
@@ -46691,6 +46875,30 @@
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
"dev": true
},
+ "@tsconfig/node10": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+ "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+ "devOptional": true
+ },
+ "@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "devOptional": true
+ },
+ "@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "devOptional": true
+ },
+ "@tsconfig/node16": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
+ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+ "devOptional": true
+ },
"@typechain/ethers-v5": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-2.0.0.tgz",
@@ -46889,10 +47097,16 @@
"@types/node": "*"
}
},
+ "@types/mocha": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz",
+ "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==",
+ "dev": true
+ },
"@types/node": {
- "version": "17.0.14",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.14.tgz",
- "integrity": "sha512-SbjLmERksKOGzWzPNuW7fJM7fk3YXVTFiZWB/Hs99gwhk+/dnrQRPBQjPW9aO+fi1tAffi9PrwFvsmOKmDTyng=="
+ "version": "18.0.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.3.tgz",
+ "integrity": "sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ=="
},
"@types/node-fetch": {
"version": "2.6.1",
@@ -47623,6 +47837,12 @@
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
+ "arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "devOptional": true
+ },
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -49699,6 +49919,12 @@
"sha.js": "^2.4.8"
}
},
+ "create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "devOptional": true
+ },
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -63267,6 +63493,12 @@
"semver": "^5.6.0"
}
},
+ "make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "devOptional": true
+ },
"makeerror": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
@@ -71680,6 +71912,41 @@
}
}
},
+ "ts-node": {
+ "version": "10.8.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.2.tgz",
+ "integrity": "sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==",
+ "devOptional": true,
+ "requires": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "dependencies": {
+ "acorn-walk": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "devOptional": true
+ },
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "devOptional": true
+ }
+ }
+ },
"ts-pnp": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
@@ -71857,6 +72124,11 @@
"is-typedarray": "^1.0.0"
}
},
+ "typescript": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
+ "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ=="
+ },
"typical": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz",
@@ -72213,6 +72485,12 @@
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA=="
},
+ "v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "devOptional": true
+ },
"v8-to-istanbul": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz",
@@ -74747,6 +75025,12 @@
}
}
},
+ "yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "devOptional": true
+ },
"yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index f584b81..6811433 100644
--- a/package.json
+++ b/package.json
@@ -79,6 +79,9 @@
"@nomiclabs/hardhat-waffle": "^2.0.3",
"@openzeppelin/hardhat-upgrades": "^1.19.0",
"@parcel/transformer-image": "^2.0.0-rc.0",
+ "@types/chai": "^4.3.1",
+ "@types/mocha": "^9.1.1",
+ "@types/node": "^18.0.3",
"babel-jest": "^28.1.1",
"chai": "^4.3.6",
"ethereum-waffle": "^3.4.4",
@@ -89,7 +92,9 @@
"parcel": "^2.0.0-rc.0",
"react-error-overlay": "^6.0.9",
"react-test-renderer": "^17.0.2",
- "solidity-coverage": "^0.7.21"
+ "solidity-coverage": "^0.7.21",
+ "ts-node": "^10.8.2",
+ "typescript": "^4.7.4"
},
"resolutions": {
"react-error-overlay": "6.0.9"
diff --git a/tests/unit/smartcontracts/FeedSortAlgoritm.ts b/tests/unit/smartcontracts/FeedSortAlgoritm.ts
new file mode 100644
index 0000000..b999899
--- /dev/null
+++ b/tests/unit/smartcontracts/FeedSortAlgoritm.ts
@@ -0,0 +1,161 @@
+import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
+import { BigNumber, Contract, ContractReceipt } from 'ethers';
+
+const { expect } = require('chai');
+const { ethers, upgrades } = require('hardhat');
+
+const handle = 'social';
+
+function getRandomNumber(max: number) {
+ return Math.floor(Math.random() * max);
+}
+
+const postContent = '0x9d6b0f937680809a01639ad1ae4770241c7c8a0ded490d2f023669f18c6d744b';
+const postImage = '0x5f04837d78fa7a656419f98d73fc1ddaac1bfdfca9a244a2ee128737a186da6e';
+
+describe('PointSocial contract', () => {
+ let identity: Contract;
+ let contract: Contract;
+ let deployer: SignerWithAddress;
+ let goodActor: SignerWithAddress;
+ let badActor: SignerWithAddress;
+ let users: SignerWithAddress[];
+
+ let badPostId: BigNumber;
+ let goodPostId: BigNumber;
+
+ before(async () => {
+ [deployer, goodActor, badActor, ...users] = await ethers.getSigners();
+ const identityFactory = await ethers.getContractFactory('Identity');
+ identity = await upgrades.deployProxy(identityFactory, [], { kind: 'uups' });
+ await identity.deployed();
+ await identity.setDevMode(true);
+ await identity.register(
+ handle,
+ deployer.address,
+ '0xed17268897bbcb67127ed550cee2068a15fdb6f69097eebeb6e2ace46305d1ce',
+ '0xe1e032c91d4c8fe6bab1f198871dbafb8842f073acff8ee9b822f748b180d7eb'
+ );
+ const factory = await ethers.getContractFactory('PointSocial');
+ contract = await upgrades.deployProxy(factory, [identity.address, handle], {
+ kind: 'uups',
+ });
+ await contract.deployed();
+ await setWeights();
+ await populateSocial();
+ await voteBadPost();
+ await voteGoodPost();
+ });
+
+ beforeEach(async () => {
+ await setWeights(1, 1, 0, 10, 10);
+ });
+
+ async function doTransaction(transaction: Promise): Promise {
+ const tx = await transaction;
+ const receipt = tx.wait();
+ return receipt;
+ }
+
+ async function setWeights(
+ likesWeightMultiplier = 0,
+ dislikesWeightWultiplier = 0,
+ oldWeightMultiplierinitialWeight = 0,
+ weightThreshold = 0,
+ initialWeight = 0
+ ) {
+ await doTransaction(
+ contract
+ .connect(deployer)
+ .setWeights(
+ BigNumber.from(likesWeightMultiplier),
+ BigNumber.from(dislikesWeightWultiplier),
+ BigNumber.from(oldWeightMultiplierinitialWeight),
+ BigNumber.from(weightThreshold),
+ BigNumber.from(initialWeight)
+ )
+ );
+ }
+
+ async function voteBadPost() {
+ await Promise.all(
+ Array.from(Array(getRandomNumber(10))).map(() =>
+ doTransaction(
+ contract.connect(users[getRandomNumber(users.length - 1)]).addDislikeToPost(badPostId)
+ )
+ )
+ );
+ }
+
+ async function voteGoodPost() {
+ await Promise.all(
+ Array.from(Array(getRandomNumber(10))).map(() =>
+ doTransaction(
+ contract.connect(users[getRandomNumber(users.length - 1)]).addLikeToPost(goodPostId)
+ )
+ )
+ );
+ }
+
+ async function populateSocial() {
+ // random posts
+ await Promise.all(
+ Array.from(Array(28)).map(() =>
+ doTransaction(
+ contract.connect(users[getRandomNumber(users.length - 1)]).addPost(postContent, postImage)
+ )
+ )
+ );
+
+ const badPostReceipt = await doTransaction(
+ contract.connect(badActor).addPost(postContent, postImage)
+ );
+ badPostId = badPostReceipt.events![0].args![0];
+
+ Array.from(Array(20)).map(() =>
+ doTransaction(
+ contract.connect(users[getRandomNumber(users.length - 1)]).addPost(postContent, postImage)
+ )
+ );
+
+ const goodPostReceipt = await doTransaction(
+ contract.connect(goodActor).addPost(postContent, postImage)
+ );
+ goodPostId = goodPostReceipt.events![0].args![0];
+
+ // more random posts
+ await Promise.all(
+ Array.from(Array(50)).map(() =>
+ doTransaction(
+ contract.connect(users[getRandomNumber(users.length - 1)]).addPost(postContent, postImage)
+ )
+ )
+ );
+ }
+
+ it('all posts should be returned', async () => {
+ const posts = await contract.getAllPosts([]);
+ expect(posts.length).to.equal(100);
+ });
+
+ describe('with weight threshold', () => {
+ beforeEach(async () => {
+ await setWeights(1, 1, 0, 10, 10);
+ });
+
+ it(`bad post should be filtered`, async () => {
+ const posts = await contract.getAllPosts([]);
+ expect(posts.some(({ id }: { id: BigNumber }) => id === badPostId)).to.equal(false);
+ });
+ });
+
+ describe('front sort logic', async () => {
+ beforeEach(async () => {
+ await setWeights(1, 1, 0, 10, 10);
+ });
+ // get X amount of posts
+ let posts = await contract.getPaginatedPosts(100, []);
+ // sort them using weight
+ posts = posts.sort(({ weight: w1 }, { weight: w2 }) => w2 - w1);
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..3a31b10
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "es2018",
+ "module": "commonjs",
+ "strict": true,
+ "esModuleInterop": true,
+ "outDir": "dist"
+ },
+ "exclude": ["dist", "node_modules"],
+ "include": ["./scripts", "./test"],
+ "files": ["./hardhat.config.ts"]
+}
\ No newline at end of file
From bf8af3e6ed0805ebb95b4dd9ae4c7a2847792739 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Sat, 9 Jul 2022 10:22:45 +0200
Subject: [PATCH 06/20] typo
---
contracts/PointSocial.sol | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index 68e7f56..9f0c7f1 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -64,7 +64,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
event ProfileChange(address indexed from, uint256 indexed date);
- event MultiplersChanged(
+ event MultipliersChanged(
address indexed from,
uint256 timestamp,
uint256 likesWeightMultiplier,
@@ -171,7 +171,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
weightThreshold = _weightThreshold;
initialWeight = _initialWeight;
- emit MultiplersChanged(
+ emit MultipliersChanged(
msg.sender,
block.timestamp,
likesWeightMultiplier,
From d9a1df8c9ba3f7255b9b4b714ff2244f1e83487e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Sat, 9 Jul 2022 10:25:11 +0200
Subject: [PATCH 07/20] missing return
---
contracts/PointSocial.sol | 1 +
1 file changed, 1 insertion(+)
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index 9f0c7f1..8062c7b 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -298,6 +298,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
i++;
}
}
+ return false;
}
function flagPost(uint256 postId) public {
From b3e87f5f2d8a65d7608d465fff0bae61e11dea2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Mon, 11 Jul 2022 11:01:43 +0200
Subject: [PATCH 08/20] sort algorithm wip
---
.eslintignore | 4 +
.eslintrc.js | 26 +
contracts/PointSocial.sol | 39 +-
package-lock.json | 1156 +++++++++++------
package.json | 8 +
tests/unit/smartcontracts/FeedSortAlgoritm.ts | 90 +-
tests/unit/smartcontracts/PointSocial.js | 2 +-
7 files changed, 873 insertions(+), 452 deletions(-)
create mode 100644 .eslintignore
create mode 100644 .eslintrc.js
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..85f5562
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,4 @@
+node_modules
+artifacts
+cache
+coverage
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..209d55a
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,26 @@
+module.exports = {
+ env: {
+ browser: false,
+ es2021: true,
+ mocha: true,
+ node: true,
+ },
+ extends: ['standard', 'plugin:prettier/recommended', 'plugin:node/recommended'],
+ parserOptions: {
+ ecmaVersion: 12,
+ },
+ overrides: [
+ {
+ files: ['hardhat.config.js'],
+ globals: { task: true },
+ },
+ {
+ files: ['scripts/**'],
+ rules: { 'no-process-exit': 'off' },
+ },
+ {
+ files: ['hardhat.config.js', 'scripts/**', 'test/**'],
+ rules: { 'node/no-unpublished-require': 'off' },
+ },
+ ],
+};
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index 8062c7b..1e3e9e3 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -334,7 +334,8 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
function _validPostToBeShown(
PostWithMetadata memory _post,
- uint256[] memory _postIdsToFilter
+ uint256[] memory _postIdsToFilter,
+ uint256 _newerThanTimestamp
) public view returns (bool) {
// not deleted posts
// posts with enough weight
@@ -342,13 +343,17 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
return
_post.createdAt != 0 &&
_post.weight >= int256(weightThreshold) &&
- !_inArray(_post.id, _postIdsToFilter);
+ !_inArray(_post.id, _postIdsToFilter) &&
+ // get newest posts if timestamp is set
+ (_newerThanTimestamp == 0 ||
+ _post.createdAt >= _newerThanTimestamp);
}
function _filterPosts(
uint256[] memory _ids,
uint256[] memory _idsToFilter,
- uint256 _maxQty
+ uint256 _maxQty,
+ uint256 _newerThanTimestamp
) internal view returns (PostWithMetadata[] memory) {
uint256 length = _ids.length;
@@ -364,7 +369,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
PostWithMetadata memory _post = _getPostWithMetadata(_ids[i - 1]);
- if (_validPostToBeShown(_post, _idsToFilter)) {
+ if (_validPostToBeShown(_post, _idsToFilter, _newerThanTimestamp)) {
_filteredArray[insertedLength] = _post;
unchecked {
insertedLength++;
@@ -399,12 +404,21 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
function getPaginatedPosts(
uint256 howMany,
uint256[] memory _viewedPostsIds
- ) public view returns (PostWithMetadata[] memory) {
- return _filterPosts(postIds, _viewedPostsIds, howMany);
+ ) external view returns (PostWithMetadata[] memory) {
+ return _filterPosts(postIds, _viewedPostsIds, howMany, 0);
+ }
+
+ function getNewPosts(
+ uint256 _qty,
+ uint256[] memory _viewedPostsIds,
+ uint256 _newerThanTimestamp
+ ) external view returns (PostWithMetadata[] memory) {
+ return
+ _filterPosts(postIds, _viewedPostsIds, _qty, _newerThanTimestamp);
}
function getAllPostsByOwner(address owner, uint256[] memory _viewedPostsIds)
- public
+ external
view
returns (PostWithMetadata[] memory)
{
@@ -412,12 +426,13 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
_filterPosts(
postIdsByOwner[owner],
_viewedPostsIds,
- postIdsByOwner[owner].length
+ postIdsByOwner[owner].length,
+ 0
);
}
function getAllPostsByOwnerLength(address owner)
- public
+ external
view
returns (uint256)
{
@@ -476,12 +491,12 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
address owner,
uint256 howMany,
uint256[] memory _viewedPostsIds
- ) public view returns (PostWithMetadata[] memory) {
- return _filterPosts(postIdsByOwner[owner], _viewedPostsIds, howMany);
+ ) external view returns (PostWithMetadata[] memory) {
+ return _filterPosts(postIdsByOwner[owner], _viewedPostsIds, howMany, 0);
}
function getPostById(uint256 id)
- public
+ external
view
returns (PostWithMetadata memory)
{
diff --git a/package-lock.json b/package-lock.json
index 2e3893c..56b23e8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -58,12 +58,20 @@
"@types/node": "^18.0.3",
"babel-jest": "^28.1.1",
"chai": "^4.3.6",
+ "eslint": "^7.29.0",
+ "eslint-config-prettier": "^8.3.0",
+ "eslint-config-standard": "^16.0.3",
+ "eslint-plugin-import": "^2.23.4",
+ "eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-prettier": "^3.4.0",
+ "eslint-plugin-promise": "^5.1.0",
"ethereum-waffle": "^3.4.4",
"ethers": "^5.6.8",
"hardhat": "^2.9.8",
"identity-obj-proxy": "^3.0.0",
"jest": "^26.6.0",
"parcel": "^2.0.0-rc.0",
+ "prettier": "^2.7.1",
"react-error-overlay": "^6.0.9",
"react-test-renderer": "^17.0.2",
"solidity-coverage": "^0.7.21",
@@ -6835,51 +6843,6 @@
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz",
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
},
- "node_modules/@typescript-eslint/eslint-plugin": {
- "version": "4.33.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz",
- "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==",
- "dependencies": {
- "@typescript-eslint/experimental-utils": "4.33.0",
- "@typescript-eslint/scope-manager": "4.33.0",
- "debug": "^4.3.1",
- "functional-red-black-tree": "^1.0.1",
- "ignore": "^5.1.8",
- "regexpp": "^3.1.0",
- "semver": "^7.3.5",
- "tsutils": "^3.21.0"
- },
- "engines": {
- "node": "^10.12.0 || >=12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "@typescript-eslint/parser": "^4.0.0",
- "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": {
- "version": "7.3.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
- "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@typescript-eslint/experimental-utils": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz",
@@ -6903,32 +6866,6 @@
"eslint": "*"
}
},
- "node_modules/@typescript-eslint/parser": {
- "version": "4.33.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz",
- "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==",
- "dependencies": {
- "@typescript-eslint/scope-manager": "4.33.0",
- "@typescript-eslint/types": "4.33.0",
- "@typescript-eslint/typescript-estree": "4.33.0",
- "debug": "^4.3.1"
- },
- "engines": {
- "node": "^10.12.0 || >=12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
"node_modules/@typescript-eslint/scope-manager": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz",
@@ -7274,9 +7211,9 @@
}
},
"node_modules/acorn": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
- "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
+ "version": "8.7.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
+ "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
"bin": {
"acorn": "bin/acorn"
},
@@ -11946,36 +11883,42 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint-config-react-app": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz",
- "integrity": "sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A==",
- "dependencies": {
- "confusing-browser-globals": "^1.0.10"
- },
- "engines": {
- "node": "^10.12.0 || >=12.0.0"
+ "node_modules/eslint-config-prettier": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
+ "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
},
"peerDependencies": {
- "@typescript-eslint/eslint-plugin": "^4.0.0",
- "@typescript-eslint/parser": "^4.0.0",
- "babel-eslint": "^10.0.0",
- "eslint": "^7.5.0",
- "eslint-plugin-flowtype": "^5.2.0",
- "eslint-plugin-import": "^2.22.0",
- "eslint-plugin-jest": "^24.0.0",
- "eslint-plugin-jsx-a11y": "^6.3.1",
- "eslint-plugin-react": "^7.20.3",
- "eslint-plugin-react-hooks": "^4.0.8",
- "eslint-plugin-testing-library": "^3.9.0"
- },
- "peerDependenciesMeta": {
- "eslint-plugin-jest": {
- "optional": true
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-config-standard": {
+ "version": "16.0.3",
+ "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz",
+ "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
},
- "eslint-plugin-testing-library": {
- "optional": true
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
}
+ ],
+ "peerDependencies": {
+ "eslint": "^7.12.1",
+ "eslint-plugin-import": "^2.22.1",
+ "eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-promise": "^4.2.1 || ^5.0.0"
}
},
"node_modules/eslint-import-resolver-node": {
@@ -12092,19 +12035,47 @@
"node": ">=4"
}
},
- "node_modules/eslint-plugin-flowtype": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.10.0.tgz",
- "integrity": "sha512-vcz32f+7TP+kvTUyMXZmCnNujBQZDNmcqPImw8b9PZ+16w1Qdm6ryRuYZYVaG9xRqqmAPr2Cs9FAX5gN+x/bjw==",
+ "node_modules/eslint-plugin-es": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz",
+ "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==",
+ "dev": true,
"dependencies": {
- "lodash": "^4.17.15",
- "string-natural-compare": "^3.0.1"
+ "eslint-utils": "^2.0.0",
+ "regexpp": "^3.0.0"
},
"engines": {
- "node": "^10.12.0 || >=12.0.0"
+ "node": ">=8.10.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
},
"peerDependencies": {
- "eslint": "^7.1.0"
+ "eslint": ">=4.19.1"
+ }
+ },
+ "node_modules/eslint-plugin-es/node_modules/eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
}
},
"node_modules/eslint-plugin-import": {
@@ -12173,26 +12144,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/eslint-plugin-jest": {
- "version": "24.7.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.7.0.tgz",
- "integrity": "sha512-wUxdF2bAZiYSKBclsUMrYHH6WxiBreNjyDxbRv345TIvPeoCEgPNEn3Sa+ZrSqsf1Dl9SqqSREXMHExlMMu1DA==",
- "dependencies": {
- "@typescript-eslint/experimental-utils": "^4.0.1"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "@typescript-eslint/eslint-plugin": ">= 4",
- "eslint": ">=5"
- },
- "peerDependenciesMeta": {
- "@typescript-eslint/eslint-plugin": {
- "optional": true
- }
- }
- },
"node_modules/eslint-plugin-jsx-a11y": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz",
@@ -12230,6 +12181,92 @@
"node": ">=6.0"
}
},
+ "node_modules/eslint-plugin-node": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
+ "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==",
+ "dev": true,
+ "dependencies": {
+ "eslint-plugin-es": "^3.0.0",
+ "eslint-utils": "^2.0.0",
+ "ignore": "^5.1.1",
+ "minimatch": "^3.0.4",
+ "resolve": "^1.10.1",
+ "semver": "^6.1.0"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=5.16.0"
+ }
+ },
+ "node_modules/eslint-plugin-node/node_modules/eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/eslint-plugin-node/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-plugin-node/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz",
+ "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=5.0.0",
+ "prettier": ">=1.13.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-promise": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.2.0.tgz",
+ "integrity": "sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==",
+ "dev": true,
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0"
+ }
+ },
"node_modules/eslint-plugin-react": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz",
@@ -12299,133 +12336,6 @@
"semver": "bin/semver.js"
}
},
- "node_modules/eslint-plugin-testing-library": {
- "version": "3.10.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-3.10.2.tgz",
- "integrity": "sha512-WAmOCt7EbF1XM8XfbCKAEzAPnShkNSwcIsAD2jHdsMUT9mZJPjLCG7pMzbcC8kK366NOuGip8HKLDC+Xk4yIdA==",
- "dependencies": {
- "@typescript-eslint/experimental-utils": "^3.10.1"
- },
- "engines": {
- "node": "^10.12.0 || >=12.0.0",
- "npm": ">=6"
- },
- "peerDependencies": {
- "eslint": "^5 || ^6 || ^7"
- }
- },
- "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/experimental-utils": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz",
- "integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==",
- "dependencies": {
- "@types/json-schema": "^7.0.3",
- "@typescript-eslint/types": "3.10.1",
- "@typescript-eslint/typescript-estree": "3.10.1",
- "eslint-scope": "^5.0.0",
- "eslint-utils": "^2.0.0"
- },
- "engines": {
- "node": "^10.12.0 || >=12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "*"
- }
- },
- "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/types": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz",
- "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==",
- "engines": {
- "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/typescript-estree": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz",
- "integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==",
- "dependencies": {
- "@typescript-eslint/types": "3.10.1",
- "@typescript-eslint/visitor-keys": "3.10.1",
- "debug": "^4.1.1",
- "glob": "^7.1.6",
- "is-glob": "^4.0.1",
- "lodash": "^4.17.15",
- "semver": "^7.3.2",
- "tsutils": "^3.17.1"
- },
- "engines": {
- "node": "^10.12.0 || >=12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/visitor-keys": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz",
- "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==",
- "dependencies": {
- "eslint-visitor-keys": "^1.1.0"
- },
- "engines": {
- "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/eslint-plugin-testing-library/node_modules/eslint-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
- "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
- "dependencies": {
- "eslint-visitor-keys": "^1.1.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/mysticatea"
- }
- },
- "node_modules/eslint-plugin-testing-library/node_modules/eslint-visitor-keys": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
- "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/eslint-plugin-testing-library/node_modules/semver": {
- "version": "7.3.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
- "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -12563,6 +12473,17 @@
"node": ">= 4"
}
},
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/eslint/node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -12572,9 +12493,9 @@
}
},
"node_modules/eslint/node_modules/semver": {
- "version": "7.3.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
- "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "version": "7.3.7",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+ "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -23678,9 +23599,9 @@
}
},
"node_modules/globals": {
- "version": "13.12.1",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz",
- "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==",
+ "version": "13.16.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz",
+ "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==",
"dependencies": {
"type-fest": "^0.20.2"
},
@@ -25280,9 +25201,9 @@
}
},
"node_modules/is-core-module": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
- "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+ "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"dependencies": {
"has": "^1.0.3"
},
@@ -27611,7 +27532,7 @@
"node_modules/lodash.truncate": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
- "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM="
+ "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw=="
},
"node_modules/lodash.uniq": {
"version": "4.5.0",
@@ -33039,9 +32960,9 @@
}
},
"node_modules/prettier": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
- "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+ "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"bin": {
"prettier": "bin-prettier.js"
},
@@ -33052,6 +32973,18 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/prettier-plugin-solidity": {
"version": "1.0.0-beta.19",
"resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.19.tgz",
@@ -33838,6 +33771,14 @@
}
}
},
+ "node_modules/react-scripts/node_modules/@babel/code-frame": {
+ "version": "7.12.11",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+ "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
+ "dependencies": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
"node_modules/react-scripts/node_modules/@babel/core": {
"version": "7.12.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz",
@@ -33930,6 +33871,100 @@
"node": ">= 8"
}
},
+ "node_modules/react-scripts/node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "4.33.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz",
+ "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==",
+ "dependencies": {
+ "@typescript-eslint/experimental-utils": "4.33.0",
+ "@typescript-eslint/scope-manager": "4.33.0",
+ "debug": "^4.3.1",
+ "functional-red-black-tree": "^1.0.1",
+ "ignore": "^5.1.8",
+ "regexpp": "^3.1.0",
+ "semver": "^7.3.5",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^4.0.0",
+ "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-scripts/node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": {
+ "version": "7.3.7",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+ "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/react-scripts/node_modules/@typescript-eslint/parser": {
+ "version": "4.33.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz",
+ "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "4.33.0",
+ "@typescript-eslint/types": "4.33.0",
+ "@typescript-eslint/typescript-estree": "4.33.0",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-scripts/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz",
+ "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==",
+ "dependencies": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/react-scripts/node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/react-scripts/node_modules/babel-jest": {
"version": "26.6.3",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz",
@@ -33988,6 +34023,171 @@
"node": ">=8"
}
},
+ "node_modules/react-scripts/node_modules/eslint-config-react-app": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz",
+ "integrity": "sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A==",
+ "dependencies": {
+ "confusing-browser-globals": "^1.0.10"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/eslint-plugin": "^4.0.0",
+ "@typescript-eslint/parser": "^4.0.0",
+ "babel-eslint": "^10.0.0",
+ "eslint": "^7.5.0",
+ "eslint-plugin-flowtype": "^5.2.0",
+ "eslint-plugin-import": "^2.22.0",
+ "eslint-plugin-jest": "^24.0.0",
+ "eslint-plugin-jsx-a11y": "^6.3.1",
+ "eslint-plugin-react": "^7.20.3",
+ "eslint-plugin-react-hooks": "^4.0.8",
+ "eslint-plugin-testing-library": "^3.9.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint-plugin-jest": {
+ "optional": true
+ },
+ "eslint-plugin-testing-library": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-scripts/node_modules/eslint-plugin-flowtype": {
+ "version": "5.10.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.10.0.tgz",
+ "integrity": "sha512-vcz32f+7TP+kvTUyMXZmCnNujBQZDNmcqPImw8b9PZ+16w1Qdm6ryRuYZYVaG9xRqqmAPr2Cs9FAX5gN+x/bjw==",
+ "dependencies": {
+ "lodash": "^4.17.15",
+ "string-natural-compare": "^3.0.1"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.1.0"
+ }
+ },
+ "node_modules/react-scripts/node_modules/eslint-plugin-jest": {
+ "version": "24.7.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.7.0.tgz",
+ "integrity": "sha512-wUxdF2bAZiYSKBclsUMrYHH6WxiBreNjyDxbRv345TIvPeoCEgPNEn3Sa+ZrSqsf1Dl9SqqSREXMHExlMMu1DA==",
+ "dependencies": {
+ "@typescript-eslint/experimental-utils": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/eslint-plugin": ">= 4",
+ "eslint": ">=5"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/eslint-plugin": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-scripts/node_modules/eslint-plugin-testing-library": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-3.10.2.tgz",
+ "integrity": "sha512-WAmOCt7EbF1XM8XfbCKAEzAPnShkNSwcIsAD2jHdsMUT9mZJPjLCG7pMzbcC8kK366NOuGip8HKLDC+Xk4yIdA==",
+ "dependencies": {
+ "@typescript-eslint/experimental-utils": "^3.10.1"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0",
+ "npm": ">=6"
+ },
+ "peerDependencies": {
+ "eslint": "^5 || ^6 || ^7"
+ }
+ },
+ "node_modules/react-scripts/node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/experimental-utils": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz",
+ "integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==",
+ "dependencies": {
+ "@types/json-schema": "^7.0.3",
+ "@typescript-eslint/types": "3.10.1",
+ "@typescript-eslint/typescript-estree": "3.10.1",
+ "eslint-scope": "^5.0.0",
+ "eslint-utils": "^2.0.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ }
+ },
+ "node_modules/react-scripts/node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/types": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz",
+ "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==",
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/react-scripts/node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz",
+ "integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==",
+ "dependencies": {
+ "@typescript-eslint/types": "3.10.1",
+ "@typescript-eslint/visitor-keys": "3.10.1",
+ "debug": "^4.1.1",
+ "glob": "^7.1.6",
+ "is-glob": "^4.0.1",
+ "lodash": "^4.17.15",
+ "semver": "^7.3.2",
+ "tsutils": "^3.17.1"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-scripts/node_modules/eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dependencies": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/react-scripts/node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/react-scripts/node_modules/react-refresh": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
@@ -37978,9 +38178,9 @@
}
},
"node_modules/table/node_modules/ajv": {
- "version": "8.9.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
- "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -47312,31 +47512,6 @@
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz",
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
},
- "@typescript-eslint/eslint-plugin": {
- "version": "4.33.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz",
- "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==",
- "requires": {
- "@typescript-eslint/experimental-utils": "4.33.0",
- "@typescript-eslint/scope-manager": "4.33.0",
- "debug": "^4.3.1",
- "functional-red-black-tree": "^1.0.1",
- "ignore": "^5.1.8",
- "regexpp": "^3.1.0",
- "semver": "^7.3.5",
- "tsutils": "^3.21.0"
- },
- "dependencies": {
- "semver": {
- "version": "7.3.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
- "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
"@typescript-eslint/experimental-utils": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz",
@@ -47350,17 +47525,6 @@
"eslint-utils": "^3.0.0"
}
},
- "@typescript-eslint/parser": {
- "version": "4.33.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz",
- "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==",
- "requires": {
- "@typescript-eslint/scope-manager": "4.33.0",
- "@typescript-eslint/types": "4.33.0",
- "@typescript-eslint/typescript-estree": "4.33.0",
- "debug": "^4.3.1"
- }
- },
"@typescript-eslint/scope-manager": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz",
@@ -47648,9 +47812,9 @@
}
},
"acorn": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
- "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
+ "version": "8.7.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
+ "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A=="
},
"acorn-globals": {
"version": "6.0.0",
@@ -51365,15 +51529,23 @@
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg=="
},
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"semver": {
- "version": "7.3.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
- "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "version": "7.3.7",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+ "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^6.0.0"
}
@@ -51401,13 +51573,19 @@
}
}
},
- "eslint-config-react-app": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz",
- "integrity": "sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A==",
- "requires": {
- "confusing-browser-globals": "^1.0.10"
- }
+ "eslint-config-prettier": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
+ "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
+ "dev": true,
+ "requires": {}
+ },
+ "eslint-config-standard": {
+ "version": "16.0.3",
+ "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz",
+ "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==",
+ "dev": true,
+ "requires": {}
},
"eslint-import-resolver-node": {
"version": "0.3.6",
@@ -51500,13 +51678,31 @@
}
}
},
- "eslint-plugin-flowtype": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.10.0.tgz",
- "integrity": "sha512-vcz32f+7TP+kvTUyMXZmCnNujBQZDNmcqPImw8b9PZ+16w1Qdm6ryRuYZYVaG9xRqqmAPr2Cs9FAX5gN+x/bjw==",
+ "eslint-plugin-es": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz",
+ "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==",
+ "dev": true,
"requires": {
- "lodash": "^4.17.15",
- "string-natural-compare": "^3.0.1"
+ "eslint-utils": "^2.0.0",
+ "regexpp": "^3.0.0"
+ },
+ "dependencies": {
+ "eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
}
},
"eslint-plugin-import": {
@@ -51562,14 +51758,6 @@
}
}
},
- "eslint-plugin-jest": {
- "version": "24.7.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.7.0.tgz",
- "integrity": "sha512-wUxdF2bAZiYSKBclsUMrYHH6WxiBreNjyDxbRv345TIvPeoCEgPNEn3Sa+ZrSqsf1Dl9SqqSREXMHExlMMu1DA==",
- "requires": {
- "@typescript-eslint/experimental-utils": "^4.0.1"
- }
- },
"eslint-plugin-jsx-a11y": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz",
@@ -51600,6 +51788,59 @@
}
}
},
+ "eslint-plugin-node": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
+ "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==",
+ "dev": true,
+ "requires": {
+ "eslint-plugin-es": "^3.0.0",
+ "eslint-utils": "^2.0.0",
+ "ignore": "^5.1.1",
+ "minimatch": "^3.0.4",
+ "resolve": "^1.10.1",
+ "semver": "^6.1.0"
+ },
+ "dependencies": {
+ "eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-prettier": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz",
+ "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==",
+ "dev": true,
+ "requires": {
+ "prettier-linter-helpers": "^1.0.0"
+ }
+ },
+ "eslint-plugin-promise": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.2.0.tgz",
+ "integrity": "sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==",
+ "dev": true,
+ "requires": {}
+ },
"eslint-plugin-react": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz",
@@ -51651,77 +51892,6 @@
"integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==",
"requires": {}
},
- "eslint-plugin-testing-library": {
- "version": "3.10.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-3.10.2.tgz",
- "integrity": "sha512-WAmOCt7EbF1XM8XfbCKAEzAPnShkNSwcIsAD2jHdsMUT9mZJPjLCG7pMzbcC8kK366NOuGip8HKLDC+Xk4yIdA==",
- "requires": {
- "@typescript-eslint/experimental-utils": "^3.10.1"
- },
- "dependencies": {
- "@typescript-eslint/experimental-utils": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz",
- "integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==",
- "requires": {
- "@types/json-schema": "^7.0.3",
- "@typescript-eslint/types": "3.10.1",
- "@typescript-eslint/typescript-estree": "3.10.1",
- "eslint-scope": "^5.0.0",
- "eslint-utils": "^2.0.0"
- }
- },
- "@typescript-eslint/types": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz",
- "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ=="
- },
- "@typescript-eslint/typescript-estree": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz",
- "integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==",
- "requires": {
- "@typescript-eslint/types": "3.10.1",
- "@typescript-eslint/visitor-keys": "3.10.1",
- "debug": "^4.1.1",
- "glob": "^7.1.6",
- "is-glob": "^4.0.1",
- "lodash": "^4.17.15",
- "semver": "^7.3.2",
- "tsutils": "^3.17.1"
- }
- },
- "@typescript-eslint/visitor-keys": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz",
- "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==",
- "requires": {
- "eslint-visitor-keys": "^1.1.0"
- }
- },
- "eslint-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
- "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
- "requires": {
- "eslint-visitor-keys": "^1.1.0"
- }
- },
- "eslint-visitor-keys": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
- "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="
- },
- "semver": {
- "version": "7.3.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
- "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
"eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -60365,9 +60535,9 @@
}
},
"globals": {
- "version": "13.12.1",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz",
- "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==",
+ "version": "13.16.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz",
+ "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==",
"requires": {
"type-fest": "^0.20.2"
}
@@ -61570,9 +61740,9 @@
}
},
"is-core-module": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
- "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+ "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"requires": {
"has": "^1.0.3"
}
@@ -63388,7 +63558,7 @@
"lodash.truncate": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
- "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM="
+ "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw=="
},
"lodash.uniq": {
"version": "4.5.0",
@@ -67585,9 +67755,18 @@
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
},
"prettier": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
- "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew=="
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+ "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g=="
+ },
+ "prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "requires": {
+ "fast-diff": "^1.1.2"
+ }
},
"prettier-plugin-solidity": {
"version": "1.0.0-beta.19",
@@ -68202,6 +68381,14 @@
"workbox-webpack-plugin": "5.1.4"
},
"dependencies": {
+ "@babel/code-frame": {
+ "version": "7.12.11",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+ "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
"@babel/core": {
"version": "7.12.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz",
@@ -68252,6 +68439,57 @@
}
}
},
+ "@typescript-eslint/eslint-plugin": {
+ "version": "4.33.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz",
+ "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==",
+ "requires": {
+ "@typescript-eslint/experimental-utils": "4.33.0",
+ "@typescript-eslint/scope-manager": "4.33.0",
+ "debug": "^4.3.1",
+ "functional-red-black-tree": "^1.0.1",
+ "ignore": "^5.1.8",
+ "regexpp": "^3.1.0",
+ "semver": "^7.3.5",
+ "tsutils": "^3.21.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "7.3.7",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+ "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "4.33.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz",
+ "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==",
+ "requires": {
+ "@typescript-eslint/scope-manager": "4.33.0",
+ "@typescript-eslint/types": "4.33.0",
+ "@typescript-eslint/typescript-estree": "4.33.0",
+ "debug": "^4.3.1"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz",
+ "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==",
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="
+ }
+ }
+ },
"babel-jest": {
"version": "26.6.3",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz",
@@ -68292,6 +68530,88 @@
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
},
+ "eslint-config-react-app": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz",
+ "integrity": "sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A==",
+ "requires": {
+ "confusing-browser-globals": "^1.0.10"
+ }
+ },
+ "eslint-plugin-flowtype": {
+ "version": "5.10.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.10.0.tgz",
+ "integrity": "sha512-vcz32f+7TP+kvTUyMXZmCnNujBQZDNmcqPImw8b9PZ+16w1Qdm6ryRuYZYVaG9xRqqmAPr2Cs9FAX5gN+x/bjw==",
+ "requires": {
+ "lodash": "^4.17.15",
+ "string-natural-compare": "^3.0.1"
+ }
+ },
+ "eslint-plugin-jest": {
+ "version": "24.7.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.7.0.tgz",
+ "integrity": "sha512-wUxdF2bAZiYSKBclsUMrYHH6WxiBreNjyDxbRv345TIvPeoCEgPNEn3Sa+ZrSqsf1Dl9SqqSREXMHExlMMu1DA==",
+ "requires": {
+ "@typescript-eslint/experimental-utils": "^4.0.1"
+ }
+ },
+ "eslint-plugin-testing-library": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-3.10.2.tgz",
+ "integrity": "sha512-WAmOCt7EbF1XM8XfbCKAEzAPnShkNSwcIsAD2jHdsMUT9mZJPjLCG7pMzbcC8kK366NOuGip8HKLDC+Xk4yIdA==",
+ "requires": {
+ "@typescript-eslint/experimental-utils": "^3.10.1"
+ },
+ "dependencies": {
+ "@typescript-eslint/experimental-utils": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz",
+ "integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==",
+ "requires": {
+ "@types/json-schema": "^7.0.3",
+ "@typescript-eslint/types": "3.10.1",
+ "@typescript-eslint/typescript-estree": "3.10.1",
+ "eslint-scope": "^5.0.0",
+ "eslint-utils": "^2.0.0"
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz",
+ "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ=="
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz",
+ "integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==",
+ "requires": {
+ "@typescript-eslint/types": "3.10.1",
+ "@typescript-eslint/visitor-keys": "3.10.1",
+ "debug": "^4.1.1",
+ "glob": "^7.1.6",
+ "is-glob": "^4.0.1",
+ "lodash": "^4.17.15",
+ "semver": "^7.3.2",
+ "tsutils": "^3.17.1"
+ }
+ }
+ }
+ },
+ "eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="
+ }
+ }
+ },
"react-refresh": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
@@ -71439,9 +71759,9 @@
},
"dependencies": {
"ajv": {
- "version": "8.9.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
- "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
diff --git a/package.json b/package.json
index 6811433..e10bdcd 100644
--- a/package.json
+++ b/package.json
@@ -84,12 +84,20 @@
"@types/node": "^18.0.3",
"babel-jest": "^28.1.1",
"chai": "^4.3.6",
+ "eslint": "^7.29.0",
+ "eslint-config-prettier": "^8.3.0",
+ "eslint-config-standard": "^16.0.3",
+ "eslint-plugin-import": "^2.23.4",
+ "eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-prettier": "^3.4.0",
+ "eslint-plugin-promise": "^5.1.0",
"ethereum-waffle": "^3.4.4",
"ethers": "^5.6.8",
"hardhat": "^2.9.8",
"identity-obj-proxy": "^3.0.0",
"jest": "^26.6.0",
"parcel": "^2.0.0-rc.0",
+ "prettier": "^2.7.1",
"react-error-overlay": "^6.0.9",
"react-test-renderer": "^17.0.2",
"solidity-coverage": "^0.7.21",
diff --git a/tests/unit/smartcontracts/FeedSortAlgoritm.ts b/tests/unit/smartcontracts/FeedSortAlgoritm.ts
index b999899..75ab9f0 100644
--- a/tests/unit/smartcontracts/FeedSortAlgoritm.ts
+++ b/tests/unit/smartcontracts/FeedSortAlgoritm.ts
@@ -2,7 +2,7 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { BigNumber, Contract, ContractReceipt } from 'ethers';
const { expect } = require('chai');
-const { ethers, upgrades } = require('hardhat');
+const { ethers, upgrades, network } = require('hardhat');
const handle = 'social';
@@ -13,6 +13,23 @@ function getRandomNumber(max: number) {
const postContent = '0x9d6b0f937680809a01639ad1ae4770241c7c8a0ded490d2f023669f18c6d744b';
const postImage = '0x5f04837d78fa7a656419f98d73fc1ddaac1bfdfca9a244a2ee128737a186da6e';
+type Post = {
+ id: BigNumber;
+ createdAt: BigNumber;
+ weight: number;
+};
+
+const MINUTE = 60;
+const HOUR = MINUTE * 60;
+const DAY = HOUR * 24;
+const MONTH = DAY * 30;
+const YEAR = MONTH * 12;
+
+async function addTime(seconds: number) {
+ await network.provider.send('evm_increaseTime', [seconds]);
+ await network.provider.send('evm_mine');
+}
+
describe('PointSocial contract', () => {
let identity: Contract;
let contract: Contract;
@@ -24,8 +41,7 @@ describe('PointSocial contract', () => {
let badPostId: BigNumber;
let goodPostId: BigNumber;
- before(async () => {
- [deployer, goodActor, badActor, ...users] = await ethers.getSigners();
+ async function deployContracts() {
const identityFactory = await ethers.getContractFactory('Identity');
identity = await upgrades.deployProxy(identityFactory, [], { kind: 'uups' });
await identity.deployed();
@@ -41,6 +57,11 @@ describe('PointSocial contract', () => {
kind: 'uups',
});
await contract.deployed();
+ }
+
+ before(async () => {
+ [deployer, goodActor, badActor, ...users] = await ethers.getSigners();
+ await deployContracts();
await setWeights();
await populateSocial();
await voteBadPost();
@@ -87,6 +108,16 @@ describe('PointSocial contract', () => {
);
}
+ async function addPost(
+ content: string,
+ image: string,
+ user: SignerWithAddress = deployer
+ ): Promise {
+ const receipt = await doTransaction(contract.connect(user).addPost(postContent, postImage));
+ const id: BigNumber = receipt.events![0].args![0];
+ return id;
+ }
+
async function voteGoodPost() {
await Promise.all(
Array.from(Array(getRandomNumber(10))).map(() =>
@@ -101,34 +132,25 @@ describe('PointSocial contract', () => {
// random posts
await Promise.all(
Array.from(Array(28)).map(() =>
- doTransaction(
- contract.connect(users[getRandomNumber(users.length - 1)]).addPost(postContent, postImage)
- )
+ addPost(postContent, postImage, users[getRandomNumber(users.length - 1)])
)
);
- const badPostReceipt = await doTransaction(
- contract.connect(badActor).addPost(postContent, postImage)
- );
- badPostId = badPostReceipt.events![0].args![0];
+ badPostId = await addPost(postContent, postImage, badActor);
- Array.from(Array(20)).map(() =>
- doTransaction(
- contract.connect(users[getRandomNumber(users.length - 1)]).addPost(postContent, postImage)
+ // more random posts
+ await Promise.all(
+ Array.from(Array(20)).map(() =>
+ addPost(postContent, postImage, users[getRandomNumber(users.length - 1)])
)
);
- const goodPostReceipt = await doTransaction(
- contract.connect(goodActor).addPost(postContent, postImage)
- );
- goodPostId = goodPostReceipt.events![0].args![0];
+ goodPostId = await addPost(postContent, postImage, goodActor);
// more random posts
await Promise.all(
Array.from(Array(50)).map(() =>
- doTransaction(
- contract.connect(users[getRandomNumber(users.length - 1)]).addPost(postContent, postImage)
- )
+ addPost(postContent, postImage, users[getRandomNumber(users.length - 1)])
)
);
}
@@ -156,6 +178,32 @@ describe('PointSocial contract', () => {
// get X amount of posts
let posts = await contract.getPaginatedPosts(100, []);
// sort them using weight
- posts = posts.sort(({ weight: w1 }, { weight: w2 }) => w2 - w1);
+ posts = posts.sort(({ weight: w1 }: Post, { weight: w2 }: Post) => w2 - w1);
+ });
+
+ describe('if a new post is created', () => {
+ let viewedPosts: Post[];
+ before(async () => {
+ viewedPosts = await contract.getPaginatedPosts(100, []);
+ await addTime(HOUR);
+ await addPost(postContent, postImage, users[1]);
+ });
+
+ it(`if the users clicks on 'get newest posts', that post should be returned`, async () => {
+ let newestViewedPostTimestamp = BigNumber.from(0);
+ const viewedPostIds = viewedPosts.map(({ createdAt, id }: Post) => {
+ newestViewedPostTimestamp = newestViewedPostTimestamp.lt(createdAt)
+ ? createdAt
+ : newestViewedPostTimestamp;
+ return id;
+ });
+ const newPostsToView = await contract.getNewPosts(
+ 10,
+ viewedPostIds,
+ newestViewedPostTimestamp
+ );
+ expect(newPostsToView.length).to.equal(1);
+ expect(newPostsToView[0].from).to.equal(users[1].address);
+ });
});
});
diff --git a/tests/unit/smartcontracts/PointSocial.js b/tests/unit/smartcontracts/PointSocial.js
index f760128..37bc9ff 100644
--- a/tests/unit/smartcontracts/PointSocial.js
+++ b/tests/unit/smartcontracts/PointSocial.js
@@ -54,7 +54,7 @@ describe('PointSocial contract', function () {
let socialFactoryDeployer = factory.connect(addr1);
await expect(
upgrades.upgradeProxy(pointSocial.address, socialFactoryDeployer)
- ).to.be.revertedWith('You are not a deployer of this identity');
+ ).to.be.revertedWith('Not a deployer');
});
});
From c201b20fa1ce38cf78a590efa036808fd41352f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Mon, 11 Jul 2022 11:07:09 +0200
Subject: [PATCH 09/20] external instead of public
---
contracts/PointSocial.sol | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index 1e3e9e3..eab3ea2 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -184,7 +184,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
function initialize(
address identityContractAddr,
string calldata identityHandle
- ) public initializer onlyProxy {
+ ) external initializer onlyProxy {
__Ownable_init();
__UUPSUpgradeable_init();
_identityContractAddr = identityContractAddr;
@@ -193,7 +193,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
function _authorizeUpgrade(address) internal view override onlyDeployer {}
- function addMigrator(address migrator) public onlyOwner {
+ function addMigrator(address migrator) external onlyOwner {
require(_migrator == address(0), "Access Denied");
_migrator = migrator;
emit StateChange(
@@ -205,7 +205,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
);
}
- function isDeployer() public view returns (bool) {
+ function isDeployer() external view returns (bool) {
return
IIdentity(_identityContractAddr).isIdentityDeployer(
_identityHandle,
@@ -214,7 +214,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
}
// Post data functions
- function addPost(bytes32 contents, bytes32 image) public {
+ function addPost(bytes32 contents, bytes32 image) external {
_postIds.increment();
uint256 newPostId = _postIds.current();
Post memory _post = Post(
@@ -243,7 +243,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 postId,
bytes32 contents,
bytes32 image
- ) public {
+ ) external {
require(postById[postId].createdAt != 0, "ERROR_POST_DOES_NOT_EXISTS");
require(
msg.sender == postById[postId].from,
@@ -262,7 +262,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
);
}
- function deletePost(uint256 postId) public {
+ function deletePost(uint256 postId) external {
require(postById[postId].createdAt != 0, "ERROR_POST_DOES_NOT_EXISTS");
require(
msg.sender == postById[postId].from,
@@ -301,7 +301,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
return false;
}
- function flagPost(uint256 postId) public {
+ function flagPost(uint256 postId) external {
require(
IIdentity(_identityContractAddr).isIdentityDeployer(
_identityHandle,
@@ -322,7 +322,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
);
}
- function getAllPosts() public view returns (PostWithMetadata[] memory) {
+ function getAllPosts() external view returns (PostWithMetadata[] memory) {
PostWithMetadata[] memory postsWithMetadata = new PostWithMetadata[](
postIds.length
);
@@ -391,7 +391,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
return _toReturnArray;
}
- function getAllPostsLength() public view returns (uint256) {
+ function getAllPostsLength() external view returns (uint256) {
uint256 length = 0;
for (uint256 i = 0; i < postIds.length; i++) {
if (postById[postIds[i]].createdAt > 0) {
@@ -504,7 +504,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
}
// Example: 1,"0x0000000000000000000000000000000000000000000068692066726f6d20706e"
- function addCommentToPost(uint256 postId, bytes32 contents) public {
+ function addCommentToPost(uint256 postId, bytes32 contents) external {
_commentIds.increment();
uint256 newCommentId = _commentIds.current();
Comment memory _comment = Comment(
@@ -527,7 +527,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
);
}
- function editCommentForPost(uint256 commentId, bytes32 contents) public {
+ function editCommentForPost(uint256 commentId, bytes32 contents) external {
Comment storage comment = commentById[commentId];
require(comment.createdAt != 0, "ERROR_POST_DOES_NOT_EXISTS");
@@ -546,7 +546,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
);
}
- function deleteCommentForPost(uint256 postId, uint256 commentId) public {
+ function deleteCommentForPost(uint256 postId, uint256 commentId) external {
require(
commentById[commentId].createdAt != 0,
"ERROR_COMMENT_DOES_NOT_EXISTS"
@@ -576,7 +576,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
}
function getAllCommentsForPost(uint256 postId)
- public
+ external
view
returns (Comment[] memory)
{
@@ -589,7 +589,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
return _comments;
}
- function getCommentById(uint256 id) public view returns (Comment memory) {
+ function getCommentById(uint256 id) external view returns (Comment memory) {
return commentById[id];
}
@@ -784,7 +784,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
bytes32 about_,
bytes32 avatar_,
bytes32 banner_
- ) public {
+ ) external {
profileByOwner[msg.sender].displayName = name_;
profileByOwner[msg.sender].displayLocation = location_;
profileByOwner[msg.sender].displayAbout = about_;
@@ -806,7 +806,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
bytes32 image,
uint16 likesCount,
uint256 createdAt
- ) public {
+ ) external {
require(msg.sender == _migrator, "Access Denied");
Post memory _post = Post({
@@ -839,7 +839,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
address author,
bytes32 contents,
uint256 createdAt
- ) public {
+ ) external {
require(msg.sender == _migrator, "Access Denied");
Comment memory _comment = Comment({
@@ -871,7 +871,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
bytes32 about,
bytes32 avatar,
bytes32 banner
- ) public {
+ ) external {
require(msg.sender == _migrator, "Access Denied");
profileByOwner[user].displayName = name;
From f658e0caa214d516643993731f4d2346cacb1021 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Mon, 11 Jul 2022 17:55:35 +0200
Subject: [PATCH 10/20] wip
---
src/components/feed/Feed.jsx | 53 +-
src/components/post/PostCard.jsx | 1405 ++++++++++++++++--------------
src/mappers/Post.ts | 31 +
src/pages/post/Post.jsx | 255 +++---
src/types.d.ts | 16 +
5 files changed, 948 insertions(+), 812 deletions(-)
create mode 100644 src/mappers/Post.ts
create mode 100644 src/types.d.ts
diff --git a/src/components/feed/Feed.jsx b/src/components/feed/Feed.jsx
index 34e9c77..fc5ab44 100644
--- a/src/components/feed/Feed.jsx
+++ b/src/components/feed/Feed.jsx
@@ -17,7 +17,10 @@ import PostCard from '../post/PostCard';
import EventConstants from '../../events';
import PostManager from '../../services/PostManager';
-const NUM_POSTS_PER_CALL = 5;
+import getPostData from '../../mappers/Post';
+
+const NUM_POSTS_PER_PAGE = 5;
+const NUM_POSTS_PER_CALL = 100;
const useStyles = makeStyles((theme) => ({
root: {
@@ -66,6 +69,7 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
});
const styles = useStyles();
const [posts, setPosts] = useState([]);
+ const [viewedPostIds, setViewedPostIds] = useState([]);
const [length, setLength] = useState(0);
const [loading, setLoading] = useState(false);
const [reload, setReload] = useState(false);
@@ -139,8 +143,6 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
};
const fetchPosts = async () => {
- const viewedPostIds = posts.map(({ id }) => id);
-
try {
setLoading(true);
@@ -148,33 +150,11 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
? PostManager.getPaginatedPostsByOwner(account, NUM_POSTS_PER_CALL, viewedPostIds)
: PostManager.getPaginatedPosts(NUM_POSTS_PER_CALL, viewedPostIds));
- const newPosts = data.map(
- ([
- id,
- from,
- contents,
- image,
- createdAt,
- likesCount,
- commentsCount,
- dislikesCount,
- liked,
- disliked,
- ]) => ({
- id,
- from,
- contents,
- image,
- createdAt: createdAt * 1000,
- likesCount: parseInt(likesCount, 10),
- dislikesCount: parseInt(dislikesCount, 10),
- commentsCount: parseInt(commentsCount, 10),
- liked,
- disliked,
- })
- );
+ const newPosts = data.map(getPostData);
- return newPosts.sort(({ weight: w1 }, { weight: w2 }) => w1 - w2);
+ return newPosts
+ .sort(({ weight: w1 }, { weight: w2 }) => w2 - w1)
+ .slice(0, NUM_POSTS_PER_PAGE);
} catch (error) {
console.log(error.message);
setAlert(error.message);
@@ -187,6 +167,8 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
try {
setLoading(true);
const posts = await fetchPosts();
+
+ // save posts
setPosts((prev) => {
if (refresh) {
return posts;
@@ -194,6 +176,16 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
const result = unionWith(prev, posts, isEqual);
return result;
});
+
+ // save viewed posts
+ setViewedPostIds((prev) => {
+ posts.forEach(({ id }) => {
+ if (!prev.includes(id)) {
+ prev.push(id);
+ }
+ });
+ return prev;
+ });
} catch (error) {
console.log(error);
setAlert(error.message);
@@ -203,8 +195,7 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
};
const reloadPosts = async (refresh = false) => {
- await getPostsLength();
- await getPosts(refresh);
+ await Promise.all([getPostsLength(), getPosts(refresh)]);
setReload(false);
};
diff --git a/src/components/post/PostCard.jsx b/src/components/post/PostCard.jsx
index facccf2..19a6e03 100644
--- a/src/components/post/PostCard.jsx
+++ b/src/components/post/PostCard.jsx
@@ -1,5 +1,5 @@
import clsx from 'clsx';
-import { useEffect, useState, useRef, createRef } from "react";
+import { useEffect, useState, useRef, createRef } from 'react';
import { useAppContext } from '../../context/AppContext';
import { makeStyles } from '@material-ui/core/styles';
import { usePushingGutterStyles } from '@mui-treasury/styles/gutter/pushing';
@@ -8,10 +8,10 @@ import ModeCommentOutlinedIcon from '@material-ui/icons/ModeCommentOutlined';
import ThumbUpOutlinedIcon from '@material-ui/icons/ThumbUpOutlined';
import ThumbDownOutlinedIcon from '@material-ui/icons/ThumbDownOutlined';
-import { Link } from "wouter";
+import { Link } from 'wouter';
import RichTextField from '../generic/RichTextField';
-import CircularProgressWithIcon from "../../components/generic/CircularProgressWithIcon";
+import CircularProgressWithIcon from '../../components/generic/CircularProgressWithIcon';
import CancelOutlinedIcon from '@material-ui/icons/CancelOutlined';
import EditOutlinedIcon from '@material-ui/icons/EditOutlined';
@@ -23,29 +23,30 @@ import VisibilityOutlinedIcon from '@material-ui/icons/VisibilityOutlined';
import AccountTreeOutlinedIcon from '@material-ui/icons/AccountTreeOutlined';
import LanguageOutlinedIcon from '@material-ui/icons/LanguageOutlined';
-import EventConstants from "../../events";
-
-import { format } from "timeago.js";
-
-import {Backdrop,
- Button,
- Card,
- CardActions,
- CardContent,
- CardHeader,
- CircularProgress,
- Collapse,
- Dialog,
- DialogActions,
- DialogContent,
- DialogContentText,
- DialogTitle,
- IconButton,
- Menu,
- MenuItem,
- ListItemIcon,
- Typography,
- } from '@material-ui/core';
+import EventConstants from '../../events';
+
+import { format } from 'timeago.js';
+
+import {
+ Backdrop,
+ Button,
+ Card,
+ CardActions,
+ CardContent,
+ CardHeader,
+ CircularProgress,
+ Collapse,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogContentText,
+ DialogTitle,
+ IconButton,
+ Menu,
+ MenuItem,
+ ListItemIcon,
+ Typography,
+} from '@material-ui/core';
import Skeleton from '@material-ui/lab/Skeleton';
@@ -58,673 +59,775 @@ import CommentList from '../comments/CommentList';
import CardMediaSelector from '../generic/CardMediaSelector';
import CardMediaContainer from '../generic/CardMediaContainer';
-import point from "../../services/PointSDK";
+import point from '../../services/PointSDK';
import UserManager from '../../services/UserManager';
import PostManager from '../../services/PostManager';
const EMPTY = '0x0000000000000000000000000000000000000000000000000000000000000000';
const useStyles = makeStyles((theme) => ({
- backdrop: {
- position: "absolute",
- zIndex: theme.zIndex.drawer - 1,
- opacity: 0.9
- },
- card: {
- },
- avatar: {
- cursor:'pointer'
- },
- media: {
- height: 0,
- minHeight: '250px',
- maxHeight: '300px',
- paddingTop: '56.25%', // 16:9
- objectFit: 'contain',
- backgroundSize: 'contain',
- },
- editor: {
- height: '250px',
- minHeight: '250px',
- maxHeight: '300px',
- objectFit: 'contain',
- backgroundSize: 'contain',
- backgroundColor: '#ccc',
- },
- image: {
- objectFit: 'contain',
- backgroundSize: 'contain',
- minHeight: '250px',
- maxHeight: '300px',
- },
- expand: {
- transform: 'rotate(0deg)',
- marginLeft: 'auto',
- transition: theme.transitions.create('transform', {
- duration: theme.transitions.duration.shortest,
- }),
- },
- expandOpen: {
- transform: 'rotate(180deg)',
- },
- wrapper: {
- margin: theme.spacing(1),
- position: 'relative',
- },
- warning: {
- padding: theme.spacing(2, 2),
- display: "flex",
- flexDirection: "column",
- alignItems:"center",
- justifyContent: "center",
- backgroundColor: "#eee"
- },
+ backdrop: {
+ position: 'absolute',
+ zIndex: theme.zIndex.drawer - 1,
+ opacity: 0.9,
+ },
+ card: {},
+ avatar: {
+ cursor: 'pointer',
+ },
+ media: {
+ height: 0,
+ minHeight: '250px',
+ maxHeight: '300px',
+ paddingTop: '56.25%', // 16:9
+ objectFit: 'contain',
+ backgroundSize: 'contain',
+ },
+ editor: {
+ height: '250px',
+ minHeight: '250px',
+ maxHeight: '300px',
+ objectFit: 'contain',
+ backgroundSize: 'contain',
+ backgroundColor: '#ccc',
+ },
+ image: {
+ objectFit: 'contain',
+ backgroundSize: 'contain',
+ minHeight: '250px',
+ maxHeight: '300px',
+ },
+ expand: {
+ transform: 'rotate(0deg)',
+ marginLeft: 'auto',
+ transition: theme.transitions.create('transform', {
+ duration: theme.transitions.duration.shortest,
+ }),
+ },
+ expandOpen: {
+ transform: 'rotate(180deg)',
+ },
+ wrapper: {
+ margin: theme.spacing(1),
+ position: 'relative',
+ },
+ warning: {
+ padding: theme.spacing(2, 2),
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: '#eee',
+ },
}));
function DataURIToBlob(dataURI) {
- const splitDataURI = dataURI.split(',')
- const byteString = splitDataURI[0].indexOf('base64') >= 0 ? atob(splitDataURI[1]) : decodeURI(splitDataURI[1])
- const mimeString = splitDataURI[0].split(':')[1].split(';')[0]
+ const splitDataURI = dataURI.split(',');
+ const byteString =
+ splitDataURI[0].indexOf('base64') >= 0 ? atob(splitDataURI[1]) : decodeURI(splitDataURI[1]);
+ const mimeString = splitDataURI[0].split(':')[1].split(';')[0];
- const ia = new Uint8Array(byteString.length)
- for (let i = 0; i < byteString.length; i++)
- ia[i] = byteString.charCodeAt(i)
+ const ia = new Uint8Array(byteString.length);
+ for (let i = 0; i < byteString.length; i++) ia[i] = byteString.charCodeAt(i);
- return new Blob([ia], { type: mimeString })
+ return new Blob([ia], { type: mimeString });
}
-const PostCard = ({post, setAlert, canExpand=true, startExpanded=false, singlePost=false}) => {
-
- const [loading, setLoading] = useState(true);
- const [countersLoading, setCountersLoading] = useState(true);
-
- const [expanded, setExpanded] = useState(startExpanded);
- const [flagged, setFlagged] = useState(post.isFlagged);
-
- const [actionsOpen, setActionsOpen] = useState(false);
- const [shareOpen, setShareOpen] = useState(false);
- const [edit, setEdit] = useState(false);
- const [prompt, showPrompt] = useState(false);
- const [promptFlag, showPromptFlag] = useState(false);
-
-
- const styles = useStyles();
- const inputRef = useRef();
- const mediaRef = createRef();
-
- const gutterStyles = usePushingGutterStyles({ space: 1, firstExcluded: false });
- const iconLabelStyles = useLabelIconStyles({ linked: true });
-
- const { walletAddress, deployer, profile, identity, goHome, events} = useAppContext();
-
- const [name, setName] = useState();
-
- const [content, setContent] = useState();
- const [media, setMedia] = useState();
- const [date, setDate] = useState();
-
- const [comments, setComments] = useState();
-
- const [like, setLike] = useState(false);
- const [likes, setLikes] = useState(0);
- const [likeLoading, setLikeLoading] = useState(false);
-
- const [dislike, setDislike] = useState(false);
- const [dislikes, setDislikes] = useState(0);
- const [dislikeLoading, setDislikeLoading] = useState(false);
-
- const [isOwner, setIsOwner] = useState(false);
-
- const actionsAnchor = useRef();
- const shareAnchor = useRef();
- const expandButton = useRef();
-
- useEffect(() => {
- loadPost();
- }, []);
-
- useEffect(() => {
- if (events.listeners["PointSocial"] &&
- events.listeners["PointSocial"]["StateChange"]) {
- events.listeners["PointSocial"]["StateChange"]
- .on("StateChange", handleEvents, { type: "post", id: post.id });
- }
- return () => {
- if (events.listeners["PointSocial"] &&
- events.listeners["PointSocial"]["StateChange"]) {
- events.listeners["PointSocial"]["StateChange"]
- .removeListener("StateChange", handleEvents, { type: "post", id: post.id });
- }
- }
- }, []);
-
- const loadPost = async () => {
-
- if (post.from.toLowerCase() === walletAddress.toLowerCase()) {
- setIsOwner(true);
- setName((profile && profile.displayName) || identity);
- }
- else {
- try {
- const profile = await UserManager.getProfile(post.from);
- const { identity } = await point.ownerToIdentity(post.from);
- const name = (profile[0] === EMPTY)? identity : await point.getString(profile[0], { encoding: 'utf-8' });
- setName(name);
- }
- catch(error) {
- setAlert(error.message);
- }
- }
+const PostCard = ({
+ post,
+ setAlert,
+ canExpand = true,
+ startExpanded = false,
+ singlePost = false,
+}) => {
+ const [loading, setLoading] = useState(true);
+ const [countersLoading, setCountersLoading] = useState(true);
- try {
- const contents = (post.contents === EMPTY)? EMPTY : await point.getString(post.contents, { encoding: 'utf-8' })
- setContent(contents);
+ const [expanded, setExpanded] = useState(startExpanded);
+ const [flagged, setFlagged] = useState(post.flagged);
- if (post.image !== EMPTY) {
- setMedia(`/_storage/${post.image}`);
- }
+ const [actionsOpen, setActionsOpen] = useState(false);
+ const [shareOpen, setShareOpen] = useState(false);
+ const [edit, setEdit] = useState(false);
+ const [prompt, showPrompt] = useState(false);
+ const [promptFlag, showPromptFlag] = useState(false);
- setLike(post.liked);
- setDislike(post.disliked);
- setCountersLoading(false);
- }
- catch(error) {
- setAlert(error.message);
- }
-
- setDate(post.createdAt);
- setLikes(post.likesCount);
- setDislikes(post.dislikesCount);
- setComments(post.commentsCount);
-
- setCountersLoading(false);
- setLoading(false);
- };
+ const styles = useStyles();
+ const inputRef = useRef();
+ const mediaRef = createRef();
+ const gutterStyles = usePushingGutterStyles({ space: 1, firstExcluded: false });
+ const iconLabelStyles = useLabelIconStyles({ linked: true });
- const handleEvents = async(event) => {
- if (event &&
- (event.component === EventConstants.Component.Post) &&
- (event.id === post.id)) {
- switch(event.action) {
- case EventConstants.Action.Like: {
- setLikeLoading(true);
- const data = await PostManager.getPost(post.id);
- console.log(data);
- const [,,,,,_likes,, _dislikes, _liked, _disliked] = data;
- console.log('like', _likes, _dislikes, _liked, _disliked);
- setLikes(parseInt(_likes, 10));
- setDislikes(parseInt(_dislikes, 10));
- setLike(_liked);
- setDislike(_disliked);
- setLikeLoading(false);
- }
- break;
- case EventConstants.Action.Dislike: {
- setLikeLoading(true);
- const data = await PostManager.getPost(post.id);
- console.log(data);
- const [,,,,,_likes,, _dislikes, _liked, _disliked] = data;
- console.log('like', _likes, _dislikes, _liked, _disliked);
- setLikes(parseInt(_likes, 10));
- setDislikes(parseInt(_dislikes, 10));
- setLike(_liked);
- setDislike(_disliked);
- setLikeLoading(false);
- }
- break;
- case EventConstants.Action.Edit:
- if (event.from.toLowerCase() !== walletAddress.toLowerCase()) {
- await loadPost();
- }
- break;
- case EventConstants.Action.Comment: {
- const p = await PostManager.getPost(post.id);
- if (p && (parseInt(p[4]) !== 0)) {
- setComments(parseInt(p[6]));
- }
- }
- break;
- case EventConstants.Action.Flag: {
- const isFlagged = await PostManager.isFlaggedPost(post.id);
- post.isFlagged = isFlagged;
- setFlagged(isFlagged);
- }
- break;
- default:
- break;
- }
- }
- }
+ const { walletAddress, deployer, profile, identity, goHome, events } = useAppContext();
- const handleAction = (action) => {
- switch(action) {
- case 'edit':
- startEdit();
- break;
- case 'save':
- saveEdit();
- break;
- default:
- case 'cancel':
- cancelEdit();
- break;
- case 'delete':
- showPrompt(true);
- break;
- case 'flag':
- showPromptFlag(true);
- break;
- }
- setActionsOpen(false);
- };
+ const [name, setName] = useState();
- const share = async (type) => {
- try {
- setShareOpen(false);
- const url = `https://social.point${((type === 'web2')? '.link' : '')}/post/${post.id}`;
+ const [content, setContent] = useState();
+ const [media, setMedia] = useState();
+ const [date, setDate] = useState();
- if (window.navigator.share) {
- await window.navigator.share({ url });
- }
- else if (window.navigator.clipboard) {
- await window.navigator.clipboard.writeText(url);
- setAlert("Copied to clipboard!|success");
- }
- }
- catch(error) {
- setAlert(error.message);
- }
- }
+ const [comments, setComments] = useState();
- const deletePost = async () => {
- try {
- setLoading(true);
- await PostManager.deletePost(post.id);
- if (singlePost) {
- await goHome();
- }
- }
- catch(error) {
- setAlert(error.message);
- setLoading(false);
- }
- };
+ const [like, setLike] = useState(false);
+ const [likes, setLikes] = useState(0);
+ const [likeLoading, setLikeLoading] = useState(false);
- const cancelEdit = async () => {
- setEdit(false);
- };
-
- const startEdit = async () => {
- setEdit(true);
- };
-
- const saveEdit = async () => {
-
- try {
- const newContent = (inputRef.current.value.trim())? inputRef.current.value.trim() : "";
- const newMedia = mediaRef.current.media();
+ const [dislike, setDislike] = useState(false);
+ const [dislikes, setDislikes] = useState(0);
+ const [dislikeLoading, setDislikeLoading] = useState(false);
- setEdit(false);
- setLoading(true);
+ const [isOwner, setIsOwner] = useState(false);
- let contentId;
- if (!newContent) {
- contentId = EMPTY;
- }
- if (newContent === content) {
- contentId = post.contents;
- }
- else {
- const storageId = (newContent === EMPTY)? newContent : await point.putString(newContent, { encoding: 'utf-8' });
- contentId = storageId;
- }
-
- let imageId;
- if (!newMedia) {
- imageId = EMPTY;
- }
- else if (newMedia === media) {
- imageId = post.image;
- }
- else {
- const formData = new FormData()
- formData.append("postfile", DataURIToBlob(newMedia));
- const storageId = await point.postFile(formData);
- imageId = storageId;
- }
+ const actionsAnchor = useRef();
+ const shareAnchor = useRef();
+ const expandButton = useRef();
- if (contentId === imageId && contentId === EMPTY) {
- throw new Error("Sorry, but you can't create an empty post");
- }
-
- await PostManager.editPost(post.id, contentId, imageId);
-
- setContent(newContent);
- setMedia(newMedia);
- }
- catch(error) {
- console.log(error);
- setAlert(error.message);
- }
- finally {
- setLoading(false);
- }
- };
+ useEffect(() => {
+ loadPost();
+ }, []);
- const flagPost = async () => {
- try {
- showPromptFlag(false);
- setLoading(true);
- await PostManager.flagPost(post.id);
- }
- catch(error) {
- console.error(error.message);
- setAlert(error.message);
- }
- finally {
- setLoading(false);
- }
+ useEffect(() => {
+ if (events.listeners['PointSocial'] && events.listeners['PointSocial']['StateChange']) {
+ events.listeners['PointSocial']['StateChange'].on('StateChange', handleEvents, {
+ type: 'post',
+ id: post.id,
+ });
}
-
- const toggleLike = async () => {
- try {
- setLikeLoading(true);
- await PostManager.addLikeToPost(post.id);
- }
- catch(error) {
- setAlert(error.message);
- }
- setLikeLoading(false);
+ return () => {
+ if (events.listeners['PointSocial'] && events.listeners['PointSocial']['StateChange']) {
+ events.listeners['PointSocial']['StateChange'].removeListener('StateChange', handleEvents, {
+ type: 'post',
+ id: post.id,
+ });
+ }
+ };
+ }, []);
+
+ const loadPost = async () => {
+ if (post.from.toLowerCase() === walletAddress.toLowerCase()) {
+ setIsOwner(true);
+ setName((profile && profile.displayName) || identity);
+ } else {
+ try {
+ const profile = await UserManager.getProfile(post.from);
+ const { identity } = await point.ownerToIdentity(post.from);
+ const name =
+ profile[0] === EMPTY
+ ? identity
+ : await point.getString(profile[0], { encoding: 'utf-8' });
+ setName(name);
+ } catch (error) {
+ setAlert(error.message);
+ }
}
- const toggleDislike = async () => {
- try {
- setDislikeLoading(true);
- await PostManager.addDislikeToPost(post.id);
- } catch (error) {
- setAlert(error.message);
- }
- setDislikeLoading(false);
+ try {
+ const contents =
+ post.contents === EMPTY
+ ? EMPTY
+ : await point.getString(post.contents, { encoding: 'utf-8' });
+ setContent(contents);
+
+ if (post.image !== EMPTY) {
+ setMedia(`/_storage/${post.image}`);
+ }
+
+ setLike(post.liked);
+ setDislike(post.disliked);
+ setCountersLoading(false);
+ } catch (error) {
+ setAlert(error.message);
}
- const handleActionsOpen = () => {
- setActionsOpen(true);
- };
-
- const handleActionsClose = () => {
- setActionsOpen(false);
- };
-
- const handleShareOpen = () => {
- setShareOpen(true);
- };
-
- const handleShareClose = () => {
- setShareOpen(false);
- };
+ setDate(post.createdAt);
+ setLikes(post.likesCount);
+ setDislikes(post.dislikesCount);
+ setComments(post.commentsCount);
- const handleExpandClick = () => {
- setExpanded(!expanded);
- };
+ setCountersLoading(false);
+ setLoading(false);
+ };
- const dialog = <>
-
+ const handleEvents = async (event) => {
+ if (event && event.component === EventConstants.Component.Post && event.id === post.id) {
+ switch (event.action) {
+ case EventConstants.Action.Like:
+ {
+ setLikeLoading(true);
+ const data = await PostManager.getPost(post.id);
+ console.log(data);
+ const [, , , , , _likes, , _dislikes, _liked, _disliked] = data;
+ console.log('like', _likes, _dislikes, _liked, _disliked);
+ setLikes(parseInt(_likes, 10));
+ setDislikes(parseInt(_dislikes, 10));
+ setLike(_liked);
+ setDislike(_disliked);
+ setLikeLoading(false);
+ }
+ break;
+ case EventConstants.Action.Dislike:
+ {
+ setLikeLoading(true);
+ const data = await PostManager.getPost(post.id);
+ console.log(data);
+ const [, , , , , _likes, , _dislikes, _liked, _disliked] = data;
+ console.log('like', _likes, _dislikes, _liked, _disliked);
+ setLikes(parseInt(_likes, 10));
+ setDislikes(parseInt(_dislikes, 10));
+ setLike(_liked);
+ setDislike(_disliked);
+ setLikeLoading(false);
+ }
+ break;
+ case EventConstants.Action.Edit:
+ if (event.from.toLowerCase() !== walletAddress.toLowerCase()) {
+ await loadPost();
+ }
+ break;
+ case EventConstants.Action.Comment:
+ {
+ const p = await PostManager.getPost(post.id);
+ if (p && parseInt(p[4]) !== 0) {
+ setComments(parseInt(p[6]));
+ }
+ }
+ break;
+ case EventConstants.Action.Flag:
+ {
+ const flagged = await PostManager.flaggedPost(post.id);
+ post.flagged = flagged;
+ setFlagged(flagged);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ const handleAction = (action) => {
+ switch (action) {
+ case 'edit':
+ startEdit();
+ break;
+ case 'save':
+ saveEdit();
+ break;
+ default:
+ case 'cancel':
+ cancelEdit();
+ break;
+ case 'delete':
+ showPrompt(true);
+ break;
+ case 'flag':
+ showPromptFlag(true);
+ break;
+ }
+ setActionsOpen(false);
+ };
+
+ const share = async (type) => {
+ try {
+ setShareOpen(false);
+ const url = `https://social.point${type === 'web2' ? '.link' : ''}/post/${post.id}`;
+
+ if (window.navigator.share) {
+ await window.navigator.share({ url });
+ } else if (window.navigator.clipboard) {
+ await window.navigator.clipboard.writeText(url);
+ setAlert('Copied to clipboard!|success');
+ }
+ } catch (error) {
+ setAlert(error.message);
+ }
+ };
+
+ const deletePost = async () => {
+ try {
+ setLoading(true);
+ await PostManager.deletePost(post.id);
+ if (singlePost) {
+ await goHome();
+ }
+ } catch (error) {
+ setAlert(error.message);
+ setLoading(false);
+ }
+ };
+
+ const cancelEdit = async () => {
+ setEdit(false);
+ };
+
+ const startEdit = async () => {
+ setEdit(true);
+ };
+
+ const saveEdit = async () => {
+ try {
+ const newContent = inputRef.current.value.trim() ? inputRef.current.value.trim() : '';
+ const newMedia = mediaRef.current.media();
+
+ setEdit(false);
+ setLoading(true);
+
+ let contentId;
+ if (!newContent) {
+ contentId = EMPTY;
+ }
+ if (newContent === content) {
+ contentId = post.contents;
+ } else {
+ const storageId =
+ newContent === EMPTY
+ ? newContent
+ : await point.putString(newContent, { encoding: 'utf-8' });
+ contentId = storageId;
+ }
+
+ let imageId;
+ if (!newMedia) {
+ imageId = EMPTY;
+ } else if (newMedia === media) {
+ imageId = post.image;
+ } else {
+ const formData = new FormData();
+ formData.append('postfile', DataURIToBlob(newMedia));
+ const storageId = await point.postFile(formData);
+ imageId = storageId;
+ }
+
+ if (contentId === imageId && contentId === EMPTY) {
+ throw new Error("Sorry, but you can't create an empty post");
+ }
+
+ await PostManager.editPost(post.id, contentId, imageId);
+
+ setContent(newContent);
+ setMedia(newMedia);
+ } catch (error) {
+ console.log(error);
+ setAlert(error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const flagPost = async () => {
+ try {
+ showPromptFlag(false);
+ setLoading(true);
+ await PostManager.flagPost(post.id);
+ } catch (error) {
+ console.error(error.message);
+ setAlert(error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const toggleLike = async () => {
+ try {
+ setLikeLoading(true);
+ await PostManager.addLikeToPost(post.id);
+ } catch (error) {
+ setAlert(error.message);
+ }
+ setLikeLoading(false);
+ };
+
+ const toggleDislike = async () => {
+ try {
+ setDislikeLoading(true);
+ await PostManager.addDislikeToPost(post.id);
+ } catch (error) {
+ setAlert(error.message);
+ }
+ setDislikeLoading(false);
+ };
+
+ const handleActionsOpen = () => {
+ setActionsOpen(true);
+ };
+
+ const handleActionsClose = () => {
+ setActionsOpen(false);
+ };
+
+ const handleShareOpen = () => {
+ setShareOpen(true);
+ };
+
+ const handleShareClose = () => {
+ setShareOpen(false);
+ };
+
+ const handleExpandClick = () => {
+ setExpanded(!expanded);
+ };
+
+ const dialog = (
+ <>
+
>
-
- const flagPrompt = <>
-
- >
-
- const postActions = <>
-
-
>
-
- const shareActions = <>
-
+ );
+
+ const shareActions = (
+ <>
+
>
-
- return (
- <>
-
-
- {loading && }
- {flagged &&
-
- {setFlagged(false)}}>
-
-
- Warning: Potentially Sensitive Content
-
- }
-
-
- }
- action={(isOwner || deployer) && postActions}
- title={
-
-
- {
- loading
- ?
-
- :
- (post.from === walletAddress) ? ((profile && profile.displayName) || identity) : name
- }
-
-
+ );
+
+ return (
+ <>
+
+
+ {loading && }
+ {flagged && (
+
+ {
+ setFlagged(false);
+ }}
+ >
+
+
+
+ {' '}
+ Warning: Potentially Sensitive Content
+
+
+ )}
+
+
+ }
+ action={(isOwner || deployer) && postActions}
+ title={
+
+
+ {loading ? (
+
+ ) : post.from === walletAddress ? (
+ (profile && profile.displayName) || identity
+ ) : (
+ name
+ )}
+
+
+ }
+ subheader={
+
+ {' '}
+ {loading ? : format(date)}
+
+ }
+ />
+
+ {edit ? (
+ loading ? (
+
+ ) : (
+
+ )
+ ) : (
+
+ {loading ? : content !== EMPTY && content}
+
+ )}
+
+ {loading ? (
+
+ ) : (
+ <>
+ {edit ? (
+
+ ) : (
+ media &&
+ )}
+ >
+ )}
+
+ {countersLoading || loading ? (
+
+ ) : (
+ <>
+
+ {likeLoading ? (
+
+ ) : (
+
+ {like ? (
+
+ ) : (
+
+ )}
+ {likes}
+
+ )}
+
+ {dislikeLoading ? (
+
+
}
-
-
-
-
-
- {dialog}
- {flagPrompt}
-
- >
- )
-}
+ props={{
+ size: 16,
+ style: { color: '#f00', marginLeft: '8px', marginBottom: '8px' },
+ }}
+ />
+
+ ) : (
+
+ {dislike ? (
+
+ ) : (
+
+ )}
+ {dislikes}
+
+ )}
+
+
+ canExpand
+ ? expandButton && expandButton.current && expandButton.current.click()
+ : window.open(`/post/${post.id}`, '_blank')
+ }
+ >
+
+ {comments}
+
+
+
+
+
+ {shareActions}
+ >
+ )}
+ {canExpand && (
+
+
+
+ )}
+
+
+
+
+
+ {dialog}
+ {flagPrompt}
+
+ >
+ );
+};
-export default PostCard
\ No newline at end of file
+export default PostCard;
diff --git a/src/mappers/Post.ts b/src/mappers/Post.ts
new file mode 100644
index 0000000..302266f
--- /dev/null
+++ b/src/mappers/Post.ts
@@ -0,0 +1,31 @@
+export default function getPostData(post: any[]): Post {
+ const [
+ id,
+ from,
+ contents,
+ image,
+ createdAt,
+ likesCount,
+ commentsCount,
+ dislikesCount,
+ liked,
+ disliked,
+ weight,
+ flagged,
+ ] = post;
+
+ return {
+ id: parseInt(id, 10),
+ from,
+ contents,
+ image,
+ createdAt: createdAt * 1000,
+ likesCount: parseInt(likesCount, 10),
+ dislikesCount: parseInt(dislikesCount, 10),
+ commentsCount: parseInt(commentsCount, 10),
+ liked: !!liked,
+ disliked: !!disliked,
+ weight: parseInt(weight, 10),
+ flagged: !!flagged,
+ };
+}
diff --git a/src/pages/post/Post.jsx b/src/pages/post/Post.jsx
index 31cdaad..6ea9155 100644
--- a/src/pages/post/Post.jsx
+++ b/src/pages/post/Post.jsx
@@ -1,6 +1,6 @@
-import Appbar from "../../components/topbar/Appbar";
-import { useRoute } from "wouter";
-import { useEffect, useState } from "react";
+import Appbar from '../../components/topbar/Appbar';
+import { useRoute } from 'wouter';
+import { useEffect, useState } from 'react';
import { useAppContext } from '../../context/AppContext';
import { makeStyles } from '@material-ui/core/styles';
@@ -11,7 +11,9 @@ import Backdrop from '@material-ui/core/Backdrop';
import Snackbar from '@material-ui/core/Snackbar';
import MuiAlert from '@material-ui/lab/Alert';
-import CircularProgressWithIcon from "../../components/generic/CircularProgressWithIcon";
+import getPostData from '../../mappers/Post';
+
+import CircularProgressWithIcon from '../../components/generic/CircularProgressWithIcon';
import Box from '@material-ui/core/Box';
import Container from '@material-ui/core/Container';
@@ -24,141 +26,134 @@ import Typography from '@material-ui/core/Typography';
import PostCard from '../../components/post/PostCard';
function Alert(props) {
- return ;
+ return ;
}
const useStyles = makeStyles((theme) => ({
- backdrop: {
- zIndex: theme.zIndex.drawer + 1,
- },
- container: {
- padding: theme.spacing(2, 2),
- display: "flex",
- minHeight: "90vh",
- flexDirection: "column",
- justifyContent: "center"
- }
+ backdrop: {
+ zIndex: theme.zIndex.drawer + 1,
+ },
+ container: {
+ padding: theme.spacing(2, 2),
+ display: 'flex',
+ minHeight: '90vh',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ },
}));
const Post = () => {
- const [match, params] = useRoute("/post/:id");
- const [loading, setLoading] = useState(true);
- const [alert, setAlert] = useState("");
- const [post, setPost] = useState();
-
- const { walletAddress, events } = useAppContext();
-
- const styles = useStyles();
-
- const handleAlert = (event, reason) => {
- if (reason === 'clickaway') {
- return;
- }
- setAlert("");
- };
-
- const getPost = async () => {
- console.log("hola");
- try {
- const isPost = params.id.match(/^\d+$/);
- let isFlagged = false;
-
- if (isPost) {
- const post = await PostManager.getPost(params.id);
-
- try {
- isFlagged = await PostManager.isFlaggedPost(params.id);
- }
- catch(error) {
- console.warn(error);
- }
-
- if (post && (parseInt(post[4]) !== 0)) {
- setPost({
- id: post[0],
- from: post[1],
- contents: post[2],
- image: post[3],
- createdAt: parseInt(post[4])*1000,
- likesCount: parseInt(post[5]),
- commentsCount: parseInt(post[6]),
- isFlagged
- })
- }
- }
- }
- catch(error) {
- setAlert(error.message);
- }
- finally {
- setLoading(false);
- }
- };
-
- useEffect(() => {
- getPost();
- }, []);
-
- useEffect(() => {
- getEvents();
- return () => {
- events.listeners["PointSocial"]["StateChange"].removeListener("StateChange", handleEvents);
- events.unsubscribe("PointSocial", "StateChange");
- };
- }, []);
-
- const getEvents = async() => {
- try {
- const ev = await events.subscribe("PointSocial", "StateChange");
- ev.on("StateChange", handleEvents);
- }
- catch(error) {
- console.log(error.message);
- }
- }
-
- const handleEvents = async(event) => {
- if (event) {
- //console.log(event.returnValues);
+ const [match, params] = useRoute('/post/:id');
+ const [loading, setLoading] = useState(true);
+ const [alert, setAlert] = useState('');
+ const [post, setPost] = useState();
+
+ const { walletAddress, events } = useAppContext();
+
+ const styles = useStyles();
+
+ const handleAlert = (event, reason) => {
+ if (reason === 'clickaway') {
+ return;
+ }
+ setAlert('');
+ };
+
+ const getPost = async () => {
+ try {
+ const isPost = params.id.match(/^\d+$/);
+
+ if (isPost) {
+ const post = getPostData(await PostManager.getPost(params.id));
+
+ if (post && post.createdAt !== 0) {
+ setPost(post);
}
}
-
-
- const main = (post)?
+ } catch (error) {
+ setAlert(error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ getPost();
+ }, []);
+
+ useEffect(() => {
+ getEvents();
+ return () => {
+ events.listeners['PointSocial']['StateChange'].removeListener('StateChange', handleEvents);
+ events.unsubscribe('PointSocial', 'StateChange');
+ };
+ }, []);
+
+ const getEvents = async () => {
+ try {
+ const ev = await events.subscribe('PointSocial', 'StateChange');
+ ev.on('StateChange', handleEvents);
+ } catch (error) {
+ console.log(error.message);
+ }
+ };
+
+ const handleEvents = async (event) => {
+ if (event) {
+ //console.log(event.returnValues);
+ }
+ };
+
+ const main = post ? (
-
+
- :
+ ) : (
<>
- { !loading &&
-
-
-
- Post not found
-
-
- }
+ {!loading && (
+
+
+
+ Post not found
+
+
+ )}
>
-
- return (
-
- { walletAddress &&
}
-
- {
- walletAddress?
- :
- } props={{color : 'inherit'}} />
- }
-
- {
- walletAddress && main
- }
-
- { alert.split("|")[0] }
-
-
- );
-}
-
-
-export default Post
\ No newline at end of file
+ );
+
+ return (
+
+ {walletAddress &&
}
+
+ {walletAddress ? (
+
+ ) : (
+ }
+ props={{ color: 'inherit' }}
+ />
+ )}
+
+ {walletAddress && main}
+
+
+ {alert.split('|')[0]}
+
+
+
+ );
+};
+
+export default Post;
diff --git a/src/types.d.ts b/src/types.d.ts
new file mode 100644
index 0000000..4b49e92
--- /dev/null
+++ b/src/types.d.ts
@@ -0,0 +1,16 @@
+type Address = string;
+
+type Post = {
+ id: number;
+ from: Address;
+ contents: string;
+ image: string;
+ createdAt: number;
+ likesCount: number;
+ commentsCount: number;
+ dislikesCount: number;
+ weight: number;
+ liked: boolean;
+ disliked: boolean;
+ flagged: boolean;
+};
From 738168e282716625eddb35ed8f3f378e416e32e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Mon, 11 Jul 2022 18:40:12 +0200
Subject: [PATCH 11/20] fixes
---
contracts/PointSocial.sol | 2 +-
src/components/feed/Feed.jsx | 52 +++++++++++++++++++++++++++---------
2 files changed, 40 insertions(+), 14 deletions(-)
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index eab3ea2..e8eb0cd 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -342,7 +342,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
// posts not viewed already
return
_post.createdAt != 0 &&
- _post.weight >= int256(weightThreshold) &&
+ (weightThreshold == 0 || _post.weight >= int256(weightThreshold)) &&
!_inArray(_post.id, _postIdsToFilter) &&
// get newest posts if timestamp is set
(_newerThanTimestamp == 0 ||
diff --git a/src/components/feed/Feed.jsx b/src/components/feed/Feed.jsx
index fc5ab44..cb9383b 100644
--- a/src/components/feed/Feed.jsx
+++ b/src/components/feed/Feed.jsx
@@ -70,6 +70,7 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
const styles = useStyles();
const [posts, setPosts] = useState([]);
const [viewedPostIds, setViewedPostIds] = useState([]);
+ const [newestPost, setNewestPost] = useState(null);
const [length, setLength] = useState(0);
const [loading, setLoading] = useState(false);
const [reload, setReload] = useState(false);
@@ -106,8 +107,8 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
switch (event.action) {
case EventConstants.Action.Create:
if (event.from.toString().toLowerCase() === walletAddress.toLowerCase()) {
- // Autoload own posts
- await reloadPosts(true);
+ // Add new owned post to top of the feed
+ await addPostToFeed(event.id);
} else {
setReload(true);
}
@@ -142,13 +143,24 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
setLoading(false);
};
- const fetchPosts = async () => {
+ const addPostToFeed = async (id) => {
+ const newPost = getPostData(await PostManager.getPost(id));
+ setPosts((posts) => {
+ return [newPost, ...posts];
+ });
+ };
+
+ const fetchPosts = async (onlyNew = false) => {
try {
setLoading(true);
const data = await (account
? PostManager.getPaginatedPostsByOwner(account, NUM_POSTS_PER_CALL, viewedPostIds)
- : PostManager.getPaginatedPosts(NUM_POSTS_PER_CALL, viewedPostIds));
+ : PostManager.getPaginatedPosts(
+ NUM_POSTS_PER_CALL,
+ viewedPostIds,
+ onlyNew && newestPost ? newestPost.createdAt / 1000 : 0 // get new posts only
+ ));
const newPosts = data.map(getPostData);
@@ -163,18 +175,17 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
}
};
- const getPosts = async (refresh = false) => {
+ const getPosts = async (newPosts = false) => {
try {
setLoading(true);
- const posts = await fetchPosts();
+ const posts = await fetchPosts(newPosts);
// save posts
setPosts((prev) => {
- if (refresh) {
- return posts;
+ if (newPosts) {
+ return unionWith(posts, prev, isEqual);
}
- const result = unionWith(prev, posts, isEqual);
- return result;
+ return unionWith(prev, posts, isEqual);
});
// save viewed posts
@@ -186,6 +197,16 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
});
return prev;
});
+
+ setNewestPost((current) => {
+ let newest = current;
+ posts.forEach((post) => {
+ if (!newest || post.id > newest.id) {
+ newest = post;
+ }
+ });
+ return newest;
+ });
} catch (error) {
console.log(error);
setAlert(error.message);
@@ -194,8 +215,13 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
}
};
- const reloadPosts = async (refresh = false) => {
- await Promise.all([getPostsLength(), getPosts(refresh)]);
+ const getNewPosts = async () => {
+ await Promise.all([getPostsLength(), getPosts(true)]);
+ setReload(false);
+ };
+
+ const reloadPosts = async () => {
+ await Promise.all([getPostsLength(), getPosts()]);
setReload(false);
};
@@ -224,7 +250,7 @@ const Feed = ({ account, setAlert, setUpperLoading, canPost = false }) => {
color="secondary"
size="small"
onClick={() => {
- reloadPosts();
+ getNewPosts();
}}
>
Reload
From 22213a70fa9d7a8c78a1bd37aa06e028e7d5d5e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Tue, 12 Jul 2022 17:49:31 +0200
Subject: [PATCH 12/20] restore contract layout
---
contracts/PointSocial.sol | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index e8eb0cd..71bcc14 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -92,6 +92,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
address private _migrator;
mapping(address => Profile) public profileByOwner;
+ mapping(uint256 => bool) public postIsFlagged;
enum Action {
Migrator,
@@ -140,8 +141,6 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 public weightThreshold;
uint256 public initialWeight;
- mapping(uint256 => bool) public postIsFlagged;
-
modifier postExists(uint256 _postId) {
require(postById[_postId].from != address(0), "Post does not exist");
_;
From b91ce03fc16e07d309610460a25ffa295896841e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Bevilacqua?=
Date: Tue, 12 Jul 2022 18:03:14 +0200
Subject: [PATCH 13/20] remove age punishment from filtering
---
contracts/PointSocial.sol | 35 ++++++++++++++++++++++++-----------
1 file changed, 24 insertions(+), 11 deletions(-)
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index 71bcc14..37a94aa 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -69,7 +69,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 timestamp,
uint256 likesWeightMultiplier,
uint256 dislikesWeightWultiplier,
- uint256 oldWeightMultiplier,
+ uint256 ageWeightMultiplier,
uint256 initialWeight
);
@@ -137,7 +137,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 public likesWeightMultiplier;
uint256 public dislikesWeightWultiplier;
- uint256 public oldWeightMultiplier;
+ uint256 public ageWeightMultiplier;
uint256 public weightThreshold;
uint256 public initialWeight;
@@ -160,13 +160,13 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
function setWeights(
uint256 _likesWeightMultiplier,
uint256 _dislikesWeightWultiplier,
- uint256 _oldWeightMultiplier,
+ uint256 _ageWeightMultiplier,
uint256 _weightThreshold,
uint256 _initialWeight
) external onlyDeployer {
likesWeightMultiplier = _likesWeightMultiplier;
dislikesWeightWultiplier = _dislikesWeightWultiplier;
- oldWeightMultiplier = _oldWeightMultiplier;
+ ageWeightMultiplier = _ageWeightMultiplier;
weightThreshold = _weightThreshold;
initialWeight = _initialWeight;
@@ -175,7 +175,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
block.timestamp,
likesWeightMultiplier,
dislikesWeightWultiplier,
- oldWeightMultiplier,
+ ageWeightMultiplier,
initialWeight
);
}
@@ -331,19 +331,32 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
return postsWithMetadata;
}
+ /**
+ * @notice Validate that a post must be shown or not
+ * @dev Validate that a post must be shown or not
+ * @param _post - Post to be validated with metadata
+ * @param _postIdsToFilter - Already seen post ids
+ * @param _newerThanTimestamp - newest post seen timestamp
+ */
function _validPostToBeShown(
PostWithMetadata memory _post,
uint256[] memory _postIdsToFilter,
uint256 _newerThanTimestamp
) public view returns (bool) {
- // not deleted posts
- // posts with enough weight
- // posts not viewed already
+ // Conditions:
+ // 1. CreatedAt must be different than 0
+ // 2. Weight must be equal or higher than weightThreshold (if set)
+ // 3. Post must not have been seen before (not include on post ids array)
+ // 4. Must be newer than timestamp (if set)
+ uint256 ageWeight = (block.timestamp - _post.createdAt) *
+ ageWeightMultiplier;
+
return
_post.createdAt != 0 &&
- (weightThreshold == 0 || _post.weight >= int256(weightThreshold)) &&
+ (weightThreshold == 0 ||
+ (_post.weight + int256(ageWeight)) >=
+ int256(weightThreshold)) &&
!_inArray(_post.id, _postIdsToFilter) &&
- // get newest posts if timestamp is set
(_newerThanTimestamp == 0 ||
_post.createdAt >= _newerThanTimestamp);
}
@@ -454,7 +467,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 likesWeight = post.likesCount * likesWeightMultiplier;
uint256 weightPunishment = (dislikesCount * dislikesWeightWultiplier) +
(block.timestamp - post.createdAt) *
- oldWeightMultiplier;
+ ageWeightMultiplier;
int256 weight = int256(initialWeight) +
int256(likesWeight) -
int256(weightPunishment);
From 224dcb83ff65c7d4d692d0e062ee97a942aeee50 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Antonio=20Jos=C3=A9=20L=C3=B3pez?=
Date: Tue, 12 Jul 2022 22:05:45 -0400
Subject: [PATCH 14/20] Implemented new smartcontract for user handling
---
contracts/PSUser.sol | 88 ++++++++++++++++++++++++++++++++++++++++++++
point.deploy.json | 3 +-
2 files changed, 90 insertions(+), 1 deletion(-)
create mode 100644 contracts/PSUser.sol
diff --git a/contracts/PSUser.sol b/contracts/PSUser.sol
new file mode 100644
index 0000000..a0af268
--- /dev/null
+++ b/contracts/PSUser.sol
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.8.0;
+pragma experimental ABIEncoderV2;
+
+import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
+import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
+import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
+import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
+import "point-contract-manager/contracts/IIdentity.sol";
+
+contract PSUser is Initializable, UUPSUpgradeable, OwnableUpgradeable {
+
+ using EnumerableSet for EnumerableSet.AddressSet;
+
+ address private _identityContractAddr;
+ string private _identityHandle;
+
+ struct Connections {
+ EnumerableSet.AddressSet following;
+ EnumerableSet.AddressSet followers;
+ EnumerableSet.AddressSet blocked;
+ }
+
+ mapping(address => Connections) private _connectionsByUser;
+
+ function initialize(
+ address identityContractAddr,
+ string calldata identityHandle
+ ) public initializer onlyProxy {
+ __Ownable_init();
+ __UUPSUpgradeable_init();
+ _identityContractAddr = identityContractAddr;
+ _identityHandle = identityHandle;
+ }
+
+ function _authorizeUpgrade(address) internal view override {
+ require(
+ IIdentity(_identityContractAddr).isIdentityDeployer(
+ _identityHandle,
+ msg.sender
+ ),
+ "You are not a deployer of this identity"
+ );
+ }
+
+ function followUser(address _user) public returns (bool) {
+ return
+ EnumerableSet.add(_connectionsByUser[msg.sender].following, _user)
+ &&
+ EnumerableSet.add(_connectionsByUser[_user].followers, msg.sender);
+ }
+
+ function unfollowUser(address _user) public returns (bool) {
+ return
+ EnumerableSet.remove(_connectionsByUser[msg.sender].following, _user)
+ &&
+ EnumerableSet.remove(_connectionsByUser[_user].followers, msg.sender);
+ }
+
+ function isFollowing(address _owner, address _user) public view returns (bool) {
+ return EnumerableSet.contains(_connectionsByUser[_owner].following, _user);
+ }
+
+ function getFollowing(address _user) public view returns (address[] memory) {
+ return EnumerableSet.values(_connectionsByUser[_user].following);
+ }
+
+ function getFollowers(address _user) public view returns (address[] memory) {
+ return EnumerableSet.values(_connectionsByUser[_user].followers);
+ }
+
+ function blockUser(address _user) public returns (bool) {
+ return
+ EnumerableSet.remove(_connectionsByUser[msg.sender].following, _user)
+ &&
+ EnumerableSet.remove(_connectionsByUser[_user].followers, msg.sender)
+ &&
+ EnumerableSet.add(_connectionsByUser[msg.sender].blocked, _user);
+ }
+
+ function unBlockUser(address _user) public returns (bool) {
+ return EnumerableSet.remove(_connectionsByUser[msg.sender].blocked, _user);
+ }
+
+ function isBlocked(address _owner, address _user) public view returns (bool) {
+ return EnumerableSet.contains(_connectionsByUser[_owner].blocked, _user);
+ }
+}
\ No newline at end of file
diff --git a/point.deploy.json b/point.deploy.json
index cb05487..538612e 100644
--- a/point.deploy.json
+++ b/point.deploy.json
@@ -3,7 +3,8 @@
"target": "social.point",
"keyvalue": {},
"contracts": [
- "PointSocial"
+ "PointSocial",
+ "PSUser"
],
"upgradable": true,
"rootDir": "public"
From b812c9598319ee632d25ec68933604218564e0e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Antonio=20Jos=C3=A9=20L=C3=B3pez?=
Date: Tue, 12 Jul 2022 22:07:47 -0400
Subject: [PATCH 15/20] WIP: Implementing follow users features
---
src/components/profile/ProfileCard.jsx | 121 +++++++++++++++++++++++--
src/services/UserManager.js | 6 ++
2 files changed, 120 insertions(+), 7 deletions(-)
diff --git a/src/components/profile/ProfileCard.jsx b/src/components/profile/ProfileCard.jsx
index 3649efe..371d3dd 100644
--- a/src/components/profile/ProfileCard.jsx
+++ b/src/components/profile/ProfileCard.jsx
@@ -6,7 +6,6 @@ import { useTheme } from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
-import Avatar from '@material-ui/core/Avatar';
import Backdrop from '@material-ui/core/Backdrop';
import Box from '@material-ui/core/Box';
import Card from '@material-ui/core/Card';
@@ -35,6 +34,12 @@ import SaveOutlinedIcon from '@material-ui/icons/SaveOutlined';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import RoomOutlinedIcon from '@material-ui/icons/RoomOutlined';
import PanoramaOutlinedIcon from '@material-ui/icons/PanoramaOutlined';
+import PersonAddIcon from '@material-ui/icons/PersonAdd';
+import BlockOutlinedIcon from '@material-ui/icons/BlockOutlined';
+import PersonAddDisabledIcon from '@material-ui/icons/PersonAddDisabled';
+import CheckOutlinedIcon from '@material-ui/icons/CheckOutlined';
+import LockOpenOutlinedIcon from '@material-ui/icons/LockOpenOutlined';
+import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
import TabPanel from '../tabs/TabPanel';
import Feed from '../feed/Feed';
@@ -199,6 +204,13 @@ const useStyles = makeStyles((theme) => ({
justify: "center",
alignItems:"center"
},
+ followBox: {
+ display: 'flex',
+ justifyContent: "end",
+ justify: "end",
+ alignItems:"end",
+ margin: theme.spacing(2)
+ }
}));
const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
@@ -211,12 +223,20 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
const [name, setName] = useState(EMPTY);
const [location, setLocation] = useState(EMPTY);
const [about, setAbout] = useState(EMPTY);
+
const [followers, setFollowers] = useState(0);
const [following, setFollowing] = useState(0);
+
const [avatar, setAvatar] = useState(EMPTY);
const [banner, setBanner] = useState(EMPTY);
const [profile, setProfile] = useState();
+
+ const [isOwner, setIsOwner] = useState(false);
+ const [isFollowed, setFollowed] = useState(false);
+ const [isFollower, setFollower] = useState(false);
+ const [isBlocked, setIsBlocked] = useState(false);
+
const [loading, setLoading] = useState(true);
const [actionsOpen, setActionsOpen] = useState(false);
const [edit, setEdit] = useState(false);
@@ -263,6 +283,10 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
const loadProfile = async () => {
try {
+ const owner = address.toLowerCase() === walletAddress.toLowerCase();
+ if (owner) {
+ setIsOwner(true);
+ }
const profile = await UserManager.getProfile(address);
if (profile) {
setProfile({
@@ -274,7 +298,14 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
followersCount: 0,
followingCount: 0,
});
- }
+ }
+
+ if (!owner) {
+ setFollowed(await UserManager.isFollowing(walletAddress, address));
+ setFollower(await UserManager.isFollowing(address, walletAddress));
+ setIsBlocked(await UserManager.isBlocked(address, walletAddress));
+ }
+
}
catch(error) {
setAlert(error.message);
@@ -302,6 +333,10 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
case 'cancel':
cancelEdit();
break;
+ case 'block':
+ case 'unblock':
+ toggleBlock();
+ break;
}
setActionsOpen(false);
@@ -404,7 +439,7 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
followingCount: 0,
};
- const result = await UserManager.setProfile(
+ await UserManager.setProfile(
updatedProfile.displayName,
updatedProfile.displayLocation,
updatedProfile.displayAbout,
@@ -432,6 +467,50 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
}
};
+ const toggleFollow = async () => {
+ try {
+ setLoading(true);
+ if (isFollowed) {
+ await UserManager.unfollowUser(address);
+ setFollowed(false);
+ setAlert(`You're no longer following to ${name}|success`);
+ }
+ else {
+ await UserManager.followUser(address);
+ setFollowed(true);
+ setAlert(`Now you're following ${name}|success`);
+ }
+ }
+ catch(error) {
+ setAlert(error.message);
+ }
+ finally {
+ setLoading(false);
+ }
+ }
+
+ const toggleBlock = async () => {
+ try {
+ setLoading(true);
+ if (isBlocked) {
+ await UserManager.unblockUser(address);
+ setIsBlocked(false);
+ setAlert(`You unblocked all activity from${name}|success`);
+ }
+ else {
+ await UserManager.blockUser(address);
+ setIsBlocked(true);
+ setAlert(`You blocked all activity from ${name}|success`);
+ }
+ }
+ catch(error) {
+ setAlert(error.message);
+ }
+ finally {
+ setLoading(false);
+ }
+ }
+
const bannerContent =
@@ -445,7 +524,7 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
className={styles.actionIcon}>@{identity} { !sm && `• ${address}` }
}/>
- { (walletAddress === address) &&
+ {
{
transformOrigin={{ vertical: "top", horizontal: "right" }}
onClose={handleActionsClose}
open={actionsOpen}>
- {!edit &&
+ {!isOwner && isBlocked &&
+
+ }
+ {!isOwner && !isBlocked &&
+
+ }
+ {!edit && isOwner &&
}
- {edit &&
+ {edit && isOwner &&
}
- {edit &&
+ {edit && isOwner &&
- */}
+ }
+
+
{/* Temporarily disabling until functionality is available
*/}
}/>
+ }/>
+ }/>
>
diff --git a/src/components/user/UserItem.jsx b/src/components/user/UserItem.jsx
new file mode 100644
index 0000000..e36a0d5
--- /dev/null
+++ b/src/components/user/UserItem.jsx
@@ -0,0 +1,71 @@
+import { useState, useEffect } from "react";
+import { useAppContext } from '../../context/AppContext';
+import { makeStyles } from '@material-ui/core/styles';
+
+import ListItem from '@material-ui/core/ListItem';
+import Divider from '@material-ui/core/Divider';
+import ListItemText from '@material-ui/core/ListItemText';
+import ListItemAvatar from '@material-ui/core/ListItemAvatar';
+import UserAvatar from "../avatar/UserAvatar";
+
+import point from "../../services/PointSDK";
+import UserManager from "../../services/UserManager";
+
+const useStyles = makeStyles((theme) => ({
+ root: {
+ cursor:'pointer',
+ },
+}));
+
+const EMPTY = '0x0000000000000000000000000000000000000000000000000000000000000000';
+
+const UserItem = ({address}) => {
+
+ const styles = useStyles();
+ const [loading, setLoading] = useState(false);
+ const [name, setName] = useState('');
+ const [about, setAbout] = useState('');
+
+
+
+ const { walletAddress, profile, identity } = useAppContext();
+
+ useEffect(() => {
+ loadUser();
+ }, []);
+
+ const loadUser = async () => {
+ setLoading(true);
+ if (address.toString().toLowerCase() === walletAddress.toString().toLowerCase()) {
+ setName((profile && profile.displayName) || identity);
+ setAbout(profile.displayAbout || "Hey I'm using Point Social!");
+ }
+ else {
+ try {
+ const profile = await UserManager.getProfile(address);
+ const { identity } = await point.ownerToIdentity(address);
+ const name = (profile[0] === EMPTY)? identity : await point.getString(profile[0], { encoding: 'utf-8' });
+ const about = (profile[2] === EMPTY)? "Hey I'm using Point Social!" : await point.getString(profile[2], {encoding: 'utf-8'});
+ setName(name);
+ setAbout(about);
+ }
+ catch(error) {}
+ }
+ setLoading(false);
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+
+};
+
+export default UserItem
\ No newline at end of file
diff --git a/src/components/user/UserList.jsx b/src/components/user/UserList.jsx
new file mode 100644
index 0000000..03f060d
--- /dev/null
+++ b/src/components/user/UserList.jsx
@@ -0,0 +1,87 @@
+import { useEffect, useState, useRef } from "react";
+import { makeStyles } from '@material-ui/core/styles';
+import { useAppContext } from '../../context/AppContext';
+
+import CircularProgressWithIcon from "../../components/generic/CircularProgressWithIcon";
+
+import {Box,
+ Divider,
+ List,
+ Typography,
+ } from '@material-ui/core';
+
+import Skeleton from '@material-ui/lab/Skeleton';
+
+import InboxOutlinedIcon from '@material-ui/icons/InboxOutlined';
+import RichTextField from '../generic/RichTextField';
+import SendOutlinedIcon from '@material-ui/icons/SendOutlined';
+import IconButton from '@material-ui/core/IconButton';
+import SmsOutlinedIcon from '@material-ui/icons/SmsOutlined';
+
+import point from "../../services/PointSDK";
+import UserManager from "../../services/UserManager";
+import EventConstants from "../../events";
+
+import UserItem from "./UserItem";
+
+const useStyles = makeStyles((theme) => ({
+ backdrop: {
+ position: "absolute",
+ zIndex: theme.zIndex.drawer - 1,
+ opacity: 0.9
+ },
+ root: {
+ width: '100%',
+ height: '100%',
+ backgroundColor: theme.palette.background.paper,
+ },
+ inline: {
+ display: 'inline',
+ },
+ empty: {
+ padding: theme.spacing(2, 2),
+ display: "flex",
+ flexDirection: "column",
+ alignItems:"center",
+ justifyContent: "center",
+ marginBottom: theme.spacing(6)
+ },
+ commentBox: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center'
+ },
+ list: {
+ minHeight: '50vh'
+ }
+}));
+
+const UserList = ({users}) => {
+
+ const styles = useStyles();
+ return (
+
+ {
+ (users.length === 0)
+ ?
+
+
+
+ No users yet.
+
+
+ :
+
+ {
+ users.map((user) => (
+
+
+ ))
+ }
+
+ }
+
+ )
+}
+
+export default UserList
\ No newline at end of file
diff --git a/src/services/UserManager.js b/src/services/UserManager.js
index 9015381..59242fa 100644
--- a/src/services/UserManager.js
+++ b/src/services/UserManager.js
@@ -9,6 +9,12 @@ class UserManager {
static blockUser = async (user) => point.contractCall("PSUser", "blockUser", [user]);
static unblockUser = async (user) => point.contractCall("PSUser", "unBlockUser", [user]);
static isBlocked = async (owner, user) => point.contractCall("PSUser", "isBlocked", [owner, user]);
+ static blockList = async () => point.contractCall("PSUser", "blockList", []);
+ static followingList = async (user) => point.contractCall("PSUser", "followingList", [user]);
+ static followersList = async (user) => point.contractCall("PSUser", "followersList", [user]);
+ static followingCount = async (user) => point.contractCall("PSUser", "followingCount", [user]);
+ static followersCount = async (user) => point.contractCall("PSUser", "followersCount", [user]);
+
}
export default UserManager
From a518877edf2d9a639c6a44ad4f365c8667b8cf60 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Antonio=20Jos=C3=A9=20L=C3=B3pez?=
Date: Thu, 14 Jul 2022 16:01:11 -0400
Subject: [PATCH 18/20] smartcontract: Implemented events and fixes when
blocking a user
---
contracts/PSUser.sol | 29 +++++++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/contracts/PSUser.sol b/contracts/PSUser.sol
index af5fc51..4b9f29d 100644
--- a/contracts/PSUser.sol
+++ b/contracts/PSUser.sol
@@ -51,6 +51,15 @@ contract PSUser is Initializable, UUPSUpgradeable, OwnableUpgradeable {
_;
}
+ enum FollowAction {
+ Follow,
+ UnFollow,
+ Block,
+ UnBlock
+ }
+
+ event FollowEvent(address indexed from, address indexed to, FollowAction action);
+
function initialize(
address identityContractAddr,
string calldata identityHandle
@@ -65,17 +74,22 @@ contract PSUser is Initializable, UUPSUpgradeable, OwnableUpgradeable {
}
function followUser(address _user) public notBlocked(_user) returns (bool) {
- return
+ bool result =
EnumerableSet.add(_connectionsByUser[msg.sender].following, _user)
&&
EnumerableSet.add(_connectionsByUser[_user].followers, msg.sender);
+
+ emit FollowEvent(msg.sender, _user, FollowAction.Follow);
+ return result;
}
function unfollowUser(address _user) public returns (bool) {
- return
+ bool result =
EnumerableSet.remove(_connectionsByUser[msg.sender].following, _user)
&&
EnumerableSet.remove(_connectionsByUser[_user].followers, msg.sender);
+ emit FollowEvent(msg.sender, _user, FollowAction.UnFollow);
+ return result;
}
function isFollowing(address _owner, address _user) public view returns (bool) {
@@ -100,13 +114,20 @@ contract PSUser is Initializable, UUPSUpgradeable, OwnableUpgradeable {
function blockUser(address _user) public returns (bool) {
EnumerableSet.remove(_connectionsByUser[msg.sender].following, _user);
+ EnumerableSet.remove(_connectionsByUser[msg.sender].followers, _user);
+ EnumerableSet.remove(_connectionsByUser[_user].following, msg.sender);
EnumerableSet.remove(_connectionsByUser[_user].followers, msg.sender);
- return
+ bool result =
EnumerableSet.add(_connectionsByUser[msg.sender].blocked, _user);
+ emit FollowEvent(msg.sender, _user, FollowAction.Block);
+ return result;
}
function unBlockUser(address _user) public returns (bool) {
- return EnumerableSet.remove(_connectionsByUser[msg.sender].blocked, _user);
+ bool result =
+ EnumerableSet.remove(_connectionsByUser[msg.sender].blocked, _user);
+ emit FollowEvent(msg.sender, _user, FollowAction.UnBlock);
+ return result;
}
function isBlocked(address _owner, address _user) public view returns (bool) {
From fa06cac6ff046d237c204f70a26818dec274000b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Antonio=20Jos=C3=A9=20L=C3=B3pez?=
Date: Thu, 14 Jul 2022 16:03:31 -0400
Subject: [PATCH 19/20] Implemented events for following actions
---
src/components/profile/ProfileCard.jsx | 114 +++++++++++++++++--------
src/components/user/UserItem.jsx | 4 +-
src/events.js | 6 ++
3 files changed, 86 insertions(+), 38 deletions(-)
diff --git a/src/components/profile/ProfileCard.jsx b/src/components/profile/ProfileCard.jsx
index 51290fd..41b3271 100644
--- a/src/components/profile/ProfileCard.jsx
+++ b/src/components/profile/ProfileCard.jsx
@@ -49,6 +49,8 @@ import UserList from '../user/UserList';
import point from "../../services/PointSDK";
import UserManager from "../../services/UserManager";
+import EventConstants from "../../events";
+
const MAX_FILE_SIZE = 100 * 1024 * 1024;
const EMPTY = '0x0000000000000000000000000000000000000000000000000000000000000000';
@@ -234,11 +236,11 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
const [banner, setBanner] = useState(EMPTY);
const [profile, setProfile] = useState();
-
const [isOwner, setIsOwner] = useState(false);
const [isFollowed, setFollowed] = useState(false);
const [isFollower, setFollower] = useState(false);
const [isBlocked, setIsBlocked] = useState(false);
+ const [imBlocked, setImBlocked] = useState(false);
const [loading, setLoading] = useState(true);
const [actionsOpen, setActionsOpen] = useState(false);
@@ -252,7 +254,7 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
const displayAboutRef = useRef();
const actionsAnchor = useRef();
- const { walletAddress, setUserProfile } = useAppContext();
+ const { walletAddress, setUserProfile, events } = useAppContext();
useEffect(() => {
loadProfile();
@@ -262,6 +264,42 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
renderProfile(profile);
}, [profile]);
+ useEffect(() => {
+ getEvents();
+ return () => {
+ events.listeners["PSUser"]["FollowEvent"].removeListener("FollowEvent", handleEvents, { type: 'profile', id: address});
+ events.unsubscribe("PSUser", "FollowEvent");
+ };
+ }, []);
+
+ const getEvents = async() => {
+ try {
+ (await events.subscribe("PSUser", "FollowEvent")).on("FollowEvent", handleEvents, { type: 'profile', id: address});
+ }
+ catch(error) {
+ console.log(error.message);
+ }
+ }
+
+ const handleEvents = async(event) => {
+ console.log(event);
+ if (event && (((event.from.toLowerCase() === walletAddress.toLowerCase()) &&
+ (event.to.toLowerCase() === address.toLowerCase())) ||
+ ((event.to.toLowerCase() === walletAddress.toLowerCase()) &&
+ (event.from.toLowerCase() === address.toLowerCase())))) {
+ switch(event.action) {
+ case EventConstants.FollowAction.Follow:
+ case EventConstants.FollowAction.UnFollow:
+ case EventConstants.FollowAction.Block:
+ case EventConstants.FollowAction.UnBlock:
+ await loadFollowStatus();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
const renderProfile = async (profile) => {
if (profile) {
try {
@@ -271,37 +309,10 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
const location = (profile.displayLocation === EMPTY)? "Point Network" : await point.getString(profile.displayLocation, { encoding: 'utf-8' });
setLocation(location);
const about = (profile.displayLocation === EMPTY)? "Hey I'm using Point Social!" : await point.getString(profile.displayAbout, { encoding: 'utf-8' });
- setAbout(about);
- setFollowers(profile.followersCount || (await UserManager.followersCount(address)) || 0);
- setFollowing(profile.followingCount || (await UserManager.followingCount(address)) || 0);
-
+ setAbout(about);
setAvatar(`/_storage/${profile.avatar}`);
setBanner((profile.banner=== EMPTY)?defaultBanner:`/_storage/${profile.banner}`);
-
- try {
- setFollowersList(await UserManager.followersList(address));
- }
- catch(error) {
- setFollowersList([]);
- }
-
- try {
- setFollowingList(await UserManager.followingList(address));
- }
- catch(error) {
- setFollowingList([]);
- }
-
- const owner = address.toLowerCase() === walletAddress.toLowerCase();
- if (owner) {
- setIsOwner(true);
- }
- else {
- setFollowed(await UserManager.isFollowing(walletAddress, address));
- setFollower(await UserManager.isFollowing(address, walletAddress));
- setIsBlocked(await UserManager.isBlocked(walletAddress, address));
- }
-
+ await loadFollowStatus();
setLoading(false);
}
catch(error) {
@@ -310,6 +321,36 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
}
}
+ const loadFollowStatus = async () => {
+ setFollowers(await UserManager.followersCount(address) || 0);
+ setFollowing(await UserManager.followingCount(address) || 0);
+
+ try {
+ setFollowersList(await UserManager.followersList(address));
+ }
+ catch(error) {
+ setFollowersList([]);
+ }
+
+ try {
+ setFollowingList(await UserManager.followingList(address));
+ }
+ catch(error) {
+ setFollowingList([]);
+ }
+
+ const owner = address.toLowerCase() === walletAddress.toLowerCase();
+ if (owner) {
+ setIsOwner(true);
+ }
+ else {
+ setFollowed(await UserManager.isFollowing(walletAddress, address));
+ setFollower(await UserManager.isFollowing(address, walletAddress));
+ setIsBlocked(await UserManager.isBlocked(walletAddress, address));
+ setImBlocked(await UserManager.isBlocked(address, walletAddress));
+ }
+ }
+
const loadProfile = async () => {
try {
const profile = await UserManager.getProfile(address);
@@ -643,10 +684,13 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
isBlocked?
: } label={isBlocked? "Unblock" : "Block"} onClick={toggleBlock}/>
:
- isFollower && } label="Follows you" color="primary"/>
+ imBlocked?
+ } label="Blocked you" color="secondary"/>
+ :
+ isFollower && } label="Follows you" color="primary"/>
}
- { !isBlocked && : } label={isFollowed? "Unfollow" : "Follow"} onClick={toggleFollow}/> }
+ { !(isBlocked || imBlocked) && : } label={isFollowed? "Unfollow" : "Follow"} onClick={toggleFollow}/> }
}
{ edit && }
@@ -721,8 +765,8 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
}
-
-
+
+
{/* Temporarily disabling until functionality is available
*/}
diff --git a/src/components/user/UserItem.jsx b/src/components/user/UserItem.jsx
index e36a0d5..c1fcbb4 100644
--- a/src/components/user/UserItem.jsx
+++ b/src/components/user/UserItem.jsx
@@ -26,8 +26,6 @@ const UserItem = ({address}) => {
const [name, setName] = useState('');
const [about, setAbout] = useState('');
-
-
const { walletAddress, profile, identity } = useAppContext();
useEffect(() => {
@@ -58,7 +56,7 @@ const UserItem = ({address}) => {
<>
-
+ window.open(`/profile/${address}`, "_blank") }}/>
diff --git a/src/events.js b/src/events.js
index a27642a..3dc1210 100644
--- a/src/events.js
+++ b/src/events.js
@@ -14,6 +14,12 @@ const EventConstants = {
Feed: "1",
Post: "2",
Comment: "3",
+ },
+ FollowAction: {
+ Follow : "0",
+ UnFollow : "1",
+ Block : "2",
+ UnBlock : "3"
}
}
From 3868c1b65f2837ca912a2394bc8ac6b5451d3dfe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Antonio=20Jos=C3=A9=20L=C3=B3pez?=
Date: Tue, 19 Jul 2022 22:16:20 -0400
Subject: [PATCH 20/20] WIP: Merging post weight functionality
---
contracts/PointSocial.sol | 243 +++++++++++++++++------
point.deploy.json | 3 +-
src/components/profile/ProfileCard.jsx | 6 +-
src/services/UserManager.js | 22 +-
tests/unit/smartcontracts/PointSocial.js | 2 +-
5 files changed, 198 insertions(+), 78 deletions(-)
diff --git a/contracts/PointSocial.sol b/contracts/PointSocial.sol
index 37a94aa..daa947a 100644
--- a/contracts/PointSocial.sol
+++ b/contracts/PointSocial.sol
@@ -3,14 +3,18 @@ pragma solidity >=0.8.0;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/utils/Counters.sol";
+import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
+
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "point-contract-manager/contracts/IIdentity.sol";
-contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
+contract SocialStorage {
+
using Counters for Counters.Counter;
+
Counters.Counter internal _postIds;
Counters.Counter internal _commentIds;
Counters.Counter internal _likeIds;
@@ -70,11 +74,12 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 likesWeightMultiplier,
uint256 dislikesWeightWultiplier,
uint256 ageWeightMultiplier,
- uint256 initialWeight
+ uint256 initialWeight,
+ uint256 followWeight
);
- address private _identityContractAddr;
- string private _identityHandle;
+ address internal _identityContractAddr;
+ string internal _identityHandle;
// posts
uint256[] public postIds;
@@ -90,7 +95,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
mapping(uint256 => uint256[]) public likeIdsByPost;
mapping(uint256 => Like) public likeById;
- address private _migrator;
+ address internal _migrator;
mapping(address => Profile) public profileByOwner;
mapping(uint256 => bool) public postIsFlagged;
@@ -116,8 +121,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
Counters.Counter internal _dislikeIds;
mapping(uint256 => uint256[]) public dislikeIdsByPost;
mapping(address => uint256[]) public dislikeIdsByUser;
- mapping(address => mapping(uint256 => uint256))
- public dislikeIdByUserAndPost;
+ mapping(address => mapping(uint256 => uint256)) public dislikeIdByUserAndPost;
mapping(uint256 => Dislike) public dislikeById;
struct PostWithMetadata {
@@ -140,44 +144,56 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 public ageWeightMultiplier;
uint256 public weightThreshold;
uint256 public initialWeight;
+ uint256 public followWeight;
+
+ //mapping(bytes32 => address) internal _contractExtensions;
+ /*
+ * Follow layout storage
+ */
+ using EnumerableSet for EnumerableSet.AddressSet;
+
+ struct FollowConnections {
+ EnumerableSet.AddressSet following;
+ EnumerableSet.AddressSet followers;
+ EnumerableSet.AddressSet blocked;
+ }
+
+ mapping(address => FollowConnections) internal _followConnectionsByUser;
+
+ enum FollowAction {
+ Follow,
+ UnFollow,
+ Block,
+ UnBlock
+ }
+
+ event FollowEvent(
+ address indexed from,
+ address indexed to,
+ FollowAction action);
+
+}
+
+contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, SocialStorage {
+ using Counters for Counters.Counter;
modifier postExists(uint256 _postId) {
- require(postById[_postId].from != address(0), "Post does not exist");
+ require(postById[_postId].from != address(0), "ERROR_POST_DOES_NOT_EXISTS");
_;
}
modifier onlyDeployer() {
- require(
- IIdentity(_identityContractAddr).isIdentityDeployer(
- _identityHandle,
- msg.sender
- ),
- "Not a deployer"
+ require(IIdentity(_identityContractAddr).isIdentityDeployer(_identityHandle, msg.sender),
+ "ERROR_NOT_DEPLOYER"
);
_;
}
- function setWeights(
- uint256 _likesWeightMultiplier,
- uint256 _dislikesWeightWultiplier,
- uint256 _ageWeightMultiplier,
- uint256 _weightThreshold,
- uint256 _initialWeight
- ) external onlyDeployer {
- likesWeightMultiplier = _likesWeightMultiplier;
- dislikesWeightWultiplier = _dislikesWeightWultiplier;
- ageWeightMultiplier = _ageWeightMultiplier;
- weightThreshold = _weightThreshold;
- initialWeight = _initialWeight;
-
- emit MultipliersChanged(
- msg.sender,
- block.timestamp,
- likesWeightMultiplier,
- dislikesWeightWultiplier,
- ageWeightMultiplier,
- initialWeight
- );
+ modifier isContract(address _contract) {
+ uint size;
+ assembly { size := extcodesize(_contract) }
+ require(size > 0, "ERROR_NO_CONTRACT");
+ _;
}
function initialize(
@@ -188,6 +204,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
__UUPSUpgradeable_init();
_identityContractAddr = identityContractAddr;
_identityHandle = identityHandle;
+ // _contractExtensions["PSFollow"] = address(new PSFollow());
}
function _authorizeUpgrade(address) internal view override onlyDeployer {}
@@ -212,6 +229,32 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
);
}
+ function setWeights(
+ uint256 _likesWeightMultiplier,
+ uint256 _dislikesWeightWultiplier,
+ uint256 _ageWeightMultiplier,
+ uint256 _weightThreshold,
+ uint256 _initialWeight,
+ uint256 _followWeight
+ ) external onlyDeployer {
+ likesWeightMultiplier = _likesWeightMultiplier;
+ dislikesWeightWultiplier = _dislikesWeightWultiplier;
+ ageWeightMultiplier = _ageWeightMultiplier;
+ weightThreshold = _weightThreshold;
+ initialWeight = _initialWeight;
+ followWeight = _followWeight;
+
+ emit MultipliersChanged(
+ msg.sender,
+ block.timestamp,
+ likesWeightMultiplier,
+ dislikesWeightWultiplier,
+ ageWeightMultiplier,
+ initialWeight,
+ followWeight
+ );
+ }
+
// Post data functions
function addPost(bytes32 contents, bytes32 image) external {
_postIds.increment();
@@ -242,8 +285,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 postId,
bytes32 contents,
bytes32 image
- ) external {
- require(postById[postId].createdAt != 0, "ERROR_POST_DOES_NOT_EXISTS");
+ ) external postExists(postId) {
require(
msg.sender == postById[postId].from,
"ERROR_CANNOT_EDIT_OTHERS_POSTS"
@@ -261,8 +303,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
);
}
- function deletePost(uint256 postId) external {
- require(postById[postId].createdAt != 0, "ERROR_POST_DOES_NOT_EXISTS");
+ function deletePost(uint256 postId) external postExists(postId) {
require(
msg.sender == postById[postId].from,
"ERROR_CANNOT_DELETE_OTHERS_POSTS"
@@ -300,18 +341,8 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
return false;
}
- function flagPost(uint256 postId) external {
- require(
- IIdentity(_identityContractAddr).isIdentityDeployer(
- _identityHandle,
- msg.sender
- ),
- "ERROR_PERMISSION_DENIED"
- );
- require(postById[postId].createdAt != 0, "ERROR_POST_DOES_NOT_EXISTS");
-
+ function flagPost(uint256 postId) external postExists(postId) onlyDeployer {
postIsFlagged[postId] = !postIsFlagged[postId];
-
emit StateChange(
postId,
msg.sender,
@@ -380,8 +411,9 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
}
PostWithMetadata memory _post = _getPostWithMetadata(_ids[i - 1]);
+ bool _blocked = isBlocked(msg.sender, _post.from) || isBlocked(_post.from, msg.sender);
- if (_validPostToBeShown(_post, _idsToFilter, _newerThanTimestamp)) {
+ if (!_blocked && _validPostToBeShown(_post, _idsToFilter, _newerThanTimestamp)) {
_filteredArray[insertedLength] = _post;
unchecked {
insertedLength++;
@@ -434,13 +466,18 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
view
returns (PostWithMetadata[] memory)
{
- return
- _filterPosts(
- postIdsByOwner[owner],
- _viewedPostsIds,
- postIdsByOwner[owner].length,
- 0
- );
+ if (isBlocked(msg.sender, owner) || isBlocked(owner, msg.sender)) {
+ return new PostWithMetadata[](0);
+ }
+ else {
+ return
+ _filterPosts(
+ postIdsByOwner[owner],
+ _viewedPostsIds,
+ postIdsByOwner[owner].length,
+ 0
+ );
+ }
}
function getAllPostsByOwnerLength(address owner)
@@ -468,9 +505,11 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 weightPunishment = (dislikesCount * dislikesWeightWultiplier) +
(block.timestamp - post.createdAt) *
ageWeightMultiplier;
+ uint256 follow = isFollowing(msg.sender, post.from)? followWeight : 0;
+
int256 weight = int256(initialWeight) +
int256(likesWeight) -
- int256(weightPunishment);
+ int256(weightPunishment) + int256(follow);
PostWithMetadata memory postWithMetadata = PostWithMetadata(
post.id,
@@ -515,7 +554,6 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
return _getPostWithMetadata(id);
}
- // Example: 1,"0x0000000000000000000000000000000000000000000068692066726f6d20706e"
function addCommentToPost(uint256 postId, bytes32 contents) external {
_commentIds.increment();
uint256 newCommentId = _commentIds.current();
@@ -790,6 +828,10 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
return false;
}
+ /**********************************************************************
+ * User Profile Functions
+ ***********************************************************************/
+
function setProfile(
bytes32 name_,
bytes32 location_,
@@ -809,7 +851,9 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
return profileByOwner[id_];
}
- // Data Migrator Functions - only callable by _migrator
+ /**********************************************************************
+ * Data Migrator Functions - only callable by _migrator
+ ***********************************************************************/
function add(
uint256 id,
@@ -894,4 +938,81 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable {
emit ProfileChange(user, block.timestamp);
}
+
+ /**********************************************************************
+ * Follow functions
+ ***********************************************************************/
+
+ modifier onlyMutuals (address _user) {
+ require(isFollowing(msg.sender, _user), "ERROR_NOT_MUTUAL");
+ require(isFollowing(_user, msg.sender), "ERROR_NOT_MUTUAL");
+ _;
+ }
+
+ modifier onlyFollowers (address _user) {
+ require((msg.sender == _user) || isFollowing(msg.sender, _user), "ERROR_NOT_FOLLOWING");
+ _;
+ }
+
+ modifier notBlocked (address _user) {
+ require(!isBlocked(msg.sender, _user), "ERROR_USER_BLOCKED");
+ require(!isBlocked(_user, msg.sender), "ERROR_USER_BLOCKED");
+ _;
+ }
+
+ function followUser(address _user) public notBlocked(_user) {
+ EnumerableSet.add(_followConnectionsByUser[msg.sender].following, _user);
+ EnumerableSet.add(_followConnectionsByUser[_user].followers, msg.sender);
+ emit FollowEvent(msg.sender, _user, FollowAction.Follow);
+ }
+
+ function unfollowUser(address _user) public {
+ EnumerableSet.remove(_followConnectionsByUser[msg.sender].following, _user);
+ EnumerableSet.remove(_followConnectionsByUser[_user].followers, msg.sender);
+ emit FollowEvent(msg.sender, _user, FollowAction.UnFollow);
+ }
+
+ function isFollowing(address _owner, address _user) public view returns (bool) {
+ return EnumerableSet.contains(_followConnectionsByUser[_owner].following, _user);
+ }
+
+ function followingList(address _user) public onlyFollowers(_user) view returns (address[] memory) {
+ return EnumerableSet.values(_followConnectionsByUser[_user].following);
+ }
+
+ function followingCount(address _user) public view returns (uint256) {
+ return EnumerableSet.length(_followConnectionsByUser[_user].following);
+ }
+
+ function followersList(address _user) public onlyFollowers(_user) view returns (address[] memory) {
+ return EnumerableSet.values(_followConnectionsByUser[_user].followers);
+ }
+
+ function followersCount(address _user) public view returns (uint256) {
+ return EnumerableSet.length(_followConnectionsByUser[_user].followers);
+ }
+
+ function blockUser(address _user) public {
+ EnumerableSet.remove(_followConnectionsByUser[msg.sender].following, _user);
+ EnumerableSet.remove(_followConnectionsByUser[msg.sender].followers, _user);
+ EnumerableSet.remove(_followConnectionsByUser[_user].following, msg.sender);
+ EnumerableSet.remove(_followConnectionsByUser[_user].followers, msg.sender);
+ EnumerableSet.add(_followConnectionsByUser[msg.sender].blocked, _user);
+ emit FollowEvent(msg.sender, _user, FollowAction.Block);
+ }
+
+ function unBlockUser(address _user) public {
+ EnumerableSet.remove(_followConnectionsByUser[msg.sender].blocked, _user);
+ emit FollowEvent(msg.sender, _user, FollowAction.UnBlock);
+ }
+
+ function isBlocked(address _owner, address _user) public view returns (bool) {
+ return EnumerableSet.contains(_followConnectionsByUser[_owner].blocked, _user);
+ }
+
+ function blockList() public view returns (address[] memory) {
+ return EnumerableSet.values(_followConnectionsByUser[msg.sender].blocked);
+ }
+
+
}
diff --git a/point.deploy.json b/point.deploy.json
index 538612e..cb05487 100644
--- a/point.deploy.json
+++ b/point.deploy.json
@@ -3,8 +3,7 @@
"target": "social.point",
"keyvalue": {},
"contracts": [
- "PointSocial",
- "PSUser"
+ "PointSocial"
],
"upgradable": true,
"rootDir": "public"
diff --git a/src/components/profile/ProfileCard.jsx b/src/components/profile/ProfileCard.jsx
index 41b3271..17fa791 100644
--- a/src/components/profile/ProfileCard.jsx
+++ b/src/components/profile/ProfileCard.jsx
@@ -267,14 +267,14 @@ const ProfileCard = ({ address, identity, setUpperLoading, setAlert }) => {
useEffect(() => {
getEvents();
return () => {
- events.listeners["PSUser"]["FollowEvent"].removeListener("FollowEvent", handleEvents, { type: 'profile', id: address});
- events.unsubscribe("PSUser", "FollowEvent");
+ events.listeners["PointSocial"]["FollowEvent"].removeListener("FollowEvent", handleEvents, { type: 'profile', id: address});
+ events.unsubscribe("PointSocial", "FollowEvent");
};
}, []);
const getEvents = async() => {
try {
- (await events.subscribe("PSUser", "FollowEvent")).on("FollowEvent", handleEvents, { type: 'profile', id: address});
+ (await events.subscribe("PointSocial", "FollowEvent")).on("FollowEvent", handleEvents, { type: 'profile', id: address});
}
catch(error) {
console.log(error.message);
diff --git a/src/services/UserManager.js b/src/services/UserManager.js
index 59242fa..a910e4e 100644
--- a/src/services/UserManager.js
+++ b/src/services/UserManager.js
@@ -3,17 +3,17 @@ import point from "./PointSDK"
class UserManager {
static getProfile = async (userAddress) => point.contractCall("PointSocial", "getProfile", [userAddress]);
static setProfile = async (displayName, displayLocation, displayAbout, avatar, banner) => point.contractCall("PointSocial", "setProfile", [displayName, displayLocation, displayAbout, avatar, banner]);
- static isFollowing = async (owner, user) => point.contractCall("PSUser", "isFollowing", [owner, user]);
- static followUser = async (user) => point.contractCall("PSUser", "followUser", [user]);
- static unfollowUser = async (user) => point.contractCall("PSUser", "unfollowUser", [user]);
- static blockUser = async (user) => point.contractCall("PSUser", "blockUser", [user]);
- static unblockUser = async (user) => point.contractCall("PSUser", "unBlockUser", [user]);
- static isBlocked = async (owner, user) => point.contractCall("PSUser", "isBlocked", [owner, user]);
- static blockList = async () => point.contractCall("PSUser", "blockList", []);
- static followingList = async (user) => point.contractCall("PSUser", "followingList", [user]);
- static followersList = async (user) => point.contractCall("PSUser", "followersList", [user]);
- static followingCount = async (user) => point.contractCall("PSUser", "followingCount", [user]);
- static followersCount = async (user) => point.contractCall("PSUser", "followersCount", [user]);
+ static isFollowing = async (owner, user) => point.contractCall("PointSocial", "isFollowing", [owner, user]);
+ static followUser = async (user) => point.contractCall("PointSocial", "followUser", [user]);
+ static unfollowUser = async (user) => point.contractCall("PointSocial", "unfollowUser", [user]);
+ static blockUser = async (user) => point.contractCall("PointSocial", "blockUser", [user]);
+ static unblockUser = async (user) => point.contractCall("PointSocial", "unBlockUser", [user]);
+ static isBlocked = async (owner, user) => point.contractCall("PointSocial", "isBlocked", [owner, user]);
+ static blockList = async () => point.contractCall("PointSocial", "blockList", []);
+ static followingList = async (user) => point.contractCall("PointSocial", "followingList", [user]);
+ static followersList = async (user) => point.contractCall("PointSocial", "followersList", [user]);
+ static followingCount = async (user) => point.contractCall("PointSocial", "followingCount", [user]);
+ static followersCount = async (user) => point.contractCall("PointSocial", "followersCount", [user]);
}
diff --git a/tests/unit/smartcontracts/PointSocial.js b/tests/unit/smartcontracts/PointSocial.js
index 37bc9ff..5b5f76c 100644
--- a/tests/unit/smartcontracts/PointSocial.js
+++ b/tests/unit/smartcontracts/PointSocial.js
@@ -54,7 +54,7 @@ describe('PointSocial contract', function () {
let socialFactoryDeployer = factory.connect(addr1);
await expect(
upgrades.upgradeProxy(pointSocial.address, socialFactoryDeployer)
- ).to.be.revertedWith('Not a deployer');
+ ).to.be.revertedWith('ERROR_NOT_DEPLOYER');
});
});