krz/hn

A brutalist web client for Hacker News.

clone: git clone https://gitbay.org/krz/hn.git

468bc55ada06979e53b1d03b040b2dfe6beaf017

unsigned

author: Christian Cleberg <hello@cleberg.net> · 2023-06-01T01:58:43Z

initial commit
 LICENSE                     |  24 ++++++++++
 README.md                   |   5 ++
 index.php                   | 112 ++++++++++++++++++++++++++++++++++++++++++++
 src/View/class-template.php |  39 +++++++++++++++
 templates/template.html     |  37 +++++++++++++++
 5 files changed, 217 insertions(+)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..68a49da
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..de3700d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# hn
+
+[hn](https://hn.cleberg.net) is a simple front-end alternative for Hacker News. 
+hn focuses on the links shared on Hacker News, currently leaving out an options 
+to login or view comments.
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..f5f7438
--- /dev/null
+++ b/index.php
@@ -0,0 +1,112 @@
+<?php
+
+$full_domain = 'https://hn.cleberg.net';
+$path = ltrim($_SERVER['REQUEST_URI'], '/');
+$elements = explode('/', $path);
+
+if (empty($elements[0])) {
+        get_stories(
+                'https://hacker-news.firebaseio.com/v0/topstories.json?limitToFirst=30&orderBy="$key"',
+                 'Top'
+        );
+        echo_html(
+       		$GLOBALS['full_domain'] . '/best/',
+		'The top 30 stories from Hacker News. Proxied by hn.',
+		'hn',
+		$html_output
+        );
+} else {
+	switch (array_shift($elements)) {
+		case 'top':
+			get_stories(
+                                'https://hacker-news.firebaseio.com/v0/topstories.json?limitToFirst=30&orderBy="$key"',
+			        'Top'
+		        );
+                        echo_html(
+       	                	$GLOBALS['full_domain'] . '/best/',
+		                'The top 30 stories from Hacker News. Proxied by hn.',
+		                'hn',
+		                $html_output
+                        );
+			break;
+
+		case 'best':
+			echo 'best';
+			break;
+
+		case 'new':
+			echo 'new';
+			break;
+
+		case 'ask':
+			echo 'ask';
+			break;
+
+		case 'show':
+			echo 'show';
+			break;
+
+		case 'job':
+			echo 'job';
+			break;
+
+		default:
+			header('HTTP/1.1 404 Not Found');
+    }
+}
+
+/**
+ * Extract a set of stories from Hacker News API and format in HTML
+ *
+ * @access public
+ * @author cmc <hello@cleberg.net>
+ * @param string $api_url The API endpoint to use for extraction
+ * @param string $inline_title The <h1> title to use in the HTML
+ * @return string $html_output The formatted HTML result of stories from the API
+ */
+function get_stories($api_url, $inline_title)
+{
+        $response_raw = file_get_contents($api_url);
+	$response = json_decode($response_raw, true);
+
+	$html_output = '<h1>' . $inline_title . '</h1>';
+
+	for ($i = 0; $i < count($response); $i++) {
+		$sub_url = 'https://hacker-news.firebaseio.com/v0/item/' . $response[$i] . '.json';
+		$sub_response_raw = file_get_contents($sub_url);
+		$sub_response = json_decode($sub_response_raw, true);
+
+		$html = '<div><a href="' . $sub_response['url'] . '">' . $sub_response['title'] . '</a>';
+		$html .= '<p><time datetime="' . date('Y-m-d h:m:s', $sub_response['time']) . '">';
+		$html .= date('Y-m-d h:m:s', $sub_response['time'])  . '</time> by ';
+		$html .= $sub_response['by'] . ' | ' . $sub_response['score'] . ' points</p></div>';
+		$html_output .= $html;
+	}
+
+	return $html_output;
+}
+
+/**
+ * Send formatted HTML results to the user via a template
+ *
+ * @access public
+ * @author cmc <hello@cleberg.net>
+ * @param string $page_url Canoncial URL for HTML header
+ * @param string $page_description Page description for HTML header
+ * @param string $page_title Page title for HTML header
+ * @param string $page_content Page content to display in <main>
+ */
+function echo_html(string $page_url, string $page_description, string $page_title, string $page_content) {
+	include_once '_classes/template.php';
+
+	$template = new HN\View\Template(
+                $page_url,
+	        $page_description,
+	        $page_title,
+	        $page_content
+	);
+
+	$template->echo_template();
+}
+
+// EOF
diff --git a/src/View/class-template.php b/src/View/class-template.php
new file mode 100644
index 0000000..ea75648
--- /dev/null
+++ b/src/View/class-template.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace HN\View;
+
+/**
+ * Template View
+ *
+ * @author cmc <hello@cleberg.net>
+ */
+class Template
+{
+	public function __construct(string $canonical_url, string $page_description, string $page_title, string $content_col)
+	{
+		$this->canonical_url = $canonical_url;
+		$this->description = $page_description;
+		$this->title = $page_title;
+		$this->content = $content_col;
+		$this->current_year = date("Y");
+	}
+
+	public function echo_template()
+	{
+		// Get the template file
+		$template_file = 'templates/template.html';
+		$page = file_get_contents($template_file);
+
+		// Replace the template variables
+		$page = str_replace('{page_title}', $this->title, $page);
+		$page = str_replace('{page_description}', $this->description, $page);
+		$page = str_replace('{canonical_url}', $this->canonical_url, $page);
+		$page = str_replace('{content}', $this->content, $page);
+		$page = str_replace('{current_year}', $this->current_year, $page);
+
+		// Echo the filled-out template
+		echo $page;
+	}
+}
+
+// EOF
diff --git a/templates/template.html b/templates/template.html
new file mode 100644
index 0000000..638562a
--- /dev/null
+++ b/templates/template.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>{page_title}</title>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <meta http-equiv="x-ua-compatible" content="ie=edge">
+    <meta name="author" content="My Name">
+    <meta name="description" content="{page_description}">
+    <link rel="canonical" href="{canoncial_url}">
+    <link rel="icon" href="/favicon.ico">
+    <link rel="stylesheet" href="/static/styles.css">
+</head>
+
+<body>
+<main id="main">
+    <nav class="links">
+        <span><a href="/">Top</a> &middot; </span>
+        <span><a href="/best/">Best</a> &middot;</span>
+        <span><a href="/new/">New</a> &middot;</span>
+        <span><a href="/ask/">Ask</a> &middot;</span>
+        <span><a href="/show/">Show</a> &middot;</span>
+        <span><a href="/job/">Job</a></span>
+    </nav>
+    {content}
+</main>
+
+<footer>
+    <h2>Contact</h2>
+    <p><a href="https://sr.ht/~cmc/hn/"></a></p>
+    <p>Copyright &copy; 2023 - {current_year}</p>
+</footer>
+
+</body>
+
+</html>