Last active
October 31, 2020 21:54
-
-
Save graemecode/2576f71d867bfa5216fcf9ded55af1e8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<style type="text/css"> | |
#recent-avatars-container img { | |
width: 64px; | |
height: 64px; | |
margin: 5px; | |
} | |
#cropped-image-preview img { | |
border: 1px solid #ccc; | |
} | |
</style> | |
<body> | |
<h1>Recently Uploaded Avatars</h1> | |
<div id="recent-avatars-container"></div> | |
<h1>Choose Your Avatar</h1> | |
<input type="file" id="avatar-file-input" accept="image/*"/> | |
<h1>Preview Formatted Avatar</h1> | |
<div id="cropped-image-preview"></div> | |
<h1>Login With Your Wallet Key</h1> | |
<input type="file" id="key-file-input" accept="application/json"> | |
<button id="upload-avatar-btn" disabled="disabled">Upload Avatar</button> | |
<script src="https://unpkg.com/arweave/bundles/web.bundle.min.js"></script> | |
<script type="module"> | |
const {Arweave} = window; | |
const arweave = Arweave.init(); | |
let uploadData; | |
let wallet; | |
async function parseAndDisplayImage(txId) { | |
const data = await arweave.transactions.getData(txId); | |
const transaction = await arweave.transactions.get(txId); | |
let contentType; | |
transaction.get("tags").forEach(tag => { | |
let key = tag.get("name", {decode: true, string: true}); | |
let value = tag.get("value", {decode: true, string: true}); | |
if (key === "Content-Type") { | |
contentType = value; | |
} | |
}); | |
if (!contentType) { | |
// Can"t render without content type! | |
return; | |
} | |
const url = base64URL(data); | |
const link = document.createElement("a"); | |
link.href = `https://viewblock.io/arweave/tx/${txId}`; | |
link.target = "_blank"; | |
const image = document.createElement("img"); | |
image.src = `data:${contentType};base64,${url}`; | |
link.appendChild(image); | |
document.querySelector("#recent-avatars-container").appendChild(link); | |
} | |
const getRecentUploads = async () => { | |
// Needs to paginate. | |
let transactionIds = await arweave.arql({ | |
// For testing, this gets a series of usable Avatars that already exist. | |
op: "equals", | |
expr1: "Content-Type", | |
expr2: "image/png" | |
// When deployed, we'll limit to this app's images. | |
// op: "equals", | |
// expr1: "App-Name", | |
// expr2: "Avatar" | |
}); | |
const maxAvatarsToDisplay = 8; | |
let numDisplayed = 0; | |
if (transactionIds.length === 0) { | |
document.querySelector("#recent-avatars-container").innerHTML = "No avatars have been uploaded yet!"; | |
} else { | |
for (let txId of transactionIds) { | |
try { | |
await parseAndDisplayImage(txId); | |
numDisplayed += 1; | |
} catch (e) { | |
console.log("Error parsing avatar", e); | |
} | |
if (numDisplayed === maxAvatarsToDisplay) { | |
break; | |
} | |
} | |
} | |
}; | |
getRecentUploads(); | |
const handleAvatarFileChange = event => { | |
const input = event.target; | |
const contentType = (event.target).files[0].type; | |
const fileReader = new FileReader(); | |
fileReader.onload = async ev => { | |
const fileContent = ev.target.result.toString(); | |
// This is what we"ll store on Arweave. | |
const data = URLBase64(fileContent).split("base64,")[1]; | |
uploadData = { | |
contentType, | |
data, | |
}; | |
document.querySelector("#upload-avatar-btn").disabled = false; | |
const outputImage = document.createElement("img"); | |
outputImage.onload = () => { | |
const preview = document.querySelector("#cropped-image-preview"); | |
outputImage.width = 128; | |
outputImage.height = 128; | |
preview.innerHTML = ""; | |
preview.appendChild(outputImage); | |
}; | |
outputImage.src = fileContent; | |
}; | |
fileReader.readAsDataURL(input.files[0]); | |
}; | |
const handleKeyFileChange = async event => { | |
const fileReader = new FileReader(); | |
fileReader.onload = () => { | |
wallet = JSON.parse(fileReader.result.toString()); | |
}; | |
fileReader.readAsText(event.target.files[0]); | |
}; | |
const handleAvatarUpload = async () => { | |
if (!(uploadData && uploadData.contentType && uploadData.data)) { | |
alert("Please specify an image before uploading"); | |
return; | |
} | |
if (!wallet) { | |
alert("Please connect your wallet before uploading"); | |
return; | |
} | |
const {contentType, data} = uploadData; | |
const tx = await arweave.createTransaction({data}, wallet); | |
tx.addTag("Content-Type", contentType); | |
tx.addTag("App-Name", "Avatar"); | |
tx.addTag("App-Version", "0.1"); | |
// Unix-Time: This tag is the a unix timestamp, seconds since epoch. | |
const unixTime = Math.floor(Date.now() / 1000); | |
tx.addTag("Unix-Time", unixTime.toString()); | |
await arweave.transactions.sign(tx, wallet); | |
console.log({tx}); | |
const res = await arweave.transactions.post(tx); | |
if (res.status !== 200 && res.status !== 202) { | |
console.log(res); | |
alert(`Error submitting transaction, code: ${res.status}`); | |
return false; | |
} | |
}; | |
document.querySelector("#key-file-input").addEventListener("change", event => { | |
handleKeyFileChange(event); | |
}); | |
document.querySelector("#avatar-file-input").addEventListener("change", event => { | |
handleAvatarFileChange(event); | |
}); | |
document.querySelector("#upload-avatar-btn").addEventListener("click", event => { | |
handleAvatarUpload(event); | |
}); | |
const base64URL = (url) => { | |
url = url.replace(/\-/g, "+").replace(/\_/g, "/"); | |
while (url.length % 4) { | |
url += "="; | |
} | |
return url; | |
}; | |
const URLBase64 = (url) => { | |
return url.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, ""); | |
}; | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment