Skip to content

Instantly share code, notes, and snippets.

@ewathedoer
Last active August 9, 2016 11:21
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 ewathedoer/b97f3206601ef4c95246da6907a11cf1 to your computer and use it in GitHub Desktop.
Save ewathedoer/b97f3206601ef4c95246da6907a11cf1 to your computer and use it in GitHub Desktop.
Twitch Viewer
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8">
<title>Twitch Viewer</title>
<meta name="author" content="Designed and coded by the doer.">
<meta name="description" content="The project created during Free Code Camp course according to Twitch.tv specification">
</head>
<body class="container-fluid">
<section id="page-header-section" class="page-header" role="banner">
<h1>Game Watcher</h1>
</section>
<nav role="navigation">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a id="games-tab" href="#game-tab" aria-controls="games-tab" role="tab" data-toggle="tab">Games</a></li>
<li role="presentation"><a id="player-tab" href="#user-tab" aria-controls="player-tab" role="tab" data-toggle="tab">Players</a></li>
</ul>
<!-- Tab panels -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active fade in" id="game-tab">
<section id="get-games" class="row">
<div role="search" class="input-group input-group-lg col-xs-10 col-xs-offset-1 col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4">
<span class="input-group-addon glyphicon glyphicon-search" id="sizing-addon1"></span>
<input id="search-game" type="text" class="form-control" placeholder="Look for a game" aria-label="Look for a game" aria-describedby="sizing-addon1" autocomplete="off">
</div>
</section>
<!-- template section for displaying games created dinamically-->
<section id="game-list">
<div class="row">
<div class="game template text-center col-xs-12 col-sm-6 col-sm-offset-0 col-md-4">
<a href="#" class="game-item">
</a>
</div> <!-- col -->
</div> <!-- row -->
</section> <!-- game-list -->
<!-- template section for displaying streams -->
<section id="streams">
<div id="stream-row" class="row">
<div class="stream template text-center col-xs-12 col-sm-10 col-sm-offset-1 col-md-4 col-md-offset-0" id="">
<a href="" class="stream-link" target="_blank">
<img src="" alt="" class="stream-image img-responsive center-block" />
<span class="stream-title"></span>
<span class="watching-now"></span>
<span class="language"></span>
</a>
</div> <!-- col -->
</div> <!-- row -->
<div id="off-stream" class="hidden text-center">
Nobody is playing <span class="game-off"></span> now.
<br/>Have a look at other games, start streaming your round or start coding!
</div>
<div id="games-to-top" class="row text-right hidden">
<div class="col-xs-12 col-sm-10 col-sm-offset-1">
<a id="games-top" href="#page-header-section">
back to top
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>
</a>
</div> <!-- col -->
</div> <!-- row -->
</section> <!-- streams -->
</div>
<div role="tabpanel" class="tab-pane fade" id="user-tab">
<!-- add a user by name -->
<section id="add-user" class="row">
<div class="input-group input-group-lg col-xs-10 col-xs-offset-1 col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4" name="add a Twitch player to follow">
<span class="input-group-addon glyphicon glyphicon-user" id="sizing-addon2"></span>
<input id="search-user" type="text" class="form-control" placeholder="Twitch player" aria-label="Add a Twitch player to follow" aria-describedby="sizing-addon1" autocomplete="off" data-toggle="popover" data-trigger="manual" title="No need to double users" data-content="The user has already been added to the list. Look around." data-placement="bottom">
<span class="input-group-btn">
<button id="add-button" class="btn btn-default" type="button">
Add
</button>
</span>
</div>
</section>
<section id="user-selection" class="row">
<div class="col-xs-12 col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-4 col-lg-offset-4">
<span>Show</span>
<span class="toggle">
<label class="checkbox-inline">
<input id="toggle" type="checkbox" checked data-toggle="toggle" data-on="all" data-off="online" data-onstyle="info" data-offstyle="success" data-style="toggle-pace">
</label>
</span>
<span>players</span>
</div> <!-- col -->
</section> <!-- user selection -->
<!-- template section for displaying users created dinamically-->
<section id="user-section" class="row">
<div id="users" class="col-xs-12 col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-4 col-lg-offset-4 text-center">
<!-- user templates -->
<div class="online template" id="">
<a href="" class="link" target="_blank">
<span class="row">
<span class="col-xs-12 col-sm-4 col-md-6">
<img src="" alt="" class="image img-responsive" />
</span>
<span class="col-xs-12 col-sm-8 col-md-6">
<span class="title"></span>
<span class="user"></span>
</span>
</span>
</a>
</div>
<div class="offline template" id=""></div>
</div> <!-- col -->
</section> <!-- users & row-->
<div id="everybody-offline" class="hidden text-center">
Nobody from the Twitch players you follow is streaming now.
<br/>Look for new players to follow in games or check the names on <a href="https://www.twitch.tv/" target="_blank">Twitch</a>!
</div>
<div id="back-to-top" class="row text-right hidden">
<div class="col-xs-12 col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-4 col-lg-offset-4">
<a id="arrow-top" href="#page-header-section">
back to top
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>
</a>
</div> <!-- col -->
</div> <!-- row -->
</div> <!-- user tab -->
</div> <!-- tab content -->
</nav> <!-- tabs -->
<footer>
<div class="row">
<div class="col-xs-10 col-xs-offset-1 col-md-7 col-md-offset-1 text-center">
<div id="fcc-box">
<h5>
No CODE, no&nbsp;GAME
</h5>
<p>
If you want to learn to code, <a href="https://www.freecodecamp.com" target="_blank">Free Code Camp</a> is a place to go! You can watch Free Code Camp users coding on Twitch.
</p>
<!-- FCC online -->
<div class="card-fcc online" id="fcc-on">
<a href="" class="link" target="_blank">
<img src="" alt="" class="image img-responsive" />
<h4 class="title"></h4>
</a>
</div>
<!-- FCC offline-->
<div class="card-fcc offline" id="fcc-off">
<a href="https://www.twitch.tv/freecodecamp/profile" class="link" target="_blank">
<img src="http://theonewhodo.es/twitch/fcc-icon-big.png" alt="Free Code Camp" class="img-responsive center-block img-footer" />
<h4 class="title">
isn't streaming now.
</h4>
</a>
</div>
</div> <!--fcc box -->
</div> <!-- col FCC -->
<div class="col-xs-10 col-xs-offset-1 col-md-4 col-md-offset-0 text-center">
<div class="scratch">
<h3>
Coding for kids
</h3>
<p>
<a href="https://scratch.mit.edu/about/" class="link" target="_blank">Scratch</a> is where kids learn to code for&nbsp;free.
<br/>Pass the info to young creators!
</p>
</div> <!-- scratch-box -->
<div class="twitch box">
<h3>Stream on Twitch</h3>
<p>
Wanna have your Twitch channel?
<br/><a href="https://www.twitch.tv" target="_blank">Create your Twitch account</a>
<br/> and start streaming today!
</p>
</div> <!-- twitch-box -->
<div class="author box">
<p>
Designed and coded by <a href="http://theonewhodo.es/portfolio/" target="_blank">the doer</a>
</p>
</div> <!-- author-box -->
</div> <!-- col -->
</div> <!-- row -->
</footer>
</body>
</html>
// the issue with opening Twitch links: https://github.com/FreeCodeCamp/freecodecamp/issues/1340
var chosenGames = ["Minecraft", "Mario Kart 8"];
var chosenUsers = ["oisketchio", "hengestwitched", "stainbesgames", "mrspeedrun", "akimgames", "cloakedyoshi"];
function addUserBox (name, init) {
// bypassing the existance check when initializing the site
if (addUsersToLocalStorage(name) || init) {
$("#search-user").popover("hide");
getUser(name);
} else {
$("#search-user").popover("show");
}
}
function getUser (name) {
$.getJSON("https://api.twitch.tv/kraken/streams/" + name + "?callback=?", function (result) {
// when the account does not exist or the account is closed
if (result.hasOwnProperty("error")) {
var clonedOffline = $(".offline.template").clone();
clonedOffline.attr("id", "offline-" + name).removeClass("template");
$("#users").prepend(clonedOffline);
$("#offline-" + name).html("User " + name + " doesn't exist on Twitch.");
}
//when a user is offline
else if (result.stream == null){
var clonedOffline = $(".offline.template").clone();
clonedOffline.attr("id", "offline-" + name).removeClass("template");
$("#users").prepend(clonedOffline);
$("#offline-" + name).html(name + " is offline");
}
//when a user is streaming
else {
// hiding everybody offline hint in case a new user added when everybody is offline
$("#everybody-offline").addClass("hidden");
var clonedOnline = $(".online.template").clone();
clonedOnline.attr("id", "online-" + name).removeClass("template");
$("#users").prepend(clonedOnline);
var id = "#online-" + name;
$(id + " .link").attr("href", result.stream.channel.url);
$(id + " .title").html(result.stream.channel.status);
$(id + " .user").html("by " + result.stream.channel.display_name);
$(id + " .image").attr("src", result.stream.preview.medium);
$(id + " .image").attr("alt", result.stream.channel.display_name);
}
});
}
function addGamesToLocalStorage (name) {
// Check browser support for local storage
if (typeof(Storage) !== "undefined") {
// check if game name was once chosen to the array, if indexOf equals -1 it does not exist in the array yet
if (chosenGames.indexOf(name) == -1) {
// Store chosen games by appending them to the chosenGames array
chosenGames.push(name);
// local storage requires converting strings to the format needed
localStorage.setItem("gameName", JSON.stringify(chosenGames));
return true;
} else {
return false;
}
}
// whether local storage is supported by a browser or not the button should be possible to add
return true;
}
function addGameButton (name, init) {
// bypassing the existance check when initializing the site
if (addGamesToLocalStorage(name) || init) {
var clonedGame = $(".game.template").clone();
// setting html of selected item
$(".game-item", clonedGame).html(name);
clonedGame.removeClass("template");
$("#game-list").append(clonedGame);
}
}
function getStreams (game) {
// cleaning results from previous streams list but leaving template
$(".stream:not(.template):not(#off-stream)").remove();
$.getJSON("https://api.twitch.tv/kraken/streams?game=" + encodeURI(game) + "&callback=?", function (result) {
// when nobody plays the game right now
if (result.streams.length == 0) {
$(".game-off").html(game);
$("#off-stream").removeClass("hidden");
} else {
$("#off-stream").addClass("hidden");
for (var i=0; i<result.streams.length; i++) {
var stream = result.streams[i];
//when a user is streaming
var clonedStream = $(".stream.template").clone();
clonedStream.attr("id", "stream-" + stream._id).removeClass("template");
$("#streams #stream-row").append(clonedStream);
var id = "#stream-" + stream._id;
$(id + " .stream-link").attr("href", stream.channel.url);
$(id + " .stream-title").html(stream.channel.status);
$(id + " .stream-image").attr("src", stream.preview.medium);
$(id + " .stream-image").attr("alt", stream.channel.display_name);
$(id + " .language").html("Language: " + stream.channel.language);
var counter;
if (stream.viewers == 1) {
counter = " person is ";
} else {
counter = " people are ";
}
$(id + " .watching-now").html(stream.viewers + counter +"watching now");
}
}
// scrolling to the stream section when game button clicked
$('html, body').animate({
scrollTop: $("#streams").offset().top
}, 1000);
// arrow to top
gamesTop();
});
}
function addTwitchPlayer () {
var name = $("#search-user").val();
addUserBox(name);
// cleaning search
$("#search-user").val("");
arrowBack();
}
function addUsersToLocalStorage (name) {
// check browser support
if (typeof(Storage) !== "undefined") {
// check if a user name was once chosen to the array, if indexOf equals -1 it does not exist in the array yet && prevent empty strings to be add a user box
if (chosenUsers.indexOf(name) == -1 && name !== "") {
// store chosen users by prepending them to the chosenUsers array
chosenUsers.push(name);
// local storage requires converting strings to the format needed
localStorage.setItem("userName", JSON.stringify(chosenUsers));
return true;
} else {
return false;
}
}
// whether local storage is supported by a browser or not the user should be added
return true;
}
function getFreeCodeCamp (name) {
var name = "freecodecamp";
$.getJSON("https://api.twitch.tv/kraken/streams/" + name + "?callback=?", function (result) {
// when the account does not exist or the account is closed
if (result.hasOwnProperty("error")) {
fccFooter(false);
$("#fcc-off").html("Write to Quincy and ask about FCC Twitch.");
}
//when FCC is offline
else if (result.stream == null){
fccFooter(false);
}
//when FCC is streaming
else {
fccFooter(true);
var id = "#fcc-on";
$(id + " .link").attr("href", result.stream.channel.url);
$(id + " .title").html(result.stream.channel.status);
$(id + " .image").attr("src", result.stream.preview.medium);
$(id + " .image").attr("alt", result.stream.channel.display_name);
}
});
}
function fccFooter(live){
if (live) {
$("#fcc-off").addClass("hidden");
$("#fcc-on").removeClass("hidden");
} else {
$("#fcc-off").removeClass("hidden");
$("#fcc-on").addClass("hidden");
}
}
function arrowBack () {
if ($("body").height() - $("footer").height() > $(window).height()){
$("#back-to-top").removeClass("hidden");
} else {
$("#back-to-top").addClass("hidden");
}
}
function gamesTop () {
if ($("body").height() - $("footer").height() > $(window).height()){
$("#games-to-top").removeClass("hidden");
} else {
$("#games-to-top").addClass("hidden");
}
}
$(document).ready(function () {
// converting fata from JSON to an array & setting temporary variable for the case when local storage is empty
var startGames = JSON.parse(localStorage.getItem("gameName"));
if (startGames !== null) {
chosenGames = startGames;
}
// adding the game buttons (if local storage empty ==> I add only two first games pre-chosen in the chosenGames array)
for (var i = 0; i < chosenGames.length; i++) {
// init == true because the page is loaded the first time
addGameButton(chosenGames[i], true);
}
var startUsers = JSON.parse(localStorage.getItem("userName"));
if (startUsers !== null) {
chosenUsers = startUsers;
}
// adding the users (if local storage empty ==> I add only the few first users pre-chosen in the chosenUsers array)
for (var i = 0; i < chosenUsers.length; i++) {
// init == true because the page is loaded the first time
addUserBox(chosenUsers[i], true);
}
// FCC in footer
getFreeCodeCamp();
// autocomplete with Bootstrap typehead from API response
$("#search-game").typeahead({
// start suggesting after the 3rd character provided by a user
minLength: 3,
// getting the list of suggestions for a query a user provides
source: function(query, process) {
$.getJSON("https://api.twitch.tv/kraken/search/games?q=" + query + "&type=suggest&live=true", function (result){
// preparing game names to feed typeahead
var gameNames = [];
for (var i=0; i<result.games.length; i++) {
gameNames.push(result.games[i].name);
}
// up to this moment there are all names gathered
return process(gameNames);
});
},
afterSelect: function(selectedItem) {
addGameButton(selectedItem);
// cleaning search
$("#search-game").val("");
}
});
// listening for the future game-items clicks
$(document).on("click", ".game-item", function (e) {
getStreams($(this).html());
// preventing redirection of a tag
e.preventDefault();
});
// listening for the new Twitch user accounts added to the players list
$("#add-button").on("click", function () {
addTwitchPlayer();
});
// listening for enter pressed on input
$("#search-user").on("keypress", function (e) {
if (e.which == 13) {
addTwitchPlayer();
} else {
// popover hint for doubled players
$("#search-user").popover("hide");
}
});
// arrow back for players section with timeout
$("#player-tab").on("click", function () {
setTimeout(function () {
arrowBack();
},1000);
});
// toggle all & online in players
$("#toggle").on("change", function(){
if ($(this).prop("checked")) {
$(".offline:not(.template):not(.card-fcc)").show();
$("#everybody-offline").addClass("hidden");
} else {
$(".offline:not(.card-fcc)").hide();
// setting everybody-offline hint
if ($(".online:not(.card-fcc):visible").length == 0){
$("#everybody-offline").removeClass("hidden");
} else {
$("#everybody-offline").addClass("hidden");
}
}
arrowBack();
});
});
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-3-typeahead/4.0.1/bootstrap3-typeahead.js"></script>
<script src="https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js"></script>
/* color palette
links: #73298F;
hover, focus for links (action color): #711C7F;
dark text: #424242;
lighter blue on cards on focus/ hover: #390E40;
action yellow: #FFD052;
orange: #FFA952;
background yellow: #FFE59E;
green: #1FB286;
dark green: #167F60;
*/
body {
font-family: 'Roboto Slab', serif;
background-color: #9C27B0;
}
.online, .offline {
border: 1px solid #9C27B0;
margin: 0 1em 1em;
border-radius: 10px;
}
.offline {
padding: 2em;
}
.online.template,
.offline.template,
.game.template,
.stream.template {
display: none;
}
#users a {
display: block;
padding: 2em;
}
.card-fcc {
margin-top: 2em;
padding-top: 2em;
background-color: transparent;
}
#fcc-off.offline {
background-color: transparent;
}
footer {
color: #FFF;
height: auto;
padding: 3em 0;
background-color: #9C27B0;
}
.box {
padding-top: 3em;
}
.image {
margin: 0 auto;
border-radius: 10px;
}
.img-footer {
width: 70%;
}
h5 {
font-family: 'VT323', serif;
font-size: 4em;
}
h1 {
color: #FFF;
font-family: 'VT323', serif;
font-size: 4em;
}
a {
color: #FFEB3B;
}
a:hover,
a:focus {
color: #FFA952;
text-decoration: none;
}
.offline {
background: #CFD8DC;
font-size: 1.2em;
padding: 1em;
}
#fcc-box p {
font-size: 1.5em;
}
#get-games,
#add-user {
padding: 5em 0;
}
#get-games .glyphicon,
#add-user .glyphicon {
top: 0;
}
.page-header {
border-bottom: none;
}
.tab-pane {
background-color: #AB47BC;
border-radius: 0 0 10px 10px;
}
#user-tab {
padding: 0 0 5em;
}
#users .online {
background-color: #1FB286;
transition: background-color 0.2s;
}
#streams .stream a {
display: block;
background-color: #FFF9C4;
padding: 2em;
border: 1px solid #9C27B0;
margin-bottom: 1em;
border-radius: 10px;
margin: 1em;
transition: background-color 0.7s;
}
#users .online a {
color: #FFF9C4;
}
#streams .stream a {
color: #424242;
}
#users .online a:hover,
#users .online a:focus {
color: white;
border-radius: 10px;
background-color: #711C7F;
text-decoration: none;
transition: all 0.2s;
}
#streams .stream a:hover,
#streams .stream a:focus {
color: #FFF;
background-color: #711C7F;
text-decoration: none;
transition: background-color 0.2s;
}
#streams span.stream-title {
font-size: 1.5em;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
white-space: nowrap;
padding-top: 1em;
}
#users .online .title {
display: block;
font-size: 1.5em;
margin-bottom: 1em;
}
#users .online .user {
font-size: 2em;
color: #FFF;
}
#users .online img,
#streams .stream-link img {
border-radius: 10px;
}
.nav-tabs>li.active>a,
.nav-tabs>li.active>a:focus,
.nav-tabs>li.active>a:hover {
color: #424242;
background-color: #FFD052;
border-color: #FFD052;
border-radius: 10px 10px 0 0;
}
.nav-tabs>li>a:hover,
.nav>li>a:focus {
color: #FFF;
background-color: transparent;
border-color: #FFEB3B;
border-radius: 10px 10px 0 0;
}
.nav-tabs>li {
font-size: 2em;
}
.nav-tabs {
border-bottom: 1px solid #FFD052;
}
#streams {
clear: both;
padding-bottom: 5em;
}
.stream-link > span {
display: block;
}
#off-stream,
#everybody-offline {
color: #FFF;
background-color: #FFA952;
padding: 1em;
border-radius: 10px;
font-size: 1.5em;
float: none;
margin: 0 2em;
}
#everybody-offline a:hover,
#everybody-offline a:focus {
color: #121212;
}
.game a {
max-width: 100%;
white-space: nowrap;
text-overflow: ellipsis;
display: block;
overflow: hidden;
font-size: 1.5em;
background: #73298F;
padding: 2em;
margin: 1em;
border-radius: 10px;
color: #FFF;
}
.game a:hover,
.game a:focus {
background-color: #711C7F;
color: #FFEB3B;
border-radius: 10px;
}
.game-off {
font-weight: bold;
}
#add-button {
background-color: #73298F;
color: #FFF;
border-color: #73298F;
}
#add-button:focus,
#add-button:hover {
background-color: #711C7F;
}
.page-header {
margin: 0;
}
#back-to-top,
#games-to-top {
padding: 1em 1em 3em 0;
font-size: 1.5em;
}
#user-selection {
color: #FFF;
font-size: 1.5em;
padding: 0 0 1em 2em;
}
.btn-info {
background-color: #73298F;
border-color: #73298F;
}
.btn-success {
background-color: #1FB286;
}
.btn-success.active{
background-color: #1FB286;
border-color: #1FB286
}
.btn-success.active:hover {
background-color: #167F60;
border-color: #167F60;
}
.toggle.btn {
min-width: 80px;
}
.btn-info:hover,
.btn-info:focus,
.btn-info:active,
.btn-info:active:hover {
background-color: #711C7F;
border-color: #711C7F;
}
.dropdown-menu>.active>a,
.dropdown-menu>.active>a:focus,
.dropdown-menu>.active>a:hover {
background-color: #FFEB3B;
color: #424242;
}
.popover-title {
background-color: #FF9800;
color: #FFF;
}
/* media queries */
@media screen and (max-width: 768px) {
footer .row > * {
margin-bottom: 3em;
}
.scratch {
padding-top: 3em;
}
.game a {
padding: 1.5em 1em;
}
.nav-tabs>li {
font-size: 1.5em;
}
h1 {
font-size: 3em;
}
#users .title {
font-size: 1.5em;
}
#get-games, #add-user {
padding: 3em 0;
}
#streams {
padding-bottom: 3em;
}
#user-tab {
padding: 0 0 3em;
}
}
@media screen and (min-width: 768px) {
#game-list {
padding: 0 2em;
}
#fcc-box p {
padding: 0 3em;
}
#off-stream,
#everybody-offline {
margin: 0 5em;
}
}
@media screen and (max-width: 767px) {
#users .online .title {
padding-top: 1em;
font-size: 1.2em;
}
#fcc-box p {
font-size: 1em;
}
#search-user,
#search-game {
font-size: 1.2em;
}
}
@media screen and (min-width: 1200px) {
#game-list {
padding: 0 5em;
}
#streams .stream a {
margin: 1em 4em;
}
#off-stream,
#everybody-offline {
margin: 0 10em;
}
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Roboto+Slab:400" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=VT323" rel="stylesheet" />
<link href="https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css" rel="stylesheet" />

Twitch Viewer

This is a Twitch Viewer app designed and coded during FCC course. To store user's choices I implemented local storage. To delete the users or games simply refresh your local storage.

A user can look for a favorite game and who's playing it now (first 25 most viewed players get displayed). Users can add Twitch players to the list and follow them. If the observed players are streaming, a user can see what they are streaming. A user can click the status of a live streaming player and be sent directly to the player's channel. NOTE: A user has to be logged in on Twitch to watch the streams after getting redirected to Twitch.

Search games uses Bootstrap Typeahead. It is activated after introducing the 3rd character and help users to find the game faster.

In the footer there are additional suggestions for a user and FCC Twitch channel always visible.

A Pen by Ewa the doer on CodePen.

License.

Twitch Viewer

This is a Twitch Viewer app designed and coded during FCC course. To store user's choices I implemented local storage. To delete the users or games simply refresh your local storage.

A user can look for a favorite game and who's playing it now (first 25 most viewed players get displayed). Users can add Twitch players to the list and follow them. If the observed players are streaming, a user can see what they are streaming. A user can click the status of a live streaming player and be sent directly to the player's channel. NOTE: A user has to to watch the app in debug mode of codepen to get redirected to Twitch (link: https://s.codepen.io/thedoer/debug/GZxdQO).

Search games uses Bootstrap Typeahead. It is activated after introducing the 3rd character and help users to find the game faster.

In the footer there are additional suggestions for a user and FCC Twitch channel always visible.

A Pen by Ewa the doer on CodePen.

License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment