diff --git a/packages/contracts/src/config/config.json b/packages/contracts/src/config/config.json index a6561b8..1d26e44 100644 --- a/packages/contracts/src/config/config.json +++ b/packages/contracts/src/config/config.json @@ -1,5 +1,5 @@ { - "DocumentRegistryAddress": "0xBF5f700E437474ad78a1a244e3625C8453Db72C0", - "SignchainAddress": "0x2b5De22765A433601eF480f4a88A01B36f127092", - "SigningModuleAddress": "0x9032b75FDA26E678cB4B51C9Ef0d2AD8AeaB651E" + "DocumentRegistryAddress": "0x414D48239f7e610e63D0d968b29d47fC823c89aA", + "SignchainAddress": "0xf27276e9fb701D28d91De7666847FD40e5E07EAf", + "SigningModuleAddress": "0x62d4AE524625e95D2d9B16EdB82b601d20E564DF" } \ No newline at end of file diff --git a/packages/middleware/models/UserRegistration.js b/packages/middleware/models/UserRegistration.js index fb58a41..6a69e77 100644 --- a/packages/middleware/models/UserRegistration.js +++ b/packages/middleware/models/UserRegistration.js @@ -7,6 +7,13 @@ const UserRegistration = { did: {type:'string'}, name: {type: 'string'}, email: {type: 'string'}, + profileDetails: { + type: 'object', + properties: { + DOB: {type: 'string'}, + phoneNumber: {type:'string'} + } + }, address: { type: 'string'}, publicKey: { type: 'string'}, userType: { type: 'number'}, diff --git a/packages/react-app/src/App.jsx b/packages/react-app/src/App.jsx index 923c260..3727ee7 100644 --- a/packages/react-app/src/App.jsx +++ b/packages/react-app/src/App.jsx @@ -15,7 +15,7 @@ import {definitions} from "./ceramic/config.json" import Dashboard from "./components/Dashboard"; import Documents from "./components/Documents"; -import Profile from "./components/Profile"; +import Profile from "./components/Profile/Profile"; import Layout from "./components/Layout"; import Steps from './components/Stepper/Steps' import Verify from './components/Verify/Verify' @@ -31,6 +31,7 @@ import {Ed25519Provider} from 'key-did-provider-ed25519' import {PrivateKey} from "@textile/hub"; import Onboarding from './components/Onboarding/Onboarding' import WarningPopup from './components/warnings/WarningPopup' +import NetworkChange from './components/warnings/NetworkChange' import DocumentDetails from './components/Documents/DocumentDetails' const blockExplorer = "https://etherscan.io/" @@ -69,10 +70,6 @@ function App() { setInjectedProvider(new Web3Provider(provider)); }, [setInjectedProvider]); - async function test (seed, identity, idx){ - - } - async function loginUser(seed, identity, idx, address) { const pass = Buffer.from(new Uint8Array(seed)).toString("hex") const user = JSON.parse(localStorage.getItem('USER')) @@ -82,7 +79,7 @@ function App() { const client = await loginUserWithChallenge(identity); let userInfo if (client !== null) { - userInfo = await getLoginUser(user.address, idx) + userInfo = await getLoginUser(user.address) if (userInfo !== null) { localStorage.setItem("USER", JSON.stringify(userInfo)) localStorage.setItem("password", "12345"); @@ -218,6 +215,8 @@ function App() { idx={idx} identity = {identity} />}/> + + }/> { async function getUserData() { try { if (idx) { const data = await idx.get(definitions.profile, idx.id); - setUser(data); + + const userThreadDb = JSON.parse(localStorage.getItem('USER')) + setUser(userThreadDb); + setName(userThreadDb.name); + setEmail(userThreadDb.email); + setDob(userThreadDb.profileDetails.DOB); + setPhoneNumber(userThreadDb.profileDetails.phoneNumber) + setUserId(userThreadDb._id) setUserLoading(false); if(data){ console.log("data fetched") }else{ + // Registration on idx console.log("Something is wrong with IDX") + let notary = true + if (userThreadDb.userType===0){ + notary = false + } + await idx.set(definitions.profile, { + name: userThreadDb.name, + email: userThreadDb.email, + notary: notary, + userAddress: userThreadDb.address + }); } } } catch (err) { @@ -28,6 +55,11 @@ export default function Profile({ ceramic, idx }) { getUserData(); }, [idx]); + + const updateProfile = async ()=>{ + await updateUserProfile(name, email, dob, phoneNumber, userId, idx) + } + return !userLoading ? ( user ? <> diff --git a/packages/react-app/src/components/Profile/EditProfile.Styles.js b/packages/react-app/src/components/Profile/EditProfile.Styles.js new file mode 100644 index 0000000..af15d7b --- /dev/null +++ b/packages/react-app/src/components/Profile/EditProfile.Styles.js @@ -0,0 +1,41 @@ +import styled from "styled-components"; + +export const FormContainer = styled.div` + overflow: hidden; + width: 392px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-bottom: 20px; + .logo-container { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 15px; + } + .form-input { + margin: 12px 0 12px 0; + width: 392px; + height: 37.8px !important; + width: 100%; + } + .checkbox { + color: #717171; + margin-top: 14px; + margin-bottom: 2px; + } + /* .btn-primary { + width: 374px !important; + height: 38px !important; + background-color: #4c51bf; + color: #fff; + margin-right: 1px; + } */ + + .form-input-btn { + width: 385px; + color: #fff; + background-color: #4c51bf; + } +`; diff --git a/packages/react-app/src/components/Profile/EditProfile.js b/packages/react-app/src/components/Profile/EditProfile.js new file mode 100644 index 0000000..edac137 --- /dev/null +++ b/packages/react-app/src/components/Profile/EditProfile.js @@ -0,0 +1,144 @@ +import React, { useEffect, useState } from "react"; +import { Button, Checkbox, Form, Input, Message, Modal } from "semantic-ui-react"; + +import { FormContainer } from "./EditProfile.Styles"; +import { updateUserProfile } from "../../lib/threadDb"; +import Warning from "./Warning"; +import UpdateSuccess from "./updateSuccess"; +import FinalUpdate from "./FinalUpdate"; + +function EditProfile({ open, setOpen, user, DOB, PhoneNumber, idx }) { + const [name, setName] = useState(user.name); + const [email, setEmail] = useState(user.email); + + const [dob, setDob] = useState(DOB); + const [userId, setUserId] = useState(user._id); + const [phoneNumber, setPhoneNumber] = useState(PhoneNumber); + const [loader, setloader] = useState(false); + const [warning, setWarning] = useState(false); + const [success, setSuccess] = useState(false); + const [updated, setUpdated] = useState(false); + + const updateProfile = async () => { + console.log("Function called!!!"); + setloader(true); + + if (name.length === 0) { + setName("NA"); + } + + if (phoneNumber.length !== 0 && phoneNumber !== "NA") { + const pattern = /^\d{10}$/; + if (!phoneNumber.match(pattern)) { + // alert("Wrong mobile number!!"); + setloader(false); + setWarning(true); + return; + } + } else { + setPhoneNumber("NA"); + } + + if (email.length !== 0 && email !== "NA") { + const pattern = /^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/; + if (!email.match(pattern)) { + // alert("Wrong email!!"); + setloader(false); + setWarning(true); + return; + } + } else { + setEmail("NA"); + } + + const result = await updateUserProfile(name, email, dob, phoneNumber, userId, idx, user.publicKey); + if (result) { + setloader(false); + setOpen(false); + setSuccess(true); + } else { + setUpdated(true); + } + + // + }; + + return ( + <> + + + + setOpen(false)} + onOpen={() => setOpen(true)} + size="small" + style={{ width: "450px" }} + > + Profile Update + + + +
+ + setName(e.target.value)} + /> + + + + setEmail(e.target.value)} + /> + + + + setPhoneNumber(e.target.value)} + /> + + + + setDob(e.target.value)} + /> + + + +
+
+
+
+
+ + ); +} + +export default EditProfile; diff --git a/packages/react-app/src/components/Profile/FinalUpdate.js b/packages/react-app/src/components/Profile/FinalUpdate.js new file mode 100644 index 0000000..2a969c6 --- /dev/null +++ b/packages/react-app/src/components/Profile/FinalUpdate.js @@ -0,0 +1,20 @@ +import React from "react"; +import { Button, Modal } from "semantic-ui-react"; + +function FinalUpdate({ updated, setUpdated }) { + return ( + setUpdated(true)} onClose={() => setUpdated(false)}> + OOPS!! + +

Something went wrong. profile could not be updated. Please try again

+
+ + + +
+ ); +} + +export default FinalUpdate; diff --git a/packages/react-app/src/components/Profile/Profile.js b/packages/react-app/src/components/Profile/Profile.js new file mode 100644 index 0000000..8ef888a --- /dev/null +++ b/packages/react-app/src/components/Profile/Profile.js @@ -0,0 +1,147 @@ +// /* eslint-disable */ +import React, { useEffect, useState } from "react"; + +import { definitions } from "../../ceramic/config.json"; +import { Header, Icon, Loader, Segment, Card, Table, Button } from "semantic-ui-react"; +import EditProfile from "./EditProfile"; +import { ProfileContainer } from "../styles/Profile.Style"; + +export default function Profile({ ceramic, idx }) { + const [open, setOpen] = useState(false); + const [user, setUser] = useState(null); + const [userLoading, setUserLoading] = useState(true); + const [phoneNumber, setPhoneNumber] = useState(null); + const [dob, setDob] = useState(null); + + useEffect(() => { + async function getUserData() { + try { + if (idx) { + const data = await idx.get(definitions.profile, idx.id); + const userThreadDb = JSON.parse(localStorage.getItem("USER")); + setUserLoading(false); + if (data) { + setUser(data); + setPhoneNumber(data.phoneNumber) + setDob(data.dob) + setUserLoading(false); + console.log("data fetched"); + console.log("Data:", data); + } else { + setUser(userThreadDb); + setPhoneNumber(userThreadDb.profileDetails.phoneNumber) + setDob(userThreadDb.profileDetails.DOB) + setUserLoading(false); + // Registration on idx + console.log("Something is wrong with IDX"); + let notary = true; + if (userThreadDb.userType === 0) { + notary = false; + } + await idx.set(definitions.profile, { + name: userThreadDb.name, + email: userThreadDb.email, + notary: notary, + userAddress: userThreadDb.address, + phoneNumber: userThreadDb.profileDetails.phoneNumber, + dob: userThreadDb.profileDetails.DOB + }); + } + } + } catch (err) { + console.log(err); + } + } + getUserData(); + }, [idx]); + + return !userLoading ? ( + user ? ( + <> + + +
+
+ +

{user.name}

+

+ {idx.id}{" "} +

+
+
+ + + + + + + Type + + {user.notary ? "Notary" : "Party"} + + + + + {" "} + + Email + + {user.email} + + + + {" "} + + Phone + + {phoneNumber} + + + + {" "} + + DOB + + {dob} + + + {/* + + + Member Since + + {user.joindate} + */} + +
+ +
+
+
+
+
+
+ + ) : ( + +
+ + No profile found :(. +
+ There was an issue fetching the user profile. +
+ ) + ) : ( + + Fetching profile + + ); +} diff --git a/packages/react-app/src/components/Profile/Warning.js b/packages/react-app/src/components/Profile/Warning.js new file mode 100644 index 0000000..2d1aa4e --- /dev/null +++ b/packages/react-app/src/components/Profile/Warning.js @@ -0,0 +1,20 @@ +import React from "react"; +import { Button, Icon, Modal } from "semantic-ui-react"; + +function Warning({ warning, setWarning }) { + return ( + setWarning(true)} onClose={() => setWarning(false)}> + OOPS!! + +

Invalid Email/Phone format.

+
+ + + +
+ ); +} + +export default Warning; diff --git a/packages/react-app/src/components/Profile/updateSuccess.js b/packages/react-app/src/components/Profile/updateSuccess.js new file mode 100644 index 0000000..e7220dc --- /dev/null +++ b/packages/react-app/src/components/Profile/updateSuccess.js @@ -0,0 +1,20 @@ +import React from "react"; +import { Button, Icon, Modal } from "semantic-ui-react"; + +function updateSuccess({ success, setSuccess }) { + return ( + setSuccess(true)} onClose={() => setSuccess(false)}> + Success + +

Your Profile has been Updated.

+
+ + + +
+ ); +} + +export default updateSuccess; diff --git a/packages/react-app/src/components/Stepper/Steps.js b/packages/react-app/src/components/Stepper/Steps.js index b140b84..d6de469 100644 --- a/packages/react-app/src/components/Stepper/Steps.js +++ b/packages/react-app/src/components/Stepper/Steps.js @@ -1,15 +1,15 @@ /* eslint-disable */ -import React, {useEffect, useState} from "react"; +import React, { useEffect, useState } from "react"; import "antd/dist/antd.css"; import "./stepper.css"; -import {Steps} from "antd"; -import {Button, Grid} from "semantic-ui-react"; +import { Steps } from "antd"; +import { Button, Grid } from "semantic-ui-react"; import SelectFiles from "./SelectFiles"; import SelectParties from "./SelectParties"; import Preview from "./Preview"; -import {getAllUsers, registerDoc} from "../../lib/threadDb"; -import {sendMail} from "../../lib/notifications"; +import { getAllUsers, registerDoc } from "../../lib/threadDb"; +import { sendMail } from "../../lib/notifications"; import DocumentSubmitPopup from "../warnings/DocumentSubmitPopup"; import StepSignError from "../warnings/StepSignError"; @@ -178,6 +178,7 @@ const stepper = props => { onClick={() => next()} className="next-btn" style={{ background: "#4C51BF", color: "#fff" }} + disabled={!title || !parties} > Next diff --git a/packages/react-app/src/components/auth/SignUp.js b/packages/react-app/src/components/auth/SignUp.js index efe95fe..0936bb4 100644 --- a/packages/react-app/src/components/auth/SignUp.js +++ b/packages/react-app/src/components/auth/SignUp.js @@ -1,9 +1,9 @@ -import React, {useEffect, useState} from "react"; -import {Button, Checkbox, Form, Input, Message, Modal} from "semantic-ui-react"; +import React, { useEffect, useState } from "react"; +import { Button, Checkbox, Form, Input, Message, Modal } from "semantic-ui-react"; import logo from "../../images/logoInverted.png"; -import {definitions} from "../../ceramic/config.json"; -import {FormContainer} from "../styles/SignUp.Styles"; -import {loginUserWithChallenge, registerNewUser} from "../../lib/threadDb"; +import { definitions } from "../../ceramic/config.json"; +import { FormContainer } from "../styles/SignUp.Styles"; +import { loginUserWithChallenge, registerNewUser } from "../../lib/threadDb"; const index = require("../../lib/e2ee.js"); const moment = require("moment"); @@ -12,9 +12,9 @@ function SignUp({ authStatus, setUserStatus, identity, address, idx, seed }) { const [open, setOpen] = useState(true); const [name, setName] = useState(""); - const [email, setEmail] = useState(""); + const [email, setEmail] = useState("NA"); const [notary, setNotary] = useState(false); - const [error, setError] = useState({status: false, message: ''}); + const [error, setError] = useState({ status: false, message: "" }); const SignupStatus = { preInit: 0, init: 1, wallet: 2, ceramic: 3, contract: 4 }; const [signupStatus, setSignupStatus] = useState(SignupStatus.preInit); @@ -43,17 +43,17 @@ function SignUp({ authStatus, setUserStatus, identity, address, idx, seed }) { const accounts = await index.getAllAccounts(pass); setSignupStatus(SignupStatus.ceramic); try { - await idx.set(definitions.profile, { - name: name, - email: email, - notary: notary, - joindate : moment(new Date()).format("ll"), - userAddress: address - }); - } - catch(e) { - console.log("Failed to create profile on IDX") - + await idx.set(definitions.profile, { + name: name, + email: email, + notary: notary, + joindate: moment(new Date()).format("ll"), + userAddress: address, + phoneNumber: 'NA', + dob: 'NA' + }); + } catch (e) { + console.log("Failed to create profile on IDX"); } setSignupStatus(SignupStatus.contract); //const dbClient = await authorizeUser(password) @@ -65,13 +65,13 @@ function SignUp({ authStatus, setUserStatus, identity, address, idx, seed }) { email, accounts[0], notary ? userType.notary : userType.party, - address + address, ); if (registrationStatus) { setUserStatus(authStatus.loggedIn); } else { localStorage.clear(); - setError({status: true, message: 'An account with same email/ wallet address exists'}) + setError({ status: true, message: "An account with same email/ wallet address exists" }); setSignupStatus(SignupStatus.init); } } @@ -107,12 +107,13 @@ function SignUp({ authStatus, setUserStatus, identity, address, idx, seed }) { /> - + setEmail(e.target.value)} /> @@ -128,15 +129,15 @@ function SignUp({ authStatus, setUserStatus, identity, address, idx, seed }) { setNotary(!notary); }} /> - { - error.status ? - Account creation failed -

{error.message}

-
: null - } + {error.status ? ( + + Account creation failed +

{error.message}

+
+ ) : null} {signupStatus == SignupStatus.init ? ( - ) : signupStatus == SignupStatus.wallet ? ( diff --git a/packages/react-app/src/components/styles/Profile.Style.js b/packages/react-app/src/components/styles/Profile.Style.js index 581cb05..77e8518 100644 --- a/packages/react-app/src/components/styles/Profile.Style.js +++ b/packages/react-app/src/components/styles/Profile.Style.js @@ -5,12 +5,17 @@ export const ProfileContainer = styled.div` justify-content: center; background-color: #f0f2f5; width: 100%; - height: 80vh; + /* height: 80vh; */ border-radius: 4px; .profile { align-items: center; text-align: center; } + .form-input-btn { + width: 385px; + color: #fff; + background-color: #4c51bf; + } .profileContainer img { border-radius: 50%; width: 140px; @@ -35,4 +40,17 @@ export const ProfileContainer = styled.div` grid-template-columns: repeat(3, 1fr); margin-top: 40px; } + .meta-info { + display: flex; + margin: auto; + justify-content: center; + } + .ui.card > .content { + text-align: inherit; + } + .about-card { + width: 700px; + margin-right: 18px; + overflow: hidden; + } `; diff --git a/packages/react-app/src/components/warnings/NetworkChange.js b/packages/react-app/src/components/warnings/NetworkChange.js new file mode 100644 index 0000000..d407053 --- /dev/null +++ b/packages/react-app/src/components/warnings/NetworkChange.js @@ -0,0 +1,45 @@ +import React, { useState } from "react"; +import { Button, Message, Modal } from "semantic-ui-react"; +import { SignInWarningContainer } from "../styles/WarningPopup.Styles"; +import Warn from "../../images/icons/warn.svg"; + +function NetworkChange() { + const [open, setOpen] = useState(true); + return ( +
+ setOpen(true)} + onOpen={() => setOpen(true)} + size="small" + style={{ width: "512px" }} + > + + + +
+ +
+
+

Foreign Network Detected

+ +

Signchain is currently avaliable on Rinkeby Network. Please switch your active wallet Network.

+
+
+ +
+
+
+
+
+
+ ); +} + +export default NetworkChange; diff --git a/packages/react-app/src/contracts/DocumentRegistry.address.js b/packages/react-app/src/contracts/DocumentRegistry.address.js index 59d0266..77fbc6e 100644 --- a/packages/react-app/src/contracts/DocumentRegistry.address.js +++ b/packages/react-app/src/contracts/DocumentRegistry.address.js @@ -1 +1 @@ -module.exports = "0xBF5f700E437474ad78a1a244e3625C8453Db72C0"; \ No newline at end of file +module.exports = "0x414D48239f7e610e63D0d968b29d47fC823c89aA"; \ No newline at end of file diff --git a/packages/react-app/src/contracts/Signchain.address.js b/packages/react-app/src/contracts/Signchain.address.js index beebc67..098f6f7 100644 --- a/packages/react-app/src/contracts/Signchain.address.js +++ b/packages/react-app/src/contracts/Signchain.address.js @@ -1 +1 @@ -module.exports = "0x2b5De22765A433601eF480f4a88A01B36f127092"; \ No newline at end of file +module.exports = "0xf27276e9fb701D28d91De7666847FD40e5E07EAf"; \ No newline at end of file diff --git a/packages/react-app/src/contracts/SigningModule.address.js b/packages/react-app/src/contracts/SigningModule.address.js index 2b9da74..4e1a775 100644 --- a/packages/react-app/src/contracts/SigningModule.address.js +++ b/packages/react-app/src/contracts/SigningModule.address.js @@ -1 +1 @@ -module.exports = "0x9032b75FDA26E678cB4B51C9Ef0d2AD8AeaB651E"; \ No newline at end of file +module.exports = "0x62d4AE524625e95D2d9B16EdB82b601d20E564DF"; \ No newline at end of file diff --git a/packages/react-app/src/images/icons/warn.svg b/packages/react-app/src/images/icons/warn.svg new file mode 100644 index 0000000..deb4130 --- /dev/null +++ b/packages/react-app/src/images/icons/warn.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/react-app/src/lib/threadDb.js b/packages/react-app/src/lib/threadDb.js index cfa0f82..84d1a64 100644 --- a/packages/react-app/src/lib/threadDb.js +++ b/packages/react-app/src/lib/threadDb.js @@ -5,7 +5,7 @@ const { Client, Where, ThreadID } = require('@textile/hub') const wallet = require('wallet-besu') const ethers = require('ethers') const io = require('socket.io-client'); - +const {definitions} = require('../ceramic/config.json') export const registerNewUser = async function(did, name, email, privateKey, userType, address){ try { @@ -17,6 +17,10 @@ export const registerNewUser = async function(did, name, email, privateKey, user did:did, name: name, email: email, + profileDetails:{ + DOB: 'NA', + phoneNumber: 'NA' + }, address: address, publicKey: publicKey.toString("hex"), userType: userType, @@ -25,8 +29,8 @@ export const registerNewUser = async function(did, name, email, privateKey, user } const query = new Where('address').eq(address) - const query1 = new Where('email').eq(email).or(query) - const result = await client.find(threadId, 'RegisterUser', query1) + //const query1 = new Where('email').eq(email).or(query) + const result = await client.find(threadId, 'RegisterUser', query) if (result.length<1){ await client.create(threadId, 'RegisterUser', [data]) localStorage.setItem("USER", JSON.stringify(data)) @@ -101,14 +105,12 @@ export const getCredentials = async function(){ return {client, threadDb} } -export const getLoginUser = async function(address, idx){ +export const getLoginUser = async function(address){ try { const {threadDb, client} = await getCredentials() const query = new Where('address').eq(address) const threadId = ThreadID.fromBytes(threadDb) const result = await client.find(threadId, 'RegisterUser', query) - // By passing Ceramic IDX profile check - // const ceramicResult = await idx.get(definitions.profile, idx.id) if (result.length<1){ console.log("Please register user!") return null @@ -480,4 +482,56 @@ export const downloadFiles = async function (name, key, loggedUser,documentLocat }) } +export const updateUserProfile = async function(name, email, dob, phoneNumber,userId, idx, key){ + const {threadDb, client} = await getCredentials() + const threadId = ThreadID.fromBytes(threadDb) + console.log("UserId:",userId, email) + const query = new Where('publicKey').eq(key) + + const emailQuery = new Where('email').eq(email) + const result = await client.find(threadId, 'RegisterUser', emailQuery) + console.log("result:",result[0]) + try { + if ((result.length === 1 && result[0]._id === userId) || result.length<1) { + console.log("Updating!!") + const user = await client.find(threadId, 'RegisterUser', query) + console.log("User:" + user[0]) + + if (user.length === 1) { + user[0].name = name + user[0].email = email + user[0].profileDetails.DOB = dob + user[0].profileDetails.phoneNumber = phoneNumber + + await client.save(threadId, 'RegisterUser', [user[0]]) + + console.log("Updated on ThreadDB!!!") + let notary = true; + if (user[0].userType === 0) { + notary = false; + } + + await idx.set(definitions.profile, { + name: name, + email: email, + notary: notary, + userAddress: user[0].address, + phoneNumber: phoneNumber, + dob: dob + }); + console.log("Updated on IDX!!!") + + return true; + } + return false; + } else { + console.log("Email already exists!!!") + return false; + } + }catch (e){ + console.log("Exce:",e) + return false + } + +}