Drag and Drop

Drag and Drop to IPFS and Hypercore

Introduction

This tutorial guides you through building a simple yet powerful web application that allows users to drag and drop files, publishing them directly to IPFS and Hypercore. Imagine Alice, an artist who wants to share her digital art easily. With this app, she can drag her artwork into the browser, and it's instantly available on the decentralized web.

Prerequisites

Setting Up Your Environment

  1. Install Agregore Browser: Download and install the Agregore browser from Agregore's official website.
  2. Create a Project Folder: Create a new folder on your computer where you will store your project files.

Building the Application

Step 1: HTML Structure

Create an index.html file in your project folder and add the following HTML structure:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Drag and Drop to IPFS and Hypercore</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <main>
        <label for="protocolSelect">Protocol:</label>
        <select id="protocolSelect">
            <option value="ipfs" selected>IPFS</option>
            <option value="hyper">Hypercore</option>
        </select>
        <section id="uploadBox">Drop a file here</section>
        <ul id="uploadListBox"></ul>
    </main>
    <script src="script.js"></script>
</body>
</html>

Notice the simplicity of our HTML file.

Step 2: Adding styles

Create a styles.css file for styling the application:

@import url("agregore://theme/vars.css");
html {
    background: var(--ag-theme-background);
    color: var(--ag-theme-text);
    font-family: var(--ag-theme-font-family);
}
#uploadBox {
    padding: 1em;
    border: 0.25em solid var(--ag-theme-primary);
    border-radius: 0.5em;
    display: flex;
    justify-content: center;
    align-items: center;
}

a {
    color: var(--ag-theme-secondary);
    padding:0.15em 0.5em;
    border-radius: 0.5em;
}
main {
    margin: 1em;
}

Explanation of Flex Properties in #uploadBox

Importing the Global Agregore Theme

Step 3: JavaScript for Drag-and-Drop

In your script.js file, add the JavaScript code:

function $(query) {
    return document.querySelector(query)
}

const uploadBox = $('#uploadBox')
uploadBox.ondragover = () => false
uploadBox.ondrop = async (e) => {
    e.preventDefault()
    const { dataTransfer } = e
    if(!dataTransfer) return

    await uploadFiles(dataTransfer.files);
}

const uploadListBox = $('#uploadListBox')

const protocolSelect = $('#protocolSelect')

async function uploadFiles(files) {
    const protocol = protocolSelect.value;

    const formData = new FormData();
    // Append each file to the FormData
    for (const file of files) {
        formData.append('file', file, file.name);
    }

    // Construct the URL based on the protocol
    let url;
    if (protocol === 'hyper') {
        const hyperdriveUrl = await generateHyperdriveKey('drag-and-drop');
        url = `${hyperdriveUrl}`;
    } else {
        url = `ipfs://bafyaabakaieac/`;
    }

    // Perform the upload for each file
    try {
        const response = await fetch(url, {
            method: 'PUT',
            body: formData,
        });

        if (!response.ok) {
            addError(files, await response.text());
        }
        const urlResponse = protocol === 'hyper' ? response.url : response.headers.get('Location');
        addURL(urlResponse);
    } catch (error) {
        console.error(`Error uploading ${files}:`, error);
    }
}

async function generateHyperdriveKey(name) {
    try {
        const response = await fetch(`hyper://localhost/?key=${name}`, { method: 'POST' });
        if (!response.ok) {
            throw new Error(`Failed to generate Hyperdrive key: ${response.statusText}`);
        }
        return await response.text();  // This returns the hyper:// URL
    } catch (error) {
        console.error('Error generating Hyperdrive key:', error);
        throw error;
    }
}

function addURL(url) {
    uploadListBox.innerHTML += `<li><a href="${url}">${url}</a></li>`
}

function addError(name, text) {
    uploadListBox.innerHTML += `<li class="log">Error in ${name}: ${text}</li>`
}

Explaining the code

First, we define a function to simplify document.querySelector

function $(query) {
    return document.querySelector(query);
}

Then, we select the upload box and setup drag and drop events

const uploadBox = $('#uploadBox');
uploadBox.ondragover = () => false;
uploadBox.ondrop = async (e) => {
    e.preventDefault();
    const { dataTransfer } = e;
    if (!dataTransfer) return;

    for (const file of dataTransfer.files) {
        await uploadFile(file);
    }
};

We define the selectors for the uploaded files list and the protocol dropdown selector

const uploadListBox = $('#uploadListBox');
const protocolSelect = $('#protocolSelect');

We create a function to upload the files.

async function uploadFile(file) {
    // ... [full code above]
}

For Hypercore uploads, we generate a Hyperdrive key.

async function generateHyperdriveKey(name) {
    // ... [full code above]
}

Finally, we add two utility functions.

function addURL(url) {
    uploadListBox.innerHTML += `<li><a href="${url}">${url}</a></li>`;
}

function addError(name, text) {
    uploadListBox.innerHTML += `<li class="log">Error in ${name}: ${text}</li>`;
}

Testing the Application

Conclusion

Congratulations! You've just built a decentralized file-sharing app. This tutorial not only introduced you to basic web development concepts but also to the exciting world of decentralized web technologies like IPFS and Hypercore.

Feel free to experiment further with the app, perhaps by adding new features or refining the user interface. The decentralized web is an open canvas for your creativity!