mirror of
https://github.com/redlib-org/redlib.git
synced 2025-04-02 04:37:35 +03:00
feat: smaller imports and exports (#373)
* feat: smaller imports and exports * test(prefs): extend tests * style(clippy) * style: bubble up error * style: update some wording
This commit is contained in:
parent
7d3160c149
commit
2e95e1fc6e
8 changed files with 285 additions and 109 deletions
84
Cargo.lock
generated
84
Cargo.lock
generated
|
@ -71,12 +71,6 @@ version = "1.0.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.7.1"
|
||||
|
@ -139,6 +133,12 @@ dependencies = [
|
|||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base2048"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71f4fe417e8cc3bb9b437dfa9290ce92bd2730ba5374719bdfd9147fbc8f17cd"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
|
@ -151,6 +151,15 @@ version = "0.22.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
|
@ -319,18 +328,6 @@ version = "0.7.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
|
||||
|
||||
[[package]]
|
||||
name = "common-words-all"
|
||||
version = "0.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a6ff47eb813c9e315610ceca0ddd247827e22f2cdadc4189e4676a81470c77"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"csv",
|
||||
"glob",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.18.1"
|
||||
|
@ -394,27 +391,6 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf"
|
||||
dependencies = [
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.10"
|
||||
|
@ -698,12 +674,6 @@ version = "0.31.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.15"
|
||||
|
@ -1381,13 +1351,14 @@ version = "0.35.1"
|
|||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-recursion",
|
||||
"base2048",
|
||||
"base64 0.22.1",
|
||||
"bincode",
|
||||
"brotli",
|
||||
"build_html",
|
||||
"cached",
|
||||
"chrono",
|
||||
"clap",
|
||||
"common-words-all",
|
||||
"cookie",
|
||||
"dotenvy",
|
||||
"fastrand",
|
||||
|
@ -1403,6 +1374,7 @@ dependencies = [
|
|||
"pretty_env_logger",
|
||||
"pulldown-cmark",
|
||||
"regex",
|
||||
"revision",
|
||||
"rinja",
|
||||
"route-recognizer",
|
||||
"rss",
|
||||
|
@ -1459,6 +1431,26 @@ version = "0.8.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "revision"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22f53179a035f881adad8c4d58a2c599c6b4a8325b989c68d178d7a34d1b1e4c"
|
||||
dependencies = [
|
||||
"revision-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "revision-derive"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0ec466e5d8dca9965eb6871879677bef5590cf7525ad96cae14376efb75073"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
|
|
|
@ -48,12 +48,14 @@ arc-swap = "1.7.1"
|
|||
serde_json_path = "0.7.1"
|
||||
async-recursion = "1.1.1"
|
||||
pulldown-cmark = { version = "0.12.0", features = ["simd", "html"], default-features = false }
|
||||
common-words-all = { version = "0.0.2", default-features = false, features = ["english", "one"] }
|
||||
hyper-rustls = { version = "0.24.2", features = [ "http2" ] }
|
||||
tegen = "0.1.4"
|
||||
serde_urlencoded = "0.7.1"
|
||||
chrono = { version = "0.4.39", default-features = false, features = [ "std" ] }
|
||||
htmlescape = "0.3.1"
|
||||
bincode = "1.3.3"
|
||||
base2048 = "2.0.2"
|
||||
revision = "0.10.0"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -246,6 +246,7 @@ async fn main() {
|
|||
app
|
||||
.at("/check_update.js")
|
||||
.get(|_| resource(include_str!("../static/check_update.js"), "text/javascript", false).boxed());
|
||||
app.at("/copy.js").get(|_| resource(include_str!("../static/copy.js"), "text/javascript", false).boxed());
|
||||
|
||||
app.at("/commits.atom").get(|_| async move { proxy_commit_info().await }.boxed());
|
||||
app.at("/instances.json").get(|_| async move { proxy_instances().await }.boxed());
|
||||
|
@ -284,6 +285,7 @@ async fn main() {
|
|||
// Configure settings
|
||||
app.at("/settings").get(|r| settings::get(r).boxed()).post(|r| settings::set(r).boxed());
|
||||
app.at("/settings/restore").get(|r| settings::restore(r).boxed());
|
||||
app.at("/settings/encoded-restore").post(|r| settings::encoded_restore(r).boxed());
|
||||
app.at("/settings/update").get(|r| settings::update(r).boxed());
|
||||
|
||||
// RSS Subscriptions
|
||||
|
|
|
@ -5,12 +5,13 @@ use std::collections::HashMap;
|
|||
// CRATES
|
||||
use crate::server::ResponseExt;
|
||||
use crate::subreddit::join_until_size_limit;
|
||||
use crate::utils::{redirect, template, Preferences};
|
||||
use crate::utils::{deflate_decompress, redirect, template, Preferences};
|
||||
use cookie::Cookie;
|
||||
use futures_lite::StreamExt;
|
||||
use hyper::{Body, Request, Response};
|
||||
use rinja::Template;
|
||||
use time::{Duration, OffsetDateTime};
|
||||
use url::form_urlencoded;
|
||||
|
||||
// STRUCTS
|
||||
#[derive(Template)]
|
||||
|
@ -262,3 +263,25 @@ pub async fn restore(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||
pub async fn update(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
Ok(set_cookies_method(req, false))
|
||||
}
|
||||
|
||||
pub async fn encoded_restore(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
let body = hyper::body::to_bytes(req.into_body())
|
||||
.await
|
||||
.map_err(|e| format!("Failed to get bytes from request body: {}", e))?;
|
||||
|
||||
let encoded_prefs = form_urlencoded::parse(&body)
|
||||
.find(|(key, _)| key == "encoded_prefs")
|
||||
.map(|(_, value)| value)
|
||||
.ok_or_else(|| "encoded_prefs parameter not found in request body".to_string())?;
|
||||
|
||||
let bytes = base2048::decode(&encoded_prefs).ok_or_else(|| "Failed to decode base2048 encoded preferences".to_string())?;
|
||||
|
||||
let out = deflate_decompress(bytes)?;
|
||||
|
||||
let mut prefs: Preferences = bincode::deserialize(&out).map_err(|e| format!("Failed to deserialize bytes into Preferences struct: {}", e))?;
|
||||
prefs.available_themes = vec![];
|
||||
|
||||
let url = format!("/settings/restore/?{}", prefs.to_urlencoded()?);
|
||||
|
||||
Ok(redirect(&url))
|
||||
}
|
||||
|
|
119
src/utils.rs
119
src/utils.rs
|
@ -9,16 +9,19 @@ use crate::{client::json, server::RequestExt};
|
|||
use cookie::Cookie;
|
||||
use htmlescape::decode_html;
|
||||
use hyper::{Body, Request, Response};
|
||||
use libflate::deflate::{Decoder, Encoder};
|
||||
use log::error;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use revision::revisioned;
|
||||
use rinja::Template;
|
||||
use rust_embed::RustEmbed;
|
||||
use serde::{Serialize, Serializer};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_json::Value;
|
||||
use serde_json_path::{JsonPath, JsonPathExt};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::env;
|
||||
use std::io::{Read, Write};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use time::{macros::format_description, Duration, OffsetDateTime};
|
||||
|
@ -618,32 +621,55 @@ pub struct Params {
|
|||
pub before: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize)]
|
||||
#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||
#[revisioned(revision = 1)]
|
||||
pub struct Preferences {
|
||||
#[serde(skip)]
|
||||
#[revision(start = 1)]
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub available_themes: Vec<String>,
|
||||
#[revision(start = 1)]
|
||||
pub theme: String,
|
||||
#[revision(start = 1)]
|
||||
pub front_page: String,
|
||||
#[revision(start = 1)]
|
||||
pub layout: String,
|
||||
#[revision(start = 1)]
|
||||
pub wide: String,
|
||||
#[revision(start = 1)]
|
||||
pub blur_spoiler: String,
|
||||
#[revision(start = 1)]
|
||||
pub show_nsfw: String,
|
||||
#[revision(start = 1)]
|
||||
pub blur_nsfw: String,
|
||||
#[revision(start = 1)]
|
||||
pub hide_hls_notification: String,
|
||||
#[revision(start = 1)]
|
||||
pub video_quality: String,
|
||||
#[revision(start = 1)]
|
||||
pub hide_sidebar_and_summary: String,
|
||||
#[revision(start = 1)]
|
||||
pub use_hls: String,
|
||||
#[revision(start = 1)]
|
||||
pub autoplay_videos: String,
|
||||
#[revision(start = 1)]
|
||||
pub fixed_navbar: String,
|
||||
#[revision(start = 1)]
|
||||
pub disable_visit_reddit_confirmation: String,
|
||||
#[revision(start = 1)]
|
||||
pub comment_sort: String,
|
||||
#[revision(start = 1)]
|
||||
pub post_sort: String,
|
||||
#[serde(serialize_with = "serialize_vec_with_plus")]
|
||||
#[revision(start = 1)]
|
||||
#[serde(serialize_with = "serialize_vec_with_plus", deserialize_with = "deserialize_vec_with_plus")]
|
||||
pub subscriptions: Vec<String>,
|
||||
#[serde(serialize_with = "serialize_vec_with_plus")]
|
||||
#[revision(start = 1)]
|
||||
#[serde(serialize_with = "serialize_vec_with_plus", deserialize_with = "deserialize_vec_with_plus")]
|
||||
pub filters: Vec<String>,
|
||||
#[revision(start = 1)]
|
||||
pub hide_awards: String,
|
||||
#[revision(start = 1)]
|
||||
pub hide_score: String,
|
||||
#[revision(start = 1)]
|
||||
pub remove_default_feeds: String,
|
||||
}
|
||||
|
||||
|
@ -654,6 +680,17 @@ where
|
|||
serializer.serialize_str(&vec.join("+"))
|
||||
}
|
||||
|
||||
fn deserialize_vec_with_plus<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let string = String::deserialize(deserializer)?;
|
||||
if string.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
Ok(string.split('+').map(|s| s.to_string()).collect())
|
||||
}
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "static/themes/"]
|
||||
#[include = "*.css"]
|
||||
|
@ -698,6 +735,29 @@ impl Preferences {
|
|||
pub fn to_urlencoded(&self) -> Result<String, String> {
|
||||
serde_urlencoded::to_string(self).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub fn to_bincode(&self) -> Result<Vec<u8>, String> {
|
||||
bincode::serialize(self).map_err(|e| e.to_string())
|
||||
}
|
||||
pub fn to_compressed_bincode(&self) -> Result<Vec<u8>, String> {
|
||||
deflate_compress(self.to_bincode()?)
|
||||
}
|
||||
pub fn to_bincode_str(&self) -> Result<String, String> {
|
||||
Ok(base2048::encode(&self.to_compressed_bincode()?))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deflate_compress(i: Vec<u8>) -> Result<Vec<u8>, String> {
|
||||
let mut e = Encoder::new(Vec::new());
|
||||
e.write_all(&i).map_err(|e| e.to_string())?;
|
||||
e.finish().into_result().map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub fn deflate_decompress(i: Vec<u8>) -> Result<Vec<u8>, String> {
|
||||
let mut decoder = Decoder::new(&i[..]);
|
||||
let mut out = Vec::new();
|
||||
decoder.read_to_end(&mut out).map_err(|e| format!("Failed to read from gzip decoder: {}", e))?;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Gets a `HashSet` of filters from the cookie in the given `Request`.
|
||||
|
@ -1584,3 +1644,52 @@ How`s your monitor by the way? Any IPS bleed whatsoever? I either got lucky or t
|
|||
|
||||
assert_eq!(render_bullet_lists(input), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_prefs_serialization_loop_json() {
|
||||
let prefs = Preferences::default();
|
||||
let serialized = serde_json::to_string(&prefs).unwrap();
|
||||
let deserialized: Preferences = serde_json::from_str(&serialized).unwrap();
|
||||
assert_eq!(prefs, deserialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_prefs_serialization_loop_bincode() {
|
||||
let prefs = Preferences::default();
|
||||
test_round_trip(&prefs, false);
|
||||
test_round_trip(&prefs, true);
|
||||
}
|
||||
|
||||
static KNOWN_GOOD_CONFIGS: &[&str] = &[
|
||||
"ఴӅβØØҞÉဏႢձĬ༧ȒʯऌԔӵ୮༏",
|
||||
"ਧՊΥÀÃǎƱГ۸ඣമĖฤ႙ʟาúໜϾௐɥঀĜໃહཞઠѫҲɂఙ࿔DzઉƲӟӻĻฅΜδ໖ԜǗဖငƦơ৶Ą௩ԹʛใЛʃශаΏ",
|
||||
"ਧԩΥÀÃΊ౭൩ඔႠϼҭöҪƸռઇԾॐნɔາǒՍҰच௨ಖມŃЉŐདƦ๙ϩএఠȝഽйʮჯඒϰळՋ௮ສ৵ऎΦѧਹಧଟƙŃ३î༦ŌပղयƟแҜ།",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_known_good_configs_deserialization() {
|
||||
for config in KNOWN_GOOD_CONFIGS {
|
||||
let bytes = base2048::decode(config).unwrap();
|
||||
let decompressed = deflate_decompress(bytes).unwrap();
|
||||
assert!(bincode::deserialize::<Preferences>(&decompressed).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_known_good_configs_full_round_trip() {
|
||||
for config in KNOWN_GOOD_CONFIGS {
|
||||
let bytes = base2048::decode(config).unwrap();
|
||||
let decompressed = deflate_decompress(bytes).unwrap();
|
||||
let prefs: Preferences = bincode::deserialize(&decompressed).unwrap();
|
||||
test_round_trip(&prefs, false);
|
||||
test_round_trip(&prefs, true);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_round_trip(input: &Preferences, compression: bool) {
|
||||
let serialized = bincode::serialize(input).unwrap();
|
||||
let compressed = if compression { deflate_compress(serialized).unwrap() } else { serialized };
|
||||
let decompressed = if compression { deflate_decompress(compressed).unwrap() } else { compressed };
|
||||
let deserialized: Preferences = bincode::deserialize(&decompressed).unwrap();
|
||||
assert_eq!(*input, deserialized);
|
||||
}
|
||||
|
|
9
static/copy.js
Normal file
9
static/copy.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
async function copy() {
|
||||
await navigator.clipboard.writeText(document.getElementById('bincode_str').value);
|
||||
}
|
||||
|
||||
async function set_listener() {
|
||||
document.getElementById('copy').addEventListener('click', copy);
|
||||
}
|
||||
|
||||
window.addEventListener('load', set_listener);
|
|
@ -553,14 +553,18 @@ aside {
|
|||
.subscribe,
|
||||
.unsubscribe,
|
||||
.filter,
|
||||
.unfilter {
|
||||
.unfilter,
|
||||
.copy,
|
||||
.import {
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.subscribe,
|
||||
.filter {
|
||||
.filter,
|
||||
.copy,
|
||||
.import {
|
||||
color: var(--foreground);
|
||||
background-color: var(--accent);
|
||||
}
|
||||
|
|
|
@ -4,22 +4,22 @@
|
|||
{% block title %}Redlib Settings{% endblock %}
|
||||
|
||||
{% block subscriptions %}
|
||||
{% call utils::sub_list("") %}
|
||||
{% call utils::sub_list("") %}
|
||||
{% endblock %}
|
||||
|
||||
{% block search %}
|
||||
{% call utils::search("".to_owned(), "") %}
|
||||
{% call utils::search("".to_owned(), "") %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="settings">
|
||||
<div id="settings">
|
||||
<form action="/settings" method="POST">
|
||||
<div class="prefs">
|
||||
<fieldset>
|
||||
<legend>Appearance</legend>
|
||||
<div class="prefs-group">
|
||||
<label for="theme">Theme:</label>
|
||||
<select name="theme" id="theme">
|
||||
<select name="theme" id="theme">
|
||||
{% call utils::options(prefs.theme, prefs.available_themes, "system") %}
|
||||
</select>
|
||||
</div>
|
||||
|
@ -29,144 +29,162 @@
|
|||
<div class="prefs-group">
|
||||
<label for="remove_default_feeds">Remove default feeds</label>
|
||||
<input type="hidden" value="off" name="remove_default_feeds">
|
||||
<input type="checkbox" name="remove_default_feeds" id="remove_default_feeds" {% if prefs.remove_default_feeds == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="remove_default_feeds" id="remove_default_feeds" {% if
|
||||
prefs.remove_default_feeds=="on" %}checked{% endif %}>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="front_page">Front page:</label>
|
||||
<select name="front_page" id="front_page">
|
||||
<select name="front_page" id="front_page">
|
||||
{% call utils::options(prefs.front_page, ["default", "popular", "all"], "default") %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="layout">Layout:</label>
|
||||
<select name="layout" id="layout">
|
||||
<select name="layout" id="layout">
|
||||
{% call utils::options(prefs.layout, ["card", "clean", "compact"], "card") %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="wide">Wide UI:</label>
|
||||
<input type="hidden" value="off" name="wide">
|
||||
<input type="checkbox" name="wide" id="wide" {% if prefs.wide == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="wide" id="wide" {% if prefs.wide=="on" %}checked{% endif %}>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Content</legend>
|
||||
<div class="prefs-group">
|
||||
<label for="video_quality">Video quality:</label>
|
||||
<select name="video_quality" id="video_quality">
|
||||
<select name="video_quality" id="video_quality">
|
||||
{% call utils::options(prefs.video_quality, ["best", "medium", "worst"], "best") %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="post_sort" title="Applies only to subreddit feeds">Default subreddit post sort:</label>
|
||||
<select name="post_sort">
|
||||
{% call utils::options(prefs.post_sort, ["hot", "new", "top", "rising", "controversial"], "hot") %}
|
||||
<select name="post_sort">
|
||||
{% call utils::options(prefs.post_sort, ["hot", "new", "top", "rising", "controversial"], "hot")
|
||||
%}
|
||||
</select>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="comment_sort">Default comment sort:</label>
|
||||
<select name="comment_sort" id="comment_sort">
|
||||
{% call utils::options(prefs.comment_sort, ["confidence", "top", "new", "controversial", "old"], "confidence") %}
|
||||
<select name="comment_sort" id="comment_sort">
|
||||
{% call utils::options(prefs.comment_sort, ["confidence", "top", "new", "controversial", "old"],
|
||||
"confidence") %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="blur_spoiler">Blur spoiler previews:</label>
|
||||
<input type="hidden" value="off" name="blur_spoiler">
|
||||
<input type="checkbox" name="blur_spoiler" id="blur_spoiler" {% if prefs.blur_spoiler == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="blur_spoiler" id="blur_spoiler" {% if prefs.blur_spoiler=="on"
|
||||
%}checked{% endif %}>
|
||||
</div>
|
||||
{% if !crate::utils::sfw_only() %}
|
||||
{% if !crate::utils::sfw_only() %}
|
||||
<div class="prefs-group">
|
||||
<label for="show_nsfw">Show NSFW posts:</label>
|
||||
<input type="hidden" value="off" name="show_nsfw">
|
||||
<input type="checkbox" name="show_nsfw" id="show_nsfw" {% if prefs.show_nsfw == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="show_nsfw" id="show_nsfw" {% if prefs.show_nsfw=="on" %}checked{% endif
|
||||
%}>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="blur_nsfw">Blur NSFW previews:</label>
|
||||
<input type="hidden" value="off" name="blur_nsfw">
|
||||
<input type="checkbox" name="blur_nsfw" id="blur_nsfw" {% if prefs.blur_nsfw == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="blur_nsfw" id="blur_nsfw" {% if prefs.blur_nsfw=="on" %}checked{% endif
|
||||
%}>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class="prefs-group">
|
||||
<label for="autoplay_videos">Autoplay videos</label>
|
||||
<input type="hidden" value="off" name="autoplay_videos">
|
||||
<input type="checkbox" name="autoplay_videos" id="autoplay_videos" {% if prefs.autoplay_videos == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="autoplay_videos" id="autoplay_videos" {% if prefs.autoplay_videos=="on"
|
||||
%}checked{% endif %}>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="fixed_navbar">Keep navbar fixed</label>
|
||||
<input type="hidden" value="off" name="fixed_navbar">
|
||||
<input type="checkbox" name="fixed_navbar" {% if prefs.fixed_navbar == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="fixed_navbar" {% if prefs.fixed_navbar=="on" %}checked{% endif %}>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="hide_sidebar_and_summary">Hide the summary and sidebar</label>
|
||||
<input type="hidden" value="off" name="hide_sidebar_and_summary">
|
||||
<input type="checkbox" name="hide_sidebar_and_summary" {% if prefs.hide_sidebar_and_summary == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="hide_sidebar_and_summary" {% if prefs.hide_sidebar_and_summary=="on"
|
||||
%}checked{% endif %}>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="use_hls">Use HLS for videos</label>
|
||||
<details id="feeds">
|
||||
<summary>Why?</summary>
|
||||
<div id="feed_list" class="helper">Reddit videos require JavaScript (via HLS.js) to be enabled to be played with audio. Therefore, this toggle lets you either use Redlib JS-free or utilize this feature.</div>
|
||||
<div id="feed_list" class="helper">Reddit videos require JavaScript (via HLS.js) to be enabled
|
||||
to be played with audio. Therefore, this toggle lets you either use Redlib JS-free or
|
||||
utilize this feature.</div>
|
||||
</details>
|
||||
<input type="hidden" value="off" name="use_hls">
|
||||
<input type="checkbox" name="use_hls" id="use_hls" {% if prefs.use_hls == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="use_hls" id="use_hls" {% if prefs.use_hls=="on" %}checked{% endif %}>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="hide_hls_notification">Hide notification about possible HLS usage</label>
|
||||
<input type="hidden" value="off" name="hide_hls_notification">
|
||||
<input type="checkbox" name="hide_hls_notification" id="hide_hls_notification" {% if prefs.hide_hls_notification == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="hide_hls_notification" id="hide_hls_notification" {% if
|
||||
prefs.hide_hls_notification=="on" %}checked{% endif %}>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="hide_awards">Hide awards</label>
|
||||
<input type="hidden" value="off" name="hide_awards">
|
||||
<input type="checkbox" name="hide_awards" id="hide_awards" {% if prefs.hide_awards == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="hide_awards" id="hide_awards" {% if prefs.hide_awards=="on" %}checked{%
|
||||
endif %}>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="hide_score">Hide score</label>
|
||||
<input type="hidden" value="off" name="hide_score">
|
||||
<input type="checkbox" name="hide_score" id="hide_score" {% if prefs.hide_score == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="hide_score" id="hide_score" {% if prefs.hide_score=="on" %}checked{%
|
||||
endif %}>
|
||||
</div>
|
||||
<div class="prefs-group">
|
||||
<label for="disable_visit_reddit_confirmation">Do not confirm before visiting content on Reddit</label>
|
||||
<label for="disable_visit_reddit_confirmation">Do not confirm before visiting content on
|
||||
Reddit</label>
|
||||
<input type="hidden" value="off" name="disable_visit_reddit_confirmation">
|
||||
<input type="checkbox" name="disable_visit_reddit_confirmation" {% if prefs.disable_visit_reddit_confirmation == "on" %}checked{% endif %}>
|
||||
<input type="checkbox" name="disable_visit_reddit_confirmation" {% if
|
||||
prefs.disable_visit_reddit_confirmation=="on" %}checked{% endif %}>
|
||||
</div>
|
||||
</fieldset>
|
||||
<input id="save" type="submit" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
{% if prefs.subscriptions.len() > 0 %}
|
||||
<div class="prefs" id="settings_subs">
|
||||
<legend>Subscribed Feeds</legend>
|
||||
{% for sub in prefs.subscriptions %}
|
||||
<div>
|
||||
{% let feed -%}
|
||||
{% if sub.starts_with("u_") -%}{% let feed = format!("u/{}", &sub[2..]) -%}{% else -%}{% let feed = format!("r/{}", sub) -%}{% endif -%}
|
||||
<a href="/{{ feed }}">{{ feed }}</a>
|
||||
<form action="/r/{{ sub }}/unsubscribe/?redirect=settings" method="POST">
|
||||
<button class="unsubscribe">Unsubscribe</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="prefs" id="settings_subs">
|
||||
<legend>Subscribed Feeds</legend>
|
||||
{% for sub in prefs.subscriptions %}
|
||||
<div>
|
||||
{% let feed -%}
|
||||
{% if sub.starts_with("u_") -%}{% let feed = format!("u/{}", &sub[2..]) -%}{% else -%}{% let feed =
|
||||
format!("r/{}", sub) -%}{% endif -%}
|
||||
<a href="/{{ feed }}">{{ feed }}</a>
|
||||
<form action="/r/{{ sub }}/unsubscribe/?redirect=settings" method="POST">
|
||||
<button class="unsubscribe">Unsubscribe</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if !prefs.filters.is_empty() %}
|
||||
<div class="prefs" id="settings_filters">
|
||||
<legend>Filtered Feeds</legend>
|
||||
{% for sub in prefs.filters %}
|
||||
<div>
|
||||
{% let feed -%}
|
||||
{% if sub.starts_with("u_") -%}{% let feed = format!("u/{}", &sub[2..]) -%}{% else -%}{% let feed = format!("r/{}", sub) -%}{% endif -%}
|
||||
<a href="/{{ feed }}">{{ feed }}</a>
|
||||
<form action="/r/{{ sub }}/unfilter/?redirect=settings" method="POST">
|
||||
<button class="unfilter">Unfilter</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="prefs" id="settings_filters">
|
||||
<legend>Filtered Feeds</legend>
|
||||
{% for sub in prefs.filters %}
|
||||
<div>
|
||||
{% let feed -%}
|
||||
{% if sub.starts_with("u_") -%}{% let feed = format!("u/{}", &sub[2..]) -%}{% else -%}{% let feed =
|
||||
format!("r/{}", sub) -%}{% endif -%}
|
||||
<a href="/{{ feed }}">{{ feed }}</a>
|
||||
<form action="/r/{{ sub }}/unfilter/?redirect=settings" method="POST">
|
||||
<button class="unfilter">Unfilter</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div id="settings_note">
|
||||
<p><b>Note:</b> settings and subscriptions are saved in browser cookies. Clearing your cookies will reset them.</p>
|
||||
<p><b>Note:</b> settings and subscriptions are saved in browser cookies. Clearing your cookies will reset them.
|
||||
</p>
|
||||
<br>
|
||||
{% match prefs.to_urlencoded() %}
|
||||
{% when Ok with (encoded_prefs) %}
|
||||
|
@ -176,7 +194,24 @@
|
|||
<p>There was an error creating your restore link: {{ err }}</p>
|
||||
<p>Please report this issue</p>
|
||||
{% endmatch %}
|
||||
<br />
|
||||
<div>
|
||||
<script src="/copy.js"></script>
|
||||
<label for="bincode_str">Or, export/import here (be sure to save first):</label>
|
||||
<br />
|
||||
<input type="text" id="bincode_str" name="bincode_str"
|
||||
value="{% match prefs.to_bincode_str() %}{% when Ok with (bincode_str) %}{{ bincode_str }}{% when Err with (err) %}Error: {{ err }}{% endmatch %}"
|
||||
readonly>
|
||||
<button id="copy" class="copy">Copy</button>
|
||||
|
||||
<br />
|
||||
<form action="/settings/encoded-restore/" method="POST">
|
||||
<input type="text" id="encoded_prefs" name="encoded_prefs" value=""
|
||||
placeholder="Paste your encoded settings here">
|
||||
<button class="import" type="submit">Import</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue