I have made a simple web app that finds and shows and enabling you to print a random Sadghuru quote from goodreads.com with Node.js, Express, Puppeteer and Vue.js. App includes some basic CSS animations also print-friendly. I don't know very well Sadghuru but I see he is a wise person. I love to read his quotes.
Here is my server (Index.js) (Packages: dotenv, cors, express, puppeteer)
require('dotenv').config();
// in .env file (PORT=5000)
const PORT = process.env.PORT || 3000;
const express = require('express');
const cors = require('cors');
const puppeteer = require('puppeteer');
const app = express();
app.use(cors());
app.use(express.json());
app.get('/', async (req, res) => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const rndPage = Math.floor(Math.random() * 26) + 1;
await page.goto(`https://www.goodreads.com/author/quotes/30378.Sadhguru?page=${rndPage}`);
const quotes = await page.$$('.quoteText');
const rndQuote = Math.floor(Math.random() * (quotes.length - 1)) + 1;
const quote = await quotes.find((el, index) => index === rndQuote).evaluate(el => el.innerText);
await browser.close();
return res.json({ quote });
});
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
const browser = await puppeteer.launch();
Try this launch({ headless: false }); and browser will show.
There are 26 pages for Sadghuru quotes. page parameter can be random 1 to 26. goodreads.com/author/quotes/30378.Sadhguru?..{rndPage}
// Get all quote div elements.
const quotes = await page.$$('.quoteText');
// Random number 1-26
const rndQuote = Math.floor(Math.random() * (quotes.length - 1)) + 1;
// Find a random quote from all quotes in the random page then grab text in the div.
const quote = await quotes.find((el, index) => index === rndQuote).evaluate(el => el.innerText);
Then we close puppeteer browser. (await browser.close()) Then we send the quote as json. (res.json({quote}))
And Frontend
App.vue
<template>
<div id="app">
<h1 class="header-text">Sadghuru Quotes</h1>
<QuoteCard :quote="quote" :loadingAnim="loading" @onclick="findQuote()" />
<button @click="printQuote()" id="printBtn"><span class="material-icons">print</span></button>
</div>
</template>
<script>
import axios from 'axios';
import QuoteCard from './components/QuoteCard.vue';
export default {
name: 'App',
components: {
QuoteCard
},
data() {
return {
quote: null,
loading: true,
}
},
mounted() {
this.fetchQuote();
},
methods: {
async fetchQuote() {
this.loading = true;
const result = await axios.get('http://localhost:5000/');
this.quote = result.data.quote;
this.loading = false;
},
findQuote: function() {
if (!this.loading) this.fetchQuote();
},
printQuote() {
window.print();
}
}
}
</script>
<style>
.header-text {
text-align: center;
margin: 2rem 0 2rem 0;
}
#printBtn {
position: fixed;
right: 2rem;
bottom: 2rem;
outline: none;
border: 0;
padding: 1rem;
border-radius: 50%;
cursor: pointer;
}
#printBtn:hover {
animation-name: printbtnanim;
animation-duration: 1s;
animation-iteration-count: infinite;
}
#printBtn span {
font-size: 48px;
}
@keyframes printbtnanim {
0% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
@media print {
html, body { color: #000; }
#printBtn {
display: none;
}
.header-text {
display: none;
}
}
</style>
QuoteCard.vue
<template>
<div class="quotecard" :class="{'quotecard-anim': loadingAnim, 'fadein-anim': !loadingAnim }" @click="onClick()">
<p>{{loadingAnim ? 'Loading...' : quote}}</p>
</div>
</template>
<script>
export default {
name: 'QuoteCard',
props: {
quote: String,
loadingAnim: Boolean
},
methods: {
onClick() {
this.$emit('onclick');
}
}
}
</script>
<style scoped>
.quotecard {
cursor: pointer;
width: 70vw;
max-height: 50vh;
overflow: auto;
padding: 2rem;
margin: 4rem auto 0 auto;
border-radius: 10px;
box-shadow: 0 0 55px 1px rgba(0, 250, 250, 0.3);
transition: all .5s;
text-align: center;
font-size: 1.4rem;
}
.quotecard:hover {
box-shadow: 0 0 55px 1px rgba(0, 250, 250, 0.8);
}
.quotecard-anim {
animation-name: quotecardanim;
animation-duration: 1s;
animation-iteration-count: infinite;
}
.fadein-anim {
animation-name: fadeinanim;
animation-duration: 2s;
}
@keyframes quotecardanim {
0% {
box-shadow: 0 0 55px 1px rgba(0, 250, 250, .1);
}
50% {
box-shadow: 0 0 55px 1px rgba(0, 250, 250, 1);
}
100% {
box-shadow: 0 0 55px 1px rgba(0, 250, 250, .1);
}
}
@keyframes fadeinanim {
from { opacity: 0; }
to { opacity: 1; }
}
::-webkit-scrollbar{
width: 4px;
height: 4px;
}
::-webkit-scrollbar-thumb{
background: #ADFFFF;
border-radius: 15px;
}
::-webkit-scrollbar-thumb:hover{
background: #D4D4D4;
}
::-webkit-scrollbar-track{
background: #F0F0F0;
border-radius: 0px;
box-shadow: inset 0px 0px 0px 0px #F0F0F0;
}
@media print {
.quotecard {
box-shadow: none;
width: 100%;
max-height: none;
padding: 1cm;
margin: 0;
border: 3px dashed #000;
border-radius: 0;
}
.quotecard::after {
content: 'Sad Ghuru';
display: block;
margin-top: 1cm;
text-align: right;
font-weight: bold;
}
}
</style>
style.css
@import url('https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Italianno&display=swap');
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body {
font-family: 'Architects Daughter', cursive;
background: rgb(41, 41, 41) linear-gradient(rgb(41, 41, 41), rgb(0, 0, 0)) no-repeat top left;
background-size: cover;
color: #fff;
height: 100%;
width: 100%;
overflow: hidden;
}
h1 {
font-family: 'Architects Daughter', cursive;
}
index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
Top comments (0)