refactor: lots of improvements (#69)

This commit is contained in:
Ayo Ayco 2023-06-24 22:32:20 +02:00 committed by GitHub
parent f8e07c3195
commit 7c67e674e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 111 additions and 109 deletions

View file

@ -2,11 +2,11 @@
import Icon from 'astro-iconify'; import Icon from 'astro-iconify';
export interface Props { export interface Props {
url: string; url: string | null;
} }
const { url } = Astro.props;
const placeholder = 'Type the article URL here'; const placeholder = 'Type the article URL here';
const { url } = Astro.props;
--- ---
<div class="address-bar"> <div class="address-bar">
@ -14,7 +14,7 @@ const placeholder = 'Type the article URL here';
<button aria-label="Back" class="left-buttons" type="button" id="app-back" name="app-back" onclick="history.go(-1); return false;" hidden> <button aria-label="Back" class="left-buttons" type="button" id="app-back" name="app-back" onclick="history.go(-1); return false;" hidden>
<Icon name="ic:round-arrow-back-ios" /> <Icon name="ic:round-arrow-back-ios" />
</button> </button>
<input type="url" id="app-url" name="url" value={url} placeholder={placeholder} required /> <input type="url" id="app-url" name="url" value={url ?? ''} placeholder={placeholder} required />
<button aria-label="Submit" class="right-buttons" type="submit" id="submit"> <button aria-label="Submit" class="right-buttons" type="submit" id="submit">
<Icon name="ri:ai-generate" /> <Icon name="ri:ai-generate" />
</button> </button>

View file

@ -1,31 +1,29 @@
--- ---
export interface Props { export interface Props {
postDivSelector: string, routerOutlet: string,
skipSave?: boolean skipSave?: boolean
} }
const {postDivSelector, skipSave = false} = Astro.props; const {routerOutlet, skipSave = false} = Astro.props;
--- ---
<div id="library"> <div id="library">
<span id="heading"></span> <span id="heading"></span>
<ul id="post-list"></ul> <ul id="post-list"></ul>
</div> </div>
<input value={postDivSelector} name="postDivSelector" id="postDivSelector" hidden /> <input hidden value={routerOutlet} name="postDivSelector" id="postDivSelector" />
<input type="checkbox" id="skipSave" name="skipSave" checked={skipSave} hidden /> <input hidden checked={skipSave} type="checkbox" name="skipSave" id="skipSave" />
<script> <script>
import { getPostCard, renderPost } from '../utils/library' import { getPostCard, renderPost } from '../utils/library'
const cache = await caches.open('cozy-reader'); const cache = await caches.open('cozy-reader');
const url = new URL(window.location.href); const url = new URL(window.location.href);
const response = await cache.match(url) const response = await cache.match(url)
const postDivSelector = document.getElementById('postDivSelector') as HTMLInputElement; const postDivSelector = document.querySelector<HTMLInputElement>('#postDivSelector');
const skipSave = document.getElementById('skipSave') as HTMLInputElement; const skipSave = document.querySelector<HTMLInputElement>('#skipSave');
if (!response) { if (!response && !skipSave?.checked) {
if (!skipSave?.checked) {
await cache.add(url); await cache.add(url);
}
} }
const cachedRequests = (await cache.keys()) const cachedRequests = (await cache.keys())
@ -34,7 +32,7 @@ const {postDivSelector, skipSave = false} = Astro.props;
return urlObj.search !== '' && urlObj.searchParams.get('url') !== ''; return urlObj.search !== '' && urlObj.searchParams.get('url') !== '';
}); });
if(cachedRequests?.length) { if(cachedRequests?.length && postDivSelector !== null) {
const list = document.querySelector('#post-list'); const list = document.querySelector('#post-list');
const heading = document.querySelector('#library span#heading') as HTMLHeadingElement; const heading = document.querySelector('#library span#heading') as HTMLHeadingElement;
heading.innerHTML = 'History'; heading.innerHTML = 'History';
@ -80,7 +78,6 @@ const {postDivSelector, skipSave = false} = Astro.props;
if (!url) { if (!url) {
url = window.location.href; url = window.location.href;
isHome = true; isHome = true;
console.log('>>> ishome', isHome);
} else { } else {
// replace scrollPosition // replace scrollPosition
localStorage.setItem('scrollPosition', window.scrollY.toString()); localStorage.setItem('scrollPosition', window.scrollY.toString());

View file

@ -1,10 +1,15 @@
--- ---
import { ArticleData } from "@extractus/article-extractor"; import { ArticleData } from "@extractus/article-extractor";
export interface Props { export interface Props {
article: ArticleData; article: ArticleData | null;
} }
const { article } = Astro.props; const error: ArticleData = {
title: 'Something is not right',
content: '<p>The article extractor did not get any information.</p>',
}
let { article } = Astro.props;
article ??= error;
const datePublished = const datePublished =
article?.published && new Date(article.published).toDateString(); article?.published && new Date(article.published).toDateString();
--- ---

View file

@ -3,10 +3,16 @@ import { ArticleData } from "@extractus/article-extractor";
import "./reset.css"; import "./reset.css";
import Footer from "../components/Footer.astro"; import Footer from "../components/Footer.astro";
export interface Props { export interface Props {
meta: ArticleData article: ArticleData | null
} }
const { meta } = Astro.props; const app: ArticleData = {
const appTitle = `Cozy 🧸${meta.title && ` | ${meta.title}`}`; title: "Cozy 🧸",
description: "Remove distractions. Save your favorites. Get useful insights. Cozy is your modern-day reading assistant.",
content: "<p>Enter a URL in the address bar to get started.</p>",
url: '/'
};
const { article } = Astro.props;
const appTitle = article?.title ? `${article.title} | Cozy 🧸` : app.title;
--- ---
<!DOCTYPE html> <!DOCTYPE html>
@ -18,9 +24,22 @@ const appTitle = `Cozy 🧸${meta.title && ` | ${meta.title}`}`;
<title>{appTitle}</title> <title>{appTitle}</title>
{ {
meta.url !== '/' ? ( /**
* if showing a post:
* - don't allow search engines to index the page
* - add cozy metadata for the app to use
*/
article && article?.url !== '/' ? (
<meta name="robots" content="noindex"> <meta name="robots" content="noindex">
<meta name="googlebot" content="noindex"> <meta name="googlebot" content="noindex">
<meta property="cozy:title" content={article.title} />
<meta property="cozy:url" content={article.url} />
<meta property="cozy:description" content={article.description} />
<meta property="cozy:image" content={article.image} />
<meta property="cozy:source" content={article.source} />
<meta property="cozy:author" content={article.author} />
<meta property="cozy:published" content={article.published} />
) : ( ) : (
<meta property="og:title" content={appTitle} /> <meta property="og:title" content={appTitle} />
<meta property="og:url" content={Astro.url.href} /> <meta property="og:url" content={Astro.url.href} />
@ -29,15 +48,6 @@ const appTitle = `Cozy 🧸${meta.title && ` | ${meta.title}`}`;
<meta itemprop="description" content="Remove distractions. Save your favorites. Get useful insights. Cozy is your modern-day reading assistant." /> <meta itemprop="description" content="Remove distractions. Save your favorites. Get useful insights. Cozy is your modern-day reading assistant." />
) )
} }
<meta property="cozy:title" content={meta.title} />
<meta property="cozy:url" content={meta.url} />
<meta property="cozy:description" content={meta.description} />
<meta property="cozy:image" content={meta.image} />
<meta property="cozy:source" content={meta.source} />
<meta property="cozy:author" content={meta.author} />
<meta property="cozy:published" content={meta.published} />
</head> </head>
<body> <body>
<div id="app-wrapper"> <div id="app-wrapper">

View file

@ -6,40 +6,23 @@ import Layout from "../layouts/Layout.astro";
import Library from "../components/Library.astro"; import Library from "../components/Library.astro";
import Footer from "../components/Footer.astro"; import Footer from "../components/Footer.astro";
const params = Astro.url.searchParams; const url = Astro.url.searchParams.get('url');
const url = params.get('url') || ''; let article: ArticleData | null = {};
let article: ArticleData | null;
let skipSave;
const error = { if (url)
title: "Something is not right", try {
content: "<p>The article extractor did not get any result.</p>", article = await extract(url);
} } catch {
article = null;
try {
article = await extract(url);
if (!article ) {
article = error;
skipSave = true;
} }
} catch {
article = error;
skipSave = true;
}
if (url === '') {
article = {
title: "Welcome to Cozy 🧸",
content: "<p>Enter a URL above to get started.</p>",
url: '/'
};
skipSave = false;
}
const routerOutletID = "router-outlet";
--- ---
<Layout meta={article}> <Layout article={article}>
<AddressBar url={url} /> <AddressBar url={url} />
<Post slot="post" article={article} /> <div slot="post" id={routerOutletID}>
<Library slot="library" skipSave={skipSave} postDivSelector="#post"/> <Post article={article} />
</div>
<Library slot="library" skipSave={article === null} routerOutlet={routerOutletID}/>
<Footer slot="footer" /> <Footer slot="footer" />
</Layout> </Layout>

View file

@ -1,23 +1,5 @@
export function getPostCard(html: HTMLHtmlElement) { export function getPostCard(html: HTMLHtmlElement) {
const title = const {title, description, image, source, published} = getPostMeta(html);
html
.querySelector('meta[property="cozy:title"]')
?.getAttribute("content") ||
html.querySelector("title")?.innerHTML?.replace("Cozy 🧸 | ", "");
const description = html
.querySelector('meta[property="cozy:description"]')
?.getAttribute("content");
const image = html
.querySelector('meta[property="cozy:image"]')
?.getAttribute("content");
const source = html
.querySelector('meta[property="cozy:source"]')
?.getAttribute("content");
const published = html
.querySelector('meta[property="cozy:published"]')
?.getAttribute("content");
const postCard = ` const postCard = `
<div class="post-card"> <div class="post-card">
<div class="post-card__image"> <div class="post-card__image">
@ -64,37 +46,62 @@ export function getPostCard(html: HTMLHtmlElement) {
`; `;
return postCard; return postCard;
} }
export function renderPost(responseText, url, postDivSelector: string, preventPushState = false) { export function renderPost(responseText, url, postDivSelector: string, preventPushState = false) {
const postDiv = document.querySelector(postDivSelector); const postDiv = document.querySelector<HTMLDivElement>(`#${postDivSelector}`);
const html = document.createElement('html'); const html = document.createElement('html');
html.innerHTML = responseText; html.innerHTML = responseText;
const newPost = html.querySelector('body')?.querySelector('#post'); const newPost = html.querySelector('body')?.querySelector('#post');
if (postDiv && newPost?.innerHTML) { if (postDiv && newPost?.innerHTML) {
postDiv.innerHTML = newPost.innerHTML postDiv.innerHTML = newPost.innerHTML
const appUrl = document.getElementById('app-url') as HTMLInputElement;
const cozyUrl = html.querySelector('meta[property="cozy:url"]')?.getAttribute('content');
const homeBtn = document.querySelector<HTMLButtonElement>('#app-home');
const backBtn = document.querySelector<HTMLButtonElement>('#app-back');
const submitBtn = document.querySelector<HTMLButtonElement>('#app-submit');
const appUrl = document.getElementById('app-url') as HTMLInputElement;
const title = html.querySelector('meta[property="cozy:title"]')?.getAttribute('content'); const cozyUrl = html.querySelector('meta[property="cozy:url"]')?.getAttribute('content');
document.title = `Cozy 🧸 | ${title}` || 'Cozy 🧸'; const homeBtn = document.querySelector<HTMLButtonElement>('#app-home');
const backBtn = document.querySelector<HTMLButtonElement>('#app-back');
if(cozyUrl !== '/') { const submitBtn = document.querySelector<HTMLButtonElement>('#app-submit');
appUrl.value = cozyUrl || ''; if(cozyUrl !== '/') {
backBtn?.removeAttribute('disabled'); appUrl.value = cozyUrl || '';
submitBtn?.removeAttribute('disabled'); backBtn?.removeAttribute('disabled');
homeBtn?.removeAttribute('disabled'); submitBtn?.removeAttribute('disabled');
} else { homeBtn?.removeAttribute('disabled');
appUrl.value = ''; document.title = `${getCozyTitle(html)} | Cozy 🧸`;
backBtn?.setAttribute('disabled', 'true'); } else {
submitBtn?.setAttribute('disabled', 'true'); appUrl.value = '';
homeBtn?.setAttribute('disabled', 'true'); backBtn?.setAttribute('disabled', 'true');
} submitBtn?.setAttribute('disabled', 'true');
homeBtn?.setAttribute('disabled', 'true');
if(!preventPushState) { document.title = `Cozy 🧸`;
window.history.pushState({url}, '', url);
}
}
} }
if(!preventPushState) {
window.history.pushState({url}, '', url);
}
}
}
function getPostMeta(html: HTMLHtmlElement) {
const title = getCozyTitle(html);
const description = html
.querySelector('meta[property="cozy:description"]')
?.getAttribute("content");
const image = html
.querySelector('meta[property="cozy:image"]')
?.getAttribute("content");
const source = html
.querySelector('meta[property="cozy:source"]')
?.getAttribute("content");
const published = html
.querySelector('meta[property="cozy:published"]')
?.getAttribute("content");
return {title, description, image, source, published};
}
function getCozyTitle(html: HTMLHtmlElement): string | undefined {
return html.querySelector('meta[property="cozy:title"]')?.getAttribute("content")
/**
* backwards compatibility for stuff before we implemented cozy:meta tags
*/
?? html.querySelector("title")?.innerHTML?.replace("Cozy 🧸 | ", "");
}