Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Arweave #13

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions config-overrides.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const webpack = require('webpack');

module.exports = function override(config) {
const fallback = config.resolve.fallback || {};
Object.assign(fallback, {
buffer: false,
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"assert": require.resolve("assert-browserify"),
})
config.resolve.fallback = fallback;
config.plugins = (config.plugins || []).concat([
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer']
})
])

config.module.rules.unshift({
test: /\.m?js$/,
resolve: {
fullySpecified: false, // disable the behavior
},
});

return config;
}
27 changes: 23 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,38 @@
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"buffer": "^6.0.3",
"@trezor/connect-web": "^9.0.3",
"arweave": "^1.11.6",
"cids": "^1.1.9",
"crypto": "^1.0.1",
"eccrypto": "^1.1.6",
"eth-crypto": "^2.3.0",
"ethers": "^5.7.0",
"hdkey": "^2.0.1",
"mobx": "^6.6.2",
"mobx-react-lite": "^3.4.0",
"multicodec": "^3.2.1",
"multihashes": "^4.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "6",
"react-scripts": "5.0.1",
"showdown": "^2.1.0",
"slate": "^0.84.0",
"slate-history": "^0.66.0",
"slate-react": "^0.83.2",
"slate-react-presentation": "^0.1.1",
"svg-loaders-react": "^2.2.1",
"tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1",
"wagmi": "^0.6.4",
"web-vitals": "^2.1.0"
},
"homepage": "./",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"start": "react-app-rewired start --scripts-version react-scripts",
"build": "react-app-rewired build --scripts-version react-scripts",
"test": "react-app-rewired test --scripts-version react-scripts",
"eject": "react-scripts eject",
"prettify": "npx prettier --write ."
},
Expand All @@ -57,9 +70,15 @@
"devDependencies": {
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/typography": "^0.5.7",
"assert-browserify": "^2.0.0",
"autoprefixer": "^10.4.8",
"buffer": "^6.0.3",
"crypto-browserify": "^3.12.0",
"postcss": "^8.4.16",
"prettier": "^2.7.1",
"process": "^0.11.10",
"react-app-rewired": "^2.2.1",
"stream-browserify": "^3.0.0",
"tailwindcss": "^3.1.8"
}
}
4 changes: 4 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
color: #61dafb;
}

.prose {
caret-color: rgb(45, 212, 191);
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
Expand Down
141 changes: 114 additions & 27 deletions src/Blog.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,117 @@
import { useState, useEffect } from 'react'
import { useState, useEffect, useRef } from 'react'
import BlogPublisher from './components/BlogPublisher'
import Post from './components/Post'
import RemoveButton from './components/RemoveButton'
import DeletePostModal from './components/modals/DeletePost'
import ContentPopup from './components/ContentPopup'

import ArweaveWalletModal from './components/ArweaveWalletModal'

import { generate } from './blog/generator'
import { convert } from './blog/converter'
import { gateways, uploadHTML } from './ipfs'

import { useAccount } from 'wagmi'
import { useAccount, useSigner, useProvider } from 'wagmi'

import { decryptWallet, getAddress, markAsDeleted } from './utils/arweave'

import { useArweaveWalletStore } from './providers/ArweaveWalletContext'

import { generateArweaveWallet } from './utils/arweave'

const Blog = ({ callback, ensName, existingPosts, notificationsEnabled, setNotificationsEnabled, setNotificationTitle }) => {
const Blog = ({ callback, ensName, existingPosts, setExistingPosts, setEncryptedWalletData, encryptedWalletData, rootCID }) => {
const [contentURL, setContentURL] = useState(null)
const [posts, setPosts] = useState([])
const [ipfsHash, setIpfsHash] = useState()
const [selectedForRemoval, setSelectedForRemoval] = useState(null)
const { address } = useAccount()
const { data: signer } = useSigner()
const [arweaveKey, setArweaveKey] = useState()
const [arweaveWalletFirstTime, setArweaveWalletFirstTime] = useState()
const arweaveStore = useArweaveWalletStore()
const [openArweaveModal, setOpenArweaveModal] = useState(false)
const [arweaveWalletAddress, setArweaveWalletAddress] = useState()

const initializeArweaveWallet = async () => {
let { wallet, encryptedWalletData } = await generateArweaveWallet(signer, address)
let arweaveAddress = await getAddress(wallet)
setArweaveWalletAddress(arweaveAddress)
setEncryptedWalletData(encryptedWalletData)
sessionStorage.setItem("arweaveKey", JSON.stringify(wallet))
arweaveStore.setKey(wallet)
arweaveStore.setWalletAddress(arweaveAddress)
setArweaveWalletFirstTime(true)
setArweaveKey(wallet)
setOpenArweaveModal(true)
}

let deleteModalOpen = Boolean(selectedForRemoval)
useEffect(() => {
async function execute(address, encryptedData) {
let key, stringKey
let sessionKey = sessionStorage.getItem("arweaveKey")
let provider = await arweaveStore.getProvider()

if (sessionKey) {
key = JSON.parse(sessionKey)
} else {
key = await decryptWallet(provider, address, encryptedData)
stringKey = JSON.stringify(key)
sessionStorage.setItem("arweaveKey", stringKey)
}

setArweaveKey(key)
let arweaveAddress = await getAddress(key)
arweaveStore.setKey(stringKey)
arweaveStore.setWalletAddress(arweaveAddress)
setArweaveWalletAddress(arweaveAddress)
}

const RemovePost = async () => {
let hash = selectedForRemoval
let newPosts = [...existingPosts]
if (address && encryptedWalletData && !arweaveKey) {
execute(address, encryptedWalletData)
}
}, [])

let index = newPosts.findIndex(p => p == hash)
if (index > -1) newPosts.splice(index, 1)
useEffect(() => {

let html = await generate({
hashes: newPosts,
ens: ensName
})
async function fetch_and_store(wallet) {
let arweaveAddress = await getAddress(wallet)
setArweaveWalletAddress(arweaveAddress)
sessionStorage.setItem("arweaveKey", JSON.stringify(wallet))
arweaveStore.setKey(wallet)
arweaveStore.setWalletAddress(arweaveAddress)
setArweaveKey(wallet)
}

let response = await uploadHTML(html)
let key = sessionStorage.getItem("arweaveKey")

if (callback) {
await callback(response.Hash)
if (key) {
fetch_and_store(JSON.parse(key))
}

setIpfsHash(response.Hash)
setContentURL(`https://${ensName}.limo`)
}, [])

let deleteModalOpen = Boolean(selectedForRemoval)

const RemovePost = async () => {
let id = selectedForRemoval
let response = await markAsDeleted(arweaveKey, id)

let newPosts = [...existingPosts]

let index = newPosts.findIndex(p => p == id)
if (index > -1) newPosts.splice(index, 1)

setExistingPosts(newPosts)
}

useEffect(() => {
if (!existingPosts.length) return

Promise.all(
existingPosts.map(hash =>
fetch(`${gateways.infura}/${hash}`).then(res => res.text())
existingPosts.map(id =>
fetch(`https://bnjr5drbo27dam2cmqdbijmmof7mnxlydfjoywtwbdofa3uzol7q.arweave.net/${id}`).then(res => res.text())
)
)
.then(posts => posts.map(markdown => convert(markdown)))
.then(setPosts)
}, [existingPosts])

Expand All @@ -60,12 +121,16 @@ const Blog = ({ callback, ensName, existingPosts, notificationsEnabled, setNotif
return (
<section style={{ maxWidth: 750, margin: '0 auto' }}>
{posts.map((post, i) => (
<div className="relative" key={existingPosts[i]}>
<div className="relative" key={i}>
<RemoveButton
onClick={() => setSelectedForRemoval(existingPosts[i])}
onClick={() =>
// TODO
console.log('TODO: removal feature')
// setSelectedForRemoval(existingPosts[i])
}
className="absolute right-0 top-2"
/>
<Post html={post} />
<Post content={post} />
</div>
))}
</section>
Expand Down Expand Up @@ -99,23 +164,45 @@ const Blog = ({ callback, ensName, existingPosts, notificationsEnabled, setNotif
</div>
)

if (!encryptedWalletData && !arweaveKey ) {
return (
<div className="text-white">
<h1>It looks like you don't have an Arweave Wallet</h1>
<button
type="button"
onClick={() => initializeArweaveWallet()}
className="block items-center rounded-md border border-transparent bg-teal-600 px-3 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-offset-2 mt-4"
>
Generate Arweave Wallet
</button>

<p className="bg-zinc-800 p-2 mt-4 text-center">
You will be prompted to export your MetaMask public key. We will use this key to encrypt your newly generated Arweve Wallet
</p>
</div>
)
}

return (
<>
{contentURL && renderSuccess()}
<BlogPublisher
callback={callback}
ensName={ensName}
existingPosts={existingPosts}
notificationsEnabled={notificationsEnabled}
setNotificationsEnabled={setNotificationsEnabled}
setNotificationTitle={setNotificationTitle}
encryptedWalletData={encryptedWalletData}
setEncryptedWalletData={setEncryptedWalletData}
arweaveKey={arweaveKey}
arweaveWalletAddress={arweaveWalletAddress}
rootCID={rootCID}
/>
{renderPosts()}
<DeletePostModal
onClickConfirm={RemovePost}
open={deleteModalOpen}
setOpen={() => setSelectedForRemoval(null)}
/>
<ArweaveWalletModal open={openArweaveModal} setOpen={setOpenArweaveModal} address={arweaveWalletAddress} />
</>
)
}
Expand Down
Loading