forked from realaravinth/libmedium
show post preview
This commit is contained in:
parent
59d0dd3a84
commit
33f02c2ece
7 changed files with 177 additions and 53 deletions
|
@ -3,11 +3,18 @@ query GetPost($id: ID!) {
|
|||
title
|
||||
createdAt
|
||||
readingTime
|
||||
uniqueSlug
|
||||
creator {
|
||||
name
|
||||
id
|
||||
imageId
|
||||
}
|
||||
previewImage {
|
||||
id
|
||||
}
|
||||
previewContent {
|
||||
subtitle
|
||||
}
|
||||
content {
|
||||
bodyModel {
|
||||
paragraphs {
|
||||
|
|
|
@ -3,63 +3,84 @@ type Query {
|
|||
}
|
||||
|
||||
schema {
|
||||
query: Query
|
||||
query: Query
|
||||
}
|
||||
|
||||
type Paragraphs {
|
||||
text: String!
|
||||
type: String!
|
||||
href: String
|
||||
layout: String
|
||||
iframe: IFrame
|
||||
metadata: MetaData
|
||||
markups: [MarkUp! ]!
|
||||
type Paragraphs {
|
||||
text: String!
|
||||
type: String!
|
||||
href: String
|
||||
layout: String
|
||||
iframe: IFrame
|
||||
metadata: MetaData
|
||||
markups: [MarkUp!]!
|
||||
}
|
||||
|
||||
type MarkUp {
|
||||
title: String
|
||||
type: String!
|
||||
href: String
|
||||
userId: String
|
||||
start: Int!
|
||||
end: Int!
|
||||
anchorType: String
|
||||
title: String
|
||||
type: String!
|
||||
href: String
|
||||
userId: String
|
||||
start: Int!
|
||||
end: Int!
|
||||
anchorType: String
|
||||
}
|
||||
|
||||
|
||||
type IFrame {
|
||||
mediaResource: MediaResource
|
||||
mediaResource: MediaResource
|
||||
}
|
||||
|
||||
type MediaResource{
|
||||
href: String!
|
||||
iframeSrc: String!
|
||||
iframeWidth: Int!
|
||||
iframeHeight: Int
|
||||
type MediaResource {
|
||||
href: String!
|
||||
iframeSrc: String!
|
||||
iframeWidth: Int!
|
||||
iframeHeight: Int
|
||||
}
|
||||
|
||||
type MetaData {
|
||||
id: String!
|
||||
originalWidth: Int
|
||||
originalHeight: Int
|
||||
id: String!
|
||||
originalWidth: Int
|
||||
originalHeight: Int
|
||||
}
|
||||
|
||||
|
||||
type BodyModel { paragraphs: [Paragraphs! ]! }
|
||||
|
||||
type Content { bodyModel: BodyModel! }
|
||||
|
||||
type User { id: String! name: String! imageId: String! }
|
||||
|
||||
type Post {
|
||||
id: ID!
|
||||
readingTime: Float!
|
||||
title: String!
|
||||
createdAt: Int!
|
||||
content: Content!
|
||||
creator: User!
|
||||
type BodyModel {
|
||||
paragraphs: [Paragraphs!]!
|
||||
}
|
||||
|
||||
type Data { post: Post }
|
||||
type Content {
|
||||
bodyModel: BodyModel!
|
||||
}
|
||||
|
||||
type AutogeneratedMainType { data: Data }
|
||||
type User {
|
||||
id: String!
|
||||
name: String!
|
||||
imageId: String!
|
||||
}
|
||||
|
||||
type Post {
|
||||
id: ID!
|
||||
readingTime: Float!
|
||||
title: String!
|
||||
createdAt: Int!
|
||||
content: Content!
|
||||
creator: User!
|
||||
previewImage: PreviewImage
|
||||
previewContent: PreviewContent
|
||||
uniqueSlug: String!
|
||||
}
|
||||
|
||||
type PreviewImage {
|
||||
id: String
|
||||
}
|
||||
|
||||
type PreviewContent {
|
||||
subtitle: String!
|
||||
}
|
||||
|
||||
type Data {
|
||||
post: Post
|
||||
}
|
||||
|
||||
type AutogeneratedMainType {
|
||||
data: Data
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ use sled::{Db, Tree};
|
|||
use crate::proxy::StringUtils;
|
||||
use crate::SETTINGS;
|
||||
|
||||
const POST_CACHE_VERSION: usize = 1;
|
||||
const POST_CACHE_VERSION: usize = 2;
|
||||
const GIST_CACHE_VERSION: usize = 1;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -53,6 +53,10 @@ impl PostResp {
|
|||
pub fn get_gist_id<'a>(&self, url: &'a str) -> &'a str {
|
||||
url.split('/').last().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_subtitle(&self) -> &str {
|
||||
self.preview_content.as_ref().unwrap().subtitle.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
|
@ -111,7 +115,7 @@ impl Data {
|
|||
for (tree, key, current_version) in trees {
|
||||
if let Ok(Some(v)) = tree.get(key) {
|
||||
let version = bincode::deserialize::<usize>(&v[..]).unwrap();
|
||||
let clean = !(version == current_version);
|
||||
let clean = version != current_version;
|
||||
|
||||
if clean {
|
||||
log::info!(
|
||||
|
|
23
src/proxy.rs
23
src/proxy.rs
|
@ -17,6 +17,7 @@
|
|||
use std::ops::{Bound, RangeBounds};
|
||||
|
||||
use actix_web::{http::header, web, HttpResponse, Responder};
|
||||
use chrono::{TimeZone, Utc};
|
||||
use futures::future::join_all;
|
||||
use reqwest::header::CONTENT_TYPE;
|
||||
use sailfish::TemplateOnce;
|
||||
|
@ -110,6 +111,9 @@ impl StringUtils for str {
|
|||
#[template(rm_whitespace = true)]
|
||||
pub struct Post {
|
||||
pub data: PostResp,
|
||||
pub date: String,
|
||||
pub preview_img: String,
|
||||
pub reading_time: usize,
|
||||
pub id: String,
|
||||
pub gists: Option<Vec<(String, crate::data::GistContent)>>,
|
||||
}
|
||||
|
@ -166,7 +170,7 @@ async fn page(path: web::Path<(String, String)>, data: AppData) -> impl Responde
|
|||
.unwrap()
|
||||
.href;
|
||||
if src.contains("gist.github.com") {
|
||||
let gist_id = post_data.get_gist_id(&src);
|
||||
let gist_id = post_data.get_gist_id(src);
|
||||
let fut = data.get_gist(gist_id.to_owned());
|
||||
futs.push(fut);
|
||||
}
|
||||
|
@ -179,10 +183,27 @@ async fn page(path: web::Path<(String, String)>, data: AppData) -> impl Responde
|
|||
Some(x)
|
||||
};
|
||||
|
||||
let date = Utc
|
||||
.timestamp_millis(post_data.created_at)
|
||||
.format("%b %e, %Y")
|
||||
.to_string();
|
||||
let reading_time = post_data.reading_time.floor() as usize;
|
||||
let preview_img = post_data
|
||||
.preview_image
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.id
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let preview_img = crate::V1_API_ROUTES.proxy.get_medium_asset(preview_img);
|
||||
|
||||
let page = Post {
|
||||
id: id.to_owned(),
|
||||
data: post_data,
|
||||
date,
|
||||
gists,
|
||||
reading_time,
|
||||
preview_img,
|
||||
};
|
||||
|
||||
let page = page.render_once().unwrap();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<. let gist_id = data.get_gist_id(&src); .>
|
||||
<. let gist_id = data.get_gist_id(src); .>
|
||||
<. let (_, gist)= gists.as_ref().unwrap().iter().find(|(id, _)| id == gist_id).as_ref().unwrap(); .>
|
||||
<. for file in &gist.files {.>
|
||||
<code> <.= file.get_html_content() .> </code>
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title><.= data.title .></title>
|
||||
<. include!("./post_meta.html"); .>
|
||||
</head>
|
||||
<body>
|
||||
<main class="container">
|
||||
<. use chrono::{TimeZone, Utc}; .>
|
||||
<. let dt = Utc.timestamp_millis(data.created_at); .>
|
||||
<. let date = dt.format("%b %e, %Y").to_string(); .>
|
||||
<h1><.= data.title .></h1>
|
||||
<p class="meta">
|
||||
<a class="author" href="https://medium.com/u/<.= data.creator.id .>" rel="noreferrer">
|
||||
|
@ -20,7 +15,7 @@
|
|||
/>
|
||||
<.= data.creator.name .></a
|
||||
>
|
||||
on <.= &date .> · <.= data.reading_time.floor() as usize .> min read
|
||||
on <.= &date .> · <.= reading_time .> min read
|
||||
</p>
|
||||
<article>
|
||||
<. let paragraphs = &data.content.body_model.paragraphs; .>
|
||||
|
|
76
templates/post_meta.html
Normal file
76
templates/post_meta.html
Normal file
|
@ -0,0 +1,76 @@
|
|||
<title><.= data.title .> | by <.= data.creator.name .> | <.= &date .></title>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,minimum-scale=1,initial-scale=1,maximum-scale=1"
|
||||
/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="twitter:app:name:iphone" content="libmedium" />
|
||||
<meta property="og:site_name" content="libmedium" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2021-10-21T10:54:12.523Z" />
|
||||
<meta
|
||||
name="title"
|
||||
content="Moscow state university network built by students | by Pavel Safronov | Oct, 2021 | libmedium"
|
||||
/>
|
||||
<meta property="og:title" content="<.= data.title .>" />
|
||||
<meta property="twitter:title" content="<.= data.title .>" />
|
||||
<meta name="twitter:app:url:iphone" content="medium://p/211539855cf9" />
|
||||
<meta data-rh="true" property="al:android:app_name" content="libmedium" />
|
||||
<meta name="description" content="<.= data.get_subtitle() .>" />
|
||||
<meta property="og:description" content="<.= data.get_subtitle() .>" />
|
||||
<meta property="twitter:description" content="<.= data.get_subtitle() .>" />
|
||||
<meta
|
||||
property="og:url"
|
||||
content="http://<.= &*crate::SETTINGS.server.domain .>/<.= data.creator.name .>/<.= data.unique_slug .>"
|
||||
/>
|
||||
<meta property="og:image" content="<.= preview_img .>" />
|
||||
<meta name="twitter:image:src" content="<.= preview_img .>" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta
|
||||
property="article:author"
|
||||
content="https://medium.com/@<.= data.creator.id .>"
|
||||
/>
|
||||
<!--
|
||||
<meta name="twitter:creator" content="@username" />
|
||||
-->
|
||||
<meta name="author" content="<.= data.creator.name .>" />
|
||||
<meta
|
||||
data-rh="true"
|
||||
name="robots"
|
||||
content="index,follow,max-image-preview:large"
|
||||
/>
|
||||
<meta name="twitter:label1" content="Reading time" />
|
||||
<meta name="twitter:data1" content="<.= reading_time .>min read" />
|
||||
<!--
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="152x152"
|
||||
href="https://miro.medium.com/fit/c/152/152/1*sHhtYhaCe2Uc3IU0IgKwIQ.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="120x120"
|
||||
href="https://miro.medium.com/fit/c/120/120/1*sHhtYhaCe2Uc3IU0IgKwIQ.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="76x76"
|
||||
href="https://miro.medium.com/fit/c/76/76/1*sHhtYhaCe2Uc3IU0IgKwIQ.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="60x60"
|
||||
href="https://miro.medium.com/fit/c/60/60/1*sHhtYhaCe2Uc3IU0IgKwIQ.png"
|
||||
/>
|
||||
<link
|
||||
rel="mask-icon"
|
||||
href="https://cdn-static-1.medium.com/_/fp/icons/libmedium-Avatar-500x500.svg"
|
||||
color="#171717"
|
||||
/>
|
||||
-->
|
||||
<link rel="author" href="https://medium.com/<.= data.creator.name .>" />
|
||||
<link
|
||||
rel="canonical"
|
||||
href="http://<.= &*crate::SETTINGS.server.domain .>/<.= data.creator.name .>/<.= data.unique_slug .>"
|
||||
/>
|
Loading…
Reference in a new issue