Skip to content

Instantly share code, notes, and snippets.

@graemecode
Last active October 31, 2020 21:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save graemecode/2576f71d867bfa5216fcf9ded55af1e8 to your computer and use it in GitHub Desktop.
Save graemecode/2576f71d867bfa5216fcf9ded55af1e8 to your computer and use it in GitHub Desktop.
<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