TUTORIAL - Create a Steem Browser Extension - JavaScript - Basic - Part 3

in #utopian-io8 years ago (edited)

extension_part3.png

What Will I Learn?

  • Use a custom extension Icon
  • Use extension options
  • A loading indicator screen

Requirements

  • Read my previous parts (Click here)
  • A TextEditor (e.g Atom.io)
  • Basic JavaScript understanding

Difficulty

  • Basic

Tutorial Contents

Custom Extension Icon

Every good product has its own Icon or Logo. I will show you as first step in this Tutorial how you can add a custom icon to your extension. For this purpose you need your logo ready as .png as 16x16px, 48x48px, 128x128px. Next open the manifest.json and add inside the icons key following values

"icons": {
      "16": "./img/icon_16px.png",
      "48": "./img/icon_48px.png",
      "128": "./img/icon_128px.png"
}

Please make sure your icons exists in the img folder. (Btw. you can specify any path you want)

Test it and reload your extension
Screen Shot 2018-01-29 at 15.14.05.png

Using Extension Option Page

Every good extension does not define it's user depended variables inside of the code. To make it dynamic for every user we will use chrome extension options feature (here). These options can be opened and edited inside the lists of all your installed extensions.

Screen Shot 2018-01-29 at 15.18.21.png
(This is what the Options Button is used for)

But how can we as creator of the extension create one ?
Open your manifest.json and add following code between browser_action and content_security_policy:

"browser_action": {
     ...
  },
  "options_ui": {
    "page": "options.html",
    "chrome_style": true
  },
  "permissions": [
    "storage"
  ],
  "content_security_policy": "script-src 'self' https://cdn.steemjs.com; object-src 'self'"

options_ui defines the html which is used for the option popup, use options.html.
permissions is used because we need to safe the set options, therefore we need to access the local storage and need permissions for that.

Create now the options.html and options.js files !

options.html

<html>
<head>
    <title>Options</title>
    <script type="text/javascript" src="options.js"></script>
</head>
<body>
    Steem User Account:
    <input id="username-input" type="text" name="" value="">
    <hr />
    <button id="save">Save</button>
    <div id="status"></div>
</body>
</html>

In the header we include the options.js script. Else we define some simple design with id's to modify the html from our script!

options.js

var input = null;
window.addEventListener('load', onLoad);
function onLoad() {
    input = document.getElementById("username-input");
    document.getElementById("save").addEventListener('click', saveOptions);
    chrome.storage.sync.get(["username"], function(items) {
        var username = '';
        if (items.username !== undefined) {
            username = items.username;
        }
        input.value = username;
    });
}
function saveOptions() {
    const username = input.value;
    chrome.storage.sync.set({
        username: username,
    }, function() {
        var status = document.getElementById('status');
        status.textContent = 'Options saved.';
        setTimeout(function() {
            status.textContent = '';
        }, 750);
    });
}

We have to functions saveOptions and onLoad. onLoad is called when our html body loads, and sets the input variable accordingly as well as adding a click event for when the save button is pressed which fires the function saveOptions. Also does onLoad read our local storage and searches for the key "username". If a "username" is already set we will display it as the value for our input field.

saveOptions first gets the input field value and then tries to set it in our local storage for the key "username". If done it calls a callback where we shortly display a Options saved messaged below our save button (750ms).

Try it
Screen Shot 2018-01-29 at 15.50.55.png

Using a loading indicator

At this point of the series you maybe realized that loading our account informations takes some time. Plus loading data from the local storage consumes time too, our whole application get asynchronous and needs to wait for the tasks to finish BEFORE displaying the actual information. But what should we display in the meantime ? The empty design ? Nah, this is what loading indicators are meant for!

The idea is to show a loading indicator until our whole data is ready, then hide the indicator and show the actual design. For this purpose edit your index.html and index.js

index.html

<html>
    <head>
        <script src="https://cdn.steemjs.com/lib/latest/steem.min.js"></script>
        <script src="./index.js"></script>
        <style>
            .profile-pic {
                margin: auto;
                width: 36px;
                height: 36px;
                border-radius: 18px;
                overflow: hidden;
                background-size: cover;
                background-repeat: no-repeat;
                background-position: 50% 50%;
            }
            .hidden {
                display: none;
            }
            .loader {
                border: 4px solid #f3f3f3;
                border-top: 4px solid #3498db;
                border-radius: 50%;
                width: 15px;
                height: 15px;
                margin: auto;
                animation: spin 2s linear infinite;
            }
            @keyframes spin {
                0% { transform: rotate(0deg); }
                100% { transform: rotate(360deg); }
            }
        </style>
    </head>
    <body style="width: 200px">
        <div id="indicator" class="loader">
        </div>
        <div id="container" class="hidden">
            <div id="username" style="text-align: center;">
            </div>
            <div id="follow_stats">
            </div>
            <hr>
            <div id="post_count">
            </div>
            <div id="steem_balance">
            </div>
            <div id="sbd_balance">
            </div>
        </div>
    </body>
</html>

We added 2 new style classes: hidden (to hide elements) and loader (presenting a loading indicator).
Please read more about keyframes here

In the html we added a new div with the id "indicator" and as styling the class "loader".
The container div should be hidden from now on!

index.js

steem.api.setOptions({ url: 'wss://rpc.buildteam.io' });
window.addEventListener('load', onLoad);
function onLoad() {
    chrome.storage.sync.get(["username"], function(items) {
        var username = items.username;
        if (username === undefined) {
            return; // Should show error
        }
        getAccountData(username);
    });
}
function getAccountData(username) {
    var followCount = 0;
    var followingCount = 0;
    const indicator = document.getElementById("indicator");
    const container = document.getElementById("container");
    steem.api.getAccounts([username], function (err, result) {
        if (err || !result) {
            console.log('Error loading account: ' + err); // Should show error
            return;
        }
        const userData = result[0];
        console.log(userData);
        const userHeader = document.getElementById("username");
        userHeader.innerHTML = "<div class='profile-pic' style='background-image: url(https://steemitimages.com/u/"+ userData.name +"/avatar);'></div>" +
        "<div>@"+ userData.name +"</div>";
        const postCount = document.getElementById("post_count");
        postCount.innerHTML = "<div style='display: inline-block;'>Post Count</div>" +
        "<div style='display: inline-block; float: right'>"+ userData.post_count +"</div>";
        const steemBalance = document.getElementById("steem_balance");
        steemBalance.innerHTML = "<div style='display: inline-block;'>STEEM</div>" +
        "<div style='display: inline-block; float: right'>"+ userData.balance +"</div>";
        const sbdBalance = document.getElementById("sbd_balance");
        sbdBalance.innerHTML = "<div style='display: inline-block;'>SBD</div>" +
        "<div style='display: inline-block; float: right'>"+ userData.sbd_balance +"</div>";
        steem.api.getFollowCount('moonrise', function(err, result) {
            if (err || !result) {
                console.log('Error loading follow count: ' + err); // Should show error
                return;
            }
            followCount = result.follower_count;
            followingCount = result.following_count;
            const followStata = document.getElementById("follow_stats");
            followStata.innerHTML = "<div style='display: inline-block;'>Follower: "+ followCount +"</div>" +
            "<div style='display: inline-block; float: right'>Following: "+ followingCount +"</div>";
            // Loaded everthing, so show content!
            indicator.className += " hidden";
            container.classList.remove('hidden');
        });
    });
}

(Click here for better formatted view)

Basically we changed the logic of our index.js as follows. First call the onLoad function and load the username from our settings. Then get the Account data and then the following / follower data. If something fails, the loading indicator won't hide. In this case we will show an error in the next episode.

If nothing fails we will hide the indicator & show our content div.

indicator.className += " hidden";
container.classList.remove('hidden');

The changes might look confusing, but basically we are now capsuling our api calls.

Try it and you will see the loading indicator before it shows the content !

Thanks for reading :)

What's up next ?

  • Error Screen

Full Source Code

Curriculum



Posted on Utopian.io - Rewarding Open Source Contributors

Sort:  

Thank you for the contribution. It has been approved.

Would've been cool to see a GIF of the loading indicator!

You can contact us on Discord.
[utopian-moderator]

Hey @moonrise I am @utopian-io. I have just upvoted you!

Achievements

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • Seems like you contribute quite often. AMAZING!

Suggestions

  • Contribute more often to get higher and higher rewards. I wish to see you often!
  • Work on your followers to increase the votes/rewards. I follow what humans do and my vote is mainly based on that. Good luck!

Get Noticed!

  • Did you know project owners can manually vote with their own voting power or by voting power delegated to their projects? Ask the project owner to review your contributions!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x

Coin Marketplace

STEEM 0.04
TRX 0.32
JST 0.077
BTC 65548.83
ETH 1717.21
USDT 1.00
SBD 0.42