Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added images/No_Image_Available.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/backdrop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 69 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" type="text/css" href="styles/styles.css">


<link href="https://fonts.googleapis.com/css?family=Source+Serif+Pro:700" rel="stylesheet">
<title>Responsive News Reader</title>
</head>

<body>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to have indentation used here. It will make the HTML structure easier to read

<header class="header">
<h2 class="header-title">Fetch News</h2>
<button class="country-button">Top UK Stories</button>

<form class="search-form">
<input type="text" class="search-text" />
<select name="date-range" class="date-range">
<option value="0">Today</option>
<option value="1">Last Month</option>
<option value="12">Last Year</option>
<option value="all-time">All Time</option>
</select>
<button type="submit">Search</button>
</form>
<button class="advanced-search">Advanced Search</button>

</header>
<div class="content">
<div class="advanced-search-options">
<form class="advanced-search-form">

<h6>Sort results by:</h6>
<select class="sort-by-select">
<option value="relevancy">Most Relevant</option>
<option value="popularity">Most Popular</option>
<option value="publishedAt">Newest stories</option>
</select>



<select name="language-select" class="language-select">
<option value="en">English</option>
<option value="fr">French</option>
<option value="de">German</option>
<option value="es">Spanish</option>
</select>

<input type="submit" />
</form>
</div>
<div class="news-display">

</div>


<div class="pagination">
<button class="back-page" href="#">&laquo;</button>
<button class="next-page" value="next" href="#">&raquo;</button>
</div>
</div>


<script src="src/scripts.js"></script>
</body>
</html>
225 changes: 225 additions & 0 deletions src/scripts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
// News API key: 9ed005ef4eb94baf913fce701c69972f

//TODO: stylization, excludeDomains/blocklist

//Object contains possible URLS for fetch requests
const apiRequests = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice. I like how you have created an object that encapsulates all URL generation logic in one place

usTop20: 'https://newsapi.org/v2/top-headlines?country=us&apiKey=9ed005ef4eb94baf913fce701c69972f',

ukTop20: 'https://newsapi.org/v2/top-headlines?country=gb&apiKey=9ed005ef4eb94baf913fce701c69972f',


//object contains stored parameters for searches
customParameters : {
q: {val: null, string: ""},
from: {val: null, string: ""},
excludeDomains: {val: null, string: ""},
language: {val: null, string: ""},
page: {val: 1, string: ""},
sortBy: {val: null, string: ""}
},


//getURL provides a url for API with specified parameters above
getURL: function(){
const customURL = `https://newsapi.org/v2/everything?${this.customParameters.q.string}${this.customParameters.from.string}${this.customParameters.excludeDomains.string}${this.customParameters.page.string}${this.customParameters.language.string}${this.customParameters.sortBy.string}&apiKey=9ed005ef4eb94baf913fce701c69972f`
return customURL
},

updateURL: function(parameter, update){
this.customParameters[parameter].val = update
if (parameter === "q"){
this.customParameters[parameter].string = `${parameter}=${update}`
}else{
this.customParameters[parameter].string = `&${parameter}=${update}`
}
},


blockList: []

}


//Object contains functions attached to buttons and page features
const pageHandlers = {

changeCountry: function(){
const countryButtonElement = document.querySelector(".country-button")
countryButtonElement.addEventListener("click", event => {
paginationElement.style.display = "none";
countryButtonElement.textContent === "Top UK Stories"
? ( countryButtonElement.textContent = "Top US Stories",
fetchNews(apiRequests.ukTop20))
: (countryButtonElement.textContent = "Top UK Stories",
fetchNews(apiRequests.usTop20))
})
},

search: function(event){
event.preventDefault();
if (searchTextElement.value === ""){
return
}
const dateRange = formatDate(dateRangeElement.value)
paginationElement.style.display = "inline"
apiRequests.updateURL("q", searchTextElement.value)
apiRequests.updateURL("from", dateRange)
fetchNews(apiRequests.getURL())
searchTextElement.value = ""
pageHandlers.checkPage()
},


checkPage: function(){
apiRequests.customParameters.page.val === 1
? prevPageElement.disabled = true
: prevPageElement.disabled = false
},


paginationControl: function(event){
let currentPage = apiRequests.customParameters.page.val
event.target.value === "next"
? currentPage ++
: currentPage --
apiRequests.updateURL("page", currentPage)
fetchNews(apiRequests.getURL())
pageHandlers.checkPage()


},

updateAdvancedOptions: event => {
event.preventDefault()
apiRequests.updateURL("language", languageSelector.value)
apiRequests.updateURL("sortBy", sortBySelector.value )
pageHandlers.search(event)
},

openCloseAdvanced: event => {
advancedSearchMenu.style.display === "none"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could rewritten slightly cleaner as
advancedSearchMenu.style.display = advancedSearchMenu.style.display === "none ? = "block" : "none"

? advancedSearchMenu.style.display = "block"
: advancedSearchMenu.style.display = "none"
},

blockSource: event => {
const host = linkElement.hostname
console.log(host)
}

}

function assignElement(elementType, elementClassName, elementTextContent){
const output = document.createElement(elementType)
output.className = elementClassName
output.textContent = elementTextContent
return output
}


//creates each story panel from fetch response
function createStoryPanel(article){
const storyDivElement = assignElement("div", "story-div-element")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic here would be easier to implement as an HTML string template

const headerElement = assignElement("div", "story-header")
const headerLeftElement = assignElement("div", "story-header-left")
const publicationInfoElement = assignElement("span", "publication-info")
const headlineElement = assignElement("h2", "headline", article.title)
const publicationNameElement = assignElement("h4", "publication-name", article.source.name)
const publicationDateElement = assignElement("h4", "publication-date", convertDateForDisplay(article.publishedAt))
const blockButtonElement = assignElement("button", "block-button", "Block")
const storyImageElement = assignElement("img", "story-image")
storyImageElement.setAttribute("src", article.urlToImage)
if (storyImageElement.src === "http://127.0.0.1:3000/null"){
storyImageElement.setAttribute("src", "../images/No_Image_Available.jpg")
}
const descriptionElement = assignElement("p", "description-element", article.description)
const linkElement = assignElement("a", "full-story-link", "See full story")
linkElement.setAttribute("href", article.url)
blockButtonElement.addEventListener("click", event => {
let host = linkElement.hostname
host = host.replace("www.", "")
apiRequests.blockList.push(host)
const blockedDomains = apiRequests.blockList.join(",")
apiRequests.updateURL("excludeDomains", blockedDomains)
fetchNews(apiRequests.getURL())
})

publicationInfoElement.appendChild(publicationNameElement)
publicationInfoElement.appendChild(publicationDateElement)

headerLeftElement.appendChild(headlineElement)
headerLeftElement.appendChild(publicationInfoElement)
headerLeftElement.appendChild(blockButtonElement)

headerElement.appendChild(headerLeftElement)
headerElement.appendChild(storyImageElement)

storyDivElement.appendChild(headerElement)
storyDivElement.appendChild(descriptionElement)
storyDivElement.appendChild(linkElement)
newsDisplayElement.appendChild(storyDivElement)
}



//formats date for search
function formatDate(monthsAgo){
if ( monthsAgo != "all-time") {
const newDate = new Date
newDate.setMonth(newDate.getMonth() - monthsAgo);
const isoDate = newDate.toISOString().slice(0,10)
return `${isoDate}`
}else{
return null
}
}

//formats date for display

function convertDateForDisplay(date){
const inputDate = new Date(date)
return inputDate.toLocaleString()
}


function fetchNews(apiAddress){
fetch(apiAddress)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice. good to see how you pass in url as param and implement display logic in own function

.then(response => {
return response.json()
})
.then(body => {
newsDisplayElement.innerHTML = ""
body.articles.forEach(article => {
createStoryPanel(article)
})
})
}


const contentElement = document.querySelector(".content")
const newsDisplayElement = document.querySelector(".news-display")
const paginationElement = document.querySelector(".pagination")
const nextPageElement = document.querySelector(".next-page")
const prevPageElement = document.querySelector(".back-page")
const searchFormElement = document.querySelector(".search-form")
const searchTextElement = document.querySelector(".search-text")
const dateRangeElement = document.querySelector(".date-range")
const advancedSearchElement = document.querySelector(".advanced-search")
const advancedSearchMenu = document.querySelector(".advanced-search-options")
const advancedSearchForm = document.querySelector(".advanced-search-form")
const languageSelector = document.querySelector(".language-select")
const sortBySelector = document.querySelector(".sort-by-select")
searchFormElement.addEventListener("submit", pageHandlers.search)
prevPageElement.addEventListener("click", pageHandlers.paginationControl)
nextPageElement.addEventListener("click", pageHandlers.paginationControl)
advancedSearchElement.addEventListener("click", pageHandlers.openCloseAdvanced)
advancedSearchForm.addEventListener("submit", pageHandlers.updateAdvancedOptions)






fetchNews(apiRequests.usTop20)
pageHandlers.changeCountry()
Loading