Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
3115ff3436 | |||
443b198c12 | |||
ac84d8d2db | |||
e27cf94fbf | |||
68495fb280 | |||
bec5c78709 | |||
abfcfdf09e | |||
dad01749e6 | |||
2efb73cee3 | |||
ace21b21d5 | |||
280e16bd7f | |||
44d44a529c | |||
0957f2e339 |
82
Cargo.lock
generated
82
Cargo.lock
generated
@ -93,7 +93,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655"
|
checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -267,7 +267,7 @@ checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -346,7 +346,7 @@ checksum = "e5444eec77a9ec2bfe4524139e09195862e981400c4358d3b760cae634e4c4ee"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -357,7 +357,7 @@ checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -562,7 +562,7 @@ checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -604,7 +604,7 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -696,7 +696,7 @@ dependencies = [
|
|||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -753,15 +753,6 @@ dependencies = [
|
|||||||
"version_check 0.9.2",
|
"version_check 0.9.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getopts"
|
|
||||||
version = "0.2.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.15"
|
version = "0.1.15"
|
||||||
@ -972,9 +963,9 @@ checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "0.4.6"
|
version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
@ -1015,14 +1006,13 @@ checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libreddit"
|
name = "libreddit"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"askama",
|
"askama",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"chrono",
|
"chrono",
|
||||||
"pulldown-cmark",
|
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -1272,7 +1262,7 @@ checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1283,7 +1273,7 @@ checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1340,18 +1330,6 @@ dependencies = [
|
|||||||
"unicode-xid 0.2.1",
|
"unicode-xid 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pulldown-cmark"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"getopts",
|
|
||||||
"memchr",
|
|
||||||
"unicase",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-error"
|
name = "quick-error"
|
||||||
version = "1.2.3"
|
version = "1.2.3"
|
||||||
@ -1584,14 +1562,14 @@ checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.60"
|
version = "1.0.61"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
|
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -1700,7 +1678,7 @@ dependencies = [
|
|||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1716,7 +1694,7 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha1",
|
"sha1",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1738,9 +1716,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.55"
|
version = "1.0.56"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a"
|
checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
@ -1749,22 +1727,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.22"
|
version = "1.0.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e"
|
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.22"
|
version = "1.0.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56"
|
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1831,7 +1809,7 @@ dependencies = [
|
|||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"standback",
|
"standback",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2027,12 +2005,6 @@ version = "1.7.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
|
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-width"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -2120,7 +2092,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2154,7 +2126,7 @@ checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.24",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.8",
|
"quote 1.0.8",
|
||||||
"syn 1.0.55",
|
"syn 1.0.56",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
@ -3,7 +3,7 @@ name = "libreddit"
|
|||||||
description = " Alternative private front-end to Reddit"
|
description = " Alternative private front-end to Reddit"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
repository = "https://github.com/spikecodes/libreddit"
|
repository = "https://github.com/spikecodes/libreddit"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
|
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@ -18,6 +18,5 @@ reqwest = { version = "0.10", default_features = false, features = ["rustls-tls"
|
|||||||
askama = "0.8.0"
|
askama = "0.8.0"
|
||||||
serde = "1.0.117"
|
serde = "1.0.117"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
pulldown-cmark = "0.8.0"
|
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
async-recursion = "0.3.1"
|
async-recursion = "0.3.1"
|
14
README.md
14
README.md
@ -7,8 +7,8 @@ Libre + Reddit = [Libreddit](https://libredd.it)
|
|||||||
- 🚀 Fast: written in Rust for blazing fast speeds and safety
|
- 🚀 Fast: written in Rust for blazing fast speeds and safety
|
||||||
- ☁️ Light: no JavaScript, no ads, no tracking
|
- ☁️ Light: no JavaScript, no ads, no tracking
|
||||||
- 🕵 Private: all requests are proxied through the server, including media
|
- 🕵 Private: all requests are proxied through the server, including media
|
||||||
- 🔒 Safe: does not rely on Reddit OAuth or require a Reddit API Key
|
- 🦺 Safe: does not rely on Reddit OAuth or require a Reddit API Key
|
||||||
- 📱 Responsive: works great on mobile!
|
- 🔒 Secure: strong [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) prevents browser requests to Reddit
|
||||||
|
|
||||||
Like [Invidious](https://github.com/iv-org/invidious) but for Reddit. Browse the coldest takes of [r/unpopularopinion](https://libredd.it/r/unpopularopinion) without being [tracked](#reddit).
|
Like [Invidious](https://github.com/iv-org/invidious) but for Reddit. Browse the coldest takes of [r/unpopularopinion](https://libredd.it/r/unpopularopinion) without being [tracked](#reddit).
|
||||||
|
|
||||||
@ -41,8 +41,14 @@ Like [Invidious](https://github.com/iv-org/invidious) but for Reddit. Browse the
|
|||||||
|
|
||||||
Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new) to have your [selfhosted instance](#deployment) listed here!
|
Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new) to have your [selfhosted instance](#deployment) listed here!
|
||||||
|
|
||||||
- [libredd.it](https://libredd.it) 🇺🇸 (Thank you to [YeapGuy](https://github.com/YeapGuy)!)
|
| Website | Country | Cloudflare |
|
||||||
- [libreddit.spike.codes](https://libreddit.spike.codes) 🇺🇸
|
|-|-|-|
|
||||||
|
| [libredd.it](https://libredd.it) (official) | 🇺🇸 US | |
|
||||||
|
| [libreddit.spike.codes](https://libreddit.spike.codes) (official) | 🇺🇸 US | |
|
||||||
|
| [libreddit.dothq.co](https://libreddit.dothq.co) | 🇺🇸 US | ✅ |
|
||||||
|
| [libreddit.insanity.wtf](https://libreddit.insanity.wtf) | 🇺🇸 US | ✅ |
|
||||||
|
|
||||||
|
A checkmark in the "Cloudflare" category here refers to the use of the reverse proxy, [Cloudflare](https://cloudflare). The checkmark will not be listed for a site which uses Cloudflare DNS but rather the proxying service which grants Cloudflare the ability to monitor traffic to the website.
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
|
41
src/post.rs
41
src/post.rs
@ -6,7 +6,6 @@ use async_recursion::async_recursion;
|
|||||||
|
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
use pulldown_cmark::{html, Options, Parser};
|
|
||||||
|
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
@ -27,8 +26,8 @@ async fn render(id: String, sort: Option<String>, comment_id: Option<String>) ->
|
|||||||
|
|
||||||
// Build the Reddit JSON API url
|
// Build the Reddit JSON API url
|
||||||
let url: String = match comment_id {
|
let url: String = match comment_id {
|
||||||
None => format!("{}.json?sort={}", id, sorting),
|
None => format!("{}.json?sort={}&raw_json=1", id, sorting),
|
||||||
Some(val) => format!("{}.json?sort={}&comment={}", id, sorting, val)
|
Some(val) => format!("{}.json?sort={}&comment={}&raw_json=1", id, sorting, val),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send a request to the url, receive JSON in response
|
// Send a request to the url, receive JSON in response
|
||||||
@ -80,13 +79,13 @@ async fn media(data: &serde_json::Value) -> (String, String) {
|
|||||||
let post_type: &str;
|
let post_type: &str;
|
||||||
let url = if !data["preview"]["reddit_video_preview"]["fallback_url"].is_null() {
|
let url = if !data["preview"]["reddit_video_preview"]["fallback_url"].is_null() {
|
||||||
post_type = "video";
|
post_type = "video";
|
||||||
format_url(data["preview"]["reddit_video_preview"]["fallback_url"].as_str().unwrap()).await
|
format_url(data["preview"]["reddit_video_preview"]["fallback_url"].as_str().unwrap().to_string()).await
|
||||||
} else if !data["secure_media"]["reddit_video"]["fallback_url"].is_null() {
|
} else if !data["secure_media"]["reddit_video"]["fallback_url"].is_null() {
|
||||||
post_type = "video";
|
post_type = "video";
|
||||||
format_url(data["secure_media"]["reddit_video"]["fallback_url"].as_str().unwrap()).await
|
format_url(data["secure_media"]["reddit_video"]["fallback_url"].as_str().unwrap().to_string()).await
|
||||||
} else if data["post_hint"].as_str().unwrap_or("") == "image" {
|
} else if data["post_hint"].as_str().unwrap_or("") == "image" {
|
||||||
post_type = "image";
|
post_type = "image";
|
||||||
format_url(data["preview"]["images"][0]["source"]["url"].as_str().unwrap()).await
|
format_url(data["preview"]["images"][0]["source"]["url"].as_str().unwrap().to_string()).await
|
||||||
} else {
|
} else {
|
||||||
post_type = "link";
|
post_type = "link";
|
||||||
data["url"].as_str().unwrap().to_string()
|
data["url"].as_str().unwrap().to_string()
|
||||||
@ -95,33 +94,24 @@ async fn media(data: &serde_json::Value) -> (String, String) {
|
|||||||
(post_type.to_string(), url)
|
(post_type.to_string(), url)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn markdown_to_html(md: &str) -> String {
|
|
||||||
let mut options = Options::empty();
|
|
||||||
options.insert(Options::ENABLE_TABLES);
|
|
||||||
options.insert(Options::ENABLE_FOOTNOTES);
|
|
||||||
options.insert(Options::ENABLE_STRIKETHROUGH);
|
|
||||||
options.insert(Options::ENABLE_TASKLISTS);
|
|
||||||
let parser = Parser::new_ext(md, options);
|
|
||||||
|
|
||||||
// Write to String buffer.
|
|
||||||
let mut html_output = String::new();
|
|
||||||
html::push_html(&mut html_output, parser);
|
|
||||||
html_output
|
|
||||||
}
|
|
||||||
|
|
||||||
// POSTS
|
// POSTS
|
||||||
async fn parse_post(json: serde_json::Value) -> Result<Post, &'static str> {
|
async fn parse_post(json: serde_json::Value) -> Result<Post, &'static str> {
|
||||||
|
// Retrieve post (as opposed to comments) from JSON
|
||||||
let post_data: &serde_json::Value = &json["data"]["children"][0];
|
let post_data: &serde_json::Value = &json["data"]["children"][0];
|
||||||
|
|
||||||
|
// Grab UTC time as unix timestamp
|
||||||
let unix_time: i64 = post_data["data"]["created_utc"].as_f64().unwrap().round() as i64;
|
let unix_time: i64 = post_data["data"]["created_utc"].as_f64().unwrap().round() as i64;
|
||||||
|
// Parse post score
|
||||||
let score = post_data["data"]["score"].as_i64().unwrap();
|
let score = post_data["data"]["score"].as_i64().unwrap();
|
||||||
|
|
||||||
|
// Determine the type of media along with the media URL
|
||||||
let media = media(&post_data["data"]).await;
|
let media = media(&post_data["data"]).await;
|
||||||
|
|
||||||
|
// Build a post using data parsed from Reddit post API
|
||||||
let post = Post {
|
let post = Post {
|
||||||
title: val(post_data, "title").await,
|
title: val(post_data, "title").await,
|
||||||
community: val(post_data, "subreddit").await,
|
community: val(post_data, "subreddit").await,
|
||||||
body: markdown_to_html(post_data["data"]["selftext"].as_str().unwrap()).await,
|
body: val(post_data,"selftext_html").await,
|
||||||
author: val(post_data, "author").await,
|
author: val(post_data, "author").await,
|
||||||
author_flair: Flair(
|
author_flair: Flair(
|
||||||
val(post_data, "author_flair_text").await,
|
val(post_data, "author_flair_text").await,
|
||||||
@ -131,8 +121,6 @@ async fn parse_post(json: serde_json::Value) -> Result<Post, &'static str> {
|
|||||||
url: val(post_data, "permalink").await,
|
url: val(post_data, "permalink").await,
|
||||||
score: format_num(score),
|
score: format_num(score),
|
||||||
post_type: media.0,
|
post_type: media.0,
|
||||||
media: media.1,
|
|
||||||
time: Utc.timestamp(unix_time, 0).format("%b %e %Y %H:%M UTC").to_string(),
|
|
||||||
flair: Flair(
|
flair: Flair(
|
||||||
val(post_data, "link_flair_text").await,
|
val(post_data, "link_flair_text").await,
|
||||||
val(post_data, "link_flair_background_color").await,
|
val(post_data, "link_flair_background_color").await,
|
||||||
@ -142,6 +130,9 @@ async fn parse_post(json: serde_json::Value) -> Result<Post, &'static str> {
|
|||||||
"white".to_string()
|
"white".to_string()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
nsfw: post_data["data"]["over_18"].as_bool().unwrap_or(false),
|
||||||
|
media: media.1,
|
||||||
|
time: Utc.timestamp(unix_time, 0).format("%b %e %Y %H:%M UTC").to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(post)
|
Ok(post)
|
||||||
@ -156,14 +147,14 @@ async fn parse_comments(json: serde_json::Value) -> Result<Vec<Comment>, &'stati
|
|||||||
let mut comments: Vec<Comment> = Vec::new();
|
let mut comments: Vec<Comment> = Vec::new();
|
||||||
|
|
||||||
// For each comment, retrieve the values to build a Comment object
|
// For each comment, retrieve the values to build a Comment object
|
||||||
for comment in comment_data.iter() {
|
for comment in comment_data {
|
||||||
let unix_time: i64 = comment["data"]["created_utc"].as_f64().unwrap_or(0.0).round() as i64;
|
let unix_time: i64 = comment["data"]["created_utc"].as_f64().unwrap_or(0.0).round() as i64;
|
||||||
if unix_time == 0 {
|
if unix_time == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let score = comment["data"]["score"].as_i64().unwrap_or(0);
|
let score = comment["data"]["score"].as_i64().unwrap_or(0);
|
||||||
let body = markdown_to_html(comment["data"]["body"].as_str().unwrap_or("")).await;
|
let body = val(comment, "body_html").await;
|
||||||
|
|
||||||
let replies: Vec<Comment> = if comment["data"]["replies"].is_object() {
|
let replies: Vec<Comment> = if comment["data"]["replies"].is_object() {
|
||||||
parse_comments(comment["data"]["replies"].clone()).await.unwrap_or(Vec::new())
|
parse_comments(comment["data"]["replies"].clone()).await.unwrap_or(Vec::new())
|
||||||
|
@ -36,14 +36,7 @@ pub async fn render(sub_name: String, sort: Option<String>, ends: (Option<String
|
|||||||
let sub_result = if !&sub_name.contains("+") {
|
let sub_result = if !&sub_name.contains("+") {
|
||||||
subreddit(&sub_name).await
|
subreddit(&sub_name).await
|
||||||
} else {
|
} else {
|
||||||
Ok(Subreddit {
|
Ok(Subreddit::default())
|
||||||
name: String::new(),
|
|
||||||
title: String::new(),
|
|
||||||
description: String::new(),
|
|
||||||
icon: String::new(),
|
|
||||||
members: String::new(),
|
|
||||||
active: String::new(),
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
let items_result = fetch_posts(url, String::new()).await;
|
let items_result = fetch_posts(url, String::new()).await;
|
||||||
|
|
||||||
@ -55,15 +48,9 @@ pub async fn render(sub_name: String, sort: Option<String>, ends: (Option<String
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
Ok(HttpResponse::Ok().status(StatusCode::NOT_FOUND).content_type("text/html").body(s))
|
Ok(HttpResponse::Ok().status(StatusCode::NOT_FOUND).content_type("text/html").body(s))
|
||||||
} else {
|
} else {
|
||||||
let mut sub = sub_result.unwrap();
|
let sub = sub_result.unwrap();
|
||||||
let items = items_result.unwrap();
|
let items = items_result.unwrap();
|
||||||
|
|
||||||
sub.icon = if sub.icon != "" {
|
|
||||||
format!(r#"<img class="subreddit_icon" src="{}">"#, sub.icon)
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
let s = SubredditTemplate {
|
let s = SubredditTemplate {
|
||||||
sub: sub,
|
sub: sub,
|
||||||
posts: items.0,
|
posts: items.0,
|
||||||
@ -79,7 +66,7 @@ pub async fn render(sub_name: String, sort: Option<String>, ends: (Option<String
|
|||||||
// SUBREDDIT
|
// SUBREDDIT
|
||||||
async fn subreddit(sub: &String) -> Result<Subreddit, &'static str> {
|
async fn subreddit(sub: &String) -> Result<Subreddit, &'static str> {
|
||||||
// Build the Reddit JSON API url
|
// Build the Reddit JSON API url
|
||||||
let url: String = format!("r/{}/about.json", sub);
|
let url: String = format!("r/{}/about.json?raw_json=1", sub);
|
||||||
|
|
||||||
// Send a request to the url, receive JSON in response
|
// Send a request to the url, receive JSON in response
|
||||||
let req = request(url).await;
|
let req = request(url).await;
|
||||||
@ -92,14 +79,24 @@ async fn subreddit(sub: &String) -> Result<Subreddit, &'static str> {
|
|||||||
// Otherwise, grab the JSON output from the request
|
// Otherwise, grab the JSON output from the request
|
||||||
let res = req.unwrap();
|
let res = req.unwrap();
|
||||||
|
|
||||||
|
// Metadata regarding the subreddit
|
||||||
let members = res["data"]["subscribers"].as_u64().unwrap_or(0);
|
let members = res["data"]["subscribers"].as_u64().unwrap_or(0);
|
||||||
let active = res["data"]["accounts_active"].as_u64().unwrap_or(0);
|
let active = res["data"]["accounts_active"].as_u64().unwrap_or(0);
|
||||||
|
|
||||||
|
// Fetch subreddit icon either from the community_icon or icon_img value
|
||||||
|
let community_icon: &str = res["data"]["community_icon"].as_str().unwrap().split("?").collect::<Vec<&str>>()[0];
|
||||||
|
let icon = if community_icon.is_empty() {
|
||||||
|
val(&res, "icon_img").await
|
||||||
|
} else {
|
||||||
|
community_icon.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
let sub = Subreddit {
|
let sub = Subreddit {
|
||||||
name: val(&res, "display_name").await,
|
name: val(&res, "display_name").await,
|
||||||
title: val(&res, "title").await,
|
title: val(&res, "title").await,
|
||||||
description: val(&res, "public_description").await,
|
description: val(&res, "public_description").await,
|
||||||
icon: format_url(val(&res, "icon_img").await.as_str()).await,
|
info: val(&res, "description_html").await.replace("\\", ""),
|
||||||
|
icon: format_url(icon).await,
|
||||||
members: format_num(members.try_into().unwrap()),
|
members: format_num(members.try_into().unwrap()),
|
||||||
active: format_num(active.try_into().unwrap()),
|
active: format_num(active.try_into().unwrap()),
|
||||||
};
|
};
|
||||||
|
34
src/user.rs
34
src/user.rs
@ -2,6 +2,7 @@
|
|||||||
use crate::utils::{fetch_posts, format_url, nested_val, request, ErrorTemplate, Params, Post, User};
|
use crate::utils::{fetch_posts, format_url, nested_val, request, ErrorTemplate, Params, Post, User};
|
||||||
use actix_web::{http::StatusCode, web, HttpResponse, Result};
|
use actix_web::{http::StatusCode, web, HttpResponse, Result};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
use chrono::{TimeZone, Utc};
|
||||||
|
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
@ -10,11 +11,22 @@ struct UserTemplate {
|
|||||||
user: User,
|
user: User,
|
||||||
posts: Vec<Post>,
|
posts: Vec<Post>,
|
||||||
sort: String,
|
sort: String,
|
||||||
|
ends: (String, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn render(username: String, sort: String) -> Result<HttpResponse> {
|
async fn render(username: String, sort: Option<String>, ends: (Option<String>, Option<String>)) -> Result<HttpResponse> {
|
||||||
|
let sorting = sort.unwrap_or("new".to_string());
|
||||||
|
|
||||||
|
let before = ends.1.clone().unwrap_or(String::new()); // If there is an after, there must be a before
|
||||||
|
|
||||||
// Build the Reddit JSON API url
|
// Build the Reddit JSON API url
|
||||||
let url: String = format!("user/{}/.json?sort={}", username, sort);
|
let url = match ends.0 {
|
||||||
|
Some(val) => format!("user/{}/.json?sort={}&before={}&count=25&raw_json=1", username, sorting, val),
|
||||||
|
None => match ends.1 {
|
||||||
|
Some(val) => format!("user/{}/.json?sort={}&after={}&count=25&raw_json=1", username, sorting, val),
|
||||||
|
None => format!("user/{}/.json?sort={}&raw_json=1", username, sorting),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
let user = user(&username).await;
|
let user = user(&username).await;
|
||||||
let posts = fetch_posts(url, "Comment".to_string()).await;
|
let posts = fetch_posts(url, "Comment".to_string()).await;
|
||||||
@ -27,10 +39,13 @@ async fn render(username: String, sort: String) -> Result<HttpResponse> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
Ok(HttpResponse::Ok().status(StatusCode::NOT_FOUND).content_type("text/html").body(s))
|
Ok(HttpResponse::Ok().status(StatusCode::NOT_FOUND).content_type("text/html").body(s))
|
||||||
} else {
|
} else {
|
||||||
|
let posts_unwrapped = posts.unwrap();
|
||||||
|
|
||||||
let s = UserTemplate {
|
let s = UserTemplate {
|
||||||
user: user.unwrap(),
|
user: user.unwrap(),
|
||||||
posts: posts.unwrap().0,
|
posts: posts_unwrapped.0,
|
||||||
sort: sort,
|
sort: sorting,
|
||||||
|
ends: (before, posts_unwrapped.1)
|
||||||
}
|
}
|
||||||
.render()
|
.render()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -40,10 +55,7 @@ async fn render(username: String, sort: String) -> Result<HttpResponse> {
|
|||||||
|
|
||||||
// SERVICES
|
// SERVICES
|
||||||
pub async fn page(web::Path(username): web::Path<String>, params: web::Query<Params>) -> Result<HttpResponse> {
|
pub async fn page(web::Path(username): web::Path<String>, params: web::Query<Params>) -> Result<HttpResponse> {
|
||||||
match ¶ms.sort {
|
render(username, params.sort.clone(), (params.before.clone(), params.after.clone())).await
|
||||||
Some(sort) => render(username, sort.to_string()).await,
|
|
||||||
None => render(username, "hot".to_string()).await,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// USER
|
// USER
|
||||||
@ -62,11 +74,15 @@ async fn user(name: &String) -> Result<User, &'static str> {
|
|||||||
// Otherwise, grab the JSON output from the request
|
// Otherwise, grab the JSON output from the request
|
||||||
let res = req.unwrap();
|
let res = req.unwrap();
|
||||||
|
|
||||||
|
// Grab creation date as unix timestamp
|
||||||
|
let created: i64 = res["data"]["created"].as_f64().unwrap().round() as i64;
|
||||||
|
|
||||||
// Parse the JSON output into a User struct
|
// Parse the JSON output into a User struct
|
||||||
Ok(User {
|
Ok(User {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
icon: format_url(nested_val(&res, "subreddit", "icon_img").await.as_str()).await,
|
icon: format_url(nested_val(&res, "subreddit", "icon_img").await).await,
|
||||||
karma: res["data"]["total_karma"].as_i64().unwrap(),
|
karma: res["data"]["total_karma"].as_i64().unwrap(),
|
||||||
|
created: Utc.timestamp(created, 0).format("%b %e, %Y").to_string(),
|
||||||
banner: nested_val(&res, "subreddit", "banner_img").await,
|
banner: nested_val(&res, "subreddit", "banner_img").await,
|
||||||
description: nested_val(&res, "subreddit", "public_description").await,
|
description: nested_val(&res, "subreddit", "public_description").await,
|
||||||
})
|
})
|
||||||
|
39
src/utils.rs
39
src/utils.rs
@ -11,11 +11,9 @@ use base64::encode;
|
|||||||
//
|
//
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
//
|
//
|
||||||
#[allow(dead_code)]
|
|
||||||
// Post flair with text, background color and foreground color
|
// Post flair with text, background color and foreground color
|
||||||
pub struct Flair(pub String, pub String, pub String);
|
pub struct Flair(pub String, pub String, pub String);
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
// Post containing content, metadata and media
|
// Post containing content, metadata and media
|
||||||
pub struct Post {
|
pub struct Post {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
@ -26,12 +24,12 @@ pub struct Post {
|
|||||||
pub url: String,
|
pub url: String,
|
||||||
pub score: String,
|
pub score: String,
|
||||||
pub post_type: String,
|
pub post_type: String,
|
||||||
|
pub flair: Flair,
|
||||||
|
pub nsfw: bool,
|
||||||
pub media: String,
|
pub media: String,
|
||||||
pub time: String,
|
pub time: String,
|
||||||
pub flair: Flair,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
// Comment with content, post, score and data/time that it was posted
|
// Comment with content, post, score and data/time that it was posted
|
||||||
pub struct Comment {
|
pub struct Comment {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@ -43,22 +41,23 @@ pub struct Comment {
|
|||||||
pub replies: Vec<Comment>,
|
pub replies: Vec<Comment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
// User struct containing metadata about user
|
// User struct containing metadata about user
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
pub karma: i64,
|
pub karma: i64,
|
||||||
|
pub created: String,
|
||||||
pub banner: String,
|
pub banner: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[derive(Default)]
|
||||||
// Subreddit struct containing metadata about community
|
// Subreddit struct containing metadata about community
|
||||||
pub struct Subreddit {
|
pub struct Subreddit {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
|
pub info: String,
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
pub members: String,
|
pub members: String,
|
||||||
pub active: String,
|
pub active: String,
|
||||||
@ -83,7 +82,12 @@ pub struct ErrorTemplate {
|
|||||||
// FORMATTING
|
// FORMATTING
|
||||||
//
|
//
|
||||||
|
|
||||||
pub async fn format_url(url: &str) -> String {
|
// Direct urls to proxy if proxy is enabled
|
||||||
|
pub async fn format_url(url: String) -> String {
|
||||||
|
if url.is_empty() {
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "proxy")]
|
#[cfg(feature = "proxy")]
|
||||||
return "/proxy/".to_string() + encode(url).as_str();
|
return "/proxy/".to_string() + encode(url).as_str();
|
||||||
|
|
||||||
@ -91,6 +95,7 @@ pub async fn format_url(url: &str) -> String {
|
|||||||
return url.to_string();
|
return url.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append `m` and `k` for millions and thousands respectively
|
||||||
pub fn format_num(num: i64) -> String {
|
pub fn format_num(num: i64) -> String {
|
||||||
if num > 1000000 {
|
if num > 1000000 {
|
||||||
format!("{}m", num / 1000000)
|
format!("{}m", num / 1000000)
|
||||||
@ -105,22 +110,20 @@ pub fn format_num(num: i64) -> String {
|
|||||||
// JSON PARSING
|
// JSON PARSING
|
||||||
//
|
//
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
// val() function used to parse JSON from Reddit APIs
|
// val() function used to parse JSON from Reddit APIs
|
||||||
pub async fn val(j: &serde_json::Value, k: &str) -> String {
|
pub async fn val(j: &serde_json::Value, k: &str) -> String {
|
||||||
String::from(j["data"][k].as_str().unwrap_or(""))
|
String::from(j["data"][k].as_str().unwrap_or(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
// nested_val() function used to parse JSON from Reddit APIs
|
// nested_val() function used to parse JSON from Reddit APIs
|
||||||
pub async fn nested_val(j: &serde_json::Value, n: &str, k: &str) -> String {
|
pub async fn nested_val(j: &serde_json::Value, n: &str, k: &str) -> String {
|
||||||
String::from(j["data"][n][k].as_str().unwrap())
|
String::from(j["data"][n][k].as_str().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
// Fetch posts of a user or subreddit
|
||||||
pub async fn fetch_posts(url: String, fallback_title: String) -> Result<(Vec<Post>, String), &'static str> {
|
pub async fn fetch_posts(url: String, fallback_title: String) -> Result<(Vec<Post>, String), &'static str> {
|
||||||
// Send a request to the url, receive JSON in response
|
// Send a request to the url, receive JSON in response
|
||||||
let req = request(url).await;
|
let req = request(url.clone()).await;
|
||||||
|
|
||||||
// If the Reddit API returns an error, exit this function
|
// If the Reddit API returns an error, exit this function
|
||||||
if req.is_err() {
|
if req.is_err() {
|
||||||
@ -135,9 +138,9 @@ pub async fn fetch_posts(url: String, fallback_title: String) -> Result<(Vec<Pos
|
|||||||
|
|
||||||
let mut posts: Vec<Post> = Vec::new();
|
let mut posts: Vec<Post> = Vec::new();
|
||||||
|
|
||||||
for post in post_list.iter() {
|
for post in post_list {
|
||||||
let img = if val(post, "thumbnail").await.starts_with("https:/") {
|
let img = if val(post, "thumbnail").await.starts_with("https:/") {
|
||||||
format_url(val(post, "thumbnail").await.as_str()).await
|
format_url(val(post, "thumbnail").await).await
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
@ -148,7 +151,7 @@ pub async fn fetch_posts(url: String, fallback_title: String) -> Result<(Vec<Pos
|
|||||||
posts.push(Post {
|
posts.push(Post {
|
||||||
title: if title.is_empty() { fallback_title.to_owned() } else { title },
|
title: if title.is_empty() { fallback_title.to_owned() } else { title },
|
||||||
community: val(post, "subreddit").await,
|
community: val(post, "subreddit").await,
|
||||||
body: val(post, "body").await,
|
body: val(post, "body_html").await,
|
||||||
author: val(post, "author").await,
|
author: val(post, "author").await,
|
||||||
author_flair: Flair(
|
author_flair: Flair(
|
||||||
val(post, "author_flair_text").await,
|
val(post, "author_flair_text").await,
|
||||||
@ -158,8 +161,6 @@ pub async fn fetch_posts(url: String, fallback_title: String) -> Result<(Vec<Pos
|
|||||||
score: format_num(score),
|
score: format_num(score),
|
||||||
post_type: "link".to_string(),
|
post_type: "link".to_string(),
|
||||||
media: img,
|
media: img,
|
||||||
url: val(post, "permalink").await,
|
|
||||||
time: Utc.timestamp(unix_time, 0).format("%b %e '%y").to_string(),
|
|
||||||
flair: Flair(
|
flair: Flair(
|
||||||
val(post, "link_flair_text").await,
|
val(post, "link_flair_text").await,
|
||||||
val(post, "link_flair_background_color").await,
|
val(post, "link_flair_background_color").await,
|
||||||
@ -169,9 +170,14 @@ pub async fn fetch_posts(url: String, fallback_title: String) -> Result<(Vec<Pos
|
|||||||
"white".to_string()
|
"white".to_string()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
nsfw: post["data"]["over_18"].as_bool().unwrap_or(false),
|
||||||
|
url: val(post, "permalink").await,
|
||||||
|
time: Utc.timestamp(unix_time, 0).format("%b %e '%y").to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dbg!(url);
|
||||||
|
|
||||||
Ok((posts, res["data"]["after"].as_str().unwrap_or("").to_string()))
|
Ok((posts, res["data"]["after"].as_str().unwrap_or("").to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +186,6 @@ pub async fn fetch_posts(url: String, fallback_title: String) -> Result<(Vec<Pos
|
|||||||
//
|
//
|
||||||
|
|
||||||
// Make a request to a Reddit API and parse the JSON response
|
// Make a request to a Reddit API and parse the JSON response
|
||||||
#[allow(dead_code)]
|
|
||||||
pub async fn request(mut url: String) -> Result<serde_json::Value, &'static str> {
|
pub async fn request(mut url: String) -> Result<serde_json::Value, &'static str> {
|
||||||
url = format!("https://www.reddit.com/{}", url);
|
url = format!("https://www.reddit.com/{}", url);
|
||||||
|
|
||||||
|
221
static/style.css
221
static/style.css
@ -12,10 +12,9 @@
|
|||||||
|
|
||||||
* {
|
* {
|
||||||
transition: 0.2s all;
|
transition: 0.2s all;
|
||||||
margin: 0px;
|
margin: 0;
|
||||||
color: white;
|
color: white;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-weight: normal;
|
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +29,6 @@ nav {
|
|||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
background: var(--outside);
|
background: var(--outside);
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
font-weight: bold;
|
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,10 +37,11 @@ nav {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
max-width: 750px;
|
max-width: 750px;
|
||||||
margin: 0 auto;
|
padding: 10px 20px;
|
||||||
margin-top: 25px;
|
margin: 20px auto;
|
||||||
padding: 0px 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
@ -56,6 +55,10 @@ button {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -65,9 +68,14 @@ a:not(.post_right):hover {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
#about {
|
img[src=""] {
|
||||||
padding-top: 20px;
|
display: none;
|
||||||
background: #151515;
|
}
|
||||||
|
|
||||||
|
aside {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin: 20px 20px 0 20px;
|
||||||
|
max-width: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#version {
|
#version {
|
||||||
@ -75,55 +83,57 @@ a:not(.post_right):hover {
|
|||||||
opacity: 25%;
|
opacity: 25%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Subreddit */
|
/* User & Subreddit */
|
||||||
|
|
||||||
.subreddit {
|
#user, #subreddit, #sidebar {
|
||||||
max-width: 750px;
|
margin: 40px auto 0 auto;
|
||||||
margin: 0 auto;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
padding-bottom: 25px;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
height: max-content;
|
||||||
|
background: var(--outside);
|
||||||
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subreddit_name {
|
#sidebar, #sidebar_contents {
|
||||||
margin-bottom: 10px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subreddit_right {
|
#sidebar_label {
|
||||||
display: flex;
|
border: 2px solid var(--highlighted);
|
||||||
flex-flow: column;
|
padding: 10px;
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.subreddit_icon {
|
#user_icon, #subreddit_icon {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
|
border: 2px solid var(--accent);
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
padding: 20px;
|
padding: 10px;
|
||||||
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#stats {
|
#user_name, #subreddit_name {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* User */
|
#user_description, #subreddit_description {
|
||||||
|
margin: 10px 20px;
|
||||||
.user {
|
text-align: center;
|
||||||
max-width: 750px;
|
font-size: 15px;
|
||||||
margin: 0 auto;
|
|
||||||
display: flex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.user_right {
|
#user_details, #subreddit_details {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-flow: column;
|
grid-template-columns: repeat(2, 1fr);
|
||||||
justify-content: center;
|
margin-top: 15px;
|
||||||
|
grid-column-gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user_icon {
|
#user_details > label, #subreddit_details > label {
|
||||||
width: 100px;
|
color: var(--accent);
|
||||||
height: 100px;
|
font-size: 15px;
|
||||||
border-radius: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sorting */
|
/* Sorting */
|
||||||
@ -131,23 +141,26 @@ a:not(.post_right):hover {
|
|||||||
#sort {
|
#sort {
|
||||||
background: var(--outside);
|
background: var(--outside);
|
||||||
box-shadow: var(--black-contrast);
|
box-shadow: var(--black-contrast);
|
||||||
border: 0px;
|
border: 0;
|
||||||
padding: 0px 15px;
|
padding: 0 15px;
|
||||||
margin: 20px 0px;
|
margin-bottom: 20px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
border-radius: 5px 0px 0px 5px;
|
border-radius: 5px 0 0 5px;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sort_submit {
|
#sort_submit {
|
||||||
background: var(--highlighted);
|
background: var(--highlighted);
|
||||||
border: 0px;
|
border: 0;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 0px 5px 5px 0px;
|
border-radius: 0 5px 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#sort:hover { background: var(--foreground); }
|
||||||
|
#sort_submit:hover { color: var(--accent); }
|
||||||
|
|
||||||
#sort > div, footer > a {
|
#sort > div, footer > a {
|
||||||
box-shadow: var(--black-contrast);
|
box-shadow: var(--black-contrast);
|
||||||
background: var(--outside);
|
background: var(--outside);
|
||||||
@ -177,6 +190,10 @@ a:not(.post_right):hover {
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.post.highlighted {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.post:hover {
|
.post:hover {
|
||||||
background: var(--foreground);
|
background: var(--foreground);
|
||||||
}
|
}
|
||||||
@ -194,11 +211,27 @@ a:not(.post_right):hover {
|
|||||||
.post_left {
|
.post_left {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: var(--foreground);
|
background: var(--foreground);
|
||||||
border-radius: 5px 0px 0px 5px;
|
border-radius: 5px 0 0 5px;
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.post_score {
|
||||||
|
margin-top: 20px;
|
||||||
|
color: var(--accent);
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nsfw {
|
||||||
|
color: #FF5C5D;
|
||||||
|
margin-top: 20px;
|
||||||
|
border: 1px solid #FF5C5D;
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.post_subreddit {
|
.post_subreddit {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
@ -207,11 +240,6 @@ a:not(.post_right):hover {
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post_score {
|
|
||||||
margin-top: 20px;
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_right {
|
.post_right {
|
||||||
padding: 20px 25px;
|
padding: 20px 25px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@ -222,35 +250,17 @@ a:not(.post_right):hover {
|
|||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post_right > p {
|
|
||||||
opacity: 0.75;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_media {
|
.post_media {
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post_media[src=""] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_body {
|
.post_body {
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin: 10px 5px;
|
margin: 10px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post_body > p:not(:first-child) {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_body a {
|
|
||||||
text-decoration: underline;
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
#post_url {
|
#post_url {
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
@ -264,10 +274,6 @@ a:not(.post_right):hover {
|
|||||||
max-width: 20%;
|
max-width: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post_thumbnail[src=""] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_flair {
|
.post_flair {
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
color: black;
|
color: black;
|
||||||
@ -295,17 +301,13 @@ a:not(.post_right):hover {
|
|||||||
.comment_left {
|
.comment_left {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
padding: 5px 0px;
|
padding: 5px 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment_title {
|
.comment_title { font-size: 20px; }
|
||||||
font-size: 20px;
|
.comment_link { text-decoration: underline; }
|
||||||
}
|
.comment_author { opacity: 0.9; }
|
||||||
|
|
||||||
.comment_author {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment_author.op {
|
.comment_author.op {
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
@ -331,7 +333,8 @@ a:not(.post_right):hover {
|
|||||||
background: var(--foreground);
|
background: var(--foreground);
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 10px 0px;
|
padding: 10px 0;
|
||||||
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment_right {
|
.comment_right {
|
||||||
@ -350,10 +353,6 @@ a:not(.post_right):hover {
|
|||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment_image[src=""] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment_body {
|
.comment_body {
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
@ -402,9 +401,34 @@ a:not(.post_right):hover {
|
|||||||
background: black;
|
background: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code */
|
/* Markdown */
|
||||||
|
|
||||||
pre {
|
.md > *:not(:first-child) {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.md p { font-size: 15px; }
|
||||||
|
.md h1 { font-size: 22px; }
|
||||||
|
.md h2 { font-size: 20px; }
|
||||||
|
.md h3 { font-size: 18px; }
|
||||||
|
.md h4 { font-size: 16px; }
|
||||||
|
.md h5 { font-size: 14px; }
|
||||||
|
.md h6 { font-size: 12px; }
|
||||||
|
|
||||||
|
.md blockquote {
|
||||||
|
padding-left: 8px;
|
||||||
|
margin: 4px 0 4px 8px;
|
||||||
|
border-left: 4px solid var(--highlighted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.md a {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.md li { margin: 10px 0; }
|
||||||
|
|
||||||
|
.md pre {
|
||||||
background: var(--outside);
|
background: var(--outside);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
@ -412,11 +436,13 @@ pre {
|
|||||||
box-shadow: var(--black-contrast);
|
box-shadow: var(--black-contrast);
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
.md code {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.md code:not(.md pre > code) { background: var(--highlighted); }
|
||||||
|
|
||||||
/* Tables */
|
/* Tables */
|
||||||
|
|
||||||
table {
|
table {
|
||||||
@ -437,7 +463,7 @@ td, th {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.post_left {
|
.post_left {
|
||||||
border-radius: 0px 0px 5px 5px;
|
border-radius: 0 0 5px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post_right {
|
.post_right {
|
||||||
@ -454,10 +480,25 @@ td, th {
|
|||||||
|
|
||||||
.replies > .comment {
|
.replies > .comment {
|
||||||
margin-left: -25px;
|
margin-left: -25px;
|
||||||
padding: 5px 0px;
|
padding: 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.datetime {
|
.datetime {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
main {
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside {
|
||||||
|
margin: 20px 0 0 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<div id="column_one">
|
||||||
<form>
|
<form>
|
||||||
<select id="sort" name="sort">
|
<select id="sort" name="sort">
|
||||||
<option value="confidence" {% if sort == "confidence" %}selected{% endif %}>Best</option>
|
<option value="confidence" {% if sort == "confidence" %}selected{% endif %}>Best</option>
|
||||||
@ -11,23 +12,24 @@
|
|||||||
{% for post in posts %}
|
{% for post in posts %}
|
||||||
<div class="post">
|
<div class="post">
|
||||||
<div class="post_left">
|
<div class="post_left">
|
||||||
<h3 class="post_score">{{ post.score }}</h3>
|
<p class="post_score">{{ post.score }}</p>
|
||||||
|
{% if post.nsfw %}<div class="nsfw">NSFW</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="post_right">
|
<div class="post_right">
|
||||||
<h4>
|
<p>
|
||||||
<b><a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a></b>
|
<b><a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a></b>
|
||||||
• <a class="post_author" href="/u/{{ post.author }}">u/{{ post.author }}</a>
|
• <a class="post_author" href="/u/{{ post.author }}">u/{{ post.author }}</a>
|
||||||
{% if post.author_flair.0 != "" %}
|
{% if post.author_flair.0 != "" %}
|
||||||
<small class="author_flair">{{ post.author_flair.0 }}</small>
|
<small class="author_flair">{{ post.author_flair.0 }}</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="datetime" style="float: right;">{{ post.time }}</span>
|
<span class="datetime" style="float: right;">{{ post.time }}</span>
|
||||||
</h4>
|
</p>
|
||||||
<h3 class="post_title">
|
<p class="post_title">
|
||||||
{% if post.flair.0 != "" %}
|
{% if post.flair.0 != "" %}
|
||||||
<small class="post_flair" style="color:{{ post.flair.2 }}; background:{{ post.flair.1 }}">{{ post.flair.0 }}</small>
|
<small class="post_flair" style="color:{{ post.flair.2 }}; background:{{ post.flair.1 }}">{{ post.flair.0 }}</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ post.url }}">{{ post.title }}</a>
|
<a href="{{ post.url }}">{{ post.title }}</a>
|
||||||
</h3>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<img class="post_thumbnail" src="{{ post.media }}">
|
<img class="post_thumbnail" src="{{ post.media }}">
|
||||||
</div><br>
|
</div><br>
|
||||||
@ -35,11 +37,12 @@
|
|||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
{% if ends.0 != "" %}
|
{% if ends.0 != "" %}
|
||||||
<a href="?before={{ ends.0 }}">PREV</a>
|
<a href="?sort={{ sort }}&before={{ ends.0 }}">PREV</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if ends.1 != "" %}
|
{% if ends.1 != "" %}
|
||||||
<a href="?after={{ ends.1 }}">NEXT</a>
|
<a href="?sort={{ sort }}&after={{ ends.1 }}">NEXT</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</footer>
|
</footer>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<div id="{{ item.id }}" class="comment">
|
<div id="{{ item.id }}" class="comment">
|
||||||
<div class="comment_left">
|
<div class="comment_left">
|
||||||
<h3 class="comment_score">{{ item.score }}</h3>
|
<p class="comment_score">{{ item.score }}</p>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
<details class="comment_right" open>
|
<details class="comment_right" open>
|
||||||
@ -19,17 +19,19 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
• <span class="datetime">{{ item.time }}</span>
|
• <span class="datetime">{{ item.time }}</span>
|
||||||
</summary>
|
</summary>
|
||||||
<h4 class="comment_body">{{ item.body }}</h4>
|
<p class="comment_body">{{ item.body }}</p>
|
||||||
|
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<div id="column_one">
|
||||||
<div class="post highlighted">
|
<div class="post highlighted">
|
||||||
<div class="post_left">
|
<div class="post_left">
|
||||||
<h3 class="post_score">{{ post.score }}</h3>
|
<p class="post_score">{{ post.score }}</p>
|
||||||
|
{% if post.nsfw %}<div class="nsfw">NSFW</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="post_right">
|
<div class="post_right">
|
||||||
<h4>
|
<p>
|
||||||
<b><a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a></b>
|
<b><a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a></b>
|
||||||
•
|
•
|
||||||
<a class="post_author" href="/u/{{ post.author }}">u/{{ post.author }}</a>
|
<a class="post_author" href="/u/{{ post.author }}">u/{{ post.author }}</a>
|
||||||
@ -37,7 +39,7 @@
|
|||||||
<small class="author_flair">{{ post.author_flair.0 }}</small>
|
<small class="author_flair">{{ post.author_flair.0 }}</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="datetime">{{ post.time }}</span>
|
<span class="datetime">{{ post.time }}</span>
|
||||||
</h4>
|
</p>
|
||||||
<a href="{{ post.url }}" class="post_title">
|
<a href="{{ post.url }}" class="post_title">
|
||||||
{{ post.title }}
|
{{ post.title }}
|
||||||
{% if post.flair.0 != "" %}
|
{% if post.flair.0 != "" %}
|
||||||
@ -51,7 +53,7 @@
|
|||||||
{% else if post.post_type == "link" %}
|
{% else if post.post_type == "link" %}
|
||||||
<a id="post_url" href="{{ post.media }}">{{ post.media }}</a>
|
<a id="post_url" href="{{ post.media }}">{{ post.media }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h4 class="post_body">{{ post.body }}</h4>
|
<div class="post_body">{{ post.body }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form>
|
<form>
|
||||||
@ -88,5 +90,5 @@
|
|||||||
</div></details></div>
|
</div></details></div>
|
||||||
</div>
|
</div>
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -5,21 +5,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% if sub.name != "" %}
|
<main style="max-width: 1000px;">
|
||||||
<div id="about">
|
<div id="column_one">
|
||||||
<div class="subreddit">
|
|
||||||
<div class="subreddit_left">
|
|
||||||
{{ sub.icon }}
|
|
||||||
</div>
|
|
||||||
<div class="subreddit_right">
|
|
||||||
<h2 class="subreddit_name">r/{{ sub.name }}</h2>
|
|
||||||
<p class="subreddit_description">{{ sub.description }}</p>
|
|
||||||
<div id="stats">👤 {{ sub.members }} 🟢 {{ sub.active }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<main>
|
|
||||||
<form>
|
<form>
|
||||||
<select id="sort" name="sort">
|
<select id="sort" name="sort">
|
||||||
<option value="hot" {% if sort == "hot" %}selected{% endif %}>Hot</option>
|
<option value="hot" {% if sort == "hot" %}selected{% endif %}>Hot</option>
|
||||||
@ -30,23 +17,24 @@
|
|||||||
{% for post in posts %}
|
{% for post in posts %}
|
||||||
<div class="post">
|
<div class="post">
|
||||||
<div class="post_left">
|
<div class="post_left">
|
||||||
<h3 class="post_score">{{ post.score }}</h3>
|
<p class="post_score">{{ post.score }}</p>
|
||||||
|
{% if post.nsfw %}<div class="nsfw">NSFW</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="post_right">
|
<div class="post_right">
|
||||||
<h4>
|
<p>
|
||||||
<b><a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a></b>
|
<b><a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a></b>
|
||||||
• <a class="post_author" href="/u/{{ post.author }}">u/{{ post.author }}</a>
|
• <a class="post_author" href="/u/{{ post.author }}">u/{{ post.author }}</a>
|
||||||
{% if post.author_flair.0 != "" %}
|
{% if post.author_flair.0 != "" %}
|
||||||
<small class="author_flair">{{ post.author_flair.0 }}</small>
|
<small class="author_flair">{{ post.author_flair.0 }}</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="datetime">{{ post.time }}</span>
|
<span class="datetime">{{ post.time }}</span>
|
||||||
</h4>
|
</p>
|
||||||
<h3 class="post_title">
|
<p class="post_title">
|
||||||
{% if post.flair.0 != "" %}
|
{% if post.flair.0 != "" %}
|
||||||
<small class="post_flair" style="color:{{ post.flair.2 }}; background:{{ post.flair.1 }}">{{ post.flair.0 }}</small>
|
<small class="post_flair" style="color:{{ post.flair.2 }}; background:{{ post.flair.1 }}">{{ post.flair.0 }}</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ post.url }}">{{ post.title }}</a>
|
<a href="{{ post.url }}">{{ post.title }}</a>
|
||||||
</h3>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<img class="post_thumbnail" src="{{ post.media }}">
|
<img class="post_thumbnail" src="{{ post.media }}">
|
||||||
</div><br>
|
</div><br>
|
||||||
@ -54,12 +42,32 @@
|
|||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
{% if ends.0 != "" %}
|
{% if ends.0 != "" %}
|
||||||
<a href="?before={{ ends.0 }}">PREV</a>
|
<a href="?sort={{ sort }}&before={{ ends.0 }}">PREV</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if ends.1 != "" %}
|
{% if ends.1 != "" %}
|
||||||
<a href="?after={{ ends.1 }}">NEXT</a>
|
<a href="?sort={{ sort }}&after={{ ends.1 }}">NEXT</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</footer>
|
</footer>
|
||||||
|
</div>
|
||||||
|
{% if sub.name != "" %}
|
||||||
|
<aside>
|
||||||
|
<div id="subreddit">
|
||||||
|
<img id="subreddit_icon" src="{{ sub.icon }}">
|
||||||
|
<p id="subreddit_name">r/{{ sub.name }}</p>
|
||||||
|
<p id="subreddit_description">{{ sub.description }}</p>
|
||||||
|
<div id="subreddit_details">
|
||||||
|
<label>Members</label>
|
||||||
|
<label>Active</label>
|
||||||
|
<div>{{ sub.members }}</div>
|
||||||
|
<div>{{ sub.active }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<details id="sidebar">
|
||||||
|
<summary id="sidebar_label">Sidebar</summary>
|
||||||
|
<div id="sidebar_contents">{{ sub.info }}</div>
|
||||||
|
</details>
|
||||||
|
</aside>
|
||||||
|
{% endif %}
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,18 +1,8 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Libreddit: u/{{ user.name }}{% endblock %}
|
{% block title %}Libreddit: u/{{ user.name }}{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div id="about">
|
<main style="max-width: 1000px;">
|
||||||
<div class="user">
|
<div id="column_one">
|
||||||
<div class="user_left">
|
|
||||||
<img class="user_icon" src="{{ user.icon }}">
|
|
||||||
</div>
|
|
||||||
<div class="user_right">
|
|
||||||
<h2 class="user_name">u/{{ user.name }}</h2>
|
|
||||||
<p class="user_description"><span>Karma:</span> {{ user.karma }} | <span>Description:</span> "{{ user.description }}"</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<main>
|
|
||||||
<form>
|
<form>
|
||||||
<select id="sort" name="sort">
|
<select id="sort" name="sort">
|
||||||
<option value="hot" {% if sort == "hot" %}selected{% endif %}>Hot</option>
|
<option value="hot" {% if sort == "hot" %}selected{% endif %}>Hot</option>
|
||||||
@ -24,42 +14,67 @@
|
|||||||
{% if post.title != "Comment" %}
|
{% if post.title != "Comment" %}
|
||||||
<div class='post'>
|
<div class='post'>
|
||||||
<div class="post_left">
|
<div class="post_left">
|
||||||
<h3 class="post_score">{{ post.score }}</h3>
|
<p class="post_score">{{ post.score }}</p>
|
||||||
|
{% if post.nsfw %}<div class="nsfw">NSFW</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="post_right">
|
<div class="post_right">
|
||||||
<h4>
|
<p>
|
||||||
<b><a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a></b>
|
<b><a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a></b>
|
||||||
• <a class="post_author" href="/u/{{ post.author }}">u/{{ post.author }}</a>
|
• <a class="post_author" href="/u/{{ post.author }}">u/{{ post.author }}</a>
|
||||||
{% if post.author_flair.0 != "" %}
|
{% if post.author_flair.0 != "" %}
|
||||||
<small class="author_flair">{{ post.author_flair.0 }}</small>
|
<small class="author_flair">{{ post.author_flair.0 }}</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="datetime" style="float: right;">{{ post.time }}</span>
|
<span class="datetime" style="float: right;">{{ post.time }}</span>
|
||||||
</h4>
|
</p>
|
||||||
<h3 class="post_title">
|
<p class="post_title">
|
||||||
{% if post.flair.0 == "Comment" %}
|
{% if post.flair.0 == "Comment" %}
|
||||||
{% else if post.flair.0 == "" %}
|
{% else if post.flair.0 == "" %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<small class="post_flair" style="color:{{ post.flair.2 }}; background:{{ post.flair.1 }}">{{ post.flair.0 }}</small>
|
<small class="post_flair" style="color:{{ post.flair.2 }}; background:{{ post.flair.1 }}">{{ post.flair.0 }}</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ post.url }}">{{ post.title }}</a>
|
<a href="{{ post.url }}">{{ post.title }}</a>
|
||||||
</h3>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<img class="post_thumbnail" src="{{ post.media }}">
|
<img class="post_thumbnail" src="{{ post.media }}">
|
||||||
</div><br>
|
</div><br>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="comment">
|
<div class="comment">
|
||||||
<div class="comment_left">
|
<div class="comment_left">
|
||||||
<h3 class="comment_score">{{ post.score }}</h3>
|
<p class="comment_score">{{ post.score }}</p>
|
||||||
|
<div class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="comment_right">
|
<details class="comment_right" open>
|
||||||
<h4>
|
<summary class="comment_data">
|
||||||
COMMENT
|
<a class="comment_link" href="{{ post.url }}">COMMENT</a>
|
||||||
<span class="datetime">{{ post.time }}</span>
|
<span class="datetime">{{ post.time }}</span>
|
||||||
</h4>
|
</summary>
|
||||||
<h4 class="comment_body">{{ post.body }}</h4>
|
<p class="comment_body">{{ post.body }}</p>
|
||||||
</div>
|
</details>
|
||||||
</div><br>
|
</div><br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<footer>
|
||||||
|
{% if ends.0 != "" %}
|
||||||
|
<a href="?sort={{ sort }}&before={{ ends.0 }}">PREV</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if ends.1 != "" %}
|
||||||
|
<a href="?sort={{ sort }}&after={{ ends.1 }}">NEXT</a>
|
||||||
|
{% endif %}
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
<aside>
|
||||||
|
<div id="user">
|
||||||
|
<img id="user_icon" src="{{ user.icon }}">
|
||||||
|
<p id="user_name">u/{{ user.name }}</p>
|
||||||
|
<div id="user_description">{{ user.description }}</div>
|
||||||
|
<div id="user_details">
|
||||||
|
<label>Karma</label>
|
||||||
|
<label>Created</label>
|
||||||
|
<div>{{ user.karma }}</div>
|
||||||
|
<div>{{ user.created }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
Reference in New Issue
Block a user