Add item read page (#7)

- Open items links in an external window
- Include "Keep as New" button
This commit is contained in:
Daniel J. Summers 2024-04-13 13:39:49 -04:00
parent 9b2190252f
commit ee86eeef13
4 changed files with 147 additions and 11 deletions

View File

@ -36,10 +36,26 @@ header {
} }
main { main {
padding: 0 .5rem; padding: 0 .5rem;
.item_heading {
margin-bottom: 0;
}
.item_published {
margin-bottom: 1rem;
line-height: 1.2;
}
} }
article { article {
max-width: 60rem; max-width: 60rem;
margin: auto; margin: auto;
.item_content {
border: solid 1px navy;
border-radius: .5rem;
background-color: white;
padding: .5rem;
}
} }
input[type=url], input[type=text] { input[type=url], input[type=text] {
width: 50%; width: 50%;
@ -47,11 +63,27 @@ input[type=url], input[type=text] {
padding: .25rem; padding: .25rem;
border-radius: .25rem; border-radius: .25rem;
} }
button { button,
.action_buttons a:link,
.action_buttons a:visited {
font-size: 1rem; font-size: 1rem;
font-weight: normal;
background-color: navy; background-color: navy;
color: white; color: white;
padding: .25rem 1rem; padding: .5rem 1rem;
border-radius: .25rem; border-radius: .25rem;
cursor: pointer; cursor: pointer;
} border: none;
}
button:hover,
.action_buttons a:hover {
text-decoration: none;
cursor: pointer;
background: linear-gradient(navy, #000032);
}
.action_buttons {
margin: 1rem 0;
display: flex;
flex-flow: row nowrap;
justify-content: space-evenly;
}

View File

@ -24,14 +24,9 @@ page_head('Welcome'); ?>
<h1>Your Unread Items</h1> <h1>Your Unread Items</h1>
<article><?php <article><?php
if ($item) { if ($item) {
while ($item) { while ($item) { ?>
try { <p><a href=/item?id=<?=$item['id']?>><?=htmlentities($item['item_title'])?></a><br>
$asOf = (new DateTimeImmutable($item['as_of']))->format(DATE_TIME_FORMAT); <?=htmlentities($item['feed_title'])?><br><small><em><?=date_time($item['as_of'])?></em></small><?php
} catch (Exception) {
$asOf = '(invalid date)';
} ?>
<p><a href=/item/view?id=<?=$item['id']?>><?=htmlentities($item['item_title'])?></a><br>
<?=htmlentities($item['feed_title'])?><br><small><em><?=$asOf?></em></small><?php
$item = $result->fetchArray(SQLITE3_ASSOC); $item = $result->fetchArray(SQLITE3_ASSOC);
} }
} else { ?> } else { ?>

79
src/public/item.php Normal file
View File

@ -0,0 +1,79 @@
<?php
/**
* Item View Page
*
* Retrieves and displays an item from a feed belonging to the current user
*/
include '../start.php';
Security::verifyUser();
$db = Data::getConnection();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// "Keep as New" button sends a POST request to reset the is_read flag before going back to the list of unread items
$isValidQuery = $db->prepare(<<<'SQL'
SELECT COUNT(*)
FROM item INNER JOIN feed ON feed.id = item.feed_id
WHERE item.id = :id AND feed.user_id = :user
SQL);
$isValidQuery->bindValue(':id', $_POST['id']);
$isValidQuery->bindValue(':user', $_REQUEST[Key::USER_ID]);
$isValidResult = $isValidQuery->execute();
if ($isValidResult && $isValidResult->fetchArray(SQLITE3_NUM)[0] == 1) {
$keepUnread = $db->prepare('UPDATE item SET is_read = 0 WHERE id = :id');
$keepUnread->bindValue(':id', $_POST['id']);
$keepUnread->execute();
}
$db->close();
frc_redirect('/');
}
$query = $db->prepare(<<<'SQL'
SELECT item.title AS item_title, item.item_link, item.published_on, item.updated_on, item.content, item.is_encoded,
feed.title AS feed_title
FROM item INNER JOIN feed ON feed.id = item.feed_id
WHERE item.id = :id
AND feed.user_id = :user
SQL);
$query->bindValue(':id', $_GET['id']);
$query->bindValue(':user', $_REQUEST[Key::USER_ID]);
$result = $query->execute();
$item = $result ? $result->fetchArray(SQLITE3_ASSOC) : false;
if ($item) {
$markRead = $db->prepare('UPDATE item SET is_read = 1 WHERE id = :id');
$markRead->bindValue(':id', $_GET['id']);
$markRead->execute();
}
$published = date_time($item['published_on']);
$updated = isset($item['updated_on']) ? date_time($item['updated_on']) : null;
$isEncoded = (bool) $item['is_encoded'];
page_head(htmlentities("{$item['item_title']} | {$item['feed_title']}")); ?>
<h1 class=item_heading>
<a href="<?=$item['item_link']?>" target=_blank rel=noopener><?=$item['item_title']?></a><br>
</h1>
<div class=item_published>
From <strong><?=htmlentities($item['feed_title'])?></strong><br>
Published <?=date_time($item['published_on'])?><?=$updated && $updated != $published ? " (Updated $updated)" : ''?>
</div>
<article>
<div class=item_content><?php
if ($isEncoded) {
echo str_replace('<a ', '<a target=_blank rel=noopener ', $item['content']);
} else {
echo htmlentities($item['content']);
} ?>
</div>
<form class=action_buttons action=/item method=POST>
<input type=hidden name=id value=<?=$_GET['id']?>>
<a href="/">Done</a>
<button type=submit>Keep as New</button>
</form>
</article><?php
page_foot();
$db->close();

View File

@ -49,6 +49,7 @@ function page_head(string $title): void {
?><!DOCTYPE html> ?><!DOCTYPE html>
<html lang=en> <html lang=en>
<head> <head>
<meta name=viewport content="width=device-width, initial-scale=1">
<title><?=$title?> | Feed Reader Central</title> <title><?=$title?> | Feed Reader Central</title>
<link href=/assets/style.css rel=stylesheet> <link href=/assets/style.css rel=stylesheet>
</head> </head>
@ -79,3 +80,32 @@ function page_head(string $title): void {
function page_foot(): void { function page_foot(): void {
?></main></body></html><?php ?></main></body></html><?php
} }
/**
* Redirect the user to the given URL
*
* @param string $value A local URL to which the user should be redirected
*/
function frc_redirect(string $value) {
if (str_starts_with($value, 'http')) {
http_response_code(400);
die();
}
header("Location: $value");
http_response_code(303);
die();
}
/**
* Convert a date/time string to a date/time in the configured format
*
* @param string $value The date/time string
* @return string The standard format of a date/time, or '(invalid date)' if the date could not be parsed
*/
function date_time(string $value): string {
try {
return (new DateTimeImmutable($value))->format(DATE_TIME_FORMAT);
} catch (Exception) {
return '(invalid date)';
}
}