mirror of
https://github.com/kremalicious/blog.git
synced 2024-12-22 17:23:50 +01:00
realeted posts based on tags
This commit is contained in:
parent
590279d62e
commit
6e655b6a6d
@ -26,6 +26,10 @@ The whole [blog](https://kremalicious.com) is a React-based Single Page App buil
|
||||
|
||||
...
|
||||
|
||||
### Related Posts
|
||||
|
||||
...
|
||||
|
||||
### 🏆 SEO component
|
||||
|
||||
Includes a SEO component which automatically switches all required `meta` tags for search engines, Twitter Cards, and Facebook OpenGraph tags based on the browsed route/page.
|
||||
|
@ -10,10 +10,10 @@ updated: 2018-07-11 00:52:46+02:00
|
||||
featured: true
|
||||
|
||||
tags:
|
||||
- goodies
|
||||
- tutorial
|
||||
- tor
|
||||
- macos
|
||||
- goodies
|
||||
|
||||
coinhive: true
|
||||
---
|
||||
|
@ -29,4 +29,12 @@ export const imageSizeDefault = graphql`
|
||||
}
|
||||
`
|
||||
|
||||
export const imageSizeThumb = graphql`
|
||||
fragment ImageFluidThumb on ImageSharp {
|
||||
fluid(maxWidth: 200, maxHeight: 85, quality: 85, cropFocus: CENTER) {
|
||||
...GatsbyImageSharpFluid_withWebp_noBase64
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export default Image
|
||||
|
@ -27,15 +27,10 @@
|
||||
@include ellipsis();
|
||||
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: $text-color;
|
||||
font-family: $font-family-base;
|
||||
font-size: $font-size-small;
|
||||
padding: ($spacer/4) 0;
|
||||
max-width: 70%;
|
||||
margin: -($spacer) auto $spacer auto;
|
||||
|
||||
@media (min-width: $screen-sm) {
|
||||
max-width: 50%;
|
||||
}
|
||||
margin-top: -($spacer);
|
||||
margin-bottom: $spacer;
|
||||
}
|
||||
|
@ -16,9 +16,7 @@ const query = graphql`
|
||||
title
|
||||
image {
|
||||
childImageSharp {
|
||||
fluid(maxWidth: 300, maxHeight: 130, cropFocus: CENTER) {
|
||||
...GatsbyImageSharpFluid_withWebp_noBase64
|
||||
}
|
||||
...ImageFluidThumb
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,17 +3,14 @@ import PropTypes from 'prop-types'
|
||||
import { Link } from 'gatsby'
|
||||
import Time from 'react-time'
|
||||
import slugify from 'slugify'
|
||||
import PostLinkActions from '../atoms/PostLinkActions'
|
||||
import styles from './PostMeta.module.scss'
|
||||
|
||||
const PostMeta = ({ post, meta }) => {
|
||||
const { author, updated, tags, type, linkurl } = post.frontmatter
|
||||
const { date, slug } = post.fields
|
||||
const { author, updated, tags, type } = post.frontmatter
|
||||
const { date } = post.fields
|
||||
|
||||
return (
|
||||
<footer className={styles.entryMeta}>
|
||||
{type === 'link' && <PostLinkActions slug={slug} linkurl={linkurl} />}
|
||||
|
||||
<div className={styles.byline}>
|
||||
<span className={styles.by}>by</span>
|
||||
<a className="fn" rel="author" href={meta.author.uri}>
|
||||
@ -45,7 +42,7 @@ const PostMeta = ({ post, meta }) => {
|
||||
{tags && (
|
||||
<div className={styles.tags}>
|
||||
{tags.map(tag => {
|
||||
const to = tag === 'goodies' ? '/goodies' : `/tag/${slugify(tag)}/`
|
||||
const to = tag === 'goodies' ? '/goodies' : `/tags/${slugify(tag)}/`
|
||||
|
||||
return (
|
||||
<Link key={tag} className={styles.tag} to={to}>
|
||||
|
120
src/components/molecules/RelatedPosts.jsx
Normal file
120
src/components/molecules/RelatedPosts.jsx
Normal file
@ -0,0 +1,120 @@
|
||||
import React, { Fragment, PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Link, graphql, StaticQuery } from 'gatsby'
|
||||
import Image from '../atoms/Image'
|
||||
import styles from './RelatedPosts.module.scss'
|
||||
|
||||
const query = graphql`
|
||||
query {
|
||||
allMarkdownRemark(sort: { order: DESC, fields: [fields___date] }) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
frontmatter {
|
||||
title
|
||||
type
|
||||
linkurl
|
||||
tags
|
||||
image {
|
||||
childImageSharp {
|
||||
...ImageFluidThumb
|
||||
}
|
||||
}
|
||||
}
|
||||
fields {
|
||||
slug
|
||||
date(formatString: "MMMM DD, YYYY")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const postsWithDataFilter = (postsArray, key, valuesToFind) => {
|
||||
const newArray = postsArray.filter(post => {
|
||||
const frontmatterKey = post.node.frontmatter[key]
|
||||
|
||||
if (
|
||||
frontmatterKey !== null &&
|
||||
frontmatterKey.some(r => valuesToFind.includes(r))
|
||||
) {
|
||||
return post
|
||||
}
|
||||
})
|
||||
return newArray
|
||||
}
|
||||
|
||||
const PostItem = ({ post }) => {
|
||||
return (
|
||||
<li>
|
||||
<Link to={post.node.fields.slug}>
|
||||
{post.node.frontmatter.image ? (
|
||||
<Fragment>
|
||||
<Image
|
||||
fluid={post.node.frontmatter.image.childImageSharp.fluid}
|
||||
alt={post.node.frontmatter.title}
|
||||
/>
|
||||
<h4 className={styles.postTitle}>{post.node.frontmatter.title}</h4>
|
||||
</Fragment>
|
||||
) : (
|
||||
<div className={styles.empty}>
|
||||
<h4 className={styles.postTitle}>{post.node.frontmatter.title}</h4>
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
PostItem.propTypes = {
|
||||
post: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
class RelatedPosts extends PureComponent {
|
||||
shufflePosts = () => {
|
||||
this.forceUpdate()
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<StaticQuery
|
||||
query={query}
|
||||
render={data => {
|
||||
const posts = data.allMarkdownRemark.edges
|
||||
const filteredPosts = postsWithDataFilter(
|
||||
posts,
|
||||
'tags',
|
||||
this.props.tags
|
||||
)
|
||||
|
||||
return (
|
||||
<aside className={styles.relatedPosts}>
|
||||
<h1 className={styles.title}>Related Posts</h1>
|
||||
<ul>
|
||||
{filteredPosts
|
||||
.sort(() => 0.5 - Math.random())
|
||||
.slice(0, 6)
|
||||
.map(post => (
|
||||
<PostItem key={post.node.id} post={post} />
|
||||
))}
|
||||
</ul>
|
||||
<button
|
||||
className={`${styles.button} btn`}
|
||||
onClick={this.shufflePosts}
|
||||
>
|
||||
More Related Posts
|
||||
</button>
|
||||
</aside>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
RelatedPosts.propTypes = {
|
||||
tags: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
export default RelatedPosts
|
88
src/components/molecules/RelatedPosts.module.scss
Normal file
88
src/components/molecules/RelatedPosts.module.scss
Normal file
@ -0,0 +1,88 @@
|
||||
@import 'variables';
|
||||
@import 'mixins';
|
||||
|
||||
.empty {
|
||||
height: 100%;
|
||||
min-height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: $spacer / 4;
|
||||
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.relatedPosts {
|
||||
margin-top: -($spacer * 2);
|
||||
margin-bottom: $spacer;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
flex: 0 0 48%;
|
||||
margin-bottom: $spacer;
|
||||
|
||||
@media (min-width: $screen-sm) {
|
||||
flex-basis: 31%;
|
||||
}
|
||||
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
|
||||
> div {
|
||||
@include media-frame;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
> div {
|
||||
border-color: $link-color;
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: $link-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
@include heading-band;
|
||||
|
||||
font-size: $font-size-h3;
|
||||
}
|
||||
|
||||
.postTitle {
|
||||
display: inline-block;
|
||||
margin-top: $spacer / 4;
|
||||
margin-bottom: 0;
|
||||
font-size: $font-size-small;
|
||||
line-height: $line-height-small;
|
||||
color: $brand-grey-light;
|
||||
padding-left: .2rem;
|
||||
padding-right: .2rem;
|
||||
transition: color .2s ease-out;
|
||||
|
||||
@media (min-width: $screen-md) {
|
||||
font-size: $font-size-base;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
margin: auto;
|
||||
display: block;
|
||||
margin-top: $spacer / 2;
|
||||
}
|
@ -8,16 +8,26 @@ import PostTitle from '../components/atoms/PostTitle'
|
||||
import PostLead from '../components/atoms/PostLead'
|
||||
import PostContent from '../components/atoms/PostContent'
|
||||
import PostActions from '../components/atoms/PostActions'
|
||||
import PostLinkActions from '../components/atoms/PostLinkActions'
|
||||
import SEO from '../components/atoms/SEO'
|
||||
import Coinhive from '../components/atoms/Coinhive'
|
||||
import PostMeta from '../components/molecules/PostMeta'
|
||||
import Exif from '../components/atoms/Exif'
|
||||
import RelatedPosts from '../components/molecules/RelatedPosts'
|
||||
import styles from './Post.module.scss'
|
||||
|
||||
const Post = ({ data, location }) => {
|
||||
const { markdownRemark: post } = data
|
||||
const { contentYaml: meta } = data
|
||||
const { title, image, type, linkurl, style, coinhive } = post.frontmatter
|
||||
const {
|
||||
title,
|
||||
image,
|
||||
type,
|
||||
linkurl,
|
||||
style,
|
||||
coinhive,
|
||||
tags
|
||||
} = post.frontmatter
|
||||
const { slug } = post.fields
|
||||
|
||||
return (
|
||||
@ -37,9 +47,12 @@ const Post = ({ data, location }) => {
|
||||
)}
|
||||
{image && image.fields && <Exif exif={image.fields.exif} />}
|
||||
<PostContent post={post} />
|
||||
{type === 'link' && <PostLinkActions slug={slug} linkurl={linkurl} />}
|
||||
<PostActions slug={slug} url={meta.url} />
|
||||
<PostMeta post={post} meta={meta} />
|
||||
</article>
|
||||
|
||||
{type === 'post' && <RelatedPosts tags={tags} />}
|
||||
</Layout>
|
||||
{coinhive && <Coinhive />}
|
||||
</Fragment>
|
||||
|
Loading…
Reference in New Issue
Block a user