├── pages ├── declaration │ ├── index.js │ └── [txId].js ├── _app.js ├── styles.css ├── diff │ └── [newId] │ │ └── [[...oldId]].js └── about.js ├── public ├── favicon.jpeg └── og-image.png ├── next.config.js ├── postcss.config.js ├── components ├── core │ ├── Box.js │ ├── Button.js │ └── icons │ │ ├── TwitterIconBlack.js │ │ ├── Checkmark.js │ │ └── MetaMaskIcon.js ├── Head.js ├── SocialProofConfirmation.js ├── VerificationPopup.js ├── SocialProofPopup.js ├── Signatures.js ├── Fork.js └── Sign.js ├── .gitignore ├── README.md ├── package.json ├── LICENSE.txt ├── tailwind.config.js └── arweaveFns ├── og.js └── index.js /pages/declaration/index.js: -------------------------------------------------------------------------------- 1 | import Declaration from "./[txId]"; 2 | export default Declaration; 3 | 4 | -------------------------------------------------------------------------------- /public/favicon.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verses-xyz/interdependence-web/HEAD/public/favicon.jpeg -------------------------------------------------------------------------------- /public/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verses-xyz/interdependence-web/HEAD/public/og-image.png -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css'; 2 | import './styles.css'; 3 | 4 | function MyApp({ Component, pageProps }) { 5 | return 6 | } 7 | 8 | export default MyApp; 9 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | async redirects() { 3 | return [ 4 | { 5 | source: '/', 6 | destination: '/about', 7 | permanent: true, 8 | } 9 | ]; 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /components/core/Box.js: -------------------------------------------------------------------------------- 1 | export default function Box({ title, content, includeBorder = true }) { 2 | return ( 3 |
4 |

{title}

5 |
{content}
6 |
7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # IDEs 37 | .vscode 38 | .idea -------------------------------------------------------------------------------- /components/core/Button.js: -------------------------------------------------------------------------------- 1 | export default function Button({primary, children, className, ...props}) { 2 | if (primary) { 3 | return ; 6 | } else { 7 | return ; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # interdependence-web 2 | 3 | Running the application locally: 4 | 5 | ``` 6 | npm install 7 | npm run dev 8 | ``` 9 | 10 | Deploying: 11 | 12 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss&project-name=with-tailwindcss&repository-name=with-tailwindcss) 13 | 14 | This website is based on the Next.js + Tailwind CSS Example, using [Tailwind CSS](https://tailwindcss.com/) [(v2.2)](https://blog.tailwindcss.com/tailwindcss-2-2). It follows the steps outlined in the official [Tailwind docs](https://tailwindcss.com/docs/guides/nextjs). 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@tailwindcss/typography": "^0.4.1", 10 | "ab-react-diff-viewer": "*", 11 | "arbundles": "^0.2.10", 12 | "arweave": "^1.10.18", 13 | "ethers": "^5.5.1", 14 | "next": "latest", 15 | "react": "^17.0.2", 16 | "react-async-hook": "^4.0.0", 17 | "react-dom": "^17.0.2", 18 | "react-hook-form": "^7.18.0", 19 | "react-markdown": "^7.1.0", 20 | "react-modal": "^3.14.3", 21 | "react-spinners": "^0.11.0" 22 | }, 23 | "devDependencies": { 24 | "autoprefixer": "^10.2.6", 25 | "postcss": "^8.3.5", 26 | "tailwindcss": "^2.2.4", 27 | "tailwindcss-text-indent": "^1.0.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /components/core/icons/TwitterIconBlack.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | export default function TwitterIconBlack({...props}) { 4 | return( 5 | 6 | 7 | 8 | ) 9 | } -------------------------------------------------------------------------------- /components/Head.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | 3 | export default function HeadComponent() { 4 | return 5 | A Declaration of the Interdependence of Cyberspace 6 | 7 | 8 | {/* Place any emoji as text wrapped in svg for favicon */} 9 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 verses 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /components/SocialProofConfirmation.js: -------------------------------------------------------------------------------- 1 | import Box from "./core/Box"; 2 | import Button from './core/Button'; 3 | 4 | 5 | const generateTweet = () => { 6 | const str = `I just signed A Declaration for the Interdependence of Cyberspace! interdependence.online`; 7 | window.open(`https://twitter.com/intent/tweet?text=${encodeURI(str)}`); 8 | } 9 | 10 | export default function SocialProofConfirmation({ closeModal }) { 11 | return ( 12 | Thank you for signing!

} 14 | includeBorder={false} 15 | content={ 16 |
17 |

18 | We're heartened you'll join us in the Pluriverse. {/* If you have a moment, please share what this vision means to you.*/} 19 |

20 | 21 |
22 | 27 |
28 | 29 |
30 | 35 |
36 |
} 37 | /> 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | fontFamily: { 7 | title: ['EB Garamond'], 8 | body: ['EB Garamond'], 9 | mono: ['Roboto Mono'], 10 | }, 11 | backgroundColor: (theme) => ({ 12 | ...theme('colors'), 13 | primary: 'F9F9F9', 14 | }), 15 | 16 | extend: { 17 | typography: { 18 | DEFAULT: { 19 | css: { 20 | color: '#000', 21 | } 22 | } 23 | }, 24 | outline: { 25 | blue: '4px solid #ECECEC', 26 | }, 27 | colors: { 28 | blue: { 29 | 20: '#fafafa', 30 | 420: '#0D1A33', 31 | }, 32 | red: { 33 | 20: '#fafafa', 34 | }, 35 | gray: { 36 | 20: '#4f4f4f', 37 | 80: '#EBEBEB', 38 | 120: '#666666', 39 | primary:'#333333', 40 | secondary:'#666666', 41 | placeholder:'#a7a7a7', 42 | detail: '#CCCCCC', 43 | wash: '#EBEBEB', 44 | hover: '#DBDBDB', 45 | bg:'#FAFAFA', 46 | special:'#0000FF', 47 | }, 48 | truegray: { 49 | 800: '#333333', 50 | }, 51 | purple: { 52 | 2004: "#DEADFB", 53 | 2021: "#FFE" 54 | } 55 | } 56 | 57 | }, 58 | }, 59 | variants: { 60 | extend: { 61 | }, 62 | }, 63 | plugins: [ 64 | require('@tailwindcss/typography'), 65 | ], 66 | }; 67 | -------------------------------------------------------------------------------- /components/core/icons/Checkmark.js: -------------------------------------------------------------------------------- 1 | export default function Checkmark({filled, ...props}) { 2 | if (filled) { 3 | return( 4 | 5 | 6 | ); 7 | } else { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pages/styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | scroll-behavior: smooth; 3 | background-color: #f5f5f5; 4 | } 5 | 6 | hr { 7 | overflow: visible; 8 | padding: 0; 9 | border: none; 10 | border-top: 1px solid #bbb; 11 | color: #bbb; 12 | text-align: center; 13 | width: 300px; 14 | } 15 | 16 | hr:after { 17 | content: "*\a0\a0\a0*\a0\a0\a0*"; 18 | font-weight: 400; 19 | display: inline-block; 20 | background: none; 21 | position: relative; 22 | top: -0.65em; 23 | font-size: 1.5em; 24 | padding: 0 1em; 25 | margin: 0 -1em; 26 | background: #fafafa; 27 | } 28 | 29 | @media (min-width: 800px) { 30 | .ReactModal__Content { 31 | inset: 10vh auto auto calc(50vw - 300px) !important; 32 | width: 600px !important; 33 | } 34 | } 35 | 36 | .metamaskLogo .st0{fill:#E2761B;stroke:#E2761B;stroke-linecap:round;stroke-linejoin:round;} 37 | .metamaskLogo .st1{fill:#E4761B;stroke:#E4761B;stroke-linecap:round;stroke-linejoin:round;} 38 | .metamaskLogo .st2{fill:#D7C1B3;stroke:#D7C1B3;stroke-linecap:round;stroke-linejoin:round;} 39 | .metamaskLogo .st3{fill:#233447;stroke:#233447;stroke-linecap:round;stroke-linejoin:round;} 40 | .metamaskLogo .st4{fill:#CD6116;stroke:#CD6116;stroke-linecap:round;stroke-linejoin:round;} 41 | .metamaskLogo .st5{fill:#E4751F;stroke:#E4751F;stroke-linecap:round;stroke-linejoin:round;} 42 | .metamaskLogo .st6{fill:#F6851B;stroke:#F6851B;stroke-linecap:round;stroke-linejoin:round;} 43 | .metamaskLogo .st7{fill:#C0AD9E;stroke:#C0AD9E;stroke-linecap:round;stroke-linejoin:round;} 44 | .metamaskLogo .st8{fill:#161616;stroke:#161616;stroke-linecap:round;stroke-linejoin:round;} 45 | .metamaskLogo .st9{fill:#763D16;stroke:#763D16;stroke-linecap:round;stroke-linejoin:round;} 46 | -------------------------------------------------------------------------------- /components/VerificationPopup.js: -------------------------------------------------------------------------------- 1 | import Box from "./core/Box"; 2 | import Button from './core/Button'; 3 | import React from "react"; 4 | import {verifyTwitter} from "../arweaveFns"; 5 | import {DisplayedError} from "./Sign"; 6 | import ScaleLoader from "react-spinners/ScaleLoader"; 7 | 8 | export default function VerificationPopUp({ setStage, formData, sign }) { 9 | const [loading, setIsLoading] = React.useState(false); 10 | const [displayedError, setDisplayedError] = React.useState(false); 11 | 12 | const handleError = (err) => { 13 | setDisplayedError(err.message); 14 | setIsLoading(false); 15 | } 16 | 17 | const verify = () => { 18 | const { sig, handle } = formData 19 | setIsLoading(true) 20 | verifyTwitter(sig, handle) 21 | .then((data) => { 22 | if ('message' in data) { 23 | throw new Error(data.message) 24 | } 25 | }) 26 | .then(sign) 27 | .then(() => { 28 | setStage(3) 29 | setIsLoading(false); 30 | setDisplayedError(false) 31 | }) 32 | .catch(handleError) 33 | } 34 | 35 | 36 | return ( 37 | Complete verification

} 39 | includeBorder={false} 40 | content={ 41 |
42 |

43 | After sending your tweet, click the button below to complete verification: 44 |

45 |
46 | 52 |
53 |
54 | 59 |
60 | 61 |
} 62 | /> 63 | ); 64 | } -------------------------------------------------------------------------------- /components/SocialProofPopup.js: -------------------------------------------------------------------------------- 1 | import Box from "./core/Box"; 2 | import Button from './core/Button'; 3 | import React from "react"; 4 | import ScaleLoader from "react-spinners/ScaleLoader"; 5 | import {DisplayedError} from "./Sign"; 6 | import TwitterIconBlack from "./core/icons/TwitterIconBlack"; 7 | 8 | const generateTweet = (sig) => { 9 | const str = `I am verifying for @verses_xyz: sig:${sig}`; 10 | window.open(`https://twitter.com/intent/tweet?text=${encodeURI(str)}`); 11 | } 12 | 13 | export default function SocialProofPopup({ setStage, formData, sign }) { 14 | const [loading, setIsLoading] = React.useState(false); 15 | const [displayedError, setDisplayedError] = React.useState(false); 16 | 17 | const wrappedSign = () => { 18 | setIsLoading(true) 19 | sign() 20 | .then((signatureServerResponse) => { 21 | setStage(3) 22 | setIsLoading(false); 23 | }) 24 | .catch((err) => { 25 | setDisplayedError(err.message); 26 | setIsLoading(false); 27 | }) 28 | } 29 | 30 | return ( 31 | Verify your signature

} 33 | includeBorder={false} 34 | content={ 35 |
36 |

37 | Tweet a message to prove that you control this address. Return to this page afterwards to complete verification. 38 |

39 | 40 |
41 | 53 |
54 |
55 | 57 |
58 | 59 |
} 60 | /> 61 | ); 62 | } -------------------------------------------------------------------------------- /pages/diff/[newId]/[[...oldId]].js: -------------------------------------------------------------------------------- 1 | import { useRouter } from 'next/router'; 2 | import React from "react"; 3 | import {useAsync} from "react-async-hook"; 4 | import {getDeclaration} from "../../../arweaveFns"; 5 | import HeadComponent from "../../../components/Head"; 6 | import BarLoader from "react-spinners/BarLoader"; 7 | import ReactDiffViewer from 'ab-react-diff-viewer'; 8 | 9 | export default function Diff() { 10 | const router = useRouter() 11 | const { oldId, newId } = router.query 12 | 13 | const oldDec = useAsync(getDeclaration, [oldId]); 14 | const newDec = useAsync(getDeclaration, [newId]); 15 | 16 | const loading = oldDec.loading || newDec.loading 17 | return ( 18 |
19 | 20 |
21 |
22 |

23 | Difference 24 |

25 | {!loading &&

26 | Showing the difference between the{' '} 27 | original{' '} 28 | which was written on{' '} 29 | {oldDec.result.data.timestamp}{' '} 30 | and the{' '} 31 | fork{' '} 32 | which was written on{' '} 33 | {newDec.result.data.timestamp}. 34 |

} 35 |
36 | {loading ? : 37 |
38 | 52 |
} 53 |
54 |
55 | ) 56 | } -------------------------------------------------------------------------------- /components/Signatures.js: -------------------------------------------------------------------------------- 1 | import Box from "./core/Box"; 2 | import Checkmark from './core/icons/Checkmark'; 3 | import React from "react"; 4 | import {dedupe, fetchSignatures, sortSigs} from "../arweaveFns"; 5 | 6 | const cleanHandle = (handle, address, verified) => { 7 | if (verified) { 8 | if (handle.length === 0) { 9 | return handle; 10 | } else { 11 | const firstChar = handle[0]; 12 | return firstChar === "@" ? handle : '@' + handle; 13 | } 14 | } 15 | return address.slice(0, 8); 16 | } 17 | 18 | export default function Signatures({txId, sigs, setSigs}) { 19 | const [cursor, setCursor] = React.useState("") 20 | const [sortedSigs, setSortedSigs] = React.useState([]) 21 | const [reachedEnd, setReachedEnd] = React.useState(false) 22 | 23 | React.useEffect(() => { 24 | setCursor(sigs[sigs.length-1] && sigs[sigs.length-1].CURSOR) 25 | setSortedSigs(sortSigs(dedupe(sigs))) 26 | }, [sigs]) 27 | 28 | const fetchMore = React.useCallback(async () => { 29 | const newSigs = await fetchSignatures(txId, cursor) 30 | if (newSigs.length === 0) { 31 | setReachedEnd(true) 32 | } else { 33 | setSigs(oldSigs => [...oldSigs, ...newSigs]) 34 | } 35 | }, [cursor]) 36 | 37 | return ( 38 | 41 | {sortedSigs.map((sig, index) =>
42 | )} 70 | {!reachedEnd &&
71 | 74 |
} 75 | } 76 | /> 77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /components/Fork.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Modal from 'react-modal'; 3 | import { useForm } from "react-hook-form"; 4 | import { forkDeclaration } from "../arweaveFns"; 5 | import Button from "./core/Button"; 6 | import Box from "./core/Box"; 7 | import ScaleLoader from "react-spinners/ScaleLoader"; 8 | 9 | Modal.setAppElement('#__next'); 10 | Modal.defaultStyles.overlay.backgroundColor = '#555555aa'; 11 | 12 | const customStyles = { 13 | content: { 14 | top: '10vh', 15 | left: '10vw', 16 | right: 'auto', 17 | bottom: 'auto', 18 | width: '80vw', 19 | marginRight: '-50%', 20 | borderRadius: '0.75em', 21 | padding: '0', 22 | }, 23 | }; 24 | 25 | export default function Fork({text, txId}) { 26 | const { 27 | register, 28 | handleSubmit, 29 | } = useForm({ 30 | defaultValues: { 31 | declaration: text, 32 | } 33 | }); 34 | const [modalIsOpen, setIsOpen] = React.useState(false); 35 | const [loading, setIsLoading] = React.useState(false); 36 | 37 | function openModal() { 38 | setIsOpen(true); 39 | } 40 | 41 | function closeModal() { 42 | setIsOpen(false); 43 | } 44 | const onSubmit = (data) => { 45 | setIsLoading(true) 46 | forkDeclaration(txId, data.title, data.declaration, []) 47 | .then(data => window.location.href = `/declaration/${data.id}`) 48 | .finally(() => setIsLoading(false)) 49 | } 50 | 51 | return ( 53 |
54 |

55 | If you have a revision, addition, or challenge to this declaration, we strongly encourage you to articulate your own vision and values through a fork of this document. 56 |

57 | 62 |
63 | 69 |
70 |
Fork the Declaration
71 |
72 | 73 |