feat: raw and single page views for gist files

master
Aravinth Manivannan 2022-03-20 21:48:11 +05:30
parent 13388326ef
commit 1568e48eea
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
7 changed files with 134 additions and 28 deletions

View File

@ -71,6 +71,69 @@ pub fn services(cfg: &mut web::ServiceConfig) {
cfg.service(post_comment);
}
#[derive(Serialize, Debug, Clone, PartialEq)]
pub struct HTMLFileInfo {
pub f: FileInfo,
pub raw: String,
pub highlighted_link: String,
}
impl HTMLFileInfo {
pub fn new(mut f: FileInfo, owner: &str, gist_public_id: &str) -> Self {
f.generate();
let owner = owner.to_string();
let gist = gist_public_id.into();
let raw_component = GetFilePath {
username: owner,
gist,
file: f.filename,
};
let raw = crate::V1_API_ROUTES.gist.get_file_route(&raw_component);
let highlighted_link = PAGES.gist.get_file_route(&raw_component);
f.filename = raw_component.file;
Self {
f,
raw,
highlighted_link,
}
}
}
#[derive(Serialize, Debug, Clone, PartialEq)]
pub struct HTMLGistInfo {
pub files: Vec<HTMLFileInfo>,
pub description: Option<String>,
pub owner: String,
pub created: i64,
pub updated: i64,
pub visibility: GistVisibility,
pub id: String,
}
impl From<GistInfo> for HTMLGistInfo {
fn from(mut g: GistInfo) -> Self {
let mut files = Vec::with_capacity(g.files.len());
g.files
.drain(..)
.for_each(|f| files.push(HTMLFileInfo::new(f, &g.owner, &g.id)));
Self {
files,
description: g.description,
owner: g.owner,
created: g.created,
updated: g.updated,
visibility: g.visibility,
id: g.id,
}
}
}
impl GenerateHTML for HTMLFileInfo {
fn generate(&mut self) {
self.f.generate();
}
}
#[derive(Clone)]
pub struct ViewGist {
ctx: RefCell<Context>,
@ -85,7 +148,7 @@ impl CtxError for ViewGist {
#[derive(Debug, Default, Clone, Serialize)]
pub struct PreviewPayload<'a> {
pub gist: Option<&'a GistInfo>,
pub gist: Option<&'a HTMLGistInfo>,
pub comments: Option<&'a Vec<GistComment>>,
}
@ -143,7 +206,7 @@ async fn view_util(
) -> PageResult<ViewGist, ViewGist> {
let username = id.identity();
let map_err = |e: ServiceError, gist: Option<&GistInfo>| -> PageError<ViewGist> {
let map_err = |e: ServiceError, gist: Option<&HTMLGistInfo>| -> PageError<ViewGist> {
PageError::new(
ViewGist::new(
username.as_deref(),
@ -168,12 +231,13 @@ async fn view_util(
}
}
let mut gist = data
let gist = data
.gist_preview(db.as_ref(), &mut GistID::ID(&path.gist))
.await
.map_err(|e| map_err(e, None))?;
gist.files.iter_mut().for_each(|file| file.generate());
//gist.files.iter_mut().for_each(|file| file.generate());
let gist: HTMLGistInfo = gist.into();
let comments = db.get_comments_on_gist(&path.gist).await.map_err(|e| {
let e: ServiceError = e.into();

View File

@ -17,7 +17,7 @@
use actix_auth_middleware::{Authentication, GetLoginRoute};
use serde::*;
pub use crate::api::v1::routes::PostCommentPath;
pub use crate::api::v1::routes::{GetFilePath, PostCommentPath};
/// constant [Pages](Pages) instance
pub const PAGES: Pages = Pages::new();
@ -87,20 +87,24 @@ pub struct Gists {
pub view_gist: &'static str,
/// post comment on gist
pub post_comment: &'static str,
/// get file
pub get_file: &'static str,
}
impl Gists {
/// create new instance of Gists route
pub const fn new() -> Self {
let profile = "/{username}";
let view_gist = "/{username}/{gist}";
let post_comment = "/{username}/{gist}/comment";
let profile = "/~{username}";
let view_gist = "/~{username}/{gist}";
let post_comment = "/~{username}/{gist}/comment";
let get_file = "/~{username}/{gist}/contents/{file}";
let new = "/";
Self {
profile,
new,
view_gist,
post_comment,
get_file,
}
}
@ -122,6 +126,15 @@ impl Gists {
.replace("{username}", &components.username)
.replace("{gist}", &components.gist)
}
/// get file routes with placeholders replaced with values provided.
/// filename is auto-escaped using [urlencoding::encode]
pub fn get_file_route(&self, components: &GetFilePath) -> String {
self.get_file
.replace("{username}", &components.username)
.replace("{gist}", &components.gist)
.replace("{file}", &urlencoding::encode(&components.file))
}
}
pub fn get_auth_middleware() -> Authentication<Pages> {
@ -149,9 +162,11 @@ mod tests {
fn gist_route_substitution_works() {
const NAME: &str = "bob";
const GIST: &str = "foo";
const FILE: &str = "README.md";
let get_profile = format!("/{NAME}");
let view_gist = format!("/{NAME}/{GIST}");
let post_comment = format!("/{NAME}/{GIST}/comment");
let get_file = format!("/{NAME}/{GIST}/contents/{FILE}");
let profile_component = GistProfilePathComponent { username: NAME };
@ -173,5 +188,12 @@ mod tests {
post_comment,
PAGES.gist.get_post_comment_route(&post_comment_path)
);
let file_component = GetFilePath {
username: NAME.into(),
gist: GIST.into(),
file: FILE.into(),
};
assert_eq!(get_file, PAGES.gist.get_file_route(&file_component));
}
}

View File

@ -119,12 +119,14 @@ main {
display: flex;
flex-direction: column;
/*
align-items: center;
*/
justify-content: space-evenly;
}
.auth__main {
flex-direction: row;
}
.main {
min-height: 80vh;
align-items: center;
@ -340,10 +342,26 @@ pre {
overflow-x: scroll;
}
.gist__filename {
.gist__filename-container {
padding: 8px;
background: #eeee;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
}
.gist__filename-name {
font-weight: 600;
color: #333;
}
.gist__file-anchor {
color: rgb(0, 86, 179);
margin-right: 5px;
}
.gist__filename-raw {
margin-left: 10px;
}
.gist__meta-container {

View File

@ -3,7 +3,7 @@
{% block nav %} {% include "pub_nav" %} {% endblock %}
{% block main %}
<main>
<main class="auth__main">
<section class="main">
<div class="title">
<h1>GitPad</h1>

View File

@ -1,3 +1,7 @@
<div class="gist__filename">
<a href="">{{ payload_file.filename }}</a>
<div class="gist__filename-container">
<a class="gist__filename-name" href="#{{ payload_file.f.filename }}"><span class="gist__file-anchor">#</span>{{ payload_file.f.filename }}</a>
<span>
<a class="gist__filename-link" href="{{ payload_file.highlighted_link }}">Link</a>
<a class="gist__filename-raw" href="{{ payload_file.raw }}">Raw</a>
</span>
</div>

View File

@ -1,3 +1,7 @@
{% if "text" in payload_file.content.file %}
{{ payload_file.content.file.text }}
{% endif %}
<div id="{{ payload_file.f.filename }}" class="gist_file">
{% include "gist_filename" %}
{% if "text" in payload_file.f.content.file %}
{{ payload_file.f.content.file.text }}
{% endif %}
</div>

View File

@ -7,18 +7,12 @@
{% include "gist_meta" %}
<div class="gist__data-container">
{% for payload_file in payload.gist.files %}
{% if "file" in payload_file.content %}
<div class="gist_file">
{% include "gist_filename" %}
{% include "gist_textfile" %}
</div>
{% elif "dir" in payload_file.content %}
{% for payload_file in payload_file.content.dir %}
{% if "file" in payload_file.content %}
<div class="gist_file">
{% include "gist_filename" %}
{% if "file" in payload_file.f.content %}
{% include "gist_textfile" %}
{% elif "dir" in payload_file.f.content %}
{% for payload_file in payload_file.f.content.dir %}
{% if "file" in payload_file.f.content %}
{% include "gist_textfile" %}
</div>
{% endif %}
{% endfor %}
{% endif %}