diff --git a/img/app.png b/img/app.png new file mode 100644 index 0000000..64a36e7 Binary files /dev/null and b/img/app.png differ diff --git a/img/app.svg b/img/app.svg new file mode 100644 index 0000000..33ae5e9 --- /dev/null +++ b/img/app.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..f985d06 --- /dev/null +++ b/index.html @@ -0,0 +1,137 @@ + + + + + + EveryApp : The Decentralized App Store + + + + + + + + + + + + + + +
+
+
+ +
+
+ RECENTLY ADDED +
+
+ All Apps +
+
+ +
+ + + + + +
+
+
+ + +
+ + +
+ +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/readme.md b/readme.md index cc3d3a1..59544d9 100644 --- a/readme.md +++ b/readme.md @@ -1,77 +1,21 @@ -# **Arweave’s Perpetual Open Web Hackathon** +## Project Name +EveryApp -## **Introduction** +**LIVE DEMO:** [https://everyapp.net](https://everyapp.net) -Welcome to the perpetual, open permaweb hackathon! +![screenshot](https://i.imgur.com/WndUkNC.png) -At Arweave we firmly believe that the future of the [new web should be open source](https://medium.com/@arweave/towards-an-open-source-web-9ffe201fc044) for the benefit of users and developers alike. We want to help make this a reality, so we’re rewarding developers like yourself for building open source dApps on the permaweb! +## Project Summary +EveryApp is a decentralized app store built on the Arweave Blockchain. It currently supports: -The Arweave itself is a global, permanent hard drive. The permaweb is a decentralised, immutable web built on top of the Arweave. Permaweb apps are built using normal web technologies — HTML, CSS, and Javascript — but are deployed to Arweave’s on-chain storage system, making them permanent and available in a fast, decentralised manner. You can get started and deploy a permaweb app in 2 minutes or less. +- Android apps +- Chromium apps +- FireFox apps -**Time investment:** One afternoon to one weekend. -**Rewards:** +## Project Team -**Basic:** $50 in ETH + 200 AR -A simple application with functional features and a basic UI and styling, hosted on the permaweb. - -**Advanced:** $250 in ETH + 500 AR -A dynamic and interactive web application that is hosted on the permaweb, along with ideas that contribute added functionality to the permaweb eco system. For examples: [Arweave API explorer](https://arweave.net/LHff4F45o7ipDqjoePG0PDG5BD2hLD8xf17OBg4FjRE) & [ArweaveID](https://arweave.net/fGUdNmXFmflBMGI2f9vD7KzsrAc1s1USQgQLgAVT0W0). Ideas that resemble or improve on popular web applications such as [AskWeave](https://arweave.net/HhIjOjxgHYXJU5RVjRYfAR017vbZdujbCSlaA8NQ20U) & [Scribe](https://arweave.net/VOKAC_SYiUzbJrEaIY5SEBh1pf0bGQOd8c7G68nzet4#/) are also encouraged, and all applications must include a UI with basic styling. - -**Store the UI for an existing dApp** on the permaweb: $100 in ETH + 300 AR - -If you’re unsure of which category your application fits into, [head over to our Discord server](https://discord.gg/VxJ3xsm) where both Arweave team members and the wider Arweave developer community can help guide you with your submission ideas! - -## **Resources** - -You can find the docs for getting started deploying your first permaweb app [here](https://docs.arweave.org/developers/tools/arweave-deploy). - -The docs for the JS library that allows you to read from and write to the permaweb are [here](https://github.com/ArweaveTeam/arweave-js). - -Check out some sample apps on ArweaveApps -- served right from the permaweb itself: [](https://arweave.net/35IFq9BcIgpSPti9YDYDiaQy4wMfMIKZ25t7hHZrhek) - -We’re providing some free tokens so you can get started building and deploying - grab your wallet [here](https://tokens.arweave.org/#/wallet). For guidance on how to use the Chrome web extension wallet, check out [this user guide](https://docs.arweave.org/info/wallets/arweave-web-extension-wallet). - -## **Rules** - -We want to let you get started building your app without delay, so ideas for submissions are automatically approved. However, **only submissions that meet the reward criteria outlined below will receive a reward** - so please make sure you review them carefully and make sure your app complies with them. - -If we notice that your submission is unlikely to meet the reward criteria, we will try and let you know in advance, and are happy to offer guidance on how to tweak your idea to fit this guidance. [Holla at us](https://discord.gg/VxJ3xsm) on Discord! - -### **Successful submissions will meet the following criteria:** - -- Submissions must be **your own original work**, and must be **unique or substantially different** from other permaweb applications or submissions to this hackathon. -- We are happy to accept forks of other applications, but the featureset must be substantially different or significantly expanded in the forked version. -- Apps must be **open source, with the full source code available on Github or another open code hosting repository**. Feel free to use whatever OSS licence you prefer. -- Applications should be **sufficiently complex**, as judged by the core Arweave team. For example, a simple ‘hello world’ app is not complex enough to receive a reward. Broken/non-functional submissions will also be rejected. You should aim for a minimal viable product that an end-user could reasonably start to use. -- Your submission must **include a link to a live, functioning, and running instance of the app itself, as well as a link to the repository** it is stored within. It must also be published to ArweaveApps (see the submission process below) -- All applicants must **[join our Discord Dev server](https://discord.gg/VxJ3xsm)** and share your submission in the #on-the-arweave channel upon completion -- All submissions must include a **brief description** of the application and functionality in the GitHub repo -- When you submit your work, please state whether you wish to be judged in the _Basic_ or _Advanced_ Category - -If you wish to submit **more than two** submissions, you are required to [get approval](https://discord.gg/VxJ3xsm) from us before you are eligible to start a 3rd submission. - -## **Reward details** - -- We are currently offering rewards for up to 500 successful submissions in this hackathon -- The value of ETH to USD will be pegged at the time of transfer, which follows successful submission and provision of recipients’ ETH and AR addresses. -- Only submissions which are judged by the core Arweave team to meet all of the submission criteria will receive the rewards - -## **The submission process** - -- Click ‘Start Work’, and you will be automatically accepted so you can start hacking right away. -- Please note, submissions will only receive a reward if they successfully meet the submission criteria listed above. If you have questions, please drop a comment below and we’ll be happy to help. -- Hack! Join us our tech Discord if you have any questions! -- Publish your app to [ArweaveApps](https://arweave.net/35IFq9BcIgpSPti9YDYDiaQy4wMfMIKZ25t7hHZrhek). Also, submit a comment to the GitHub issue for the hackathon campaign [here](https://github.com/ArweaveTeam/Bounties/issues/1), containing a link to your app’s repository, and a link to the live, running version of your app on the permaweb. You can generate a permaweb URL for the app by appending the transaction ID to this gateway link: [https://arweave.net/](https://arweave.net/) -- If you wish to submit more than one project, you will not be able to do so via GitCoin. In this case, you can submit an application by posting your build in the GitHub issue and provide a link to the live deployed app. We also recommend pitching your idea for additional submissions to arweave-sophie on [Discord](https://discord.gg/VxJ3xsm) before starting work. -- If your app meets the reward criteria defined above, we will release the reward! - -## **App ideas** - -This bounty allows you to build whatever app you like on top of the Arweave’s permaweb, but here are some ideas to get you started: - -- Weavelist: A public mailing list and discussion app, similar to weavemail.app except open and unencrypted. -- WeaveDirectory: An index of weavemail.app users allowing short bios to help people find community members to chat to. -- ShowerThoughts: Submit short, interesting ideas to the permaweb, never to be forgotten. - -Remember, we’d love you to join us on our developers’ [Discord server](https://discord.gg/VxJ3xsm) to chat, and get feedback and assistance with building! +* **Name:** Seena Zandipour +* **Email:** craze3@gmail.com +* **Ethereum Address:** 0x5A0f2C1d8E563db79Ee40F979F6bcD0d27f86f80 +* **Role:** CEO, Full-Stack Developer, Solidity Developer, Designer diff --git a/script.js b/script.js new file mode 100644 index 0000000..85e2ade --- /dev/null +++ b/script.js @@ -0,0 +1,346 @@ +$('.grid').click(() => { + $('#content-modal').addClass('active'); + $('.wrapper').addClass('blur'); + $("html, body").animate({ scrollTop: 0 }, "slow"); +}); + +$('.content-modal .icon').click(() => { + $('#content-modal').removeClass('active'); + $('#upload-modal').removeClass('active'); + $('.wrapper').removeClass('blur'); +}); + +$('.upload-button').click(() => { + $('#upload-modal').addClass('active'); + //$('.wrapper').addClass('blur'); + $("html, body").animate({ scrollTop: 0 }, "slow"); +}); + +// Initialize Arweave blockchain connection... +const arweave = Arweave.init({ + host: 'arweave.net',// Hostname or IP address for a Arweave node + port: 443, // Port, defaults to 1984 + protocol: 'https', // Network protocol http or https, defaults to http + timeout: 20000, // Network request timeouts in milliseconds + logging: false, // Enable network request logging +}); + +arweave.network.getInfo().then("Connected to Arweave Network:", console.log); + +// Connect to IPFS +var ipfs = window.IpfsHttpClient('ipfs.infura.io', '5001', {protocol: 'https'}); +console.log("Connected to IPFS via Infura (:5001)"); + +function upload(wallet) { + + // Read chosen wallet file (NOT UPLOADED!) + var reader = new FileReader(); + reader.onload = function(event) { + + // Parse JSON + var jsonObj = JSON.parse(event.target.result); + + // Fetch public key + arweave.wallets.jwkToAddress(jsonObj).then((address) => { + console.log("Logged in as: ", address); + + // Fetch balance + arweave.wallets.getBalance(address).then((balance) => { + var total = arweave.ar.winstonToAr(balance); + console.log("Balance: " + total + " AR"); + + // Check if we can upload + if(balance <= 0) + { + alert("Error: You must have AR in your wallet to upload an app!") + } else { + + // Get app file + var app = $('#appfile').prop('files')[0]; + var arr = app.name.split("."); + var filetype = arr[arr.length-1]; + //console.log("appfile TYPE:", filetype); + + // Create a transaction + arweave.createTransaction({ + data: 'Hello world!', + }, jsonObj).then((transaction) => { + transaction.addTag('Content-Type', 'text/html'); + transaction.addTag('appstore_filetype', filetype); + //transaction.addTag('appstore_title', $('#title').val()); + + // PROCESS UPLOAD: + // Get selected image + var file = $('#appimage').prop('files')[0]; + + var reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = function () { + console.log("Got image contents: ", reader.result); + var fileString = reader.result; + + // Create new app object to store metadata + var newApp = { + title: $('#title').val(), + description: $('#description').val(), + image: fileString + }; + + // Upload app/metadata object to IPFS: + console.log("Upload to IPFS: ", newApp, JSON.stringify(newApp)); + var metadata = JSON.stringify(newApp); + + const Buffer = window.IpfsApi().Buffer; + var ifpsBuffer = Buffer.from(metadata); + ipfs.add([ifpsBuffer], {pin:false}) + .then(response => { + var hash = response[0].hash; + if(hash) { + + transaction.addTag('appstore_metadata', hash); + + // Sign the transaction + arweave.transactions.sign(transaction, jsonObj).then((info) => { + console.log("Signed transaction:", transaction) + + // Submit the transaction + arweave.transactions.post(transaction).then((response) => { + if(!response) { + alert("Error: Unable to submit transaction!"); + } + else { + var success = 0; + switch(response.status) { + case 200: + console.log("Success!"); + success = 1; + break; + case 400: + alert("Error: Invalid transaction!"); + break; + case 500: + alert("Error: Unknown error!"); + break; + } + + } + }); + + }); + } + }); + + }; + reader.onerror = function (error) { + console.log('Error: ', error); + }; + + }); + } + }); + }); + } + reader.readAsText(document.getElementById('walletUpload').files[0]); +} + + +function getApps(filetype) { + // Create query + var query; + if(!filetype || filetype == "all") { + query = { + op: "or", + expr1: { + op: "equals", + expr1: "appstore_filetype", + expr2: "ipa" + }, + expr2: { + op: "equals", + expr1: "appstore_filetype", + expr2: "apk" + }, + expr3: { + op: "equals", + expr1: "appstore_filetype", + expr2: "crx" + }, + expr4: { + op: "equals", + expr1: "appstore_filetype", + expr2: "xpi" + } + }; + } else { + query = { + op: "equals", + expr1: "appstore_filetype", + expr2: filetype + }; + } + + // Query for all appstore transactions/uploads + arweave.arql(query).then((txids) => { + $('#apps').html(''); + console.log(txids); + // For each app... + var count = 0; + txids.forEach(txn_id => { + arweave.transactions.get(txn_id).then(transaction => { + // Use the get method to get a specific transaction field. + console.log(txn_id, transaction.get('signature')); + // NLiRQSci56KVNk-x86eLT1TyF1ST8pzE-s7jdCJbW-V... + console.log(transaction.get('data')); + //CjwhRE9DVFlQRSBodG1sPgo8aHRtbCBsYW5nPSJlbiI-C... + // Get the data base64 decoded as a Uint8Array byte array. + console.log(transaction.get('data', {decode: true})); + //Uint8Array[10,60,33,68,79,67,84,89,80,69... + // Get the data base64 decoded as a string. + console.log(transaction.get('data', {decode: true, string: true})); + // ARWEAVE / PEER EXPLORER + transaction.get('tags').forEach(tag => { + let key = tag.get('name', {decode: true, string: true}); + let value = tag.get('value', {decode: true, string: true}); + console.log(`${key} : ${value}`); + + if(key == 'appstore_metadata') { + ipfs.get(value, function (err, files) { + files.forEach((file) => { + console.log(file.path) + var payload = file.content.toString('utf8'); + + if(payload.substring(0,1) == '{') { + var metadata = JSON.parse(payload); + console.log("Metadata = ", metadata); + + // Set link + var link = 'https://arweave.net/'+txn_id; + var title = metadata.title; + var description = metadata.description; + var image = metadata.image; + + count++; + if(count == 1) { + $('#apps').append('
'+description+'
'+title+'
'); + } + if(count == 2) { + $('#apps').append('
World Premiere
The Art of the Impossible
Inside the extraordinary world of Monument Valley 2
'); + } + if(count == 3) { + $('#apps').append('
App of the day
Hitlist
Cheap flights, airline tickets
'); + } + if(count == 4) { + $('#apps').append('
Collection
The Perfect Coffee Break
'); + count = 0; + } + if(count == 1 || count % 3 == 0) { + console.log("count = ", count); + $('#apps').append(''); + } + } + + }) + }); + + } + + if(key == 'appstore_filetype') { + // set file + } + }); + // Content-Type : text/html // User-Agent : ArweaveDeploy/1.1.0 + }); + }); +}); +} + +getApps("all"); + + +// IPFS Stuff: + +function getBase64(file) { + var reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = function () { + console.log("Got image contents: ", reader.result); + }; + reader.onerror = function (error) { + console.log('Error: ', error); + }; +} + +async function ipfsUpload(data) { + const Buffer = window.IpfsApi().Buffer; + var ifpsBuffer = Buffer.from(data); + ipfs.add([ifpsBuffer], {pin:false}) + .then(response => { + var hash = response[0].hash; + if(hash) { + console.log("IPFS hash = ", hash); + //hash = web3.utils.asciiToHex(hash); + //hash = web3.utils.hexToBytes(hash); + + // Write to blockchain + console.log("HEXed Hash =", hash); + /* + //hash = web3.utils.asciiToHex(hash); //<-- Convert to hex so we can write to blockchain + contractInstance.methods.broadcast(hash).send({from: account}) + .on('transactionHash', function(hash){ + //alert("Transaction Pending. Hash = " + hash); + //alert("Transaction Pending. Hash = ", hash); + window.open("https://etherscan.io/tx/"+hash); + }) + .on('receipt', function(receipt){ + console.log("Transaction complete! TXN ID = ", receipt); + alert("Transaction complete!"); // TXN ID = " + receipt); + window.location.replace("verify#"+receipt.events.LogHash.returnValues.hash); + }) + .on('confirmation', function(confirmationNumber, receipt){ + if(confirmationNumber<20) + console.log("Confirmation #", confirmationNumber, receipt); + }) + .on('error', console.error); + */ + + } + }).catch((err) => { + console.log(err); + return false; + }); +} + +function fetchIPFS(hash) { + ipfs.get(hash, function (err, files) { + files.forEach((file) => { + //console.log(file.path) + console.log(file.content.toString('utf8')) + }) + }); +} + +function processUpload() { + // Get selected image + var file = $('#appimage').prop('files')[0]; + + var reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = function () { + console.log("Got image contents: ", reader.result); + var fileString = reader.result; + + // Create new app object to store metadata + var newApp = { + title: $('#title').val(), + description: $('#description').val(), + image: fileString + }; + + // Upload app/metadata object to IPFS: + console.log("Upload to IPFS: ", newApp, JSON.stringify(newApp)); + ipfsUpload(JSON.stringify(newApp)); + }; + reader.onerror = function (error) { + console.log('Error: ', error); + }; +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..731bd26 --- /dev/null +++ b/style.css @@ -0,0 +1,362 @@ +@import url("https://fonts.googleapis.com/css?family=Open+Sans|Raleway"); +html { + box-sizing: border-box; +} + +*, *:before, *:after { + box-sizing: inherit; +} + +body { + background-color: #f4f4f4; + font-family: 'Open Sans', sans-serif; + position: relative; +} + +.wrapper { + margin: 0 auto; + min-width: 400px; + max-width: 920px; + position: relative; +} +.wrapper.blur { + -webkit-filter: blur(4px); + filter: blur(4px); +} + +.top-status-bar { + padding: 5px 8px; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 10; + max-width: 920px; + margin: 0 auto; + display: flex; + justify-content: space-between; + background-color: #f4f4f4; + color: #555; + font-size: .65em; + font-weight: 900; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +.top-status-bar .name::after { + content: '\f1eb'; + font-family: FontAwesome; + margin-left: 5px; +} +.top-status-bar .battery-status::before { + content: '\f124 \f294'; + font-family: FontAwesome; + margin-right: 5px; +} +.top-status-bar .battery-status::after { + content: '\f241'; + font-family: FontAwesome; + margin-left: 5px; + font-weight: 100; +} + +.content-wrapper:last-child { + margin-bottom: 100px; +} + +.date-indicators { + margin: 0 30px 0; + padding-top: 50px; + font-family: 'Raleway', sans-serif; +} +.date-indicators .date { + margin-bottom: 5px; + color: #aaa; + text-transform: uppercase; + font-size: .8em; +} +.date-indicators .day { + font-size: 1.8em; + font-weight: 900; +} + +.grid-container { + padding: 20px; +} +.grid-container .gc-1 { + display: grid; + grid-template-columns: 2fr 1fr; + grid-gap: 40px; +} +@media screen and (max-width: 550px) { + .grid-container .gc-1 { + grid-template-columns: 1fr; + } +} +@media screen and (min-width: 550px) and (max-width: 950px) { + .grid-container .gc-1 { + grid-template-columns: 1fr 1fr; + } +} +.grid-container .gc-2 { + margin-top: 40px; + display: grid; + grid-template-columns: 1fr 2fr; + grid-gap: 40px; +} +@media screen and (max-width: 550px) { + .grid-container .gc-2 { + grid-template-columns: 1fr; + } +} +@media screen and (min-width: 550px) and (max-width: 950px) { + .grid-container .gc-2 { + grid-template-columns: 1fr 1fr; + } +} +.grid-container .grid { + height: 300px; + padding: 20px; + border: 1px solid #f4f4f4; + border-radius: 15px; + box-shadow: 2px 2px 40px -12px #999; + cursor: pointer; + position: relative; +} +.grid-container .grid .upper-headline { + text-transform: uppercase; + font-size: .8em; + word-spacing: 2px; + color: #999; + margin-bottom: 5px; +} +.grid-container .grid .headline { + font-size: 1.5em; + font-weight: 900; + line-height: 1.3; + margin-bottom: 5px; +} +.grid-container .grid .content.atd { + margin-top: 30px; + text-transform: uppercase; + font-size: 3em; + font-weight: 900; +} +.grid-container .grid .footline { + font-size: .85em; + position: absolute; + bottom: 15px; + font-family: 'Raleway', sans-serif; +} +.grid-container .grid .footline .head { + font-weight: 900; + margin-bottom: 5px; +} + +.footer-menu { + max-width: 920px; + margin: 0 auto; + padding: 15px; + position: fixed; + bottom: 0; + left: 0; + right: 0; + display: flex; + justify-content: space-around; + align-items: center; + border-radius: 5px; + background-color: #f4f4f4; + color: #aaa; +} +@media screen and (max-width: 550px) { + .footer-menu { + font-size: 12px; + } +} +.footer-menu .menu { + display: flex; + justify-content: space-around; + align-items: center; + cursor: pointer; + font-size: .9em; +} +.footer-menu .menu:before { + font-family: FontAwesome; + margin-right: 8px; + font-size: 22px; + font-weight: 100; +} +.footer-menu .menu.active { + color: #007bff; +} +.footer-menu .today::before { + content: '\f03a'; +} +.footer-menu .game::before { + content: '\f17b'; +} +.footer-menu .apps::before { + content: '\f268'; +} +.footer-menu .updates::before { + content: '\f269'; +} +.footer-menu .search::before { + content: '\f002'; +} +.footer-menu .upload::before { + content: '\f0ee'; +} + +.upload-button { + border-left: 1px solid #cecece; + padding-left: 28px; + } + +.content-modal { + position: absolute; + top: 0; + left: 0; + z-index: 20; + width: 100vw; + height: 100%; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + box-shadow: 3px 2px 3px #444; + opacity: 0; + visibility: hidden; + transition: opacity .4s ease-in-out, -webkit-transform .5s ease-in-out .2s; + transition: opacity .4s ease-in-out, transform .5s ease-in-out .2s; + transition: opacity .4s ease-in-out, transform .5s ease-in-out .2s, -webkit-transform .5s ease-in-out .2s; + -webkit-transform: translate(-5px, -5px) scale(1.01); + transform: translate(-5px, -5px) scale(1.01); +} +.content-modal.active { + opacity: 1; + visibility: visible; + -webkit-transform: translate(0, 0) scale(1); + transform: translate(0, 0) scale(1); +} +.content-modal .modal-body { + background-color: #fff; + width: 700px; + height: 650px; + border-radius: 10px; + padding: 30px; + margin: 90px auto; +} +.content-modal .modal-body .header { + margin-bottom: 10px; + display: flex; + justify-content: space-between; + align-items: center; +} +.content-modal .modal-body .header .label { + font-weight: 900; +} +.content-modal .modal-body .header .icon { + padding: 8px; + cursor: pointer; + font-size: 1.2em; + color: #bbb; + align-self: flex-start; +} +.content-modal .modal-body .header .upper-headline { + text-transform: uppercase; + font-size: .8em; + word-spacing: 2px; + color: #999; + margin-bottom: 5px; +} +.content-modal .modal-body .header .headline { + font-size: 1.5em; + font-weight: 900; + line-height: 1.3; + margin-bottom: 5px; +} +.content-modal .modal-body .body-content { + overflow-y: scroll; + height: 550px; + line-height: 2; + word-spacing: 2px; + color: #777; + font-family: 'Raleway', sans-serif; +} +.content-modal .modal-body .body-content a { + color: #777; + text-decoration: none; +} +.content-modal .modal-body .body-content a:hover, .content-modal .modal-body .body-content a:focus, .content-modal .modal-body .body-content a:visited { + text-decoration: none; +} +.content-modal .modal-body .body-content a:hover { + color: #444; +} +.content-modal .modal-body .ajduke { + text-align: center; + font-weight: 900; +} +.content-modal .modal-body .ajduke .fa-heart { + color: tomato; + padding: 0 5px; +} +.content-modal .modal-body .ajduke a { + text-decoration: none; + border-bottom: 1px dotted; +} +.content-modal .modal-body .ajduke a:hover, .content-modal .modal-body .ajduke a:visited, .content-modal .modal-body .ajduke a.active { + color: inherit; +} + + + + +.download-button-wrapper { + margin: 20px auto; + max-width: 360px; +} +.download-button-wrapper .download-btn { + font-size: 20px; + background: #EC422B; + color: #fff; + padding: 10px 20px; + width: 100%; +height: 60px; +border-radius: 6px; +} +.download-button-wrapper .download-btn i { + font-size: 18px; +} +.download-button-wrapper .download-btn span { + padding-left: 10px; +} +.download-button-wrapper .download-btn:hover { + background: #CE2811; +} +.download-button-wrapper .download-btn:active { + background: #9F1401; +} + +.list-wrapper { + margin: 0 auto; + max-width: 150px; +} +.list-wrapper .list-download-btn { + background: #F7F7F7; + padding: 10px 20px; + margin-left: 20px; +} +.list-wrapper .list-download-btn i { + font-size: 14px; +} +.list-wrapper .list-download-btn span { + padding-left: 10px; +} +.list-wrapper .list-download-btn:hover { + background: #EDEDED; +} +.list-wrapper .list-download-btn:active { + background: #535353; + color: #fff; +}