From 340f18aa2bc8db67669b7bdfc4aa21e391445478 Mon Sep 17 00:00:00 2001 From: Muhammad Abeer Date: Mon, 25 Oct 2021 04:45:37 +0500 Subject: [PATCH 01/35] Discord Login & Metamask integration --- src/Header.jsx | 53 +---------- src/Header.module.css | 30 ++++++ src/User.jsx | 212 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+), 52 deletions(-) create mode 100644 src/User.jsx diff --git a/src/Header.jsx b/src/Header.jsx index fad3c7609a..79355b7d9e 100644 --- a/src/Header.jsx +++ b/src/Header.jsx @@ -16,7 +16,7 @@ import cameraManager from '../camera-manager.js' import metaversefile from '../metaversefile-api.js' import ioManager from '../io-manager.js' import {parseQuery} from '../util.js' -import * as ceramicApi from '../ceramic.js'; +import User from './User'; // import * as ceramicAdmin from '../ceramic-admin.js'; import sceneNames from '../scenes/scenes.json'; @@ -191,57 +191,6 @@ const Location = ({sceneName, setSceneName, roomName, setRoomName, open, setOpen ); }; -const User = ({address, setAddress, open, setOpen, toggleOpen}) => { - const userOpen = open === 'user'; - - const [loggingIn, setLoggingIn] = useState(false); - - /* (async () => { - const {createSchema} = await ceramicAdmin.waitForLoad(); - const schema = await createSchema(); - console.log('create', schema); - })(); */ - - /* const login = async () => { - if (typeof window.ethereum !== 'undefined') { - const addresses = await window.ethereum.request({ - method: 'eth_requestAccounts', - }); - const [address] = addresses; - // console.log('address', {address}); - setAddress(address); - } else { - console.warn('no ethereum'); - } - }; */ - - return ( -
{ - e.preventDefault(); - e.stopPropagation(); - - if (address) { - toggleOpen('user'); - } else { - if (!loggingIn) { - setLoggingIn(true); - try { - const {address, profile} = await ceramicApi.login(); - // console.log('login', {address, profile}); - setAddress(address); - } catch(err) { - console.warn(err); - } finally { - setLoggingIn(false); - } - } - } - }}> - -
{loggingIn ? 'Logging in... ' : (address || 'Log in')}
-
- ); -}; const Tab = ({className, type, left, right, top, bottom, disabled, label, panels, before, after, open, toggleOpen, onclick, panelsRef}) => { if (!onclick) { diff --git a/src/Header.module.css b/src/Header.module.css index 5a9a54611c..427fe15250 100644 --- a/src/Header.module.css +++ b/src/Header.module.css @@ -688,4 +688,34 @@ body { .equipment .button:active + .background2 { background-color: #b71c1c; transition: none; +} + +.login_options { + background: black; + color: white; + font-size: initial; + text-align: left; + cursor: pointer; +} + +.metamask { + margin-left: 10px; + display: flex; + align-items: center; +} + +.discord { + margin-left: 10px; + display: flex; + align-items: center; +} + +.metamask_text { + margin-left: 10px; + +} + +.discord_text { + margin-left: 10px; + } \ No newline at end of file diff --git a/src/User.jsx b/src/User.jsx new file mode 100644 index 0000000000..560b4e9bf1 --- /dev/null +++ b/src/User.jsx @@ -0,0 +1,212 @@ + +import React, {useState, Component, useRef, useEffect} from 'react'; +import classnames from 'classnames'; +import styles from './Header.module.css'; +import * as ceramicApi from '../ceramic.js'; +// import styles from './User.module.css'; +import {storageHost, accountsHost, tokensHost, loginEndpoint} from '../constants'; +import {contracts, getAddressFromMnemonic} from '../blockchain.js'; +import {jsonParse, parseQuery} from '../util.js'; + +let userObject; + +function useComponentVisible(initialIsVisible) { + const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible); + const ref = useRef(null); + + const handleClickOutside = event => { + if (ref.current && !ref.current.contains(event.target)) { + setIsComponentVisible(false); + } + }; + + useEffect(() => { + document.addEventListener('click', handleClickOutside, true); + return () => { + document.removeEventListener('click', handleClickOutside, true); + }; + }); + + return {ref, isComponentVisible, setIsComponentVisible}; +} + +async function contentIdToStorageUrl(id) { + if (typeof id === 'number') { + const hash = await contracts.mainnetsidechain.NFT.methods.getHash(id + '').call(); + return `${storageHost}/${hash}`; + } else if (typeof id === 'string') { + return id; + } else { + return null; + } +} + +async function pullUserObject(loginToken) { + const address = getAddressFromMnemonic(loginToken.mnemonic); + const res = await fetch(`${accountsHost}/${address}`); + const result = await res.json(); + // console.log('pull user object', result); + let {name, avatarId, avatarName, avatarExt, avatarPreview, loadout, homeSpaceId, homeSpaceName, homeSpaceExt, homeSpaceFileName, homeSpacePreview, ftu} = result; + loadout = jsonParse(loadout, Array(8).fill(null)); + + const avatarNumber = parseInt(avatarId); + const avatarUrl = await contentIdToStorageUrl(!isNaN(avatarNumber) ? avatarNumber : avatarId); + const homeSpaceNumber = parseInt(homeSpaceId); + const homeSpaceUrl = await contentIdToStorageUrl(!isNaN(homeSpaceNumber) ? homeSpaceNumber : homeSpaceId); + + const inventory = await (async () => { + const res = await fetch(`${tokensHost}/${address}`); + const tokens = await res.json(); + return tokens; + })(); + + // menuActions.setInventory(inventory); + + userObject = { + name, + avatar: { + id: avatarId, + name: avatarName, + ext: avatarExt, + preview: avatarPreview, + url: avatarUrl, + }, + loadout, + inventory, + homespace: { + id: homeSpaceId, + name: homeSpaceName, + ext: homeSpaceExt, + preview: homeSpacePreview, + url: homeSpaceUrl, + }, + ftu, + }; + return userObject; +} + +const handleDiscordLogin = async discordUrl => { + if (!discordUrl) { + return; + } + const urlSearchParams = new URLSearchParams(new URL(discordUrl).search); + const {id, code} = Object.fromEntries(urlSearchParams.entries()); + let res = await fetch(loginEndpoint + `?discordid=${encodeURIComponent(id)}&discordcode=${encodeURIComponent(code)}`, { + method: 'POST', + }); + + res = await res.json(); + if (!res.error) { + return await pullUserObject(res); + } else { + console.warn('Unable to login ', res.error); + } + + // debugger; +}; + +const User = ({address, setAddress, open, setOpen, toggleOpen}) => { + const userRef = useComponentVisible(false); + const discordRef = useComponentVisible(false); + const metaMaskRef = useComponentVisible(false); + const emailRef = useComponentVisible(false); + const [loggingIn, setLoggingIn] = useState(false); + const [loginButtons, setLoginButtons] = useState(false); + + const [discordUrl, setDiscordUrl] = useState(''); + + return ( +
+
{ + e.preventDefault(); + e.stopPropagation(); + userRef.setIsComponentVisible(true); + setLoginButtons(true); + discordRef.setIsComponentVisible(false); + metaMaskRef.setIsComponentVisible(false); + }}> + +
{loggingIn ? 'Logging in... ' : (address || 'Log in')} +
+
+ { + userRef.isComponentVisible + ?
+ { + loginButtons ? <> + { + e.preventDefault(); + e.stopPropagation(); + if (address) { + toggleOpen('user'); + } else { + if (!loggingIn) { + setLoggingIn(true); + try { + const {address, profile} = await ceramicApi.login(); + // console.log('login', {address, profile}); + setAddress(address); + } catch (err) { + console.warn(err); + } finally { + setLoggingIn(false); + } + } + } + } }> + + MetaMask + + { + e.preventDefault(); + e.stopPropagation(); + setLoginButtons(false); + discordRef.setIsComponentVisible(true); + } }> + + Discord + : '' + } + { + discordRef.isComponentVisible + ?
+
+
+
+
+ +
+
+ setDiscordUrl(e.target.value)} + onKeyPress={ + async event => { + event.preventDefault(); + event.stopPropagation(); + if (event.key === 'Enter') { + const {address} = await handleDiscordLogin(discordUrl); + setAddress(address); + } + } + } /> + +
+
+ +
+
+
: '' + } + +
+ :
} + +
+ ); +}; + +export default User; From 87409206e5cf7c60fdc5ec5e275fb888cea25d13 Mon Sep 17 00:00:00 2001 From: Muhammad Abeer Date: Tue, 26 Oct 2021 21:27:41 +0500 Subject: [PATCH 02/35] Login Fixes --- public/images/discord.png | Bin 0 -> 24403 bytes public/images/metamask.png | Bin 0 -> 133174 bytes src/User.jsx | 169 ++++++++++++------------------------- util.js | 44 +++++++++- 4 files changed, 99 insertions(+), 114 deletions(-) create mode 100644 public/images/discord.png create mode 100644 public/images/metamask.png diff --git a/public/images/discord.png b/public/images/discord.png new file mode 100644 index 0000000000000000000000000000000000000000..fb6a4b4baf968ae8395d69ed75a341cbad287cb8 GIT binary patch literal 24403 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

(Ri9hTpFzp%j!>YXUR4;L$cS}F+S^cq*)&(=o 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 { if (ref.current && !ref.current.contains(event.target)) { setIsComponentVisible(false); + fn(); } }; @@ -30,83 +31,7 @@ function useComponentVisible(initialIsVisible) { return {ref, isComponentVisible, setIsComponentVisible}; } -async function contentIdToStorageUrl(id) { - if (typeof id === 'number') { - const hash = await contracts.mainnetsidechain.NFT.methods.getHash(id + '').call(); - return `${storageHost}/${hash}`; - } else if (typeof id === 'string') { - return id; - } else { - return null; - } -} - -async function pullUserObject(loginToken) { - const address = getAddressFromMnemonic(loginToken.mnemonic); - const res = await fetch(`${accountsHost}/${address}`); - const result = await res.json(); - // console.log('pull user object', result); - let {name, avatarId, avatarName, avatarExt, avatarPreview, loadout, homeSpaceId, homeSpaceName, homeSpaceExt, homeSpaceFileName, homeSpacePreview, ftu} = result; - loadout = jsonParse(loadout, Array(8).fill(null)); - - const avatarNumber = parseInt(avatarId); - const avatarUrl = await contentIdToStorageUrl(!isNaN(avatarNumber) ? avatarNumber : avatarId); - const homeSpaceNumber = parseInt(homeSpaceId); - const homeSpaceUrl = await contentIdToStorageUrl(!isNaN(homeSpaceNumber) ? homeSpaceNumber : homeSpaceId); - - const inventory = await (async () => { - const res = await fetch(`${tokensHost}/${address}`); - const tokens = await res.json(); - return tokens; - })(); - - // menuActions.setInventory(inventory); - - userObject = { - name, - avatar: { - id: avatarId, - name: avatarName, - ext: avatarExt, - preview: avatarPreview, - url: avatarUrl, - }, - loadout, - inventory, - homespace: { - id: homeSpaceId, - name: homeSpaceName, - ext: homeSpaceExt, - preview: homeSpacePreview, - url: homeSpaceUrl, - }, - ftu, - }; - return userObject; -} - -const handleDiscordLogin = async discordUrl => { - if (!discordUrl) { - return; - } - const urlSearchParams = new URLSearchParams(new URL(discordUrl).search); - const {id, code} = Object.fromEntries(urlSearchParams.entries()); - let res = await fetch(loginEndpoint + `?discordid=${encodeURIComponent(id)}&discordcode=${encodeURIComponent(code)}`, { - method: 'POST', - }); - - res = await res.json(); - if (!res.error) { - return await pullUserObject(res); - } else { - console.warn('Unable to login ', res.error); - } - - // debugger; -}; - const User = ({address, setAddress, open, setOpen, toggleOpen}) => { - const userRef = useComponentVisible(false); const discordRef = useComponentVisible(false); const metaMaskRef = useComponentVisible(false); const emailRef = useComponentVisible(false); @@ -114,6 +39,41 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const [loginButtons, setLoginButtons] = useState(false); const [discordUrl, setDiscordUrl] = useState(''); + const [loginError, setLoginError] = useState(null); + + const userRef = useComponentVisible(false, ()=>{ + setLoginError(false); + setDiscordUrl(''); + }); + + const metaMaskLogin = async e => { + e.preventDefault(); + e.stopPropagation(); + if (address) { + toggleOpen('user'); + } else { + if (!loggingIn) { + setLoggingIn(true); + try { + const {address, profile} = await ceramicApi.login(); + // console.log('login', {address, profile}); + setAddress(address); + } catch (err) { + console.warn(err); + } finally { + setLoggingIn(false); + } + } + } + } + + const discordLogin = async e => { + e.preventDefault(); + e.stopPropagation(); + setLoginButtons(false); + discordRef.setIsComponentVisible(true); + } + return (

@@ -121,13 +81,17 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { onClick={async e => { e.preventDefault(); e.stopPropagation(); - userRef.setIsComponentVisible(true); - setLoginButtons(true); - discordRef.setIsComponentVisible(false); - metaMaskRef.setIsComponentVisible(false); + if (address) { + toggleOpen('user'); + }else{ + userRef.setIsComponentVisible(true); + setLoginButtons(true); + discordRef.setIsComponentVisible(false); + metaMaskRef.setIsComponentVisible(false); + } }}> -
{loggingIn ? 'Logging in... ' : (address || 'Log in')} +
{loggingIn ? 'Logging in... ' : (address || (loginError || 'Log in'))}
{ @@ -135,37 +99,13 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { ?
{ loginButtons ? <> - { - e.preventDefault(); - e.stopPropagation(); - if (address) { - toggleOpen('user'); - } else { - if (!loggingIn) { - setLoggingIn(true); - try { - const {address, profile} = await ceramicApi.login(); - // console.log('login', {address, profile}); - setAddress(address); - } catch (err) { - console.warn(err); - } finally { - setLoggingIn(false); - } - } - } - } }> - + + MetaMask - { - e.preventDefault(); - e.stopPropagation(); - setLoginButtons(false); - discordRef.setIsComponentVisible(true); - } }> - + + Discord : '' } @@ -188,8 +128,11 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { event.preventDefault(); event.stopPropagation(); if (event.key === 'Enter') { - const {address} = await handleDiscordLogin(discordUrl); - setAddress(address); + const {address, error} = await handleDiscordLogin(discordUrl); + if(address) + setAddress(address); + else + setLoginError(String(error).toLocaleUpperCase()); } } } /> diff --git a/util.js b/util.js index df34a8d202..b425ef431a 100644 --- a/util.js +++ b/util.js @@ -1,7 +1,9 @@ import * as THREE from 'three'; import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'; // import atlaspack from './atlaspack.js'; -import {maxGrabDistance, tokensHost, storageHost} from './constants.js'; +import {tokensHost, storageHost, accountsHost} from './constants.js'; +import { loginEndpoint } from './constants.js'; +import { getAddressFromMnemonic } from './blockchain.js'; const localVector = new THREE.Vector3(); const localVector2 = new THREE.Vector3(); @@ -607,3 +609,43 @@ export function makeId(length) { return result; } +async function contentIdToStorageUrl(id) { + if (typeof id === 'number') { + const hash = await contracts.mainnetsidechain.NFT.methods.getHash(id + '').call(); + return `${storageHost}/${hash}`; + } else if (typeof id === 'string') { + return id; + } else { + return null; + } +} + +async function pullUserObject(loginToken) { + const address = getAddressFromMnemonic(loginToken.mnemonic); + const res = await fetch(`${accountsHost}/${address}`); + const result = await res.json(); + return result; +} + +export const handleDiscordLogin = async discordUrl => { + if (!discordUrl) { + return; + } + debugger; + try{ + const urlSearchParams = new URLSearchParams(new URL(discordUrl).search); + const {id, code} = Object.fromEntries(urlSearchParams.entries()); + let res = await fetch(loginEndpoint + `?discordid=${encodeURIComponent(id)}&discordcode=${encodeURIComponent(code)}`, { + method: 'POST', + }); + res = await res.json(); + if (!res.error) { + return await pullUserObject(res); + } else { + //console.warn('Unable to login ', res.error); + return res; + } + }catch(e){ + + } +}; \ No newline at end of file From b04d49a49ebb9a964ded82ddb437ae2a1ddf3531 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Tue, 26 Oct 2021 23:13:05 +0500 Subject: [PATCH 03/35] hide sidebar on login --- src/User.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/User.jsx b/src/User.jsx index c83e7d8e6b..797f6b4791 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -58,6 +58,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const {address, profile} = await ceramicApi.login(); // console.log('login', {address, profile}); setAddress(address); + userRef.setIsComponentVisible(false); } catch (err) { console.warn(err); } finally { @@ -83,6 +84,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { e.stopPropagation(); if (address) { toggleOpen('user'); + userRef.setIsComponentVisible(false); }else{ userRef.setIsComponentVisible(true); setLoginButtons(true); From acb8702e0434ffbcac956859ca70d62d0fc61c35 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Thu, 4 Nov 2021 18:46:40 +0500 Subject: [PATCH 04/35] discord integrated --- index.js | 6 +++++- src/User.jsx | 34 ++++++++++++++++++++++++++++++++++ util.js | 6 ++---- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 5b9337cb16..adebfed947 100644 --- a/index.js +++ b/index.js @@ -40,7 +40,11 @@ const certs = { req.originalUrl = u; next(); } - } else { + }else if(/^\/login/.test(o.pathname)){ + req.originalUrl = req.originalUrl.replace(/^\/(login)/,'/'); + res.redirect(req.originalUrl); + } + else { next(); } }); diff --git a/src/User.jsx b/src/User.jsx index 797f6b4791..efd1654eeb 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -74,6 +74,38 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { setLoginButtons(false); discordRef.setIsComponentVisible(true); } + + useEffect(async () => { + if(!address) { + const { + error, + error_description, + code, + id, + play, + realmId, + twitter: arrivingFromTwitter, + } = typeof window !== 'undefined' ? parseQuery(window.location.search) : {}; + if(code && id) { + const {address, error} = await handleDiscordLogin(code, id); + if(address) { + console.log('address', address) + setAddress(address); + } + else { + setLoginError(String(error).toLocaleUpperCase()); + } + + // debugger; + } + + + // handleDiscordLogin(code, id) + // console.log(code,id); + // debugger; + } + }, [address, setAddress]); + return ( @@ -154,4 +186,6 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { ); }; + + export default User; diff --git a/util.js b/util.js index 0b414fc484..5a4c83b5f5 100644 --- a/util.js +++ b/util.js @@ -627,13 +627,11 @@ async function pullUserObject(loginToken) { return result; } -export const handleDiscordLogin = async discordUrl => { - if (!discordUrl) { +export const handleDiscordLogin = async (code, id) => { + if (!code || !id) { return; } try{ - const urlSearchParams = new URLSearchParams(new URL(discordUrl).search); - const {id, code} = Object.fromEntries(urlSearchParams.entries()); let res = await fetch(loginEndpoint + `?discordid=${encodeURIComponent(id)}&discordcode=${encodeURIComponent(code)}`, { method: 'POST', }); From a3d4857a3f7815afed09e8482c62497b9c48b23a Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Thu, 4 Nov 2021 20:53:06 +0500 Subject: [PATCH 05/35] localstorage added --- src/User.jsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/User.jsx b/src/User.jsx index efd1654eeb..0b209122df 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -58,6 +58,8 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const {address, profile} = await ceramicApi.login(); // console.log('login', {address, profile}); setAddress(address); + localStorage.setItem('loginToken', address); + userRef.setIsComponentVisible(false); } catch (err) { console.warn(err); @@ -76,7 +78,14 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { } useEffect(async () => { - if(!address) { + + const storedLoginToken = localStorage.getItem("loginToken"); + + if(storedLoginToken) { + setAddress(storedLoginToken); + } + + else { const { error, error_description, @@ -91,6 +100,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { if(address) { console.log('address', address) setAddress(address); + localStorage.setItem('loginToken', address); } else { setLoginError(String(error).toLocaleUpperCase()); From 9b276f9d4f9433444c7703d3942ba0e3afdef896 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Thu, 4 Nov 2021 22:03:33 +0500 Subject: [PATCH 06/35] url added --- src/User.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/User.jsx b/src/User.jsx index 0b209122df..134a408326 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -148,7 +148,11 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { MetaMask - + Discord : '' From 75b31b749251f70499c89d6bc3d9ff1111d2c08e Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Thu, 4 Nov 2021 22:28:38 +0500 Subject: [PATCH 07/35] uri added --- src/User.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/User.jsx b/src/User.jsx index 134a408326..d90be1a28d 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -95,7 +95,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { realmId, twitter: arrivingFromTwitter, } = typeof window !== 'undefined' ? parseQuery(window.location.search) : {}; - if(code && id) { + if(code) { const {address, error} = await handleDiscordLogin(code, id); if(address) { console.log('address', address) @@ -149,7 +149,6 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { >MetaMask From 6b688c0ae4a0559411eefbb5d46387f7fb9aa597 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Thu, 4 Nov 2021 22:46:38 +0500 Subject: [PATCH 08/35] id check removed --- util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.js b/util.js index 5a4c83b5f5..435da01089 100644 --- a/util.js +++ b/util.js @@ -628,7 +628,7 @@ async function pullUserObject(loginToken) { } export const handleDiscordLogin = async (code, id) => { - if (!code || !id) { + if (!code) { return; } try{ From 9d91bd8933a3b73a19978f3668a96eef2386acd8 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Thu, 4 Nov 2021 23:50:45 +0500 Subject: [PATCH 09/35] url updated --- util.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/util.js b/util.js index 435da01089..e07c547e0a 100644 --- a/util.js +++ b/util.js @@ -632,9 +632,10 @@ export const handleDiscordLogin = async (code, id) => { return; } try{ - let res = await fetch(loginEndpoint + `?discordid=${encodeURIComponent(id)}&discordcode=${encodeURIComponent(code)}`, { - method: 'POST', - }); + const res = await fetch(`https://login.exokit.org/?discordcode=${code}&discordid=${id}`, {method: 'POST'}); + // let res = await fetch(loginEndpoint + `?discordid=${encodeURIComponent(id)}&discordcode=${encodeURIComponent(code)}`, { + // method: 'POST', + // }); res = await res.json(); if (!res.error) { return await pullUserObject(res); From c25db427a42da3a1b46f62916d72f4bccc3b6a13 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Fri, 5 Nov 2021 00:56:47 +0500 Subject: [PATCH 10/35] url updated --- constants.js | 1 + util.js | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/constants.js b/constants.js index a9e33d3da5..c7ecbd6e6a 100644 --- a/constants.js +++ b/constants.js @@ -43,6 +43,7 @@ export const aiHost = `https://ai.webaverse.com`; export const web3MainnetSidechainEndpoint = 'https://mainnetsidechain.exokit.org'; export const web3TestnetSidechainEndpoint = 'https://testnetsidechain.exokit.org'; export const worldUrl = 'worlds.webaverse.com'; +export const discordAuthUrl = `https://discord.com/api/oauth2/authorize?client_id=684141574808272937&redirect_uri=${window.location.origin}%2Flogin&response_type=code&scope=identify` export const worldMapName = 'world'; export const actionsMapName = 'actions'; diff --git a/util.js b/util.js index e07c547e0a..9623984a83 100644 --- a/util.js +++ b/util.js @@ -632,10 +632,9 @@ export const handleDiscordLogin = async (code, id) => { return; } try{ - const res = await fetch(`https://login.exokit.org/?discordcode=${code}&discordid=${id}`, {method: 'POST'}); - // let res = await fetch(loginEndpoint + `?discordid=${encodeURIComponent(id)}&discordcode=${encodeURIComponent(code)}`, { - // method: 'POST', - // }); + let res = await fetch(loginEndpoint + `?discordid=${encodeURIComponent(id)}&discordcode=${encodeURIComponent(code)}&redirect_uri=${window.location.origin}/login`, { + method: 'POST', + }); res = await res.json(); if (!res.error) { return await pullUserObject(res); From 5d551d3807e59bbb72cafd97e47e547f58f41241 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Mon, 8 Nov 2021 12:22:19 +0500 Subject: [PATCH 11/35] modal added --- package.json | 1 + src/Header.module.css | 55 +++++++++++++++++ src/User.jsx | 58 ++++++++++++------ src/components/modal.css | 126 +++++++++++++++++++++++++++++++++++++++ src/components/modal.js | 37 ++++++++++++ 5 files changed, 260 insertions(+), 17 deletions(-) create mode 100644 src/components/modal.css create mode 100644 src/components/modal.js diff --git a/package.json b/package.json index ef4aedd6c3..1b3e65941a 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "key-did-resolver": "^1.4.0", "metaversefile": "./packages/metaversefile", "openai-api": "^1.2.6", + "prop-types": "^15.7.2", "react": "17.0.2", "react-dom": "17.0.2", "smile2emoji": "^1.1.6", diff --git a/src/Header.module.css b/src/Header.module.css index 427fe15250..cad37de6f3 100644 --- a/src/Header.module.css +++ b/src/Header.module.css @@ -1,3 +1,58 @@ +.specialBtnDiv { + /* min-width: 200px; */ + /* border: 1px solid red; */ + background-color: #777777; + padding: 5px; + clip-path: polygon( + 0% 10px, /* top left */ + 10px 0%, /* top left */ + 100% 0%, /* top right */ + 100% 10px, /* top right */ + 100% calc(100% - 10px), /* bottom right */ + calc(100% - 10px) 100%, /* bottom right */ + 10px 100%, /* bottom left */ + 0 100% /* bottom left */ + ); +} + +.specialBtnDiv2 { + min-width: 200px; + /* border: 1px solid red; */ + background-color: #9c27b0; + padding: 2px; + clip-path: polygon( + 0% 10px, /* top left */ + 10px 0%, /* top left */ + 100% 0%, /* top right */ + 100% 10px, /* top right */ + 100% calc(100% - 10px), /* bottom right */ + calc(100% - 10px) 100%, /* bottom right */ + 10px 100%, /* bottom left */ + 0 100% /* bottom left */ + ); +} + +.specialBtn { + width: -webkit-fill-available; + + font-size: 24px; + clip-path: polygon( + 0% 10px, /* top left */ + 10px 0%, /* top left */ + 100% 0%, /* top right */ + 100% 10px, /* top right */ + 100% calc(100% - 10px), /* bottom right */ + calc(100% - 10px) 100%, /* bottom right */ + 10px 100%, /* bottom left */ + 0 100% /* bottom left */ + ); + background-image: linear-gradient(#d98ddf, #eec2e2, #af4db6); + color: #572c75; + border: #572c75; + padding: 8px; + /* position:relative; */ +} + body { --header-height: 50px; } diff --git a/src/User.jsx b/src/User.jsx index d90be1a28d..78458e91bf 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -4,9 +4,10 @@ import classnames from 'classnames'; import styles from './Header.module.css'; import * as ceramicApi from '../ceramic.js'; // import styles from './User.module.css'; -import {storageHost, accountsHost, tokensHost, loginEndpoint} from '../constants'; +import {storageHost, accountsHost, tokensHost, loginEndpoint, discordAuthUrl} from '../constants'; import {contracts, getAddressFromMnemonic} from '../blockchain.js'; import {jsonParse, parseQuery, handleDiscordLogin} from '../util.js'; +import Modal from "./components/modal"; let userObject; @@ -32,6 +33,14 @@ function useComponentVisible(initialIsVisible, fn) { } const User = ({address, setAddress, open, setOpen, toggleOpen}) => { + + const [show, setShow] = useState(false); + + const showModal = async e => { + e.preventDefault(); + setShow(!show); + } + const discordRef = useComponentVisible(false); const metaMaskRef = useComponentVisible(false); const emailRef = useComponentVisible(false); @@ -135,7 +144,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { } }}> -
{loggingIn ? 'Logging in... ' : (address || (loginError || 'Log in'))} +
{ showModal(e); }}>{loggingIn ? 'Logging in... ' : (address || (loginError || 'Log in'))}
{ @@ -143,20 +152,35 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { ?
{ loginButtons ? <> - - - MetaMask - - - - Discord - : '' + +
+ {/*

Webaverse Logo

*/} + +

Webaverse

+
+
+
+
+
+ +
+
+ +
+
+ Log in and experience our emmersive 3D universe. +
+
+
+ : '' } - { + {/* { discordRef.isComponentVisible ?
@@ -181,7 +205,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { else setLoginError(String(error).toLocaleUpperCase()); } - } + } } />
@@ -190,7 +214,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => {
: '' - } + } */}
:
} diff --git a/src/components/modal.css b/src/components/modal.css new file mode 100644 index 0000000000..b97ebff8e1 --- /dev/null +++ b/src/components/modal.css @@ -0,0 +1,126 @@ + +.specialBtnDiv { + /* min-width: 200px; */ + /* border: 1px solid red; */ + background-color: #777777; + padding: 5px; + clip-path: polygon( + 0% 10px, /* top left */ + 10px 0%, /* top left */ + 100% 0%, /* top right */ + 100% 10px, /* top right */ + 100% calc(100% - 10px), /* bottom right */ + calc(100% - 10px) 100%, /* bottom right */ + 10px 100%, /* bottom left */ + 0 100% /* bottom left */ + ); + } + + .specialBtnDiv2 { + width: 100px; + /* border: 1px solid red; */ + background-color: #9c27b0; + padding: 2px; + clip-path: polygon( + 0% 10px, /* top left */ + 10px 0%, /* top left */ + 100% 0%, /* top right */ + 100% 10px, /* top right */ + 100% calc(100% - 10px), /* bottom right */ + calc(100% - 10px) 100%, /* bottom right */ + 10px 100%, /* bottom left */ + 0 100% /* bottom left */ + ); + } + + .specialBtn { + width: -webkit-fill-available; + + font-size: 14px; + clip-path: polygon( + 0% 10px, /* top left */ + 10px 0%, /* top left */ + 100% 0%, /* top right */ + 100% 10px, /* top right */ + 100% calc(100% - 10px), /* bottom right */ + calc(100% - 10px) 100%, /* bottom right */ + 10px 100%, /* bottom left */ + 0 100% /* bottom left */ + ); + background-image: linear-gradient(#d98ddf, #eec2e2, #af4db6); + color: #572c75; + border: #572c75; + padding: 8px; + /* position:relative; */ + } + + +html, +body { + height: 100%; +} +body { + background: #eee; + display: flex; + justify-content: center; + align-items: center; +} +.modal { + width: 600px; + background: rgb(0 0 0 / 50%); + border: 1px solid #ccc; + transition: 1.1s ease-out; + box-shadow: -2rem 2rem 2rem rgba(0, 0, 0, 0.2); + filter: blur(0); + transform: scale(1); + opacity: 1; + visibility: visible; + /* color: black; */ + position: absolute; + top: 50vh; + left: 50%; + transform: translate(-50%, -50%); +} +.modal.off { + opacity: 0; + visibility: hidden; + filter: blur(8px); + transform: scale(0.33); + box-shadow: 1rem 0 0 rgba(0, 0, 0, 0.2); +} +@supports (offset-rotation: 0deg) { + /* offset-rotation: 0deg; + offset-path: path("M 250,100 S -300,500 -700,-200"); */ + .modal.off { + offset-distance: 100%; + } +} +@media (prefers-reduced-motion) { + .modal { + offset-path: none; + } +} +/* .modal h2 { + border-bottom: 1px solid #ccc; + padding: 1rem; + margin: 0; +} */ +.modal .content { + padding: 1rem; +} +/* .modal .actions { + border-top: 1px solid #ccc; + background: #eee; + padding: 0.5rem 1rem; +} +.modal .actions button { + border: 0; + background: #78f89f; + border-radius: 5px; + padding: 0.5rem 1rem; + font-size: 0.8rem; + line-height: 1; +} */ +#centered-toggle-button { + position: absolute; +} \ No newline at end of file diff --git a/src/components/modal.js b/src/components/modal.js new file mode 100644 index 0000000000..f91b50bbd8 --- /dev/null +++ b/src/components/modal.js @@ -0,0 +1,37 @@ +import React from "react"; +import "./modal.css"; +import PropTypes from "prop-types"; + +export default class Modal extends React.Component { + onClose = e => { + this.props.onClose && this.props.onClose(e); + }; + render() { + if (!this.props.show) { + return null; + } + return ( + + ); + } +} +Modal.propTypes = { + onClose: PropTypes.func.isRequired, + show: PropTypes.bool.isRequired +}; \ No newline at end of file From 8240e522e226bf430b1871c3551000297a3f32ee Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Mon, 8 Nov 2021 12:43:34 +0500 Subject: [PATCH 12/35] modal css --- src/Header.module.css | 8 ++++---- src/User.jsx | 2 +- src/components/modal.css | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Header.module.css b/src/Header.module.css index cad37de6f3..5f475a5012 100644 --- a/src/Header.module.css +++ b/src/Header.module.css @@ -18,7 +18,7 @@ .specialBtnDiv2 { min-width: 200px; /* border: 1px solid red; */ - background-color: #9c27b0; + background-color: #ac0159; padding: 2px; clip-path: polygon( 0% 10px, /* top left */ @@ -46,9 +46,9 @@ 10px 100%, /* bottom left */ 0 100% /* bottom left */ ); - background-image: linear-gradient(#d98ddf, #eec2e2, #af4db6); - color: #572c75; - border: #572c75; + background-image: linear-gradient(#f77aaf, #c32052, #ac0159); + color: #fff; + border: #ac0159; padding: 8px; /* position:relative; */ } diff --git a/src/User.jsx b/src/User.jsx index 78458e91bf..40b1bfe408 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -173,7 +173,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { -
+
Log in and experience our emmersive 3D universe.
diff --git a/src/components/modal.css b/src/components/modal.css index b97ebff8e1..3420467267 100644 --- a/src/components/modal.css +++ b/src/components/modal.css @@ -19,7 +19,7 @@ .specialBtnDiv2 { width: 100px; /* border: 1px solid red; */ - background-color: #9c27b0; + background-color: #ac0159; padding: 2px; clip-path: polygon( 0% 10px, /* top left */ @@ -47,9 +47,9 @@ 10px 100%, /* bottom left */ 0 100% /* bottom left */ ); - background-image: linear-gradient(#d98ddf, #eec2e2, #af4db6); - color: #572c75; - border: #572c75; + background-image: linear-gradient(#f77aaf, #c32052, #ac0159); + color: #fff; + border: #ac0159; padding: 8px; /* position:relative; */ } From 3a37697219887a0d428971fcc4d42631e89ca36e Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Mon, 8 Nov 2021 12:59:15 +0500 Subject: [PATCH 13/35] modal css --- src/User.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/User.jsx b/src/User.jsx index 40b1bfe408..1800b03081 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -156,7 +156,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => {
{/*

Webaverse Logo

*/} -

Webaverse

+

Webaverse

@@ -173,7 +173,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => {
-
+
Log in and experience our emmersive 3D universe.
From 88c4ec85be0dce7a04fd76ef9edcf45107fbb990 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Mon, 8 Nov 2021 19:07:08 +0500 Subject: [PATCH 14/35] logo removed --- src/User.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/User.jsx b/src/User.jsx index 1800b03081..00b56702d4 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -155,10 +155,10 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => {
{/*

Webaverse Logo

*/} - -

Webaverse

+ {/* +

Webaverse

*/}
-
+
From 36bce7ff41c74b05c671213435f909e642983094 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Mon, 8 Nov 2021 23:52:51 +0500 Subject: [PATCH 15/35] logout added --- src/Header.module.css | 23 ++++++++++++++++++++--- src/User.jsx | 20 ++++++++++++++++++-- src/components/modal.css | 11 +++++++---- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/Header.module.css b/src/Header.module.css index 5f475a5012..c30c9e9480 100644 --- a/src/Header.module.css +++ b/src/Header.module.css @@ -1,7 +1,22 @@ +.logoutBtn { + position: absolute; + right: 0; + background: black; + color: white; + padding: 6px; + font-size: 14px; + cursor: pointer; +} + +.logoutBtn:hover { + background: #ef5350; + color: white; +} + .specialBtnDiv { /* min-width: 200px; */ /* border: 1px solid red; */ - background-color: #777777; + background-color: #000; padding: 5px; clip-path: polygon( 0% 10px, /* top left */ @@ -33,9 +48,10 @@ } .specialBtn { + cursor: pointer; width: -webkit-fill-available; - font-size: 24px; + font-size: 20px; clip-path: polygon( 0% 10px, /* top left */ 10px 0%, /* top left */ @@ -46,7 +62,8 @@ 10px 100%, /* bottom left */ 0 100% /* bottom left */ ); - background-image: linear-gradient(#f77aaf, #c32052, #ac0159); + background: #ac0159; + /* background-image: linear-gradient(#f77aaf, #c32052, #ac0159); */ color: #fff; border: #ac0159; padding: 8px; diff --git a/src/User.jsx b/src/User.jsx index 00b56702d4..6b322ec687 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -68,6 +68,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { // console.log('login', {address, profile}); setAddress(address); localStorage.setItem('loginToken', address); + setShow(false); userRef.setIsComponentVisible(false); } catch (err) { @@ -110,6 +111,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { console.log('address', address) setAddress(address); localStorage.setItem('loginToken', address); + setShow(false); } else { setLoginError(String(error).toLocaleUpperCase()); @@ -144,14 +146,27 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { } }}> -
{ showModal(e); }}>{loggingIn ? 'Logging in... ' : (address || (loginError || 'Log in'))} +
{ showModal(e); }}> + {loggingIn ? 'Logging in... ' : (address || (loginError || 'Log in'))} +
+ { address ? +
{ + localStorage.removeItem("loginToken"); + setAddress(null) + + }} + >Logout
+ : '' + } { userRef.isComponentVisible ?
{ loginButtons ? <> +
{/*

Webaverse Logo

*/} @@ -174,10 +189,11 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => {
- Log in and experience our emmersive 3D universe. + Log in and experience our immersive 3D universe.
+ : '' } {/* { diff --git a/src/components/modal.css b/src/components/modal.css index 3420467267..b7429ea597 100644 --- a/src/components/modal.css +++ b/src/components/modal.css @@ -2,7 +2,7 @@ .specialBtnDiv { /* min-width: 200px; */ /* border: 1px solid red; */ - background-color: #777777; + background-color: #000; padding: 5px; clip-path: polygon( 0% 10px, /* top left */ @@ -34,6 +34,7 @@ } .specialBtn { + cursor: pointer; width: -webkit-fill-available; font-size: 14px; @@ -47,7 +48,8 @@ 10px 100%, /* bottom left */ 0 100% /* bottom left */ ); - background-image: linear-gradient(#f77aaf, #c32052, #ac0159); + background: #ac0159; + /* background-image: linear-gradient(#f77aaf, #c32052, #ac0159); */ color: #fff; border: #ac0159; padding: 8px; @@ -67,8 +69,8 @@ body { } .modal { width: 600px; - background: rgb(0 0 0 / 50%); - border: 1px solid #ccc; + background: rgb(0 0 0 / 100%); + border: 1px solid #000; transition: 1.1s ease-out; box-shadow: -2rem 2rem 2rem rgba(0, 0, 0, 0.2); filter: blur(0); @@ -106,6 +108,7 @@ body { margin: 0; } */ .modal .content { + cursor: auto; padding: 1rem; } /* .modal .actions { From d39aa76242895d393df59fff1fbe2a4ee8acf5ca Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Tue, 9 Nov 2021 03:45:47 +0500 Subject: [PATCH 16/35] modal redone --- src/Header.module.css | 16 ++++++++++++++++ src/User.jsx | 29 ++++++++--------------------- src/components/modal.css | 10 ++++++---- src/components/modal.js | 4 ++-- 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/Header.module.css b/src/Header.module.css index c30c9e9480..b1bc580dac 100644 --- a/src/Header.module.css +++ b/src/Header.module.css @@ -1,3 +1,12 @@ +.loginBtn { + transform: skew(-20deg) translate(-10px, -10px); + background: rgb(0, 168, 252); + color: rgb(0, 0, 0); + padding: 0.5em; + width: 8em; + text-align: center; + cursor: pointer; +} .logoutBtn { position: absolute; right: 0; @@ -145,6 +154,13 @@ body { height: 100vh; overflow: hidden; } +.header .tab .panelsLogin { + position: fixed; + display: flex; + /* width: 400px; + height: 100vh; */ + overflow: hidden; +} .header .tab .panels .panel { display: flex; width: 100%; diff --git a/src/User.jsx b/src/User.jsx index 6b322ec687..6e9e5d3de7 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -31,7 +31,6 @@ function useComponentVisible(initialIsVisible, fn) { return {ref, isComponentVisible, setIsComponentVisible}; } - const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const [show, setShow] = useState(false); @@ -169,28 +168,16 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => {
- {/*

Webaverse Logo

*/} - {/* -

Webaverse

*/}
-
-
-
-
- -
-
- -
-
- Log in and experience our immersive 3D universe. +
+
+ MetaMask
+ +
+ Discord +
+
diff --git a/src/components/modal.css b/src/components/modal.css index b7429ea597..7748a31c69 100644 --- a/src/components/modal.css +++ b/src/components/modal.css @@ -68,7 +68,7 @@ body { align-items: center; } .modal { - width: 600px; + width: 300px; background: rgb(0 0 0 / 100%); border: 1px solid #000; transition: 1.1s ease-out; @@ -79,8 +79,10 @@ body { visibility: visible; /* color: black; */ position: absolute; - top: 50vh; - left: 50%; + /* top: 50vh; + left: 50%; */ + top: 150px; + right: -150px; transform: translate(-50%, -50%); } .modal.off { @@ -109,7 +111,7 @@ body { } */ .modal .content { cursor: auto; - padding: 1rem; + padding-top: 1rem; } /* .modal .actions { border-top: 1px solid #ccc; diff --git a/src/components/modal.js b/src/components/modal.js index f91b50bbd8..69839de9fb 100644 --- a/src/components/modal.js +++ b/src/components/modal.js @@ -15,11 +15,11 @@ export default class Modal extends React.Component { {/*

Modal Window

*/}
{this.props.children} -
+ {/*
-
+
*/} {/*
-
-
*/} {/*
- +
discord diff --git a/src/components/modal.js b/src/components/modal.js index deaf29bbee..da1b9d5243 100644 --- a/src/components/modal.js +++ b/src/components/modal.js @@ -1,6 +1,5 @@ import React from "react"; import "./modal.css"; -import PropTypes from "prop-types"; export default class Modal extends React.Component { onClose = e => { @@ -24,8 +23,4 @@ export default class Modal extends React.Component {
); } -} -Modal.propTypes = { - onClose: PropTypes.func.isRequired, - show: PropTypes.bool.isRequired -}; \ No newline at end of file +} \ No newline at end of file From ad743af0fc7976f9ac26801c8f1a44ee1260fa97 Mon Sep 17 00:00:00 2001 From: Muhammad Abeer Date: Tue, 16 Nov 2021 18:33:14 +0500 Subject: [PATCH 21/35] fixed tokenhost const issue --- util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.js b/util.js index 514d0d07e0..df732443d4 100644 --- a/util.js +++ b/util.js @@ -2,7 +2,7 @@ import * as THREE from 'three'; import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'; // import atlaspack from './atlaspack.js'; import { getAddressFromMnemonic } from './blockchain.js'; -import {playersMapName, maxGrabDistance, tokensHost, storageHost, tokensHost, accountsHost, loginEndpoint} from './constants.js'; +import {playersMapName, maxGrabDistance, tokensHost, storageHost, accountsHost, loginEndpoint} from './constants.js'; const localVector = new THREE.Vector3(); const localVector2 = new THREE.Vector3(); From 04a5feb75bc751e734bf6ee8bd8e8b9f99eac4ef Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Thu, 18 Nov 2021 00:56:01 +0500 Subject: [PATCH 22/35] secure storage system implemented --- package-lock.json | 33 +++++++++++ package.json | 4 +- secure-storage.ts | 141 ++++++++++++++++++++++++++++++++++++++++++++++ src/User.jsx | 17 ++++-- 4 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 secure-storage.ts diff --git a/package-lock.json b/package-lock.json index a512d601e2..68469cc73e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "classnames": "^2.3.1", "dids": "^2.4.0", "express": "^4.17.1", + "fast-sha256": "^1.3.0", "forever": "^4.0.1", "gpt-3-encoder": "^1.1.3", "json-6": "^1.1.4", @@ -34,6 +35,8 @@ "stats.js": "^0.17.0", "three": "^0.133.1", "troika-three-text": "^0.43.1-alpha.0", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1", "wsrtc": "./packages/wsrtc", "xhr2": "^0.2.1", "yjs": "^13.5.16" @@ -3525,6 +3528,11 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -7064,6 +7072,16 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -10331,6 +10349,11 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==" + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -13055,6 +13078,16 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 886a30644c..aaaaf7ea66 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "classnames": "^2.3.1", "dids": "^2.4.0", "express": "^4.17.1", + "fast-sha256": "^1.3.0", "forever": "^4.0.1", "gpt-3-encoder": "^1.1.3", "json-6": "^1.1.4", @@ -37,7 +38,8 @@ "stats.js": "^0.17.0", "three": "^0.133.1", "troika-three-text": "^0.43.1-alpha.0", - "webm-writer": "^1.0.0", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1", "wsrtc": "./packages/wsrtc", "xhr2": "^0.2.1", "yjs": "^13.5.16" diff --git a/secure-storage.ts b/secure-storage.ts new file mode 100644 index 0000000000..24b11ddd09 --- /dev/null +++ b/secure-storage.ts @@ -0,0 +1,141 @@ +import * as sha256 from 'fast-sha256' +import nacl from 'tweetnacl' +import naclUtil from 'tweetnacl-util' + +export interface KeyStore { + getKeyIDs (): string[] + getPublicKeyData (keyID: string): PublicKeyData + getPrivateKeyData (keyID: string, password: string): PrivateKeyData + getRawKeyData (keyID: string): RawKeyData + saveKey (keyID: string, password: string, privateData: PrivateKeyData, publicData?: PublicKeyData): Promise + saveKeys (data: {keyID: string, password: string, privateData: PrivateKeyData, publicData?: PublicKeyData}[]): Promise + savePublicKeyData (keyID: string, publicData: PublicKeyData): Promise + removeKey (keyID: string): Promise +} + +interface KeyMetadata { + nonce: string, + iterations: number +} + +export interface RawKeyData { + metadata: KeyMetadata, + public: PublicKeyData, + private: string +} + +export interface KeysData { + [keyID: string]: RawKeyData +} + +export type SaveKeys = (data: KeysData) => Promise | void + +function randomNonce () { + return naclUtil.encodeBase64(nacl.randomBytes(nacl.secretbox.nonceLength)) +} + +function deriveHashFromPassword (password: string, metadata: KeyMetadata) { + return sha256.pbkdf2( + naclUtil.decodeUTF8(password), + naclUtil.decodeBase64(metadata.nonce), + metadata.iterations, + nacl.secretbox.keyLength + ) +} + +function decrypt (encryptedBase64: string, metadata: KeyMetadata, password: string) { + const secretKey = deriveHashFromPassword(password, metadata) + const decrypted = nacl.secretbox.open(naclUtil.decodeBase64(encryptedBase64), naclUtil.decodeBase64(metadata.nonce), secretKey) + + if (!decrypted) { + throw new Error('Decryption failed.') + } + return JSON.parse(naclUtil.encodeUTF8(decrypted)) +} + +function encrypt (privateData: any, metadata: KeyMetadata, password: string): string { + const secretKey = deriveHashFromPassword(password, metadata) + const data = naclUtil.decodeUTF8(JSON.stringify(privateData)) + const encrypted = nacl.secretbox(data, naclUtil.decodeBase64(metadata.nonce), secretKey) + return naclUtil.encodeBase64(encrypted) +} + +export function createStore ( + // save: SaveKeys, + initialKeys: KeysData = {}, + options: { iterations?: number } = {} +): KeyStore { + const { iterations = 10000 } = options + let keysData = initialKeys + + function save (keysData: any): any { + localStorage.setItem('key-store', JSON.stringify(keysData)); + } + + function getLocal () { + if(Object.keys(keysData).length === 0) { + keysData = JSON.parse(localStorage.getItem('key-store')); + } + } + + function saveKey (keyID: string, password: string, privateData: PrivateKeyData, publicData: PublicKeyData | {} = {}): void { + // Important: Do not re-use previous metadata! + // Use a fresh nonce. Also the previous metadata might have been forged. + const metadata = { + nonce: randomNonce(), + iterations + } + keysData[keyID] = { + metadata, + public: publicData as any, + private: encrypt(privateData, metadata, password) + } + } + + return { + getKeyIDs () { + getLocal(); + return Object.keys(keysData) + }, + getPublicKeyData (keyID: string) { + getLocal(); + return keysData[keyID].public + }, + getRawKeyData (keyID: string) { + getLocal(); + return keysData[keyID] + }, + getPrivateKeyData (keyID: string, password: string) { + getLocal(); + if(keysData[keyID]) { + return decrypt(keysData[keyID].private, keysData[keyID].metadata, password) as PrivateKeyData + } + }, + async saveKey (keyID: string, password: string, privateData: PrivateKeyData, publicData: PublicKeyData | {} = {}) { + saveKey(keyID, password, privateData, publicData) + await save(keysData); + }, + async saveKeys (data: {keyID: string, password: string, privateData: PrivateKeyData, publicData: PublicKeyData}[]) { + data.forEach(d => saveKey(d.keyID, d.password, d.privateData, d.publicData)) + await save(keysData) + }, + async savePublicKeyData (keyID: string, publicData: PublicKeyData) { + if (!keysData[keyID]) { + // Prevent creating an incomplete key record + throw new Error(`Cannot save public data for key ${keyID}. Key does not yet exist in store.`) + } + keysData[keyID] = { + ...keysData[keyID], + public: publicData + } + await save(keysData) + }, + async removeKey (keyID: string) { + if (!keysData[keyID]) { + throw new Error(`Cannot delete key ${keyID}. Key not found.`) + } + delete keysData[keyID] + await save(keysData) + } + } +} \ No newline at end of file diff --git a/src/User.jsx b/src/User.jsx index 8ff17c595f..051f265732 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -8,6 +8,9 @@ import {storageHost, accountsHost, tokensHost, loginEndpoint, discordClientId} f import {contracts, getAddressFromMnemonic} from '../blockchain.js'; import {jsonParse, parseQuery, handleDiscordLogin} from '../util.js'; import Modal from "./components/modal"; +import { createStore } from '../secure-storage.ts'; + +const store = createStore(); function useComponentVisible(initialIsVisible, fn) { const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible); @@ -64,7 +67,8 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const {address, profile} = await ceramicApi.login(); // console.log('login', {address, profile}); setAddress(address); - localStorage.setItem('loginToken', address); + store.saveKey('login-token', 'webaverse', { loginToken: address }) + setShow(false); userRef.setIsComponentVisible(false); @@ -86,10 +90,11 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { useEffect(async () => { - const storedLoginToken = localStorage.getItem("loginToken"); + let loginTokenStored = store.getPrivateKeyData('login-token', 'webaverse'); + const loginToken = loginTokenStored ? loginTokenStored.loginToken : null; - if(storedLoginToken) { - setAddress(storedLoginToken); + if(loginToken) { + setAddress(loginToken); } else { const { @@ -106,7 +111,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { if(address) { console.log('address', address) setAddress(address); - localStorage.setItem('loginToken', address); + store.saveKey('login-token', 'webaverse', { loginToken: address }) setShow(false); } else { @@ -143,7 +148,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { { address ?
{ - localStorage.removeItem("loginToken"); + store.removeKey('login-token'); setAddress(null) }} From d801b2058ead2f55e92e19677df56215b4863c51 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Mon, 22 Nov 2021 23:15:39 +0500 Subject: [PATCH 23/35] wallet integrated --- constants.js | 1 + package.json | 3 - secure-storage.ts | 141 ---------------------------------------------- src/User.jsx | 139 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 120 insertions(+), 164 deletions(-) delete mode 100644 secure-storage.ts diff --git a/constants.js b/constants.js index 1ec38fda31..74cdfe2cdd 100644 --- a/constants.js +++ b/constants.js @@ -44,6 +44,7 @@ export const web3MainnetSidechainEndpoint = 'https://mainnetsidechain.exokit.org export const web3TestnetSidechainEndpoint = 'https://testnetsidechain.exokit.org'; export const worldUrl = 'worlds.webaverse.com'; export const discordClientId = '684141574808272937'; +export const walletHost = 'secure.webaverse.com'; export const worldMapName = 'world'; export const actionsMapName = 'actions'; diff --git a/package.json b/package.json index aaaaf7ea66..69ce979db0 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "classnames": "^2.3.1", "dids": "^2.4.0", "express": "^4.17.1", - "fast-sha256": "^1.3.0", "forever": "^4.0.1", "gpt-3-encoder": "^1.1.3", "json-6": "^1.1.4", @@ -38,8 +37,6 @@ "stats.js": "^0.17.0", "three": "^0.133.1", "troika-three-text": "^0.43.1-alpha.0", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1", "wsrtc": "./packages/wsrtc", "xhr2": "^0.2.1", "yjs": "^13.5.16" diff --git a/secure-storage.ts b/secure-storage.ts deleted file mode 100644 index 24b11ddd09..0000000000 --- a/secure-storage.ts +++ /dev/null @@ -1,141 +0,0 @@ -import * as sha256 from 'fast-sha256' -import nacl from 'tweetnacl' -import naclUtil from 'tweetnacl-util' - -export interface KeyStore { - getKeyIDs (): string[] - getPublicKeyData (keyID: string): PublicKeyData - getPrivateKeyData (keyID: string, password: string): PrivateKeyData - getRawKeyData (keyID: string): RawKeyData - saveKey (keyID: string, password: string, privateData: PrivateKeyData, publicData?: PublicKeyData): Promise - saveKeys (data: {keyID: string, password: string, privateData: PrivateKeyData, publicData?: PublicKeyData}[]): Promise - savePublicKeyData (keyID: string, publicData: PublicKeyData): Promise - removeKey (keyID: string): Promise -} - -interface KeyMetadata { - nonce: string, - iterations: number -} - -export interface RawKeyData { - metadata: KeyMetadata, - public: PublicKeyData, - private: string -} - -export interface KeysData { - [keyID: string]: RawKeyData -} - -export type SaveKeys = (data: KeysData) => Promise | void - -function randomNonce () { - return naclUtil.encodeBase64(nacl.randomBytes(nacl.secretbox.nonceLength)) -} - -function deriveHashFromPassword (password: string, metadata: KeyMetadata) { - return sha256.pbkdf2( - naclUtil.decodeUTF8(password), - naclUtil.decodeBase64(metadata.nonce), - metadata.iterations, - nacl.secretbox.keyLength - ) -} - -function decrypt (encryptedBase64: string, metadata: KeyMetadata, password: string) { - const secretKey = deriveHashFromPassword(password, metadata) - const decrypted = nacl.secretbox.open(naclUtil.decodeBase64(encryptedBase64), naclUtil.decodeBase64(metadata.nonce), secretKey) - - if (!decrypted) { - throw new Error('Decryption failed.') - } - return JSON.parse(naclUtil.encodeUTF8(decrypted)) -} - -function encrypt (privateData: any, metadata: KeyMetadata, password: string): string { - const secretKey = deriveHashFromPassword(password, metadata) - const data = naclUtil.decodeUTF8(JSON.stringify(privateData)) - const encrypted = nacl.secretbox(data, naclUtil.decodeBase64(metadata.nonce), secretKey) - return naclUtil.encodeBase64(encrypted) -} - -export function createStore ( - // save: SaveKeys, - initialKeys: KeysData = {}, - options: { iterations?: number } = {} -): KeyStore { - const { iterations = 10000 } = options - let keysData = initialKeys - - function save (keysData: any): any { - localStorage.setItem('key-store', JSON.stringify(keysData)); - } - - function getLocal () { - if(Object.keys(keysData).length === 0) { - keysData = JSON.parse(localStorage.getItem('key-store')); - } - } - - function saveKey (keyID: string, password: string, privateData: PrivateKeyData, publicData: PublicKeyData | {} = {}): void { - // Important: Do not re-use previous metadata! - // Use a fresh nonce. Also the previous metadata might have been forged. - const metadata = { - nonce: randomNonce(), - iterations - } - keysData[keyID] = { - metadata, - public: publicData as any, - private: encrypt(privateData, metadata, password) - } - } - - return { - getKeyIDs () { - getLocal(); - return Object.keys(keysData) - }, - getPublicKeyData (keyID: string) { - getLocal(); - return keysData[keyID].public - }, - getRawKeyData (keyID: string) { - getLocal(); - return keysData[keyID] - }, - getPrivateKeyData (keyID: string, password: string) { - getLocal(); - if(keysData[keyID]) { - return decrypt(keysData[keyID].private, keysData[keyID].metadata, password) as PrivateKeyData - } - }, - async saveKey (keyID: string, password: string, privateData: PrivateKeyData, publicData: PublicKeyData | {} = {}) { - saveKey(keyID, password, privateData, publicData) - await save(keysData); - }, - async saveKeys (data: {keyID: string, password: string, privateData: PrivateKeyData, publicData: PublicKeyData}[]) { - data.forEach(d => saveKey(d.keyID, d.password, d.privateData, d.publicData)) - await save(keysData) - }, - async savePublicKeyData (keyID: string, publicData: PublicKeyData) { - if (!keysData[keyID]) { - // Prevent creating an incomplete key record - throw new Error(`Cannot save public data for key ${keyID}. Key does not yet exist in store.`) - } - keysData[keyID] = { - ...keysData[keyID], - public: publicData - } - await save(keysData) - }, - async removeKey (keyID: string) { - if (!keysData[keyID]) { - throw new Error(`Cannot delete key ${keyID}. Key not found.`) - } - delete keysData[keyID] - await save(keysData) - } - } -} \ No newline at end of file diff --git a/src/User.jsx b/src/User.jsx index 051f265732..7a3a8ad38d 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -4,13 +4,10 @@ import classnames from 'classnames'; import styles from './Header.module.css'; import * as ceramicApi from '../ceramic.js'; // import styles from './User.module.css'; -import {storageHost, accountsHost, tokensHost, loginEndpoint, discordClientId} from '../constants'; +import {storageHost, accountsHost, tokensHost, loginEndpoint, discordClientId, walletHost} from '../constants'; import {contracts, getAddressFromMnemonic} from '../blockchain.js'; import {jsonParse, parseQuery, handleDiscordLogin} from '../util.js'; import Modal from "./components/modal"; -import { createStore } from '../secure-storage.ts'; - -const store = createStore(); function useComponentVisible(initialIsVisible, fn) { const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible); @@ -47,6 +44,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const [loggingIn, setLoggingIn] = useState(false); const [loginButtons, setLoginButtons] = useState(false); + const [walletData, setWalletData] = useState([]); const [discordUrl, setDiscordUrl] = useState(''); const [loginError, setLoginError] = useState(null); @@ -55,9 +53,120 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { setDiscordUrl(''); }); + useEffect(() => { + if(address && sessionStorage.getItem('temp_token')) { + fetchWalletData(); + } + }); + + var popUp = null; + var iframe = null; + var walletMessenger = null; + + const openPopup = async (token) => { + popUp = window.open(walletHost, '', "height=800,width=600"); + + window.addEventListener("message", (event) => { + if (event.origin !== walletHost) + return; + + if(event.data == 'received') { + sendMessage(token); + } + }, false); + } + + const fetchWalletData = async () => { + iframe = window.open(walletHost, 'wallet') + + window.addEventListener("message", (event) => { + if (event.origin !== walletHost) + return; + + if(event.data == 'received') { + getKeys(); + } + }, false); + } + + // function for sending data to wallet + const sendDataToWallet = async (data) => { + walletMessenger = window.open(walletHost, 'walletMessenger') + + window.addEventListener("message", (event) => { + if (event.origin !== walletHost) + return; + + if(event.data == 'received') { + sendData(data); + } + }, false); + } + + const sendData = async (data) => { + + // data format + // {'key': 'key-name', 'value': 'any-value'} + + var message = JSON.stringify({'temp_token': sessionStorage.getItem('temp_token'), 'token': address, + 'data': data}); + walletMessenger.postMessage(message, walletHost); + + window.addEventListener("message", (event) => { + if (event.origin !== walletHost) + return; + + if(event.data.temp_token) { + sessionStorage.setItem("temp_token", event.data.temp_token); + } + window.removeEventListener('message', ()=>{}); + walletMessenger.close(); + + }, false); + } + + const getKeys = async () => { + + iframe.postMessage(JSON.stringify({'temp_token': sessionStorage.getItem('temp_token'), 'token': address}), walletHost); + + window.addEventListener("message", (event) => { + if (event.origin !== walletHost) + return; + + setWalletData(event.data.data) + + if(event.data.temp_token) { + sessionStorage.setItem("temp_token", event.data.temp_token); + } + window.removeEventListener('message', ()=>{}); + iframe.close(); + + }, false); + } + + const sendMessage = async (token) => { + + popUp.postMessage(JSON.stringify({'token': token}), walletHost); + + window.addEventListener("message", (event) => { + if (event.origin !== walletHost) + return; + + setWalletData(event.data.data); + + if(event.data.temp_token) { + sessionStorage.setItem("temp_token", event.data.temp_token); + } + + window.removeEventListener('message', ()=>{}); + popUp.close(); + + }, false); + } + const metaMaskLogin = async e => { e.preventDefault(); - e.stopPropagation(); + e.stopPropagation(); if (address) { toggleOpen('user'); } else { @@ -65,10 +174,8 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { setLoggingIn(true); try { const {address, profile} = await ceramicApi.login(); - // console.log('login', {address, profile}); setAddress(address); - store.saveKey('login-token', 'webaverse', { loginToken: address }) - + openPopup(address); setShow(false); userRef.setIsComponentVisible(false); @@ -90,13 +197,6 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { useEffect(async () => { - let loginTokenStored = store.getPrivateKeyData('login-token', 'webaverse'); - const loginToken = loginTokenStored ? loginTokenStored.loginToken : null; - - if(loginToken) { - setAddress(loginToken); - } - else { const { error, error_description, @@ -109,22 +209,22 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { if(code) { const {address, error} = await handleDiscordLogin(code, id); if(address) { - console.log('address', address) setAddress(address); - store.saveKey('login-token', 'webaverse', { loginToken: address }) + openPopup(address); setShow(false); } else { setLoginError(String(error).toLocaleUpperCase()); } } - } }, [address, setAddress]); return (
+ +
{ e.preventDefault(); @@ -148,9 +248,8 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { { address ?
{ - store.removeKey('login-token'); + sessionStorage.removeItem('temp_token'); setAddress(null) - }} >Logout
: '' From 6138deaf270f57183927e640ab56790432aaba57 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Fri, 26 Nov 2021 03:44:22 +0500 Subject: [PATCH 24/35] functions updated --- constants.js | 3 +- index.mjs | 3 +- scenes/street.scn | 217 +--------------------------------------------- src/User.jsx | 13 +-- 4 files changed, 9 insertions(+), 227 deletions(-) diff --git a/constants.js b/constants.js index 74cdfe2cdd..b72eb93a5f 100644 --- a/constants.js +++ b/constants.js @@ -44,7 +44,8 @@ export const web3MainnetSidechainEndpoint = 'https://mainnetsidechain.exokit.org export const web3TestnetSidechainEndpoint = 'https://testnetsidechain.exokit.org'; export const worldUrl = 'worlds.webaverse.com'; export const discordClientId = '684141574808272937'; -export const walletHost = 'secure.webaverse.com'; +// export const walletHost = 'secure.webaverse.com'; +export const walletHost = 'http://localhost:3002'; export const worldMapName = 'world'; export const actionsMapName = 'actions'; diff --git a/index.mjs b/index.mjs index 6e6acc0736..27e9a7f789 100644 --- a/index.mjs +++ b/index.mjs @@ -60,7 +60,8 @@ function makeId(length) { } }); - const isHttps = !!certs.key && !!certs.cert; + // const isHttps = !!certs.key && !!certs.cert; + const isHttps = false; const port = parseInt(process.env.PORT, 10) || (isHttps ? 443 : 3000); const httpServer = (() => { if (isHttps) { diff --git a/scenes/street.scn b/scenes/street.scn index 71191fddf9..e9106f222a 100644 --- a/scenes/street.scn +++ b/scenes/street.scn @@ -1,218 +1,3 @@ { - "objects": [ - { - "type": "application/light", - "content": { - "lightType": "ambient", - "args": [[255, 255, 255], 2] - } - }, - { - "type": "application/light", - "content": { - "lightType": "directional", - "args": [[255, 255, 255], 2], - "position": [1, 2, 3] - } - }, - { - "position": [ - 0, - 0, - 0 - ], - "start_url": "https://webaverse.github.io/street/" - }, - { - "position": [ - -20, - 30, - -30 - ], - "start_url": "https://webaverse.github.io/floating-island/" - }, - { - "position": [ - 0, - 0, - 0 - ], - "start_url": "https://webaverse.github.io/skybox/" - }, - { - "position": [ - 0, - 0, - -2 - ], - "start_url": "https://webaverse.github.io/mirror/" - }, - { - "position": [ - -10, - 0, - -30 - ], - "start_url": "https://avaer.github.io/shield/index.js" - }, - { - "position": [ - 4, - 0, - 1 - ], - "quaternion": [ - 0, - 0.7071067811865475, - 0, - 0.7071067811865475 - ], - "start_url": "https://avatar-models.exokit.org/model49.vrm", - "dynamic": true - }, - { - "position": [ - -26, - 0, - -2 - ], - "quaternion": [ - 0, - 0.7071067811865475, - 0, - 0.7071067811865475 - ], - "start_url": "https://webaverse.github.io/app/public/avatars/scillia.vrm", - "dynamic": true - }, - { - "position": [ - 2, - 1, - 0 - ], - "quaternion": [ - 0, - -0.7071067811865475, - 0, - 0.7071067811865475 - ], - "scale": [ - 2, - 2, - 2 - ], - "physics": false, - "start_url": "https://avaer.github.io/sakura/index.glbb", - "dynamic": true - }, - { - "position": [ - 2, - 1, - -6 - ], - "quaternion": [ - 0, - -0.7071067811865475, - 0, - 0.7071067811865475 - ], - "physics": false, - "start_url": "https://avaer.github.io/rainbow-dash/", - "dynamic": true - }, - { - "position": [ - -13, - 0, - 0 - ], - "quaternion": [ - 0, - 0.7071067811865475, - 0, - 0.7071067811865475 - ], - "start_url": "https://webaverse.github.io/homespace/.metaversefile" - }, - { - "position": [ - 8, - -2, - -5 - ], - "quaternion": [ - 0, - -0.7071067811865475, - 0, - 0.7071067811865475 - ], - "start_url": "https://webaverse.github.io/street-assets/station.glb", - "physics_url": "https://webaverse.github.io/street-assets/station.bin" - }, - { - "position": [ - 0, - 0, - 0 - ], - "quaternion": [ - 0, - 0, - 0, - 1 - ], - "start_url": "https://webaverse.github.io/stacks/index.js" - }, - { - "position": [ - 0, - 0, - 0 - ], - "quaternion": [ - 0, - 0, - 0, - 1 - ], - "start_url": "https://webaverse.github.io/parcels/index.js" - }, - { - "position": [-9, 0, -1], - "quaternion": [0, 0.7071067811865475, 0, 0.7071067811865476], - "start_url": "https://webaverse.github.io/chest/" - }, - { - "position": [-2, 1, -1], - "quaternion": [0, 0, 0, 1], - "start_url": "https://avaer.github.io/poster/.metaversefile" - }, - { - "position": [-1, 1, -1], - "quaternion": [0, 0, 0, 1], - "start_url": "https://webaverse.github.io/arrow-loader/" - }, - { - "position": [-5, 4, -6], - "quaternion": [ - 0, - 0.7071067811865475, - 0, - 0.7071067811865475 - ], - "start_url": "https://avaer.github.io/physicscube/" - }, - { - "position": [-2, 1, 1], - "quaternion": [0, 0.7071067811865475, 0, 0.7071067811865476], - "start_url": "https://webaverse.github.io/sword/" - }, - { - "position": [-2, 1, 2], - "quaternion": [0, 0.7071067811865475, 0, 0.7071067811865476], - "start_url": "https://webaverse.github.io/helm/" - } - ] + "objects": [] } diff --git a/src/User.jsx b/src/User.jsx index 7a3a8ad38d..dba81583bf 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -82,7 +82,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { window.addEventListener("message", (event) => { if (event.origin !== walletHost) return; - + if(event.data == 'received') { getKeys(); } @@ -107,7 +107,6 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { // data format // {'key': 'key-name', 'value': 'any-value'} - var message = JSON.stringify({'temp_token': sessionStorage.getItem('temp_token'), 'token': address, 'data': data}); walletMessenger.postMessage(message, walletHost); @@ -116,9 +115,8 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { if (event.origin !== walletHost) return; - if(event.data.temp_token) { - sessionStorage.setItem("temp_token", event.data.temp_token); - } + setWalletData(event.data.data); + window.removeEventListener('message', ()=>{}); walletMessenger.close(); @@ -133,11 +131,8 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { if (event.origin !== walletHost) return; - setWalletData(event.data.data) + setWalletData(event.data.data); - if(event.data.temp_token) { - sessionStorage.setItem("temp_token", event.data.temp_token); - } window.removeEventListener('message', ()=>{}); iframe.close(); From ccbd7deb801a7103e8cfbd9b56d0fbcb7ec4d7f0 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Fri, 26 Nov 2021 04:12:14 +0500 Subject: [PATCH 25/35] wallet integrated --- constants.js | 3 +- index.mjs | 3 +- scenes/street.scn | 217 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 218 insertions(+), 5 deletions(-) diff --git a/constants.js b/constants.js index b72eb93a5f..74cdfe2cdd 100644 --- a/constants.js +++ b/constants.js @@ -44,8 +44,7 @@ export const web3MainnetSidechainEndpoint = 'https://mainnetsidechain.exokit.org export const web3TestnetSidechainEndpoint = 'https://testnetsidechain.exokit.org'; export const worldUrl = 'worlds.webaverse.com'; export const discordClientId = '684141574808272937'; -// export const walletHost = 'secure.webaverse.com'; -export const walletHost = 'http://localhost:3002'; +export const walletHost = 'secure.webaverse.com'; export const worldMapName = 'world'; export const actionsMapName = 'actions'; diff --git a/index.mjs b/index.mjs index 27e9a7f789..6e6acc0736 100644 --- a/index.mjs +++ b/index.mjs @@ -60,8 +60,7 @@ function makeId(length) { } }); - // const isHttps = !!certs.key && !!certs.cert; - const isHttps = false; + const isHttps = !!certs.key && !!certs.cert; const port = parseInt(process.env.PORT, 10) || (isHttps ? 443 : 3000); const httpServer = (() => { if (isHttps) { diff --git a/scenes/street.scn b/scenes/street.scn index e9106f222a..71191fddf9 100644 --- a/scenes/street.scn +++ b/scenes/street.scn @@ -1,3 +1,218 @@ { - "objects": [] + "objects": [ + { + "type": "application/light", + "content": { + "lightType": "ambient", + "args": [[255, 255, 255], 2] + } + }, + { + "type": "application/light", + "content": { + "lightType": "directional", + "args": [[255, 255, 255], 2], + "position": [1, 2, 3] + } + }, + { + "position": [ + 0, + 0, + 0 + ], + "start_url": "https://webaverse.github.io/street/" + }, + { + "position": [ + -20, + 30, + -30 + ], + "start_url": "https://webaverse.github.io/floating-island/" + }, + { + "position": [ + 0, + 0, + 0 + ], + "start_url": "https://webaverse.github.io/skybox/" + }, + { + "position": [ + 0, + 0, + -2 + ], + "start_url": "https://webaverse.github.io/mirror/" + }, + { + "position": [ + -10, + 0, + -30 + ], + "start_url": "https://avaer.github.io/shield/index.js" + }, + { + "position": [ + 4, + 0, + 1 + ], + "quaternion": [ + 0, + 0.7071067811865475, + 0, + 0.7071067811865475 + ], + "start_url": "https://avatar-models.exokit.org/model49.vrm", + "dynamic": true + }, + { + "position": [ + -26, + 0, + -2 + ], + "quaternion": [ + 0, + 0.7071067811865475, + 0, + 0.7071067811865475 + ], + "start_url": "https://webaverse.github.io/app/public/avatars/scillia.vrm", + "dynamic": true + }, + { + "position": [ + 2, + 1, + 0 + ], + "quaternion": [ + 0, + -0.7071067811865475, + 0, + 0.7071067811865475 + ], + "scale": [ + 2, + 2, + 2 + ], + "physics": false, + "start_url": "https://avaer.github.io/sakura/index.glbb", + "dynamic": true + }, + { + "position": [ + 2, + 1, + -6 + ], + "quaternion": [ + 0, + -0.7071067811865475, + 0, + 0.7071067811865475 + ], + "physics": false, + "start_url": "https://avaer.github.io/rainbow-dash/", + "dynamic": true + }, + { + "position": [ + -13, + 0, + 0 + ], + "quaternion": [ + 0, + 0.7071067811865475, + 0, + 0.7071067811865475 + ], + "start_url": "https://webaverse.github.io/homespace/.metaversefile" + }, + { + "position": [ + 8, + -2, + -5 + ], + "quaternion": [ + 0, + -0.7071067811865475, + 0, + 0.7071067811865475 + ], + "start_url": "https://webaverse.github.io/street-assets/station.glb", + "physics_url": "https://webaverse.github.io/street-assets/station.bin" + }, + { + "position": [ + 0, + 0, + 0 + ], + "quaternion": [ + 0, + 0, + 0, + 1 + ], + "start_url": "https://webaverse.github.io/stacks/index.js" + }, + { + "position": [ + 0, + 0, + 0 + ], + "quaternion": [ + 0, + 0, + 0, + 1 + ], + "start_url": "https://webaverse.github.io/parcels/index.js" + }, + { + "position": [-9, 0, -1], + "quaternion": [0, 0.7071067811865475, 0, 0.7071067811865476], + "start_url": "https://webaverse.github.io/chest/" + }, + { + "position": [-2, 1, -1], + "quaternion": [0, 0, 0, 1], + "start_url": "https://avaer.github.io/poster/.metaversefile" + }, + { + "position": [-1, 1, -1], + "quaternion": [0, 0, 0, 1], + "start_url": "https://webaverse.github.io/arrow-loader/" + }, + { + "position": [-5, 4, -6], + "quaternion": [ + 0, + 0.7071067811865475, + 0, + 0.7071067811865475 + ], + "start_url": "https://avaer.github.io/physicscube/" + }, + { + "position": [-2, 1, 1], + "quaternion": [0, 0.7071067811865475, 0, 0.7071067811865476], + "start_url": "https://webaverse.github.io/sword/" + }, + { + "position": [-2, 1, 2], + "quaternion": [0, 0.7071067811865475, 0, 0.7071067811865476], + "start_url": "https://webaverse.github.io/helm/" + } + ] } From 4f3eae44d6fe5c4cdaa5ea462ff4167e64afa5e4 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Sun, 28 Nov 2021 07:31:59 +0500 Subject: [PATCH 26/35] temp_token replaced with mnemonic --- src/User.jsx | 90 +++++++++++++++++++++++----------------------------- util.js | 3 +- 2 files changed, 42 insertions(+), 51 deletions(-) diff --git a/src/User.jsx b/src/User.jsx index dba81583bf..b1bb7a4357 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -53,17 +53,11 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { setDiscordUrl(''); }); - useEffect(() => { - if(address && sessionStorage.getItem('temp_token')) { - fetchWalletData(); - } - }); - var popUp = null; var iframe = null; var walletMessenger = null; - const openPopup = async (token) => { + const openPopup = async (mnemonic) => { popUp = window.open(walletHost, '', "height=800,width=600"); window.addEventListener("message", (event) => { @@ -71,77 +65,78 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { return; if(event.data == 'received') { - sendMessage(token); + sendMessage(mnemonic); } }, false); } - const fetchWalletData = async () => { - iframe = window.open(walletHost, 'wallet') + const sendMessage = async (mnemonic) => { + + popUp.postMessage(JSON.stringify({'p_mnemonic': mnemonic}), walletHost); window.addEventListener("message", (event) => { if (event.origin !== walletHost) return; - - if(event.data == 'received') { - getKeys(); + + if(event.data.mnemonic) { + sessionStorage.setItem("mnemonic", event.data.mnemonic); + window.removeEventListener('message', ()=>{}); + popUp.close(); } + }, false); } - // function for sending data to wallet - const sendDataToWallet = async (data) => { - walletMessenger = window.open(walletHost, 'walletMessenger') + const fetchWalletData = async (mnemonic) => { + iframe = window.open(walletHost, 'wallet') window.addEventListener("message", (event) => { if (event.origin !== walletHost) return; - + if(event.data == 'received') { - sendData(data); + getKeys(mnemonic); } - }, false); + }, true); } - const sendData = async (data) => { - - // data format - // {'key': 'key-name', 'value': 'any-value'} - var message = JSON.stringify({'temp_token': sessionStorage.getItem('temp_token'), 'token': address, - 'data': data}); - walletMessenger.postMessage(message, walletHost); + const getKeys = async (mnemonic) => { + iframe.postMessage(JSON.stringify({'mnemonic': mnemonic}), walletHost); window.addEventListener("message", (event) => { if (event.origin !== walletHost) return; - setWalletData(event.data.data); + if(event.data.Message == "Password" && popUp == null) { + openPopup(event.data.mnemonic) + } + else if(event.data.data) { + setWalletData(event.data.data); + } window.removeEventListener('message', ()=>{}); - walletMessenger.close(); + iframe.close(); - }, false); + }, true); } - const getKeys = async () => { - - iframe.postMessage(JSON.stringify({'temp_token': sessionStorage.getItem('temp_token'), 'token': address}), walletHost); + // function for sending data to wallet + const sendDataToWallet = async (data) => { + walletMessenger = window.open(walletHost, 'walletMessenger') window.addEventListener("message", (event) => { if (event.origin !== walletHost) return; - setWalletData(event.data.data); - - window.removeEventListener('message', ()=>{}); - iframe.close(); - + if(event.data == 'received') { + sendData(data); + } }, false); } - const sendMessage = async (token) => { - - popUp.postMessage(JSON.stringify({'token': token}), walletHost); + const sendData = async (data) => { + var message = JSON.stringify({'mnemonic': sessionStorage.getItem('mnemonic'), 'data': data}); + walletMessenger.postMessage(message, walletHost); window.addEventListener("message", (event) => { if (event.origin !== walletHost) @@ -149,12 +144,8 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { setWalletData(event.data.data); - if(event.data.temp_token) { - sessionStorage.setItem("temp_token", event.data.temp_token); - } - window.removeEventListener('message', ()=>{}); - popUp.close(); + walletMessenger.close(); }, false); } @@ -170,7 +161,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { try { const {address, profile} = await ceramicApi.login(); setAddress(address); - openPopup(address); + // fetchWalletData(mnemonic); setShow(false); userRef.setIsComponentVisible(false); @@ -202,10 +193,10 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { twitter: arrivingFromTwitter, } = typeof window !== 'undefined' ? parseQuery(window.location.search) : {}; if(code) { - const {address, error} = await handleDiscordLogin(code, id); + const {address, error, mnemonic} = await handleDiscordLogin(code, id); if(address) { setAddress(address); - openPopup(address); + fetchWalletData(mnemonic); setShow(false); } else { @@ -215,7 +206,6 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { }, [address, setAddress]); - return (
@@ -243,7 +233,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { { address ?
{ - sessionStorage.removeItem('temp_token'); + sessionStorage.removeItem('mnemonic'); setAddress(null) }} >Logout
diff --git a/util.js b/util.js index df732443d4..195a54217f 100644 --- a/util.js +++ b/util.js @@ -622,7 +622,8 @@ async function contentIdToStorageUrl(id) { async function pullUserObject(loginToken) { const address = getAddressFromMnemonic(loginToken.mnemonic); const res = await fetch(`${accountsHost}/${address}`); - const result = await res.json(); + var result = await res.json(); + result.mnemonic = loginToken.mnemonic; return result; } From 7c7453e7a05aaedeefa8987ec667443d5ad4746a Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Mon, 29 Nov 2021 01:30:20 +0500 Subject: [PATCH 27/35] Event listener remover added --- src/User.jsx | 71 +++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/src/User.jsx b/src/User.jsx index b1bb7a4357..e617b8d0c2 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -60,108 +60,115 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const openPopup = async (mnemonic) => { popUp = window.open(walletHost, '', "height=800,width=600"); - window.addEventListener("message", (event) => { + var f = function(event){ if (event.origin !== walletHost) return; if(event.data == 'received') { sendMessage(mnemonic); - } - }, false); + window.removeEventListener("message", f, false); + } + } + window.addEventListener("message", f); + } const sendMessage = async (mnemonic) => { popUp.postMessage(JSON.stringify({'p_mnemonic': mnemonic}), walletHost); - window.addEventListener("message", (event) => { + var f = function(event){ if (event.origin !== walletHost) - return; - - if(event.data.mnemonic) { + return; + + if(event.data.mnemonic && event.data.Success) { sessionStorage.setItem("mnemonic", event.data.mnemonic); - window.removeEventListener('message', ()=>{}); + window.removeEventListener("message", f, false); popUp.close(); - } - - }, false); + } + } + window.addEventListener("message", f); } const fetchWalletData = async (mnemonic) => { iframe = window.open(walletHost, 'wallet') - window.addEventListener("message", (event) => { + var f = function(event){ if (event.origin !== walletHost) - return; + return; if(event.data == 'received') { getKeys(mnemonic); - } - }, true); + window.removeEventListener("message", f, false); + } + } + window.addEventListener("message", f); } const getKeys = async (mnemonic) => { iframe.postMessage(JSON.stringify({'mnemonic': mnemonic}), walletHost); - window.addEventListener("message", (event) => { + var f = function(event){ + if (event.origin !== walletHost) - return; - + return; + if(event.data.Message == "Password" && popUp == null) { openPopup(event.data.mnemonic) } else if(event.data.data) { setWalletData(event.data.data); + window.removeEventListener("message", f, false); + iframe.close(); } - - window.removeEventListener('message', ()=>{}); - iframe.close(); - - }, true); + } + window.addEventListener("message", f); } // function for sending data to wallet const sendDataToWallet = async (data) => { walletMessenger = window.open(walletHost, 'walletMessenger') - window.addEventListener("message", (event) => { + var f = function(event){ if (event.origin !== walletHost) return; if(event.data == 'received') { sendData(data); + window.removeEventListener("message", f, false); } - }, false); + } + window.addEventListener("message", f); } const sendData = async (data) => { var message = JSON.stringify({'mnemonic': sessionStorage.getItem('mnemonic'), 'data': data}); walletMessenger.postMessage(message, walletHost); - window.addEventListener("message", (event) => { + var f = function(event){ if (event.origin !== walletHost) return; setWalletData(event.data.data); - - window.removeEventListener('message', ()=>{}); + window.removeEventListener("message", f, false); walletMessenger.close(); - - }, false); + } + window.addEventListener("message", f); } const metaMaskLogin = async e => { e.preventDefault(); - e.stopPropagation(); + e.stopPropagation(); if (address) { toggleOpen('user'); } else { if (!loggingIn) { setLoggingIn(true); try { + const {address, profile} = await ceramicApi.login(); setAddress(address); - // fetchWalletData(mnemonic); + // fetchWalletData(mnemonic); // wallet not yet working with metamask setShow(false); userRef.setIsComponentVisible(false); From f4b86685966b6db1a3ecd5c983aca22becb15f07 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Mon, 29 Nov 2021 08:51:39 +0500 Subject: [PATCH 28/35] updated Wallet integration --- constants.js | 2 +- src/User.jsx | 106 +++++++++++++++++++++++++++------------------------ 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/constants.js b/constants.js index 74cdfe2cdd..a73fe2802c 100644 --- a/constants.js +++ b/constants.js @@ -44,7 +44,7 @@ export const web3MainnetSidechainEndpoint = 'https://mainnetsidechain.exokit.org export const web3TestnetSidechainEndpoint = 'https://testnetsidechain.exokit.org'; export const worldUrl = 'worlds.webaverse.com'; export const discordClientId = '684141574808272937'; -export const walletHost = 'secure.webaverse.com'; +export const walletHost = 'https://webaverse.github.io/weba-wallet'; export const worldMapName = 'world'; export const actionsMapName = 'actions'; diff --git a/src/User.jsx b/src/User.jsx index e617b8d0c2..3b0436426c 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -53,71 +53,77 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { setDiscordUrl(''); }); - var popUp = null; + // var popUp = null; var iframe = null; var walletMessenger = null; - const openPopup = async (mnemonic) => { - popUp = window.open(walletHost, '', "height=800,width=600"); + // const openPopup = async (mnemonic) => { + // popUp = window.open(walletHost, '', "height=800,width=600"); - var f = function(event){ - if (event.origin !== walletHost) - return; + // var f = function(event){ + // if (event.origin !== walletHost) + // return; - if(event.data == 'received') { - sendMessage(mnemonic); - window.removeEventListener("message", f, false); - } - } - window.addEventListener("message", f); + // if(event.data == 'received') { + // sendMessage(mnemonic); + // window.removeEventListener("message", f, false); + // } + // } + // window.addEventListener("message", f); - } + // } - const sendMessage = async (mnemonic) => { + // const sendMessage = async (mnemonic) => { - popUp.postMessage(JSON.stringify({'p_mnemonic': mnemonic}), walletHost); + // popUp.postMessage(JSON.stringify({'p_mnemonic': mnemonic}), walletHost); - var f = function(event){ - if (event.origin !== walletHost) - return; + // var f = function(event){ + // if (event.origin !== walletHost) + // return; - if(event.data.mnemonic && event.data.Success) { - sessionStorage.setItem("mnemonic", event.data.mnemonic); - window.removeEventListener("message", f, false); - popUp.close(); - } - } - window.addEventListener("message", f); - } - - const fetchWalletData = async (mnemonic) => { + // if(event.data.mnemonic && event.data.Success) { + // sessionStorage.setItem("mnemonic", event.data.mnemonic); + // window.removeEventListener("message", f, false); + // popUp.close(); + // } + // } + // window.addEventListener("message", f); + // } + + const fetchWalletData = async (key) => { iframe = window.open(walletHost, 'wallet') var f = function(event){ - if (event.origin !== walletHost) + if (`${event.origin}/weba-wallet` !== walletHost) return; if(event.data == 'received') { - getKeys(mnemonic); + getKeys(key); window.removeEventListener("message", f, false); } } window.addEventListener("message", f); } - const getKeys = async (mnemonic) => { - iframe.postMessage(JSON.stringify({'mnemonic': mnemonic}), walletHost); + const getKeys = async (key) => { + if(key) { + iframe.postMessage(JSON.stringify({action: 'getKey', key: key}), walletHost); + } + else { + iframe.postMessage(JSON.stringify({action: 'getAllKeys'}), walletHost); + } var f = function(event){ - if (event.origin !== walletHost) + if (`${event.origin}/weba-wallet` !== walletHost) return; - if(event.data.Message == "Password" && popUp == null) { - openPopup(event.data.mnemonic) + if(event.data.data) { + setWalletData(event.data); + window.removeEventListener("message", f, false); + iframe.close(); } - else if(event.data.data) { - setWalletData(event.data.data); + else { window.removeEventListener("message", f, false); iframe.close(); } @@ -126,30 +132,30 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { } // function for sending data to wallet - const sendDataToWallet = async (data) => { + const sendDataToWallet = async (key, value) => { + walletMessenger = window.open(walletHost, 'walletMessenger') var f = function(event){ - if (event.origin !== walletHost) + if (`${event.origin}/weba-wallet` !== walletHost) return; - + if(event.data == 'received') { - sendData(data); + sendData(key, value); window.removeEventListener("message", f, false); } } window.addEventListener("message", f); } - const sendData = async (data) => { - var message = JSON.stringify({'mnemonic': sessionStorage.getItem('mnemonic'), 'data': data}); + const sendData = async (key, value) => { + var message = JSON.stringify({'action': 'storeKey', 'key': key, 'value': value}); walletMessenger.postMessage(message, walletHost); var f = function(event){ - if (event.origin !== walletHost) + if (`${event.origin}/weba-wallet` !== walletHost) return; - - setWalletData(event.data.data); + window.removeEventListener("message", f, false); walletMessenger.close(); } @@ -158,7 +164,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const metaMaskLogin = async e => { e.preventDefault(); - e.stopPropagation(); + e.stopPropagation(); if (address) { toggleOpen('user'); } else { @@ -168,7 +174,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const {address, profile} = await ceramicApi.login(); setAddress(address); - // fetchWalletData(mnemonic); // wallet not yet working with metamask + fetchWalletData(); setShow(false); userRef.setIsComponentVisible(false); @@ -203,7 +209,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const {address, error, mnemonic} = await handleDiscordLogin(code, id); if(address) { setAddress(address); - fetchWalletData(mnemonic); + fetchWalletData(); setShow(false); } else { @@ -216,7 +222,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { return (
- +
{ e.preventDefault(); From 7b4546bbd7d73fd7268bbc3bf6ab8154553b2484 Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Mon, 29 Nov 2021 08:57:26 +0500 Subject: [PATCH 29/35] iframe hidden --- src/User.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/User.jsx b/src/User.jsx index 3b0436426c..1cd52ee56e 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -222,7 +222,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { return (
- +
{ e.preventDefault(); From 7180da4eb8e9ea101c2dcbcc4e32e329274a710a Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Tue, 30 Nov 2021 20:26:35 +0500 Subject: [PATCH 30/35] fetch from wallet storage and setAddress --- src/User.jsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/User.jsx b/src/User.jsx index 1cd52ee56e..5900d997c5 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -44,7 +44,6 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const [loggingIn, setLoggingIn] = useState(false); const [loginButtons, setLoginButtons] = useState(false); - const [walletData, setWalletData] = useState([]); const [discordUrl, setDiscordUrl] = useState(''); const [loginError, setLoginError] = useState(null); @@ -118,8 +117,11 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { if (`${event.origin}/weba-wallet` !== walletHost) return; - if(event.data.data) { - setWalletData(event.data); + if(event.data.privateKey) { + const address = getAddressFromMnemonic(event.data.privateKey); + if(address) { + setAddress(address); + } window.removeEventListener("message", f, false); iframe.close(); } @@ -174,7 +176,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const {address, profile} = await ceramicApi.login(); setAddress(address); - fetchWalletData(); + // sendDataToWallet('privateKey', mnemonic) setShow(false); userRef.setIsComponentVisible(false); @@ -209,13 +211,16 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const {address, error, mnemonic} = await handleDiscordLogin(code, id); if(address) { setAddress(address); - fetchWalletData(); + sendDataToWallet('privateKey', mnemonic) setShow(false); } else { setLoginError(String(error).toLocaleUpperCase()); } } + else { + fetchWalletData('privateKey'); + } }, [address, setAddress]); From 2a128723cd1b003183df7b2ee6bfbd77c0dd7359 Mon Sep 17 00:00:00 2001 From: Muhammad Abeer Date: Wed, 1 Dec 2021 02:22:58 +0500 Subject: [PATCH 31/35] Update index.mjs --- index.mjs | 3 --- 1 file changed, 3 deletions(-) diff --git a/index.mjs b/index.mjs index 6e6acc0736..106568ee4c 100644 --- a/index.mjs +++ b/index.mjs @@ -51,9 +51,6 @@ function makeId(length) { req.originalUrl = u; next(); } - }else if(/^\/login/.test(o.pathname)){ - req.originalUrl = req.originalUrl.replace(/^\/(login)/,'/'); - res.redirect(req.originalUrl); } else { next(); From 8a970c3bc72e0e688e5f6581f5c4b81be04ebe3b Mon Sep 17 00:00:00 2001 From: mujabmuneeb Date: Wed, 1 Dec 2021 03:50:24 +0500 Subject: [PATCH 32/35] setOpen added --- src/User.jsx | 81 ++++------------------------------------ src/components/modal.css | 4 -- 2 files changed, 7 insertions(+), 78 deletions(-) diff --git a/src/User.jsx b/src/User.jsx index 5900d997c5..954381c004 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -9,26 +9,6 @@ import {contracts, getAddressFromMnemonic} from '../blockchain.js'; import {jsonParse, parseQuery, handleDiscordLogin} from '../util.js'; import Modal from "./components/modal"; -function useComponentVisible(initialIsVisible, fn) { - const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible); - const ref = useRef(null); - - const handleClickOutside = event => { - if (ref.current && !ref.current.contains(event.target)) { - setIsComponentVisible(false); - fn(); - } - }; - - useEffect(() => { - document.addEventListener('click', handleClickOutside, true); - return () => { - document.removeEventListener('click', handleClickOutside, true); - }; - }); - - return {ref, isComponentVisible, setIsComponentVisible}; -} const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const [show, setShow] = useState(false); @@ -38,24 +18,18 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { setShow(!show); } - const discordRef = useComponentVisible(false); - const metaMaskRef = useComponentVisible(false); - const emailRef = useComponentVisible(false); const [loggingIn, setLoggingIn] = useState(false); const [loginButtons, setLoginButtons] = useState(false); const [discordUrl, setDiscordUrl] = useState(''); const [loginError, setLoginError] = useState(null); - const userRef = useComponentVisible(false, ()=>{ - setLoginError(false); - setDiscordUrl(''); - }); - - // var popUp = null; var iframe = null; var walletMessenger = null; + // popup code for getting user's password + + // var popUp = null; // const openPopup = async (mnemonic) => { // popUp = window.open(walletHost, '', "height=800,width=600"); @@ -176,10 +150,8 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { const {address, profile} = await ceramicApi.login(); setAddress(address); - // sendDataToWallet('privateKey', mnemonic) setShow(false); - userRef.setIsComponentVisible(false); } catch (err) { console.warn(err); } finally { @@ -193,7 +165,6 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { e.preventDefault(); e.stopPropagation(); setLoginButtons(false); - discordRef.setIsComponentVisible(true); } useEffect(async () => { @@ -225,7 +196,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { return ( -
+
{ e.stopPropagation(); if (address) { toggleOpen('user'); - userRef.setIsComponentVisible(false); }else{ - userRef.setIsComponentVisible(true); setLoginButtons(true); - discordRef.setIsComponentVisible(false); - metaMaskRef.setIsComponentVisible(false); + setOpen(null); + setOpen('login'); } }}> @@ -258,7 +227,7 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { : '' } { - userRef.isComponentVisible + open == 'login' ?
{ loginButtons ? <> @@ -282,42 +251,6 @@ const User = ({address, setAddress, open, setOpen, toggleOpen}) => { : '' } - {/* { - discordRef.isComponentVisible - ?
-
-
-
-
- -
-
- setDiscordUrl(e.target.value)} - onKeyPress={ - async event => { - event.preventDefault(); - event.stopPropagation(); - if (event.key === 'Enter') { - const {address, error} = await handleDiscordLogin(discordUrl); - if(address) - setAddress(address); - else - setLoginError(String(error).toLocaleUpperCase()); - } - } - } /> - -
-
- -
-
-
: '' - } */} -
:
} diff --git a/src/components/modal.css b/src/components/modal.css index 6b334379a8..24ab9104ca 100644 --- a/src/components/modal.css +++ b/src/components/modal.css @@ -1,7 +1,3 @@ -html, -body { - height: 100%; -} body { background: #eee; display: flex; From 498fec3b2c7b0635bef80fc0526d010b29524984 Mon Sep 17 00:00:00 2001 From: Muhammad Abeer Date: Wed, 1 Dec 2021 03:56:42 +0500 Subject: [PATCH 33/35] package-files-fixed --- package-lock.json | 31 ------------------------------- package.json | 1 + 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index a021561e2b..641953b5ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,6 @@ "classnames": "^2.3.1", "dids": "^2.4.0", "express": "^4.17.1", - "fast-sha256": "^1.3.0", "forever": "^4.0.1", "gpt-3-encoder": "^1.1.3", "json-6": "^1.1.4", @@ -3049,11 +3048,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-sha256": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", - "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==" - }, "node_modules/file-entry-cache": { "version": "6.0.1", "dev": true, @@ -6563,16 +6557,6 @@ "version": "2.3.1", "license": "0BSD" }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "node_modules/tweetnacl-util": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", - "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" - }, "node_modules/type-check": { "version": "0.4.0", "dev": true, @@ -9188,11 +9172,6 @@ "version": "2.0.6", "dev": true }, - "fast-sha256": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", - "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==" - }, "file-entry-cache": { "version": "6.0.1", "dev": true, @@ -11399,16 +11378,6 @@ "tslib": { "version": "2.3.1" }, - "tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "tweetnacl-util": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", - "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" - }, "type-check": { "version": "0.4.0", "dev": true, diff --git a/package.json b/package.json index 7336df3241..0c7578ce65 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "stats.js": "^0.17.0", "three": "^0.133.1", "troika-three-text": "^0.43.1-alpha.0", + "webm-writer": "^1.0.0", "wsrtc": "./packages/wsrtc", "xhr2": "^0.2.1" }, From c1857d12ab2b7b70d7e1f4aec53cbd9120bfa2b5 Mon Sep 17 00:00:00 2001 From: Muhammad Abeer Date: Wed, 1 Dec 2021 04:01:57 +0500 Subject: [PATCH 34/35] Update index.mjs --- index.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/index.mjs b/index.mjs index 106568ee4c..e3c07ab350 100644 --- a/index.mjs +++ b/index.mjs @@ -51,8 +51,7 @@ function makeId(length) { req.originalUrl = u; next(); } - } - else { + }else { next(); } }); From de56b7d49374dea04d410253ddb7f085fff83f31 Mon Sep 17 00:00:00 2001 From: Muhammad Abeer Date: Wed, 1 Dec 2021 04:02:15 +0500 Subject: [PATCH 35/35] Update index.mjs --- index.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.mjs b/index.mjs index e3c07ab350..0eb8d3fd5f 100644 --- a/index.mjs +++ b/index.mjs @@ -51,7 +51,7 @@ function makeId(length) { req.originalUrl = u; next(); } - }else { + } else { next(); } });