Compare commits
92 Commits
Author | SHA1 | Date | |
---|---|---|---|
ee0da63862 | |||
971f14bb55 | |||
9a1733ac99 | |||
c32d62fbd5 | |||
1a0d12d2ff | |||
2a27850914 | |||
bfcc4c985d | |||
1653d4fb4c | |||
79027c4c75 | |||
269bb0bfb6 | |||
7933d840b3 | |||
b875e9377e | |||
8c80946121 | |||
21d96e261f | |||
9c58d23b41 | |||
4ae2191392 | |||
d62a3ab86b | |||
9b7cd1da5a | |||
a301f1ecb6 | |||
f14639ee00 | |||
b527735f6f | |||
8cc01c58f3 | |||
a1d800a0f0 | |||
449899962a | |||
dc2030e6f3 | |||
ef5a1cd66e | |||
11e4ff42ed | |||
c71df35b22 | |||
345308a9ac | |||
75bbcefbec | |||
49a6168607 | |||
f55ea5a353 | |||
30c33d91e1 | |||
00b135fb0f | |||
5fe9ce8d7b | |||
8c04365049 | |||
d5b1c3a5bb | |||
f038aa61f4 | |||
f72c9d39be | |||
e6c2d08425 | |||
e901e99278 | |||
acd2cff747 | |||
8f913e696c | |||
226d39328c | |||
b2ad2f636c | |||
18fe7ff8cf | |||
077c222a4e | |||
2270b6cf95 | |||
758b627660 | |||
baf7272cfd | |||
6641e242af | |||
610fcfbf87 | |||
dea7f33910 | |||
c299e128ab | |||
53fa946c75 | |||
5d44a071f9 | |||
e29e203188 | |||
6ead6e08dc | |||
7360503234 | |||
140c1b1bfa | |||
040982f1fd | |||
4b0677d10e | |||
616751e054 | |||
5df957f193 | |||
7f9cb1b35a | |||
c030771d36 | |||
a562395c26 | |||
2bcdf68e40 | |||
72eaa685d0 | |||
899a414cf6 | |||
524538eeb8 | |||
a184559c21 | |||
1c9fd46e98 | |||
738941d830 | |||
06ab7a4181 | |||
6981d94417 | |||
dd60cb5b2b | |||
1d57e29d56 | |||
2d973707f3 | |||
cbb937b494 | |||
d45ee03122 | |||
162e00b243 | |||
7a32ba087e | |||
801216dfe9 | |||
21763c51cd | |||
138f8320e9 | |||
571ba3392c | |||
090ca1a140 | |||
6127f2a90c | |||
ef9bc791e1 | |||
894323becf | |||
4c89d31948 |
111
Cargo.lock
generated
111
Cargo.lock
generated
@ -401,9 +401,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.55"
|
version = "0.3.56"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598"
|
checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
@ -480,9 +480,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.4.0"
|
version = "3.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
|
checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
@ -531,9 +531,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chunked_transfer"
|
name = "chunked_transfer"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7477065d45a8fe57167bf3cf8bcd3729b54cfcb81cca49bda2d038ea89ae82ca"
|
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_fn"
|
name = "const_fn"
|
||||||
@ -628,9 +628,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.19"
|
version = "1.0.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129"
|
checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
@ -684,6 +684,7 @@ checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
@ -706,6 +707,17 @@ version = "0.3.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65"
|
checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.12"
|
version = "0.3.12"
|
||||||
@ -832,9 +844,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.17"
|
version = "0.1.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
|
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@ -932,9 +944,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.46"
|
version = "0.3.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175"
|
checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
@ -976,18 +988,19 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.82"
|
version = "0.2.84"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
|
checksum = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libreddit"
|
name = "libreddit"
|
||||||
version = "0.2.7"
|
version = "0.2.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"askama",
|
"askama",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
|
"futures",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -1013,11 +1026,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.13"
|
version = "0.4.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2"
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 0.1.10",
|
"cfg-if 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1118,9 +1131,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "6.0.1"
|
version = "6.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88034cfd6b4a0d54dd14f4a507eceee36c0b70e5a02236c4e4df571102be17f0"
|
checksum = "ab6f70b46d6325aa300f1c7bb3d470127dfc27806d8ea6bf294ee0ce643ce2b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitvec",
|
"bitvec",
|
||||||
"lexical-core",
|
"lexical-core",
|
||||||
@ -1149,9 +1162,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.22.0"
|
version = "0.23.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
|
checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
@ -1472,18 +1485,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.119"
|
version = "1.0.123"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3"
|
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.119"
|
version = "1.0.123"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd"
|
checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1636,9 +1649,9 @@ checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.58"
|
version = "1.0.60"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
|
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1673,11 +1686,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.0"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447"
|
checksum = "d8208a331e1cb318dd5bd76951d2b8fc48ca38a69f5f4e4af1b6a9f8c6236915"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1691,9 +1704,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.2.24"
|
version = "0.2.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "273d3ed44dca264b0d6b3665e8d48fb515042d42466fad93d2a45b90ec4058f7"
|
checksum = "1195b046942c221454c2539395f85413b33383a067449d78aab2b7b052a142f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const_fn",
|
"const_fn",
|
||||||
"libc",
|
"libc",
|
||||||
@ -1729,9 +1742,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.1.0"
|
version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f"
|
checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec_macros",
|
"tinyvec_macros",
|
||||||
]
|
]
|
||||||
@ -1744,9 +1757,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "0.2.24"
|
version = "0.2.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48"
|
checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.5.6",
|
"bytes 0.5.6",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@ -1952,9 +1965,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.69"
|
version = "0.2.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e"
|
checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
@ -1962,9 +1975,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.69"
|
version = "0.2.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62"
|
checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@ -1977,9 +1990,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.69"
|
version = "0.2.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084"
|
checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@ -1987,9 +2000,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.69"
|
version = "0.2.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549"
|
checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2000,15 +2013,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.69"
|
version = "0.2.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158"
|
checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.46"
|
version = "0.3.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3"
|
checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
21
Cargo.toml
21
Cargo.toml
@ -3,18 +3,19 @@ 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.7"
|
version = "0.2.9"
|
||||||
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
|
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.13.0"
|
base64 = "0.13"
|
||||||
actix-web = { version = "3.3.2", features = ["rustls"] }
|
actix-web = { version = "3.3", features = ["rustls"] }
|
||||||
askama = "0.10.5"
|
futures = "0.3"
|
||||||
ureq = "2.0.1"
|
askama = "0.10"
|
||||||
serde = { version = "1.0.118", default_features = false, features = ["derive"] }
|
ureq = "2"
|
||||||
|
serde = { version = "1.0", default_features = false, features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
async-recursion = "0.3.1"
|
async-recursion = "0.3"
|
||||||
url = "2.2.0"
|
url = "2.2"
|
||||||
regex = "1.4.2"
|
regex = "1.4"
|
||||||
time = "0.2.23"
|
time = "0.2"
|
117
README.md
117
README.md
@ -2,40 +2,34 @@
|
|||||||
|
|
||||||
> An alternative private front-end to Reddit
|
> An alternative private front-end to Reddit
|
||||||
|
|
||||||
Libre + Reddit = [Libreddit](https://libredd.it)
|

|
||||||
|
|
||||||
- 🚀 Fast: written in Rust for blazing fast speeds and safety
|
---
|
||||||
- ☁️ Light: no JavaScript, no ads, no tracking
|
|
||||||
|
**10 second pitch:** Libreddit is a portmanteau of "libre" (meaning freedom) and "Reddit". It is a private front-end 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).
|
||||||
|
|
||||||
|
- 🚀 Fast: written in Rust for blazing fast speeds and memory safety
|
||||||
|
- ☁️ Light: no JavaScript, no ads, no tracking, no bloat
|
||||||
- 🕵 Private: all requests are proxied through the server, including media
|
- 🕵 Private: all requests are proxied through the server, including media
|
||||||
- 🔒 Secure: strong [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) prevents browser requests to Reddit
|
- 🔒 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).
|
---
|
||||||
|
|
||||||
## Contents
|
## Jump to...
|
||||||
- [Screenshot](#screenshot)
|
|
||||||
- [Instances](#instances)
|
|
||||||
- [About](#about)
|
- [About](#about)
|
||||||
- [Elsewhere](#elsewhere)
|
|
||||||
- [Info](#info)
|
|
||||||
- [Teddit Comparison](#how-does-it-compare-to-teddit)
|
- [Teddit Comparison](#how-does-it-compare-to-teddit)
|
||||||
- [Comparison](#comparison)
|
- [Comparison](#comparison)
|
||||||
- [Speed](#speed)
|
|
||||||
- [Privacy](#privacy)
|
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [Cargo](#a-cargo)
|
- [Cargo](#a-cargo)
|
||||||
- [Docker](#b-docker)
|
- [Docker](#b-docker)
|
||||||
- [AUR](#c-aur)
|
- [AUR](#c-aur)
|
||||||
- [GitHub Releases](#d-github-releases)
|
- [GitHub Releases](#d-github-releases)
|
||||||
- [Repl.it](#e-replit)
|
- [Repl.it](#e-replit)
|
||||||
- Developing
|
- [Deployment](#deployment)
|
||||||
- [Deployment](#deployment)
|
|
||||||
- [Building](#building)
|
|
||||||
|
|
||||||
## Screenshot
|
---
|
||||||
|
|
||||||

|
# Instances
|
||||||
|
|
||||||
## Instances
|
|
||||||
|
|
||||||
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!
|
||||||
|
|
||||||
@ -46,25 +40,30 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new)
|
|||||||
| [libreddit.dothq.co](https://libreddit.dothq.co) | 🇺🇸 US | ✅ |
|
| [libreddit.dothq.co](https://libreddit.dothq.co) | 🇺🇸 US | ✅ |
|
||||||
| [libreddit.insanity.wtf](https://libreddit.insanity.wtf) | 🇺🇸 US | ✅ |
|
| [libreddit.insanity.wtf](https://libreddit.insanity.wtf) | 🇺🇸 US | ✅ |
|
||||||
| [libreddit.kavin.rocks](https://libreddit.kavin.rocks) | 🇮🇳 IN | ✅ |
|
| [libreddit.kavin.rocks](https://libreddit.kavin.rocks) | 🇮🇳 IN | ✅ |
|
||||||
|
| [libreddit.himiko.cloud](https://libreddit.himiko.cloud) | 🇧🇬 BG | |
|
||||||
| [spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion](http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion) | 🇮🇳 IN | |
|
| [spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion](http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion) | 🇮🇳 IN | |
|
||||||
|
|
||||||
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.
|
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
|
---
|
||||||
|
|
||||||
### Elsewhere
|
# About
|
||||||
Find Libreddit on...
|
|
||||||
- 💬 Matrix: [#libreddit:kde.org](https://matrix.to/#/#libreddit:matrix.org)
|
|
||||||
- 🐋 Docker: [spikecodes/libreddit](https://hub.docker.com/r/spikecodes/libreddit)
|
|
||||||
- :octocat: GitHub: [spikecodes/libreddit](https://github.com/spikecodes/libreddit)
|
|
||||||
- 🦊 GitLab: [spikecodes/libreddit](https://gitlab.com/spikecodes/libreddit)
|
|
||||||
|
|
||||||
### Info
|
Find Libreddit on 💬 [Matrix](https://matrix.to/#/#libreddit:kde.org), 🐋 [Docker](https://hub.docker.com/r/spikecodes/libreddit), :octocat: [GitHub](https://github.com/spikecodes/libreddit), and 🦊 [GitLab](https://gitlab.com/spikecodes/libreddit).
|
||||||
|
|
||||||
|
## Built with
|
||||||
|
|
||||||
|
- [Rust](https://www.rust-lang.org/) - Programming language
|
||||||
|
- [Actix Web](https://github.com/actix/actix-web) - Web server
|
||||||
|
- [Askama](https://github.com/djc/askama) - Templating engine
|
||||||
|
- [ureq](https://github.com/algesten/ureq) - HTTP client
|
||||||
|
|
||||||
|
## Info
|
||||||
Libreddit hopes to provide an easier way to browse Reddit, without the ads, trackers, and bloat. Libreddit was inspired by other alternative front-ends to popular services such as [Invidious](https://github.com/iv-org/invidious) for YouTube, [Nitter](https://github.com/zedeus/nitter) for Twitter, and [Bibliogram](https://sr.ht/~cadence/bibliogram/) for Instagram.
|
Libreddit hopes to provide an easier way to browse Reddit, without the ads, trackers, and bloat. Libreddit was inspired by other alternative front-ends to popular services such as [Invidious](https://github.com/iv-org/invidious) for YouTube, [Nitter](https://github.com/zedeus/nitter) for Twitter, and [Bibliogram](https://sr.ht/~cadence/bibliogram/) for Instagram.
|
||||||
|
|
||||||
Libreddit currently implements most of Reddit's (signed-out) functionalities but still lacks [a few features](https://github.com/spikecodes/libreddit/issues).
|
Libreddit currently implements most of Reddit's (signed-out) functionalities but still lacks [a few features](https://github.com/spikecodes/libreddit/issues).
|
||||||
|
|
||||||
### How does it compare to Teddit?
|
## How does it compare to Teddit?
|
||||||
|
|
||||||
Teddit is another awesome open source project designed to provide an alternative frontend to Reddit. There is no connection between the two and you're welcome to use whichever one you favor. Competition fosters innovation and Teddit's release has motivated me to build Libreddit into an even more polished product.
|
Teddit is another awesome open source project designed to provide an alternative frontend to Reddit. There is no connection between the two and you're welcome to use whichever one you favor. Competition fosters innovation and Teddit's release has motivated me to build Libreddit into an even more polished product.
|
||||||
|
|
||||||
@ -72,25 +71,27 @@ If you are looking to compare, the biggest differences I have noticed are:
|
|||||||
- Libreddit is themed around Reddit's redesign whereas Teddit appears to stick much closer to Reddit's old design. This may suit some users better as design is always subjective.
|
- Libreddit is themed around Reddit's redesign whereas Teddit appears to stick much closer to Reddit's old design. This may suit some users better as design is always subjective.
|
||||||
- Libreddit is written in [Rust](https://www.rust-lang.org) for speed and memory safety. It uses [Actix Web](https://actix.rs), which was [benchmarked as the fastest web server for single queries](https://www.techempower.com/benchmarks/#hw=ph&test=db).
|
- Libreddit is written in [Rust](https://www.rust-lang.org) for speed and memory safety. It uses [Actix Web](https://actix.rs), which was [benchmarked as the fastest web server for single queries](https://www.techempower.com/benchmarks/#hw=ph&test=db).
|
||||||
|
|
||||||
## Comparison
|
---
|
||||||
|
|
||||||
|
# Comparison
|
||||||
|
|
||||||
This section outlines how Libreddit compares to Reddit.
|
This section outlines how Libreddit compares to Reddit.
|
||||||
|
|
||||||
### Speed
|
## Speed
|
||||||
|
|
||||||
Lasted tested December 21, 2020.
|
Lasted tested Jan 17, 2021.
|
||||||
|
|
||||||
Results from Google Lighthouse ([Libreddit Report](https://lighthouse-dot-webdotdevsite.appspot.com/lh/html?url=https%3A%2F%2Flibredd.it), [Reddit Report](https://lighthouse-dot-webdotdevsite.appspot.com/lh/html?url=https%3A%2F%2Fwww.reddit.com%2F)).
|
Results from Google Lighthouse ([Libreddit Report](https://lighthouse-dot-webdotdevsite.appspot.com/lh/html?url=https%3A%2F%2Flibredd.it), [Reddit Report](https://lighthouse-dot-webdotdevsite.appspot.com/lh/html?url=https%3A%2F%2Fwww.reddit.com%2F)).
|
||||||
|
|
||||||
| | Libreddit | Reddit |
|
| | Libreddit | Reddit |
|
||||||
|---------------------|---------------|-----------|
|
|------------------------|---------------|------------|
|
||||||
| Requests | 22 | 70 |
|
| Requests | 20 | 70 |
|
||||||
| Resource Size | 135 KiB | 2,222 KiB |
|
| Resource Size (card ui)| 1,224 KiB | 1,690 KiB |
|
||||||
| Time to Interactive | **1.7 s** | **11.5 s**|
|
| Time to Interactive | **1.5 s** | **11.2 s** |
|
||||||
|
|
||||||
### Privacy
|
## Privacy
|
||||||
|
|
||||||
#### Reddit
|
### Reddit
|
||||||
|
|
||||||
**Logging:** According to Reddit's [privacy policy](https://www.redditinc.com/policies/privacy-policy), they "may [automatically] log information" including:
|
**Logging:** According to Reddit's [privacy policy](https://www.redditinc.com/policies/privacy-policy), they "may [automatically] log information" including:
|
||||||
- IP address
|
- IP address
|
||||||
@ -119,21 +120,23 @@ Results from Google Lighthouse ([Libreddit Report](https://lighthouse-dot-webdot
|
|||||||
- Third-Party Cookies
|
- Third-Party Cookies
|
||||||
- Third-Party Site
|
- Third-Party Site
|
||||||
|
|
||||||
#### Libreddit
|
### Libreddit
|
||||||
|
|
||||||
For transparency, I hope to describe all the ways Libreddit handles user privacy.
|
For transparency, I hope to describe all the ways Libreddit handles user privacy.
|
||||||
|
|
||||||
**Logging:** In production (when running the binary, hosting with docker, or using the official instances), Libreddit logs nothing. When debugging (running from source without `--release`), Libreddit logs post IDs and URL paths fetched to aid with troubleshooting.
|
**Logging:** In production (when running the binary, hosting with docker, or using the official instances), Libreddit logs when Reddit is ratelimiting Libreddit and when Reddit's JSON responses can't be parsed. When debugging (running from source without `--release`), Libreddit logs post IDs and URL paths fetched to aid with troubleshooting.
|
||||||
|
|
||||||
**DNS:** Both official domains (`libredd.it` and `libreddit.spike.codes`) use Cloudflare as the DNS resolver. Though, the sites are not proxied through Cloudflare meaning Cloudflare doesn't have access to user traffic.
|
**DNS:** Both official domains (`libredd.it` and `libreddit.spike.codes`) use Cloudflare as the DNS resolver. Though, the sites are not proxied through Cloudflare meaning Cloudflare doesn't have access to user traffic.
|
||||||
|
|
||||||
**Cookies:** Libreddit uses optional cookies to store any configured settings in [the settings menu](https://libredd.it/settings). This is not a cross-site cookie and the cookie holds no personal data, only a value of the possible layout.
|
**Cookies:** Libreddit uses optional cookies to store any configured settings in [the settings menu](https://libredd.it/settings). This is not a cross-site cookie and the cookie holds no personal data, only a value of the possible layout.
|
||||||
|
|
||||||
**Hosting:** The official instances (`libredd.it` and `libreddit.spike.codes`) are hosted on [Repl.it](https://repl.it/) which monitors usage to prevent abuse. I can understand if this invalidates certain users' threat models and therefore, selfhosting and browsing through Tor are welcomed.
|
**Hosting:** The official instances are hosted on [Repl.it](https://repl.it/) which monitors usage to prevent abuse. I can understand if this invalidates certain users' threat models and therefore, selfhosting and browsing through Tor are welcomed.
|
||||||
|
|
||||||
## Installation
|
---
|
||||||
|
|
||||||
### A) Cargo
|
# Installation
|
||||||
|
|
||||||
|
## 1) Cargo
|
||||||
|
|
||||||
Make sure Rust stable is installed along with `cargo`, Rust's package manager.
|
Make sure Rust stable is installed along with `cargo`, Rust's package manager.
|
||||||
|
|
||||||
@ -141,9 +144,9 @@ Make sure Rust stable is installed along with `cargo`, Rust's package manager.
|
|||||||
cargo install libreddit
|
cargo install libreddit
|
||||||
```
|
```
|
||||||
|
|
||||||
### B) Docker
|
## 2) Docker
|
||||||
|
|
||||||
Deploy the Docker image of Libreddit:
|
Deploy the [Docker image](https://hub.docker.com/r/spikecodes/libreddit) of Libreddit:
|
||||||
```
|
```
|
||||||
docker run -d --name libreddit -p 8080:8080 spikecodes/libreddit
|
docker run -d --name libreddit -p 8080:8080 spikecodes/libreddit
|
||||||
```
|
```
|
||||||
@ -153,23 +156,21 @@ Deploy using a different port (in this case, port 80):
|
|||||||
docker run -d --name libreddit -p 80:8080 spikecodes/libreddit
|
docker run -d --name libreddit -p 80:8080 spikecodes/libreddit
|
||||||
```
|
```
|
||||||
|
|
||||||
### C) AUR
|
## 3) AUR
|
||||||
|
|
||||||
For ArchLinux users, Libreddit is available from the AUR as [`libreddit-git`](https://aur.archlinux.org/packages/libreddit-git).
|
For ArchLinux users, Libreddit is available from the AUR as [`libreddit-git`](https://aur.archlinux.org/packages/libreddit-git).
|
||||||
|
|
||||||
Install:
|
|
||||||
```
|
```
|
||||||
yay -S libreddit-git
|
yay -S libreddit-git
|
||||||
```
|
```
|
||||||
|
|
||||||
### D) GitHub Releases
|
## 4) GitHub Releases
|
||||||
|
|
||||||
If you're on Linux and none of these methods work for you, you can grab a Linux binary from [the newest release](https://github.com/spikecodes/libreddit/releases/latest).
|
If you're on Linux and none of these methods work for you, you can grab a Linux binary from [the newest release](https://github.com/spikecodes/libreddit/releases/latest).
|
||||||
Currently, Libreddit does not have Windows or macOS binaries but those will be available soon.
|
|
||||||
|
|
||||||
### E) Repl.it
|
## 5) Repl.it
|
||||||
|
|
||||||
**Note:** Repl.it is a free option but they are *not* private and are monitor server usage to prevent abuse. If you really need a free and easy setup, this method may work best for you.
|
**Note:** Repl.it is a free option but they are *not* private and will monitor server usage to prevent abuse. If you need a free and easy setup, this method may work best for you.
|
||||||
|
|
||||||
1. Create a Repl.it account (see note above)
|
1. Create a Repl.it account (see note above)
|
||||||
2. Visit [the official Repl](https://repl.it/@spikethecoder/libreddit) and fork it
|
2. Visit [the official Repl](https://repl.it/@spikethecoder/libreddit) and fork it
|
||||||
@ -177,18 +178,22 @@ Currently, Libreddit does not have Windows or macOS binaries but those will be a
|
|||||||
|
|
||||||
In the web preview (defaults to top right), you should see your instance hosted where you can assign a [custom domain](https://docs.repl.it/repls/web-hosting#custom-domains).
|
In the web preview (defaults to top right), you should see your instance hosted where you can assign a [custom domain](https://docs.repl.it/repls/web-hosting#custom-domains).
|
||||||
|
|
||||||
## Deployment
|
---
|
||||||
|
|
||||||
Once installed, deploy Libreddit (unless you're using Docker) by running:
|
# Deployment
|
||||||
|
|
||||||
|
Once installed, deploy Libreddit to `0.0.0.0:8080` by running:
|
||||||
|
|
||||||
```
|
```
|
||||||
libreddit
|
libreddit
|
||||||
```
|
```
|
||||||
|
|
||||||
Specify a custom address for the server by passing the `-a` or `--address` argument:
|
## Options
|
||||||
```
|
|
||||||
libreddit --address=0.0.0.0:8111
|
| Short | Long | Example |
|
||||||
```
|
|-------|--------------------|-----------------------------------|
|
||||||
|
| `-a` | `--address` | `libreddit --adress=0.0.0.0:8111` |
|
||||||
|
| `-r` | `--redirect-https` | `libreddit --redirect-https` |
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
69
src/main.rs
69
src/main.rs
@ -1,5 +1,9 @@
|
|||||||
// Import Crates
|
// Import Crates
|
||||||
use actix_web::{middleware, web, App, HttpResponse, HttpServer}; // dev::Service
|
use actix_web::{
|
||||||
|
dev::{Service, ServiceResponse},
|
||||||
|
middleware, web, App, HttpResponse, HttpServer,
|
||||||
|
};
|
||||||
|
use futures::future::FutureExt;
|
||||||
|
|
||||||
// Reference local files
|
// Reference local files
|
||||||
mod post;
|
mod post;
|
||||||
@ -23,6 +27,7 @@ async fn robots() -> HttpResponse {
|
|||||||
|
|
||||||
async fn favicon() -> HttpResponse {
|
async fn favicon() -> HttpResponse {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
.content_type("image/x-icon")
|
||||||
.header("Cache-Control", "public, max-age=1209600, s-maxage=86400")
|
.header("Cache-Control", "public, max-age=1209600, s-maxage=86400")
|
||||||
.body(include_bytes!("../static/favicon.ico").as_ref())
|
.body(include_bytes!("../static/favicon.ico").as_ref())
|
||||||
}
|
}
|
||||||
@ -30,12 +35,12 @@ async fn favicon() -> HttpResponse {
|
|||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
let mut address = "0.0.0.0:8080".to_string();
|
let mut address = "0.0.0.0:8080".to_string();
|
||||||
// let mut https = false;
|
let mut force_https = false;
|
||||||
|
|
||||||
for arg in std::env::args().collect::<Vec<String>>() {
|
for arg in std::env::args().collect::<Vec<String>>() {
|
||||||
match arg.split('=').collect::<Vec<&str>>()[0] {
|
match arg.split('=').collect::<Vec<&str>>()[0] {
|
||||||
"--address" | "-a" => address = arg.split('=').collect::<Vec<&str>>()[1].to_string(),
|
"--address" | "-a" => address = arg.split('=').collect::<Vec<&str>>()[1].to_string(),
|
||||||
// "--redirect-https" | "-r" => https = true,
|
"--redirect-https" | "-r" => force_https = true,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,12 +48,36 @@ async fn main() -> std::io::Result<()> {
|
|||||||
// start http server
|
// start http server
|
||||||
println!("Running Libreddit v{} on {}!", env!("CARGO_PKG_VERSION"), &address);
|
println!("Running Libreddit v{} on {}!", env!("CARGO_PKG_VERSION"), &address);
|
||||||
|
|
||||||
HttpServer::new(|| {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
// Redirect to HTTPS
|
// Redirect to HTTPS if "--redirect-https" enabled
|
||||||
// .wrap_fn(|req, srv| { let fut = srv.call(req); async { let mut res = fut.await?; if https {} Ok(res) } })
|
.wrap_fn(move |req, srv| {
|
||||||
|
let secure = req.connection_info().scheme() == "https";
|
||||||
|
let https_url = format!("https://{}{}", req.connection_info().host(), req.uri().to_string());
|
||||||
|
srv.call(req).map(move |res: Result<ServiceResponse, _>| {
|
||||||
|
if force_https && !secure {
|
||||||
|
Ok(ServiceResponse::new(
|
||||||
|
res.unwrap().request().to_owned(),
|
||||||
|
HttpResponse::Found().header("Location", https_url).finish(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
res
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
// Append trailing slash and remove double slashes
|
// Append trailing slash and remove double slashes
|
||||||
.wrap(middleware::NormalizePath::default())
|
.wrap(middleware::NormalizePath::default())
|
||||||
|
// Apply default headers for security
|
||||||
|
.wrap(
|
||||||
|
middleware::DefaultHeaders::new()
|
||||||
|
.header("Referrer-Policy", "no-referrer")
|
||||||
|
.header("X-Content-Type-Options", "nosniff")
|
||||||
|
.header("X-Frame-Options", "DENY")
|
||||||
|
.header(
|
||||||
|
"Content-Security-Policy",
|
||||||
|
"default-src 'none'; media-src 'self'; style-src 'self' 'unsafe-inline'; base-uri 'none'; img-src 'self' data:; form-action 'self'; frame-ancestors 'none';",
|
||||||
|
),
|
||||||
|
)
|
||||||
// Default service in case no routes match
|
// Default service in case no routes match
|
||||||
.default_service(web::get().to(|| utils::error("Nothing here".to_string())))
|
.default_service(web::get().to(|| utils::error("Nothing here".to_string())))
|
||||||
// Read static files
|
// Read static files
|
||||||
@ -75,6 +104,8 @@ async fn main() -> std::io::Result<()> {
|
|||||||
// See posts and info about subreddit
|
// See posts and info about subreddit
|
||||||
.route("/", web::get().to(subreddit::page))
|
.route("/", web::get().to(subreddit::page))
|
||||||
.route("/{sort:hot|new|top|rising|controversial}/", web::get().to(subreddit::page))
|
.route("/{sort:hot|new|top|rising|controversial}/", web::get().to(subreddit::page))
|
||||||
|
// Handle subscribe/unsubscribe
|
||||||
|
.route("/{action:subscribe|unsubscribe}/", web::post().to(subreddit::subscriptions))
|
||||||
// View post on subreddit
|
// View post on subreddit
|
||||||
.service(
|
.service(
|
||||||
web::scope("/comments/{id}/{title}")
|
web::scope("/comments/{id}/{title}")
|
||||||
@ -90,23 +121,19 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.route("/{page}/", web::get().to(subreddit::wiki)),
|
.route("/{page}/", web::get().to(subreddit::wiki)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
// Universal services
|
// Front page
|
||||||
|
.route("/", web::get().to(subreddit::page))
|
||||||
|
.route("/{sort:best|hot|new|top|rising|controversial}/", web::get().to(subreddit::page))
|
||||||
|
// View Reddit wiki
|
||||||
.service(
|
.service(
|
||||||
web::scope("")
|
web::scope("/wiki")
|
||||||
// Front page
|
.route("/", web::get().to(subreddit::wiki))
|
||||||
.route("/", web::get().to(subreddit::page))
|
.route("/{page}/", web::get().to(subreddit::wiki)),
|
||||||
.route("/{sort:best|hot|new|top|rising|controversial}/", web::get().to(subreddit::page))
|
|
||||||
// View Reddit wiki
|
|
||||||
.service(
|
|
||||||
web::scope("/wiki")
|
|
||||||
.route("/", web::get().to(subreddit::wiki))
|
|
||||||
.route("/{page}/", web::get().to(subreddit::wiki)),
|
|
||||||
)
|
|
||||||
// Search all of Reddit
|
|
||||||
.route("/search/", web::get().to(search::find))
|
|
||||||
// Short link for post
|
|
||||||
.route("/{id:.{5,6}}/", web::get().to(post::item)),
|
|
||||||
)
|
)
|
||||||
|
// Search all of Reddit
|
||||||
|
.route("/search/", web::get().to(search::find))
|
||||||
|
// Short link for post
|
||||||
|
.route("/{id:.{5,6}}/", web::get().to(post::item))
|
||||||
})
|
})
|
||||||
.bind(&address)
|
.bind(&address)
|
||||||
.unwrap_or_else(|e| panic!("Cannot bind to the address {}: {}", address, e))
|
.unwrap_or_else(|e| panic!("Cannot bind to the address {}: {}", address, e))
|
||||||
|
19
src/post.rs
19
src/post.rs
@ -37,7 +37,7 @@ pub async fn item(req: HttpRequest) -> HttpResponse {
|
|||||||
dbg!(req.match_info().get("id").unwrap_or(""));
|
dbg!(req.match_info().get("id").unwrap_or(""));
|
||||||
|
|
||||||
// Send a request to the url, receive JSON in response
|
// Send a request to the url, receive JSON in response
|
||||||
match request(&path).await {
|
match request(path).await {
|
||||||
// Otherwise, grab the JSON output from the request
|
// Otherwise, grab the JSON output from the request
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
// Parse the JSON into Post and Comment structs
|
// Parse the JSON into Post and Comment structs
|
||||||
@ -97,7 +97,12 @@ async fn parse_post(json: &serde_json::Value) -> Post {
|
|||||||
score: format_num(score),
|
score: format_num(score),
|
||||||
upvote_ratio: ratio as i64,
|
upvote_ratio: ratio as i64,
|
||||||
post_type,
|
post_type,
|
||||||
thumbnail: format_url(val(post, "thumbnail").as_str()),
|
media,
|
||||||
|
thumbnail: Media {
|
||||||
|
url: format_url(val(post, "thumbnail").as_str()),
|
||||||
|
width: post["data"]["thumbnail_width"].as_i64().unwrap_or_default(),
|
||||||
|
height: post["data"]["thumbnail_height"].as_i64().unwrap_or_default(),
|
||||||
|
},
|
||||||
flair: Flair {
|
flair: Flair {
|
||||||
flair_parts: parse_rich_flair(
|
flair_parts: parse_rich_flair(
|
||||||
val(post, "link_flair_type"),
|
val(post, "link_flair_type"),
|
||||||
@ -115,10 +120,10 @@ async fn parse_post(json: &serde_json::Value) -> Post {
|
|||||||
nsfw: post["data"]["over_18"].as_bool().unwrap_or(false),
|
nsfw: post["data"]["over_18"].as_bool().unwrap_or(false),
|
||||||
stickied: post["data"]["stickied"].as_bool().unwrap_or(false),
|
stickied: post["data"]["stickied"].as_bool().unwrap_or(false),
|
||||||
},
|
},
|
||||||
media,
|
|
||||||
domain: val(post, "domain"),
|
domain: val(post, "domain"),
|
||||||
rel_time,
|
rel_time,
|
||||||
created,
|
created,
|
||||||
|
comments: format_num(post["data"]["num_comments"].as_i64().unwrap_or_default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +155,8 @@ async fn parse_comments(json: &serde_json::Value) -> Vec<Comment> {
|
|||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dbg!();
|
||||||
|
|
||||||
comments.push(Comment {
|
comments.push(Comment {
|
||||||
id: val(&comment, "id"),
|
id: val(&comment, "id"),
|
||||||
body,
|
body,
|
||||||
@ -166,7 +173,11 @@ async fn parse_comments(json: &serde_json::Value) -> Vec<Comment> {
|
|||||||
},
|
},
|
||||||
distinguished: val(&comment, "distinguished"),
|
distinguished: val(&comment, "distinguished"),
|
||||||
},
|
},
|
||||||
score: format_num(score),
|
score: if comment["data"]["score_hidden"].as_bool().unwrap_or_default() {
|
||||||
|
"•".to_string()
|
||||||
|
} else {
|
||||||
|
format_num(score)
|
||||||
|
},
|
||||||
rel_time,
|
rel_time,
|
||||||
created,
|
created,
|
||||||
replies,
|
replies,
|
||||||
|
36
src/proxy.rs
36
src/proxy.rs
@ -21,29 +21,27 @@ pub async fn handler(web::Path(b64): web::Path<String>) -> Result<HttpResponse>
|
|||||||
"v.redd.it",
|
"v.redd.it",
|
||||||
];
|
];
|
||||||
|
|
||||||
match decode(b64) {
|
let decoded = decode(b64).map(|bytes| String::from_utf8(bytes).unwrap_or_default());
|
||||||
Ok(bytes) => {
|
|
||||||
let media = String::from_utf8(bytes).unwrap_or_default();
|
|
||||||
|
|
||||||
match Url::parse(media.as_str()) {
|
match decoded {
|
||||||
Ok(url) => {
|
Ok(media) => match Url::parse(media.as_str()) {
|
||||||
let domain = url.domain().unwrap_or_default();
|
Ok(url) => {
|
||||||
|
let domain = url.domain().unwrap_or_default();
|
||||||
|
|
||||||
if domains.contains(&domain) {
|
if domains.contains(&domain) {
|
||||||
Client::default().get(media.replace("&", "&")).send().await.map_err(Error::from).map(|res| {
|
Client::default().get(media.replace("&", "&")).send().await.map_err(Error::from).map(|res| {
|
||||||
HttpResponse::build(res.status())
|
HttpResponse::build(res.status())
|
||||||
.header("Cache-Control", "public, max-age=1209600, s-maxage=86400")
|
.header("Cache-Control", "public, max-age=1209600, s-maxage=86400")
|
||||||
.header("Content-Length", res.headers().get("Content-Length").unwrap().to_owned())
|
.header("Content-Length", res.headers().get("Content-Length").unwrap().to_owned())
|
||||||
.header("Content-Type", res.headers().get("Content-Type").unwrap().to_owned())
|
.header("Content-Type", res.headers().get("Content-Type").unwrap().to_owned())
|
||||||
.streaming(res)
|
.streaming(res)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(error::ErrorForbidden("Resource must be from Reddit"))
|
Err(error::ErrorForbidden("Resource must be from Reddit"))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => Err(error::ErrorBadRequest("Can't parse base64 into URL")),
|
|
||||||
}
|
}
|
||||||
}
|
_ => Err(error::ErrorBadRequest("Can't parse base64 into URL")),
|
||||||
|
},
|
||||||
_ => Err(error::ErrorBadRequest("Can't decode base64")),
|
_ => Err(error::ErrorBadRequest("Can't decode base64")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// CRATES
|
// CRATES
|
||||||
use crate::utils::{error, fetch_posts, param, prefs, request, val, Post, Preferences};
|
use crate::utils::{cookie, error, fetch_posts, param, prefs, request, val, Post, Preferences};
|
||||||
use actix_web::{HttpRequest, HttpResponse};
|
use actix_web::{HttpRequest, HttpResponse};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
|
||||||
@ -33,7 +33,8 @@ struct SearchTemplate {
|
|||||||
|
|
||||||
// SERVICES
|
// SERVICES
|
||||||
pub async fn find(req: HttpRequest) -> HttpResponse {
|
pub async fn find(req: HttpRequest) -> HttpResponse {
|
||||||
let path = format!("{}.json?{}", req.path(), req.query_string());
|
let nsfw_results = if cookie(&req, "show_nsfw") == "on" { "&include_over_18=on" } else { "" };
|
||||||
|
let path = format!("{}.json?{}{}", req.path(), req.query_string(), nsfw_results);
|
||||||
let sub = req.match_info().get("sub").unwrap_or("").to_string();
|
let sub = req.match_info().get("sub").unwrap_or("").to_string();
|
||||||
|
|
||||||
let sort = if param(&path, "sort").is_empty() {
|
let sort = if param(&path, "sort").is_empty() {
|
||||||
@ -75,7 +76,7 @@ async fn search_subreddits(q: String) -> Vec<Subreddit> {
|
|||||||
let subreddit_search_path = format!("/subreddits/search.json?q={}&limit=3", q.replace(' ', "+"));
|
let subreddit_search_path = format!("/subreddits/search.json?q={}&limit=3", q.replace(' ', "+"));
|
||||||
|
|
||||||
// Send a request to the url
|
// Send a request to the url
|
||||||
match request(&subreddit_search_path).await {
|
match request(subreddit_search_path).await {
|
||||||
// If success, receive JSON in response
|
// If success, receive JSON in response
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
match response["data"]["children"].as_array() {
|
match response["data"]["children"].as_array() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// CRATES
|
// CRATES
|
||||||
use crate::utils::{prefs, Preferences};
|
use crate::utils::{prefs, Preferences};
|
||||||
use actix_web::{cookie::Cookie, web::Form, HttpMessage, HttpRequest, HttpResponse};
|
use actix_web::{cookie::Cookie, web::Form, HttpRequest, HttpResponse};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use time::{Duration, OffsetDateTime};
|
use time::{Duration, OffsetDateTime};
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ pub struct SettingsForm {
|
|||||||
layout: Option<String>,
|
layout: Option<String>,
|
||||||
wide: Option<String>,
|
wide: Option<String>,
|
||||||
comment_sort: Option<String>,
|
comment_sort: Option<String>,
|
||||||
hide_nsfw: Option<String>,
|
show_nsfw: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
@ -30,11 +30,11 @@ pub async fn get(req: HttpRequest) -> HttpResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set cookies using response "Set-Cookie" header
|
// Set cookies using response "Set-Cookie" header
|
||||||
pub async fn set(req: HttpRequest, form: Form<SettingsForm>) -> HttpResponse {
|
pub async fn set(_req: HttpRequest, form: Form<SettingsForm>) -> HttpResponse {
|
||||||
let mut res = HttpResponse::Found();
|
let mut res = HttpResponse::Found();
|
||||||
|
|
||||||
let names = vec!["theme", "front_page", "layout", "wide", "comment_sort", "hide_nsfw"];
|
let names = vec!["theme", "front_page", "layout", "wide", "comment_sort", "show_nsfw"];
|
||||||
let values = vec![&form.theme, &form.front_page, &form.layout, &form.wide, &form.comment_sort, &form.hide_nsfw];
|
let values = vec![&form.theme, &form.front_page, &form.layout, &form.wide, &form.comment_sort, &form.show_nsfw];
|
||||||
|
|
||||||
for (i, name) in names.iter().enumerate() {
|
for (i, name) in names.iter().enumerate() {
|
||||||
match values[i] {
|
match values[i] {
|
||||||
@ -45,10 +45,7 @@ pub async fn set(req: HttpRequest, form: Form<SettingsForm>) -> HttpResponse {
|
|||||||
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
|
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
|
||||||
.finish(),
|
.finish(),
|
||||||
),
|
),
|
||||||
None => match HttpMessage::cookie(&req, name.to_owned()) {
|
None => res.del_cookie(&Cookie::named(name.to_owned())),
|
||||||
Some(cookie) => res.del_cookie(&cookie),
|
|
||||||
None => &mut res,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// CRATES
|
// CRATES
|
||||||
use crate::utils::*;
|
use crate::utils::*;
|
||||||
use actix_web::{HttpRequest, HttpResponse, Result};
|
use actix_web::{cookie::Cookie, HttpRequest, HttpResponse, Result};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
use time::{Duration, OffsetDateTime};
|
||||||
|
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
@ -25,23 +26,43 @@ struct WikiTemplate {
|
|||||||
|
|
||||||
// SERVICES
|
// SERVICES
|
||||||
pub async fn page(req: HttpRequest) -> HttpResponse {
|
pub async fn page(req: HttpRequest) -> HttpResponse {
|
||||||
let path = format!("{}.json?{}", req.path(), req.query_string());
|
let subscribed = cookie(&req, "subscriptions");
|
||||||
let default = cookie(&req, "front_page");
|
let front_page = cookie(&req, "front_page");
|
||||||
let sub_name = req
|
let sort = req.match_info().get("sort").unwrap_or("hot").to_string();
|
||||||
|
|
||||||
|
let sub = req
|
||||||
.match_info()
|
.match_info()
|
||||||
.get("sub")
|
.get("sub")
|
||||||
.unwrap_or(if default.is_empty() { "popular" } else { default.as_str() })
|
.map(String::from)
|
||||||
.to_string();
|
.unwrap_or(if front_page == "default" || front_page.is_empty() {
|
||||||
let sort = req.match_info().get("sort").unwrap_or("hot").to_string();
|
if subscribed.is_empty() {
|
||||||
|
"popular".to_string()
|
||||||
|
} else {
|
||||||
|
subscribed.to_owned()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
front_page.to_owned()
|
||||||
|
});
|
||||||
|
|
||||||
|
let path = format!("/r/{}/{}.json?{}", sub, sort, req.query_string());
|
||||||
|
|
||||||
match fetch_posts(&path, String::new()).await {
|
match fetch_posts(&path, String::new()).await {
|
||||||
Ok((posts, after)) => {
|
Ok((posts, after)) => {
|
||||||
// If you can get subreddit posts, also request subreddit metadata
|
// If you can get subreddit posts, also request subreddit metadata
|
||||||
let sub = if !sub_name.contains('+') && sub_name != "popular" && sub_name != "all" {
|
let sub = if !sub.contains('+') && sub != subscribed && sub != "popular" && sub != "all" {
|
||||||
subreddit(&sub_name).await.unwrap_or_default()
|
// Regular subreddit
|
||||||
} else if sub_name.contains('+') {
|
subreddit(&sub).await.unwrap_or_default()
|
||||||
|
} else if sub == subscribed {
|
||||||
|
// Subscription feed
|
||||||
|
if req.path().starts_with("/r/") {
|
||||||
|
subreddit(&sub).await.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
Subreddit::default()
|
||||||
|
}
|
||||||
|
} else if sub.contains('+') {
|
||||||
|
// Multireddit
|
||||||
Subreddit {
|
Subreddit {
|
||||||
name: sub_name,
|
name: sub,
|
||||||
..Subreddit::default()
|
..Subreddit::default()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -63,12 +84,56 @@ pub async fn page(req: HttpRequest) -> HttpResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sub or unsub by setting subscription cookie using response "Set-Cookie" header
|
||||||
|
pub async fn subscriptions(req: HttpRequest) -> HttpResponse {
|
||||||
|
let mut res = HttpResponse::Found();
|
||||||
|
|
||||||
|
let sub = req.match_info().get("sub").unwrap_or_default().to_string();
|
||||||
|
let action = req.match_info().get("action").unwrap_or_default().to_string();
|
||||||
|
let mut sub_list = prefs(req.to_owned()).subs;
|
||||||
|
|
||||||
|
// Modify sub list based on action
|
||||||
|
if action == "subscribe" && !sub_list.contains(&sub) {
|
||||||
|
sub_list.push(sub.to_owned());
|
||||||
|
sub_list.sort();
|
||||||
|
} else if action == "unsubscribe" {
|
||||||
|
sub_list.retain(|s| s != &sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete cookie if empty, else set
|
||||||
|
if sub_list.is_empty() {
|
||||||
|
res.del_cookie(&Cookie::build("subscriptions", "").path("/").finish());
|
||||||
|
} else {
|
||||||
|
res.cookie(
|
||||||
|
Cookie::build("subscriptions", sub_list.join("+"))
|
||||||
|
.path("/")
|
||||||
|
.http_only(true)
|
||||||
|
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect back to subreddit
|
||||||
|
// check for redirect parameter if unsubscribing from outside sidebar
|
||||||
|
let redirect_path = param(&req.uri().to_string(), "redirect");
|
||||||
|
let path = if !redirect_path.is_empty() && redirect_path.starts_with('/') {
|
||||||
|
redirect_path
|
||||||
|
} else {
|
||||||
|
format!("/r/{}", sub)
|
||||||
|
};
|
||||||
|
|
||||||
|
res
|
||||||
|
.content_type("text/html")
|
||||||
|
.set_header("Location", path.to_owned())
|
||||||
|
.body(format!("Redirecting to <a href=\"{0}\">{0}</a>...", path))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn wiki(req: HttpRequest) -> HttpResponse {
|
pub async fn wiki(req: HttpRequest) -> HttpResponse {
|
||||||
let sub = req.match_info().get("sub").unwrap_or("reddit.com").to_string();
|
let sub = req.match_info().get("sub").unwrap_or("reddit.com").to_string();
|
||||||
let page = req.match_info().get("page").unwrap_or("index").to_string();
|
let page = req.match_info().get("page").unwrap_or("index").to_string();
|
||||||
let path: String = format!("/r/{}/wiki/{}.json?raw_json=1", sub, page);
|
let path: String = format!("/r/{}/wiki/{}.json?raw_json=1", sub, page);
|
||||||
|
|
||||||
match request(&path).await {
|
match request(path).await {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
let s = WikiTemplate {
|
let s = WikiTemplate {
|
||||||
sub,
|
sub,
|
||||||
@ -90,7 +155,7 @@ async fn subreddit(sub: &str) -> Result<Subreddit, String> {
|
|||||||
let path: String = format!("/r/{}/about.json?raw_json=1", sub);
|
let path: String = format!("/r/{}/about.json?raw_json=1", sub);
|
||||||
|
|
||||||
// Send a request to the url
|
// Send a request to the url
|
||||||
match request(&path).await {
|
match request(path).await {
|
||||||
// If success, receive JSON in response
|
// If success, receive JSON in response
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
// Metadata regarding the subreddit
|
// Metadata regarding the subreddit
|
||||||
@ -98,7 +163,7 @@ async fn subreddit(sub: &str) -> Result<Subreddit, String> {
|
|||||||
let active: i64 = res["data"]["accounts_active"].as_u64().unwrap_or_default() as i64;
|
let active: i64 = res["data"]["accounts_active"].as_u64().unwrap_or_default() as i64;
|
||||||
|
|
||||||
// Fetch subreddit icon either from the community_icon or icon_img value
|
// Fetch subreddit icon either from the community_icon or icon_img value
|
||||||
let community_icon: &str = res["data"]["community_icon"].as_str().unwrap_or("").split('?').collect::<Vec<&str>>()[0];
|
let community_icon: &str = res["data"]["community_icon"].as_str().map_or("", |s| s.split('?').collect::<Vec<&str>>()[0]);
|
||||||
let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() };
|
let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() };
|
||||||
|
|
||||||
let sub = Subreddit {
|
let sub = Subreddit {
|
||||||
|
@ -54,7 +54,7 @@ async fn user(name: &str) -> Result<User, String> {
|
|||||||
let path: String = format!("/user/{}/about.json", name);
|
let path: String = format!("/user/{}/about.json", name);
|
||||||
|
|
||||||
// Send a request to the url
|
// Send a request to the url
|
||||||
match request(&path).await {
|
match request(path).await {
|
||||||
// If success, receive JSON in response
|
// If success, receive JSON in response
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
// Grab creation date as unix timestamp
|
// Grab creation date as unix timestamp
|
||||||
|
64
src/utils.rs
64
src/utils.rs
@ -9,6 +9,7 @@ use serde_json::{from_str, Value};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use time::{Duration, OffsetDateTime};
|
use time::{Duration, OffsetDateTime};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
// use cached::proc_macro::cached;
|
||||||
|
|
||||||
//
|
//
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
@ -39,6 +40,12 @@ pub struct Flags {
|
|||||||
pub stickied: bool,
|
pub stickied: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Media {
|
||||||
|
pub url: String,
|
||||||
|
pub width: i64,
|
||||||
|
pub height: i64,
|
||||||
|
}
|
||||||
|
|
||||||
// Post containing content, metadata and media
|
// Post containing content, metadata and media
|
||||||
pub struct Post {
|
pub struct Post {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@ -52,11 +59,12 @@ pub struct Post {
|
|||||||
pub post_type: String,
|
pub post_type: String,
|
||||||
pub flair: Flair,
|
pub flair: Flair,
|
||||||
pub flags: Flags,
|
pub flags: Flags,
|
||||||
pub thumbnail: String,
|
pub thumbnail: Media,
|
||||||
pub media: String,
|
pub media: Media,
|
||||||
pub domain: String,
|
pub domain: String,
|
||||||
pub rel_time: String,
|
pub rel_time: String,
|
||||||
pub created: String,
|
pub created: String,
|
||||||
|
pub comments: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comment with content, post, score and data/time that it was posted
|
// Comment with content, post, score and data/time that it was posted
|
||||||
@ -119,8 +127,9 @@ pub struct Preferences {
|
|||||||
pub front_page: String,
|
pub front_page: String,
|
||||||
pub layout: String,
|
pub layout: String,
|
||||||
pub wide: String,
|
pub wide: String,
|
||||||
pub hide_nsfw: String,
|
pub show_nsfw: String,
|
||||||
pub comment_sort: String,
|
pub comment_sort: String,
|
||||||
|
pub subs: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -134,8 +143,9 @@ pub fn prefs(req: HttpRequest) -> Preferences {
|
|||||||
front_page: cookie(&req, "front_page"),
|
front_page: cookie(&req, "front_page"),
|
||||||
layout: cookie(&req, "layout"),
|
layout: cookie(&req, "layout"),
|
||||||
wide: cookie(&req, "wide"),
|
wide: cookie(&req, "wide"),
|
||||||
hide_nsfw: cookie(&req, "hide_nsfw"),
|
show_nsfw: cookie(&req, "show_nsfw"),
|
||||||
comment_sort: cookie(&req, "comment_sort"),
|
comment_sort: cookie(&req, "comment_sort"),
|
||||||
|
subs: cookie(&req, "subscriptions").split('+').map(String::from).filter(|s| !s.is_empty()).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,16 +179,16 @@ pub fn rewrite_url(text: &str) -> String {
|
|||||||
|
|
||||||
// Append `m` and `k` for millions and thousands respectively
|
// 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 > 1_000_000 {
|
if num >= 1_000_000 {
|
||||||
format!("{}m", num / 1_000_000)
|
format!("{}m", num / 1_000_000)
|
||||||
} else if num > 1000 {
|
} else if num >= 1000 {
|
||||||
format!("{}k", num / 1_000)
|
format!("{}k", num / 1_000)
|
||||||
} else {
|
} else {
|
||||||
num.to_string()
|
num.to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn media(data: &Value) -> (String, String) {
|
pub async fn media(data: &Value) -> (String, Media) {
|
||||||
let post_type: &str;
|
let post_type: &str;
|
||||||
// If post is a video, return the video
|
// If post is a video, return the video
|
||||||
let url = if data["preview"]["reddit_video_preview"]["fallback_url"].is_string() {
|
let url = if data["preview"]["reddit_video_preview"]["fallback_url"].is_string() {
|
||||||
@ -210,7 +220,14 @@ pub async fn media(data: &Value) -> (String, String) {
|
|||||||
data["url"].as_str().unwrap_or_default().to_string()
|
data["url"].as_str().unwrap_or_default().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
(post_type.to_string(), url)
|
(
|
||||||
|
post_type.to_string(),
|
||||||
|
Media {
|
||||||
|
url,
|
||||||
|
width: data["preview"]["images"][0]["source"]["width"].as_i64().unwrap_or_default(),
|
||||||
|
height: data["preview"]["images"][0]["source"]["height"].as_i64().unwrap_or_default(),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_rich_flair(flair_type: String, rich_flair: Option<&Vec<Value>>, text_flair: Option<&str>) -> Vec<FlairPart> {
|
pub fn parse_rich_flair(flair_type: String, rich_flair: Option<&Vec<Value>>, text_flair: Option<&str>) -> Vec<FlairPart> {
|
||||||
@ -272,7 +289,7 @@ pub fn time(created: f64) -> (String, String) {
|
|||||||
|
|
||||||
// val() function used to parse JSON from Reddit APIs
|
// val() function used to parse JSON from Reddit APIs
|
||||||
pub fn val(j: &Value, k: &str) -> String {
|
pub fn val(j: &Value, k: &str) -> String {
|
||||||
String::from(j["data"][k].as_str().unwrap_or_default())
|
j["data"][k].as_str().unwrap_or_default().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch posts of a user or subreddit and return a vector of posts and the "after" value
|
// Fetch posts of a user or subreddit and return a vector of posts and the "after" value
|
||||||
@ -281,7 +298,7 @@ pub async fn fetch_posts(path: &str, fallback_title: String) -> Result<(Vec<Post
|
|||||||
let post_list;
|
let post_list;
|
||||||
|
|
||||||
// Send a request to the url
|
// Send a request to the url
|
||||||
match request(&path).await {
|
match request(path.to_string()).await {
|
||||||
// If success, receive JSON in response
|
// If success, receive JSON in response
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
res = response;
|
res = response;
|
||||||
@ -326,10 +343,18 @@ pub async fn fetch_posts(path: &str, fallback_title: String) -> Result<(Vec<Post
|
|||||||
},
|
},
|
||||||
distinguished: val(post, "distinguished"),
|
distinguished: val(post, "distinguished"),
|
||||||
},
|
},
|
||||||
score: format_num(score),
|
score: if post["data"]["hide_score"].as_bool().unwrap_or_default() {
|
||||||
|
"•".to_string()
|
||||||
|
} else {
|
||||||
|
format_num(score)
|
||||||
|
},
|
||||||
upvote_ratio: ratio as i64,
|
upvote_ratio: ratio as i64,
|
||||||
post_type,
|
post_type,
|
||||||
thumbnail: format_url(val(post, "thumbnail").as_str()),
|
thumbnail: Media {
|
||||||
|
url: format_url(val(post, "thumbnail").as_str()),
|
||||||
|
width: post["data"]["thumbnail_width"].as_i64().unwrap_or_default(),
|
||||||
|
height: post["data"]["thumbnail_height"].as_i64().unwrap_or_default(),
|
||||||
|
},
|
||||||
media,
|
media,
|
||||||
domain: val(post, "domain"),
|
domain: val(post, "domain"),
|
||||||
flair: Flair {
|
flair: Flair {
|
||||||
@ -352,6 +377,7 @@ pub async fn fetch_posts(path: &str, fallback_title: String) -> Result<(Vec<Post
|
|||||||
permalink: val(post, "permalink"),
|
permalink: val(post, "permalink"),
|
||||||
rel_time,
|
rel_time,
|
||||||
created,
|
created,
|
||||||
|
comments: format_num(post["data"]["num_comments"].as_i64().unwrap_or_default()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,7 +399,8 @@ pub async fn error(msg: String) -> HttpResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make a request to a Reddit API and parse the JSON response
|
// Make a request to a Reddit API and parse the JSON response
|
||||||
pub async fn request(path: &str) -> Result<Value, String> {
|
// #[cached(size=100,time=60, result = true)]
|
||||||
|
pub async fn request(path: String) -> Result<Value, String> {
|
||||||
let url = format!("https://www.reddit.com{}", path);
|
let url = format!("https://www.reddit.com{}", path);
|
||||||
let user_agent = format!("web:libreddit:{}", env!("CARGO_PKG_VERSION"));
|
let user_agent = format!("web:libreddit:{}", env!("CARGO_PKG_VERSION"));
|
||||||
|
|
||||||
@ -438,11 +465,11 @@ pub async fn request(path: &str) -> Result<Value, String> {
|
|||||||
// If response is success
|
// If response is success
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
// Parse the response from Reddit as JSON
|
// Parse the response from Reddit as JSON
|
||||||
match from_str(&response.into_string().unwrap()) {
|
let json_string = &response.into_string().unwrap_or_default();
|
||||||
|
match from_str(json_string) {
|
||||||
Ok(json) => Ok(json),
|
Ok(json) => Ok(json),
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
#[cfg(debug_assertions)]
|
println!("{} - Failed to parse page JSON data: {} - {}", url, e, json_string);
|
||||||
dbg!(format!("{} - Failed to parse page JSON data", url));
|
|
||||||
Err("Failed to parse page JSON data".to_string())
|
Err("Failed to parse page JSON data".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,8 +482,7 @@ pub async fn request(path: &str) -> Result<Value, String> {
|
|||||||
}
|
}
|
||||||
// If failed to send request
|
// If failed to send request
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
#[cfg(debug_assertions)]
|
println!("{} - Couldn't send request to Reddit: {}", url, e);
|
||||||
dbg!(format!("{} - {}", url, e));
|
|
||||||
Err("Couldn't send request to Reddit, this instance may be being rate-limited. Try another.".to_string())
|
Err("Couldn't send request to Reddit, this instance may be being rate-limited. Try another.".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
User-agent: *
|
|
||||||
Allow: /
|
|
618
static/style.css
618
static/style.css
@ -1,19 +1,58 @@
|
|||||||
/* General */
|
/* Define themes */
|
||||||
|
|
||||||
|
/* Constants */
|
||||||
:root {
|
:root {
|
||||||
|
--nsfw: #ff5c5d;
|
||||||
|
--admin: #ea0027;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Automatic theme selection */
|
||||||
|
:root, .dark{
|
||||||
|
/* Default & fallback theme (dark) */
|
||||||
--accent: aqua;
|
--accent: aqua;
|
||||||
--green: #5cff85;
|
--green: #5cff85;
|
||||||
--nsfw: #FF5C5D;
|
|
||||||
--admin: #ea0027;
|
|
||||||
--text: white;
|
--text: white;
|
||||||
--foreground: #222;
|
--foreground: #222;
|
||||||
--background: #0F0F0F;
|
--background: #0f0f0f;
|
||||||
--outside: #1F1F1F;
|
--outside: #1f1f1f;
|
||||||
--post: #161616;
|
--post: #161616;
|
||||||
|
--panel-border: 1px solid #333;
|
||||||
--highlighted: #333;
|
--highlighted: #333;
|
||||||
--shadow: 0 1px 3px rgba(0,0,0,0.5);
|
--shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Browser-defined light theme */
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--accent: #009a9a;
|
||||||
|
--green: #00a229;
|
||||||
|
--text: black;
|
||||||
|
--foreground: #f5f5f5;
|
||||||
|
--background: #ddd;
|
||||||
|
--outside: #ececec;
|
||||||
|
--post: #eee;
|
||||||
|
--panel-border: 1px solid #ccc;
|
||||||
|
--highlighted: white;
|
||||||
|
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light theme setting */
|
||||||
|
.light {
|
||||||
|
--accent: #009a9a;
|
||||||
|
--green: #00a229;
|
||||||
|
--text: black;
|
||||||
|
--foreground: #f5f5f5;
|
||||||
|
--background: #ddd;
|
||||||
|
--outside: #ececec;
|
||||||
|
--post: #eee;
|
||||||
|
--panel-border: 1px solid #ccc;
|
||||||
|
--highlighted: white;
|
||||||
|
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* General */
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
@ -29,40 +68,74 @@ pre, form, fieldset, table, th, td, select, input {
|
|||||||
body {
|
body {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
padding-top: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-areas: "logo searchbox links";
|
||||||
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
background: var(--outside);
|
background: var(--outside);
|
||||||
padding: 5px 15px;
|
|
||||||
font-size: 20px;
|
|
||||||
min-height: 40px;
|
|
||||||
position: fixed;
|
|
||||||
width: calc(100% - 30px);
|
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
|
|
||||||
|
font-size: 20px;
|
||||||
|
|
||||||
|
z-index: 2;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 1;
|
padding: 5px 15px;
|
||||||
|
min-height: 40px;
|
||||||
|
width: calc(100% - 30px);
|
||||||
|
position: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav * { color: var(--text); }
|
nav * { color: var(--text); }
|
||||||
nav #reddit { color: var(--accent); }
|
nav #reddit, #code > span { color: var(--accent); }
|
||||||
nav #version { opacity: 25%; }
|
nav #code > svg { stroke: var(--accent); }
|
||||||
|
|
||||||
|
nav #logo {
|
||||||
|
grid-area: logo;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav #links {
|
||||||
|
grid-area: links;
|
||||||
|
margin-left: 10px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav #links svg {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav #version {
|
||||||
|
opacity: 50%;
|
||||||
|
vertical-align: -2px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav #libreddit {
|
||||||
|
vertical-align: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
#settings_link {
|
#settings_link {
|
||||||
font-size: 18px;
|
|
||||||
margin-left: 20px;
|
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#code {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
max-width: 1000px;
|
max-width: 1000px;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
margin: 60px auto 20px auto
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wide main {
|
.wide main {
|
||||||
@ -103,13 +176,16 @@ hr {
|
|||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: 0.2s all;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a:not(.post_right):hover {
|
a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
stroke: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
img[src=""] {
|
img[src=""] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -121,7 +197,7 @@ aside {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.post, .panel {
|
.post, .panel {
|
||||||
border: 1px solid var(--highlighted);
|
border: var(--panel-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dot {
|
.dot {
|
||||||
@ -129,20 +205,6 @@ aside {
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Light Theme */
|
|
||||||
|
|
||||||
.light {
|
|
||||||
--accent: #009a9a;
|
|
||||||
--green: #00a229;
|
|
||||||
--text: black;
|
|
||||||
--foreground: #f5f5f5;
|
|
||||||
--background: #DDD;
|
|
||||||
--outside: #ECECEC;
|
|
||||||
--post: #eee;
|
|
||||||
--highlighted: white;
|
|
||||||
--shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* User & Subreddit */
|
/* User & Subreddit */
|
||||||
|
|
||||||
#user, #subreddit, #sidebar {
|
#user, #subreddit, #sidebar {
|
||||||
@ -197,6 +259,71 @@ aside {
|
|||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Subscriptions */
|
||||||
|
|
||||||
|
#sub_subscription {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscribe, .unsubscribe {
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscribe {
|
||||||
|
color: var(--foreground);
|
||||||
|
background-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.unsubscribe {
|
||||||
|
color: var(--text);
|
||||||
|
background-color: var(--highlighted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subscribed subreddit list */
|
||||||
|
|
||||||
|
#subscriptions {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: var(--panel-border);
|
||||||
|
background-color: var(--outside);
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 15px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#subscriptions > summary {
|
||||||
|
padding: 8px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sub_list {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
min-width: 100%;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
background: var(--outside);
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: auto;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sub_list > a {
|
||||||
|
padding: 10px 20px;
|
||||||
|
transition: 0.2s background;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sub_list > .selected {
|
||||||
|
background-color: var(--accent);
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sub_list > a:not(.selected):hover {
|
||||||
|
background-color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
/* Wiki Pages */
|
/* Wiki Pages */
|
||||||
|
|
||||||
#wiki {
|
#wiki {
|
||||||
@ -223,6 +350,10 @@ aside {
|
|||||||
|
|
||||||
/* Sorting and Search */
|
/* Sorting and Search */
|
||||||
|
|
||||||
|
select, #search, #sort_options, #inside, #searchbox > *, #sort_submit {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
.search_label {
|
.search_label {
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -232,13 +363,13 @@ aside {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
background: var(--outside);
|
background: var(--outside);
|
||||||
transition: 0.2s all;
|
transition: 0.2s background;
|
||||||
}
|
}
|
||||||
|
|
||||||
select, #search {
|
select, #search {
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
height: 40px;
|
|
||||||
appearance: none;
|
appearance: none;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
@ -246,15 +377,13 @@ select, #search {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#searchbox {
|
#searchbox {
|
||||||
|
grid-area: searchbox;
|
||||||
display: flex;
|
display: flex;
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchbox > *, #sort_submit {
|
#searchbox > *, #sort_submit { background: var(--highlighted); }
|
||||||
background: var(--highlighted);
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#search {
|
#search {
|
||||||
border-right: 2px var(--outside) solid;
|
border-right: 2px var(--outside) solid;
|
||||||
@ -266,20 +395,26 @@ select, #search {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-right: 2px var(--outside) solid;
|
border-right: 2px var(--outside) solid;
|
||||||
height: 40px;
|
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
|
max-width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#restrict_sr { margin-right: 5px; }
|
#restrict_sr { margin-right: 5px; }
|
||||||
|
|
||||||
input[type="submit"] {
|
input[type="submit"], button.submit {
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0px 5px 5px 0px;
|
border-radius: 0px 5px 5px 0px;
|
||||||
transition: 0.2s all;
|
}
|
||||||
|
|
||||||
|
button.submit {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
select:hover { background: var(--foreground); }
|
select:hover { background: var(--foreground); }
|
||||||
|
|
||||||
input[type="submit"]:hover { color: var(--accent); }
|
input[type="submit"]:hover { color: var(--accent); }
|
||||||
|
button.submit:hover > svg { stroke: var(--accent); }
|
||||||
|
|
||||||
#timeframe {
|
#timeframe {
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
@ -313,10 +448,6 @@ input[type="submit"]:hover { color: var(--accent); }
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sort_options {
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sort, #search_sort {
|
#sort, #search_sort {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -336,7 +467,7 @@ input[type="submit"]:hover { color: var(--accent); }
|
|||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: 0.2s all;
|
transition: 0.2s background;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sort_options > a.selected {
|
#sort_options > a.selected {
|
||||||
@ -352,8 +483,8 @@ input[type="submit"]:hover { color: var(--accent); }
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background: var(--post);
|
background: var(--post);
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
transition: 0.2s all;
|
transition: 0.2s background;
|
||||||
border: 1px solid var(--highlighted);
|
border: var(--panel-border);
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,63 +532,41 @@ a.search_subreddit:hover {
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background: var(--post);
|
background: var(--post);
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
display: flex;
|
display: grid;
|
||||||
transition: 0.2s all;
|
transition: 0.2s background;
|
||||||
|
grid-template: "post_score post_header post_thumbnail" auto
|
||||||
|
"post_score post_title post_thumbnail" 1fr
|
||||||
|
"post_score post_media post_thumbnail" auto
|
||||||
|
"post_score post_body post_thumbnail" auto
|
||||||
|
"post_score post_footer post_thumbnail" auto
|
||||||
|
/ minmax(40px, auto) minmax(0, 1fr) fit-content(min(20%, 152px));
|
||||||
}
|
}
|
||||||
|
|
||||||
.post:not(:last-child) { margin-bottom: 10px; }
|
.post:not(:last-child) { margin-bottom: 10px; }
|
||||||
|
|
||||||
.post.highlighted {
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post.highlighted > .post_right {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post:hover {
|
.post:hover {
|
||||||
background: var(--foreground);
|
background: var(--foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post:hover > .post_left {
|
|
||||||
background: var(--highlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_left, .post_right {
|
|
||||||
display: flex;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_left {
|
|
||||||
text-align: center;
|
|
||||||
background: var(--foreground);
|
|
||||||
border-radius: 5px 0 0 5px;
|
|
||||||
flex-direction: column;
|
|
||||||
min-width: 50px;
|
|
||||||
transition: 0.2s all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_score {
|
.post_score {
|
||||||
margin-top: 20px;
|
padding-top: 16px;
|
||||||
color: var(--accent);
|
font-size: 13px;
|
||||||
}
|
|
||||||
|
|
||||||
#post_footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
opacity: 0.5;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#post_links {
|
|
||||||
display: flex;
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
text-align: end;
|
||||||
|
color: var(--accent);
|
||||||
|
grid-area: post_score;
|
||||||
|
text-align: end;
|
||||||
|
border-radius: 5px 0 0 5px;
|
||||||
|
transition: 0.2s background;
|
||||||
}
|
}
|
||||||
|
|
||||||
#post_links > li {
|
.post_score .label {
|
||||||
margin-right: 15px;
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_header {
|
||||||
|
margin: 15px 20px 5px 15px;
|
||||||
|
grid-area: post_header;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post_subreddit {
|
.post_subreddit {
|
||||||
@ -467,82 +576,8 @@ a.search_subreddit:hover {
|
|||||||
.post_title {
|
.post_title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
margin-top: 10px;
|
margin: 5px 15px;
|
||||||
}
|
grid-area: post_title;
|
||||||
|
|
||||||
.post_text {
|
|
||||||
padding: 15px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_right {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_right > * {
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_media {
|
|
||||||
max-width: 90%;
|
|
||||||
align-self: center;
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_body {
|
|
||||||
opacity: 0.9;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 10px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#post_url {
|
|
||||||
color: var(--accent);
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_thumbnail {
|
|
||||||
border-radius: 5px;
|
|
||||||
border: 1px solid var(--foreground);
|
|
||||||
width: 20%;
|
|
||||||
max-width: 140px;
|
|
||||||
display: grid;
|
|
||||||
overflow: hidden;
|
|
||||||
flex-shrink: 0;
|
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_thumbnail img {
|
|
||||||
grid-area: 1 / 1 / 2 / 2;
|
|
||||||
width: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
align-self: center;
|
|
||||||
justify-self: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_thumbnail.no_thumbnail {
|
|
||||||
background-color: var(--highlighted)
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_thumbnail svg {
|
|
||||||
grid-area: 1 / 1 / 2 / 2;
|
|
||||||
align-self: center;
|
|
||||||
justify-self: center;
|
|
||||||
stroke: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_thumbnail span {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-align: center;
|
|
||||||
background-color: rgba(0,0,0,0.8);
|
|
||||||
color: white;
|
|
||||||
grid-area: 1 / 1 / 2 / 2;
|
|
||||||
padding: 5px;
|
|
||||||
align-self: end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.post_flair {
|
.post_flair {
|
||||||
@ -567,14 +602,111 @@ a.search_subreddit:hover {
|
|||||||
|
|
||||||
.nsfw {
|
.nsfw {
|
||||||
color: var(--nsfw);
|
color: var(--nsfw);
|
||||||
margin-top: 20px;
|
margin-left: 5px;
|
||||||
border: 1px solid var(--nsfw);
|
border: 1px solid var(--nsfw);
|
||||||
padding: 5px;
|
padding: 3px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.post_media {
|
||||||
|
max-width: calc(100% - 40px);
|
||||||
|
height: auto;
|
||||||
|
align-self: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
margin: 5px auto;
|
||||||
|
grid-area: post_media;
|
||||||
|
background-color: var(--highlighted);
|
||||||
|
background-image: url("data:image/svg+xml;utf8,<svg viewBox='0 0 100 100' width='100' height='100' xmlns='http://www.w3.org/2000/svg'><path d='M15,20 h70 a10,10 0 0 1 10,10 v45 a10,10 0 0 1 -10,10 h-70 a10,10 0 0 1 -10,-10 v-45 a10,10 0 0 1 10,-10 z' fill='none' stroke='rgba(128,128,128,0.5)' stroke-width='3' /><path d='M15,75 l25,-35 l15,20 l10,-10 l20, 25 z' stroke='none' fill='rgba(128,128,128,0.5)' /><circle cx='75' cy='35' r='7' stroke='none' fill='rgba(128,128,128,0.5)'/></svg>");
|
||||||
|
background-position: 50%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_media.short {
|
||||||
|
max-height: 512px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#post_url {
|
||||||
|
color: var(--accent);
|
||||||
|
margin: 5px 20px;
|
||||||
|
grid-area: post_media;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_body {
|
||||||
|
opacity: 0.9;
|
||||||
|
font-weight: normal;
|
||||||
|
margin: 5px 15px;
|
||||||
|
grid-area: post_body;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: 14px;
|
||||||
|
grid-area: post_footer;
|
||||||
|
margin: 5px 20px 15px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_comments {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#post_links {
|
||||||
|
display: flex;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#post_links > li {
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_thumbnail {
|
||||||
|
border-radius: 5px;
|
||||||
|
border: var(--panel-border);
|
||||||
|
display: grid;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: var(--background);
|
||||||
|
grid-area: post_thumbnail;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_thumbnail svg {
|
||||||
|
grid-area: 1 / 1 / 2 / 2;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
object-fit: cover;
|
||||||
|
align-self: center;
|
||||||
|
justify-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_thumbnail.no_thumbnail {
|
||||||
|
background-color: var(--highlighted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_thumbnail.no_thumbnail svg {
|
||||||
|
grid-area: 1 / 1 / 2 / 2;
|
||||||
|
align-self: center;
|
||||||
|
justify-self: center;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_thumbnail span {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
background-color: rgba(0,0,0,0.8);
|
||||||
|
color: white;
|
||||||
|
grid-area: 1 / 1 / 2 / 2;
|
||||||
|
padding: 5px;
|
||||||
|
align-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
.stickied {
|
.stickied {
|
||||||
--accent: var(--green);
|
--accent: var(--green);
|
||||||
border: 1px solid var(--green);
|
border: 1px solid var(--green);
|
||||||
@ -632,6 +764,7 @@ a.search_subreddit:hover {
|
|||||||
padding: 10px 0 10px 5px;
|
padding: 10px 0 10px 5px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment_data > * {
|
.comment_data > * {
|
||||||
@ -707,28 +840,30 @@ a.search_subreddit:hover {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compact .post.highlighted {
|
.compact .post.highlighted { border-radius: 5px; }
|
||||||
border-radius: 5px;
|
.compact .post:not(:last-of-type):not(.highlighted):not(.stickied) { border-bottom: 0; }
|
||||||
}
|
|
||||||
|
|
||||||
.compact .post:not(:last-of-type):not(.highlighted):not(.stickied) {
|
.compact .post_score {
|
||||||
border-bottom: 0;
|
padding-top: 15px;
|
||||||
}
|
|
||||||
|
|
||||||
.compact .post_left {
|
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compact .post_header {
|
.compact .post_header {
|
||||||
|
margin: 15px 15px 2.5px 15px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compact .post_title {
|
.compact .post_title, .compact #post_url, .compact .post_body {
|
||||||
margin-top: 5px;
|
margin: 2.5px 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compact .post_text {
|
.compact .post_media {
|
||||||
padding: 10px;
|
max-width: calc(100% - 30px);
|
||||||
|
margin: 2.5px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact .post_footer {
|
||||||
|
margin: 5px 15px 15px 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compact .post_thumbnail {
|
.compact .post_thumbnail {
|
||||||
@ -740,18 +875,9 @@ a.search_subreddit:hover {
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card_post .post_right {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card_post:not(.highlighted) .post_media {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Settings */
|
/* Settings */
|
||||||
|
|
||||||
#settings {
|
#settings, #settings > form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -764,7 +890,7 @@ a.search_subreddit:hover {
|
|||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
#prefs {
|
.prefs {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -774,7 +900,7 @@ a.search_subreddit:hover {
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#prefs > div {
|
.prefs > div {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -782,17 +908,21 @@ a.search_subreddit:hover {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#prefs > div:not(:last-of-type) {
|
.prefs > div:not(:last-of-type) {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#prefs select {
|
.prefs select {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
background: var(--foreground);
|
background: var(--foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aside.prefs {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
#save {
|
#save {
|
||||||
background: var(--highlighted);
|
background: var(--highlighted);
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
@ -805,6 +935,27 @@ input[type="submit"] {
|
|||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#settings_subs {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings_subs > li {
|
||||||
|
display: flex;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
#settings_subs > li:last-of-type { margin-bottom: 0; }
|
||||||
|
|
||||||
|
#settings_subs > li > span {
|
||||||
|
padding: 10px 0;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings_subs .unsubscribe {
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Markdown */
|
/* Markdown */
|
||||||
|
|
||||||
.md > *:not(:first-child) {
|
.md > *:not(:first-child) {
|
||||||
@ -819,9 +970,10 @@ input[type="submit"] {
|
|||||||
.md h6 { font-size: 12px; }
|
.md h6 { font-size: 12px; }
|
||||||
|
|
||||||
.md blockquote {
|
.md blockquote {
|
||||||
padding-left: 6px;
|
padding: 10px;
|
||||||
margin: 4px 0 4px 5px;
|
margin: 4px 0 4px 5px;
|
||||||
border-left: 4px solid var(--highlighted);
|
border-left: 4px solid var(--highlighted);
|
||||||
|
background: var(--post);
|
||||||
}
|
}
|
||||||
|
|
||||||
.md a, .md a * {
|
.md a, .md a * {
|
||||||
@ -863,41 +1015,42 @@ input[type="submit"] {
|
|||||||
|
|
||||||
/* Tables */
|
/* Tables */
|
||||||
|
|
||||||
|
table, td, th { border: var(--panel-border); }
|
||||||
|
|
||||||
table {
|
table {
|
||||||
border: 3px var(--highlighted) solid;
|
border-width: 3px;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
td, th {
|
td, th {
|
||||||
border: 1px var(--highlighted) solid;
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mobile */
|
/* Mobile */
|
||||||
|
|
||||||
@media screen and (max-width: 480px) {
|
@media screen and (max-width: 480px) {
|
||||||
|
#version { display: none; }
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
flex-direction: column-reverse;
|
grid-template: "post_header post_header post_thumbnail" auto
|
||||||
|
"post_title post_title post_thumbnail" 1fr
|
||||||
|
"post_media post_media post_thumbnail" auto
|
||||||
|
"post_body post_body post_thumbnail" auto
|
||||||
|
"post_score post_footer post_thumbnail" auto
|
||||||
|
/ auto 1fr fit-content(min(20%, 152px));
|
||||||
}
|
}
|
||||||
|
|
||||||
.post_header {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_left {
|
|
||||||
border-radius: 0 0 5px 5px;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nsfw {
|
|
||||||
margin: 5px 0px 5px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_score {
|
.post_score {
|
||||||
margin: 5px 0;
|
margin: 5px 0px 20px 15px;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.compact .post_score { padding: 0; }
|
||||||
|
|
||||||
|
.post_score::before { content: "↑" }
|
||||||
|
|
||||||
|
.post_header { font-size: 14px; }
|
||||||
|
.post_footer { margin-left: 15px; }
|
||||||
|
|
||||||
.replies > .comment {
|
.replies > .comment {
|
||||||
margin-left: -25px;
|
margin-left: -25px;
|
||||||
@ -916,26 +1069,39 @@ td, th {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 800px) {
|
@media screen and (max-width: 800px) {
|
||||||
|
body { padding-top: 120px }
|
||||||
|
|
||||||
main {
|
main {
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin: 100px 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
flex-direction: column;
|
grid-template-areas: 'logo links' 'searchbox searchbox';
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
width: calc(100% - 20px);
|
width: calc(100% - 20px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav #links { margin-left: auto; }
|
||||||
|
nav #links span { display: none; }
|
||||||
|
nav #links svg { display: block; }
|
||||||
|
|
||||||
|
#subscriptions { position: unset; }
|
||||||
|
|
||||||
|
#sub_list {
|
||||||
|
left: 10px;
|
||||||
|
right: 10px;
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
aside, #subreddit, #user {
|
aside, #subreddit, #user {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#user, #sidebar { margin: 20px 0; }
|
#user, #sidebar { margin: 20px 0; }
|
||||||
#logo { margin: 5px auto; }
|
#logo, #links { margin-bottom: 5px; }
|
||||||
#searchbox { width: 100%; }
|
#searchbox { width: calc(100vw - 35px); }
|
||||||
#github { display: none; }
|
|
||||||
}
|
}
|
||||||
|
@ -3,30 +3,37 @@
|
|||||||
<head>
|
<head>
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<title>{% block title %}Libreddit{% endblock %}</title>
|
<title>{% block title %}Libreddit{% endblock %}</title>
|
||||||
<meta http-equiv="Referrer-Policy" content="no-referrer">
|
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'; base-uri 'none'; form-action 'self';">
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<meta name="description" content="View on Libreddit, an alternative private front-end to Reddit.">
|
<meta name="description" content="View on Libreddit, an alternative private front-end to Reddit.">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
|
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
|
||||||
<link rel="stylesheet" href="/style.css">
|
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body class="
|
<body class="
|
||||||
{% if prefs.layout != "" %}{{ prefs.layout }}{% endif %}
|
{% if prefs.layout != "" %}{{ prefs.layout }}{% endif %}
|
||||||
{% if prefs.wide == "on" %} wide{% endif %}
|
{% if prefs.wide == "on" %} wide{% endif %}
|
||||||
{% if prefs.theme == "light" %} light{% endif %}">
|
{% if prefs.theme != "system" %} {{ prefs.theme }}{% endif %}">
|
||||||
<!-- NAVIGATION BAR -->
|
<!-- NAVIGATION BAR -->
|
||||||
<nav>
|
<nav>
|
||||||
<p id="logo">
|
<div id="logo">
|
||||||
<a id="libreddit" href="/">
|
<a id="libreddit" href="/">
|
||||||
<span id="lib">lib</span><span id="reddit">reddit.</span>
|
<span id="lib">lib</span><span id="reddit">reddit.</span>
|
||||||
</a>
|
</a>
|
||||||
<span id="version">v{{ env!("CARGO_PKG_VERSION") }}</span>
|
<span id="version">v{{ env!("CARGO_PKG_VERSION") }}</span>
|
||||||
<a id="settings_link" href="/settings">settings</a>
|
{% block subscriptions %}{% endblock %}
|
||||||
</p>
|
</div>
|
||||||
{% block search %}{% endblock %}
|
{% block search %}{% endblock %}
|
||||||
<a id="github" href="https://github.com/spikecodes/libreddit">GITHUB</a>
|
<div id="links">
|
||||||
|
<a id="settings_link" href="/settings">
|
||||||
|
<span>settings</span>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||||
|
</a>
|
||||||
|
<a id="code" href="https://github.com/spikecodes/libreddit">
|
||||||
|
<span>code</span>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- MAIN CONTENT -->
|
<!-- MAIN CONTENT -->
|
||||||
@ -37,4 +44,4 @@
|
|||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -13,6 +13,10 @@
|
|||||||
<meta name="author" content="u/{{ post.author.name }}">
|
<meta name="author" content="u/{{ post.author.name }}">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block subscriptions %}
|
||||||
|
{% call utils::sub_list(post.community.as_str()) %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
<!-- OPEN COMMENT MACRO -->
|
<!-- OPEN COMMENT MACRO -->
|
||||||
{% macro comment(item) -%}
|
{% macro comment(item) -%}
|
||||||
<div id="{{ item.id }}" class="comment">
|
<div id="{{ item.id }}" class="comment">
|
||||||
@ -41,57 +45,66 @@
|
|||||||
|
|
||||||
<!-- POST CONTENT -->
|
<!-- POST CONTENT -->
|
||||||
<div class="post highlighted">
|
<div class="post highlighted">
|
||||||
<div class="post_left">
|
<p class="post_header">
|
||||||
<p class="post_score">{{ post.score }}</p>
|
<a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a>
|
||||||
{% if post.flags.nsfw %}<div class="nsfw">NSFW</div>{% endif %}
|
<span class="dot">•</span>
|
||||||
</div>
|
<a class="post_author" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
|
||||||
<div class="post_right">
|
{% if post.author.flair.flair_parts.len() > 0 %}
|
||||||
<div class="post_text">
|
<small class="author_flair">{% call utils::render_flair(post.author.flair.flair_parts) %}</small>
|
||||||
<p class="post_header">
|
{% endif %}
|
||||||
<a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a>
|
<span class="dot">•</span>
|
||||||
<span class="dot">•</span>
|
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
||||||
<a class="post_author" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
|
</p>
|
||||||
{% if post.author.flair.flair_parts.len() > 0 %}
|
<p class="post_title">
|
||||||
<small class="author_flair">{% call utils::render_flair(post.author.flair.flair_parts) %}</small>
|
<a href="{{ post.permalink }}">{{ post.title }}</a>
|
||||||
{% endif %}
|
{% if post.flair.flair_parts.len() > 0 %}
|
||||||
<span class="dot">•</span>
|
<small class="post_flair" style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }};">{% call utils::render_flair(post.flair.flair_parts) %}</small>
|
||||||
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
{% endif %}
|
||||||
</p>
|
{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
|
||||||
<a href="{{ post.permalink }}" class="post_title">
|
</p>
|
||||||
{{ post.title }}
|
|
||||||
{% if post.flair.flair_parts.len() > 0 %}
|
|
||||||
<small class="post_flair" style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }}">{% call utils::render_flair(post.flair.flair_parts) %}</small>
|
|
||||||
{% endif %}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- POST MEDIA -->
|
<!-- POST MEDIA -->
|
||||||
{% if post.post_type == "image" %}
|
{% if post.post_type == "image" %}
|
||||||
<img class="post_media" src="{{ post.media }}"/>
|
<a href="{{ post.media.url }}" style="display:contents" >
|
||||||
{% else if post.post_type == "video" || post.post_type == "gif" %}
|
<svg class="post_media"
|
||||||
<video class="post_media" src="{{ post.media }}" controls autoplay loop></video>
|
width="{{ post.media.width }}px"
|
||||||
{% else if post.post_type == "link" %}
|
height="{{ post.media.height }}px"
|
||||||
<a id="post_url" href="{{ post.media }}">{{ post.media }}</a>
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
{% endif %}
|
<image width="100%" height="100%" href="{{ post.media.url }}"/>
|
||||||
|
<desc>
|
||||||
|
<img alt="Post image" src="{{ post.media.url }}"/>
|
||||||
|
</dev>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
{% else if post.post_type == "video" || post.post_type == "gif" %}
|
||||||
|
<video class="post_media" src="{{ post.media.url }}" controls autoplay loop></video>
|
||||||
|
{% else if post.post_type == "link" %}
|
||||||
|
<a id="post_url" href="{{ post.media.url }}">{{ post.media.url }}</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<!-- POST BODY -->
|
<!-- POST BODY -->
|
||||||
<div class="post_body">{{ post.body }}</div>
|
<div class="post_body">{{ post.body }}</div>
|
||||||
<div id="post_footer">
|
<div class="post_score">{{ post.score }}<span class="label"> Upvotes</span></div>
|
||||||
<ul id="post_links">
|
<div class="post_footer">
|
||||||
<li><a href="/{{ post.id }}">permalink</a></li>
|
<ul id="post_links">
|
||||||
<li><a href="https://reddit.com/{{ post.id }}">reddit</a></li>
|
<li><a href="/{{ post.id }}">permalink</a></li>
|
||||||
</ul>
|
<li><a href="https://reddit.com/{{ post.id }}">reddit</a></li>
|
||||||
<p>{{ post.upvote_ratio }}% Upvoted</p>
|
</ul>
|
||||||
</div>
|
<p>{{ post.upvote_ratio }}% Upvoted</p>
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- SORT FORM -->
|
<!-- SORT FORM -->
|
||||||
<form id="sort">
|
<form id="sort">
|
||||||
<select name="sort">
|
<select name="sort" title="Sort comments by">
|
||||||
{% call utils::options(sort, ["confidence", "top", "new", "controversial", "old"], "confidence") %}
|
{% call utils::options(sort, ["confidence", "top", "new", "controversial", "old"], "confidence") %}
|
||||||
</select><input id="sort_submit" type="submit" value="→">
|
</select><button id="sort_submit" class="submit">
|
||||||
|
<svg width="15" viewBox="0 0 110 100" fill="none" stroke-width="10" stroke-linecap="round">
|
||||||
|
<path d="M20 50 H100" />
|
||||||
|
<path d="M75 15 L100 50 L75 85" />
|
||||||
|
→
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- COMMENTS -->
|
<!-- COMMENTS -->
|
||||||
|
@ -3,21 +3,31 @@
|
|||||||
|
|
||||||
{% block title %}Libreddit: search results - {{ params.q }}{% endblock %}
|
{% block title %}Libreddit: search results - {{ params.q }}{% endblock %}
|
||||||
|
|
||||||
|
{% block subscriptions %}
|
||||||
|
{% call utils::sub_list("") %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="column_one">
|
<div id="column_one">
|
||||||
<form id="search_sort">
|
<form id="search_sort">
|
||||||
<input id="search" type="text" name="q" placeholder="Search" value="{{ params.q }}">
|
<input id="search" type="text" name="q" placeholder="Search" value="{{ params.q }}" title="Search libreddit">
|
||||||
{% if sub != "" %}
|
{% if sub != "" %}
|
||||||
<div id="inside">
|
<div id="inside">
|
||||||
<input type="checkbox" name="restrict_sr" id="restrict_sr" {% if params.restrict_sr != "" %}checked{% endif %}>
|
<input type="checkbox" name="restrict_sr" id="restrict_sr" {% if params.restrict_sr != "" %}checked{% endif %}>
|
||||||
<label for="restrict_sr" class="search_label">in r/{{ sub }}</label>
|
<label for="restrict_sr" class="search_label">in r/{{ sub }}</label>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<select id="sort_options" name="sort">
|
<select id="sort_options" name="sort" title="Sort results by">
|
||||||
{% call utils::options(params.sort, ["relevance", "hot", "top", "new", "comments"], "") %}
|
{% call utils::options(params.sort, ["relevance", "hot", "top", "new", "comments"], "") %}
|
||||||
</select>{% if params.sort != "new" %}<select id="timeframe" name="t">
|
</select>{% if params.sort != "new" %}<select id="timeframe" name="t" title="Timeframe">
|
||||||
{% call utils::options(params.t, ["hour", "day", "week", "month", "year", "all"], "all") %}
|
{% call utils::options(params.t, ["hour", "day", "week", "month", "year", "all"], "all") %}
|
||||||
</select>{% endif %}<input id="sort_submit" type="submit" value="→">
|
</select>{% endif %}<button id="sort_submit" class="submit">
|
||||||
|
<svg width="15" viewBox="0 0 110 100" fill="none" stroke-width="10" stroke-linecap="round">
|
||||||
|
<path d="M20 50 H100" />
|
||||||
|
<path d="M75 15 L100 50 L75 85" />
|
||||||
|
→
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% if subreddits.len() > 0 %}
|
{% if subreddits.len() > 0 %}
|
||||||
@ -36,48 +46,54 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% for post in posts %}
|
{% for post in posts %}
|
||||||
|
|
||||||
{% if post.flags.nsfw && prefs.hide_nsfw == "on" %}
|
{% if post.flags.nsfw && prefs.show_nsfw != "on" %}
|
||||||
{% else if post.title != "Comment" %}
|
{% else if post.title != "Comment" %}
|
||||||
<div class="post {% if prefs.layout == "card" && post.post_type == "image" %}card_post{% endif %}">
|
<div class="post {% if post.flags.stickied %}stickied{% endif %}">
|
||||||
<div class="post_left">
|
<p class="post_header">
|
||||||
<p class="post_score">{{ post.score }}</p>
|
<a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a>
|
||||||
{% if post.flags.nsfw %}<div class="nsfw">NSFW</div>{% endif %}
|
<span class="dot">•</span>
|
||||||
</div>
|
<a class="post_author" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
|
||||||
<div class="post_right">
|
<span class="dot">•</span>
|
||||||
<div class="post_text">
|
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
||||||
<p class="post_header">
|
</p>
|
||||||
<a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a>
|
<p class="post_title">
|
||||||
<span class="dot">•</span>
|
{% if post.flair.flair_parts.len() > 0 %}
|
||||||
<a class="post_author" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
|
<small class="post_flair" style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }};">{% call utils::render_flair(post.flair.flair_parts) %}</small>
|
||||||
{% if post.author.flair.flair_parts.len() > 0 %}
|
|
||||||
<small class="author_flair">{% call utils::render_flair(post.author.flair.flair_parts) %}</small>
|
|
||||||
{% endif %}
|
|
||||||
<span class="dot">•</span>
|
|
||||||
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
|
||||||
</p>
|
|
||||||
<p class="post_title">
|
|
||||||
{% if post.flair.flair_parts.len() > 0 %}
|
|
||||||
<small class="post_flair" style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }}">{% call utils::render_flair(post.flair.flair_parts) %}</small>
|
|
||||||
{% endif %}
|
|
||||||
<a href="{{ post.permalink }}">{{ post.title }}</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- POST MEDIA/THUMBNAIL -->
|
|
||||||
{% if prefs.layout == "card" && post.post_type == "image" %}
|
|
||||||
<img class="post_media" src="{{ post.media }}"/>
|
|
||||||
{% else if post.post_type != "self" %}
|
|
||||||
<a class="post_thumbnail {% if post.thumbnail == "" %}no_thumbnail{% endif %}" href="{% if post.post_type == "link" %}{{ post.media }}{% else %}{{ post.permalink }}{% endif %}">
|
|
||||||
{% if post.thumbnail == "" %}
|
|
||||||
<svg viewBox="0 0 100 106" width="50" height="53" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M35,15h-15a10,10 0,0,0 0,20h25a10,10 0,0,0 10,-10m-12.5,0a10, 10 0,0,1 10, -10h25a10,10 0,0,1 0,20h-15" fill="none" stroke-width="5" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
{% else %}
|
|
||||||
<img src="{{ post.thumbnail }}">
|
|
||||||
{% endif %}
|
|
||||||
<span>{% if post.post_type == "link" %}{{ post.domain }}{% else %}{{ post.post_type }}{% endif %}</span>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<a href="{{ post.permalink }}">{{ post.title }}</a>{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
|
||||||
|
</p>
|
||||||
|
<!-- POST MEDIA/THUMBNAIL -->
|
||||||
|
{% if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "image" %}
|
||||||
|
<a href="{{ post.media.url }}" style="display:contents" >
|
||||||
|
<svg class="post_media {% if post.media.height / post.media.width < 2 %}short{% endif %}"
|
||||||
|
width="{{ post.media.width }}px"
|
||||||
|
height="{{ post.media.height }}px"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<image width="100%" height="100%" href="{{ post.media.url }}"/>
|
||||||
|
<desc>
|
||||||
|
<img alt="Post image" src="{{ post.media.url }}"/>
|
||||||
|
</dev>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
{% else if post.post_type != "self" %}
|
||||||
|
<a class="post_thumbnail {% if post.thumbnail.url.is_empty() %}no_thumbnail{% endif %}" href="{% if post.post_type == "link" %}{{ post.media.url }}{% else %}{{ post.permalink }}{% endif %}">
|
||||||
|
{% if post.thumbnail.url.is_empty() %}
|
||||||
|
<svg viewBox="0 0 100 106" width="140" height="53" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title>Thumbnail</title>
|
||||||
|
<path d="M35,15h-15a10,10 0,0,0 0,20h25a10,10 0,0,0 10,-10m-12.5,0a10, 10 0,0,1 10, -10h25a10,10 0,0,1 0,20h-15" fill="none" stroke-width="5" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
{% else %}
|
||||||
|
<svg width="{{ post.thumbnail.width }}px" height="{{ post.thumbnail.height }}px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<image alt="Thumbnail" width="100%" height="100%" href="{{ post.thumbnail.url }}"/>
|
||||||
|
</svg>
|
||||||
|
{% endif %}
|
||||||
|
<span>{% if post.post_type == "link" %}{{ post.domain }}{% else %}{{ post.post_type }}{% endif %}</span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="post_score">{{ post.score }}<span class="label"> Upvotes</span></div>
|
||||||
|
<div class="post_footer">
|
||||||
|
<a href="{{ post.permalink }}" class="post_comments">{{ post.comments }} comments</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -8,45 +8,63 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form id="settings" action="/settings" method="POST">
|
<div id="settings">
|
||||||
<div id="prefs">
|
<form action="/settings" method="POST">
|
||||||
<p>Appearance</p>
|
<div class="prefs">
|
||||||
<div id="theme">
|
<p>Appearance</p>
|
||||||
<label for="theme">Theme:</label>
|
<div id="theme">
|
||||||
<select name="theme">
|
<label for="theme">Theme:</label>
|
||||||
{% call utils::options(prefs.theme, ["dark", "light"], "dark") %}
|
<select name="theme">
|
||||||
</select>
|
{% call utils::options(prefs.theme, ["system", "light", "dark"], "system") %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<p>Interface</p>
|
||||||
|
<div id="front_page">
|
||||||
|
<label for="front_page">Front page:</label>
|
||||||
|
<select name="front_page">
|
||||||
|
{% call utils::options(prefs.front_page, ["default", "popular", "all"], "default") %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="layout">
|
||||||
|
<label for="layout">Layout:</label>
|
||||||
|
<select name="layout">
|
||||||
|
{% call utils::options(prefs.layout, ["card", "clean", "compact"], "card") %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="wide">
|
||||||
|
<label for="wide">Wide UI:</label>
|
||||||
|
<input type="checkbox" name="wide" {% if prefs.wide == "on" %}checked{% endif %}>
|
||||||
|
</div>
|
||||||
|
<p>Content</p>
|
||||||
|
<div id="comment_sort">
|
||||||
|
<label for="comment_sort">Default comment sort:</label>
|
||||||
|
<select name="comment_sort">
|
||||||
|
{% call utils::options(prefs.comment_sort, ["confidence", "top", "new", "controversial", "old"], "confidence") %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="show_nsfw">
|
||||||
|
<label for="show_nsfw">Show NSFW posts:</label>
|
||||||
|
<input type="checkbox" name="show_nsfw" {% if prefs.show_nsfw == "on" %}checked{% endif %}>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>Interface</p>
|
<p id="settings_note"><b>Note:</b> settings are saved in browser cookies. Clearing your cookie data will reset them.</p>
|
||||||
<div id="front_page">
|
<input id="save" type="submit" value="Save">
|
||||||
<label for="front_page">Front page:</label>
|
</form>
|
||||||
<select name="front_page">
|
{% if prefs.subs.len() > 0 %}
|
||||||
{% call utils::options(prefs.front_page, ["popular", "all"], "popular") %}
|
<aside class="prefs">
|
||||||
</select>
|
<p>Subscribed Subreddits</p>
|
||||||
</div>
|
<ul id="settings_subs">
|
||||||
<div id="layout">
|
{% for sub in prefs.subs %}
|
||||||
<label for="layout">Layout:</label>
|
<li>
|
||||||
<select name="layout">
|
<span>{{ sub }}</span>
|
||||||
{% call utils::options(prefs.layout, ["card", "clean", "compact"], "clean") %}
|
<form action="/r/{{ sub }}/unsubscribe/?redirect=/settings" method="POST">
|
||||||
</select>
|
<button class="unsubscribe">Unsubscribe</button>
|
||||||
</div>
|
</form>
|
||||||
<div id="wide">
|
</li>
|
||||||
<label for="wide">Wide UI:</label>
|
{% endfor %}
|
||||||
<input type="checkbox" name="wide" {% if prefs.wide == "on" %}checked{% endif %}>
|
</ul>
|
||||||
</div>
|
</aside>
|
||||||
<p>Content</p>
|
{% endif %}
|
||||||
<div id="comment_sort">
|
</div>
|
||||||
<label for="comment_sort">Default comment sort:</label>
|
|
||||||
<select name="comment_sort">
|
{% endblock %}
|
||||||
{% call utils::options(prefs.comment_sort, ["confidence", "top", "new", "controversial", "old"], "confidence") %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div id="hide_nsfw">
|
|
||||||
<label for="hide_nsfw">Hide NSFW posts:</label>
|
|
||||||
<input type="checkbox" name="hide_nsfw" {% if prefs.hide_nsfw == "on" %}checked{% endif %}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p id="settings_note"><b>Note:</b> settings are saved in browser cookies. Clearing your cookie data will reset them.</p>
|
|
||||||
<input id="save" type="submit" value="Save">
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
{% call utils::search(["/r/", sub.name.as_str()].concat(), "") %}
|
{% call utils::search(["/r/", sub.name.as_str()].concat(), "") %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block subscriptions %}
|
||||||
|
{% call utils::sub_list(sub.name.as_str(), "wide") %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<main>
|
<main>
|
||||||
<div id="column_one">
|
<div id="column_one">
|
||||||
@ -22,53 +26,69 @@
|
|||||||
{% call utils::sort(["/r/", sub.name.as_str()].concat(), ["hot", "new", "top", "rising", "controversial"], sort.0) %}
|
{% call utils::sort(["/r/", sub.name.as_str()].concat(), ["hot", "new", "top", "rising", "controversial"], sort.0) %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if sort.0 == "top" || sort.0 == "controversial" %}<select id="timeframe" name="t">
|
{% if sort.0 == "top" || sort.0 == "controversial" %}<select id="timeframe" name="t" title="Timeframe">
|
||||||
{% call utils::options(sort.1, ["hour", "day", "week", "month", "year", "all"], "day") %}
|
{% call utils::options(sort.1, ["hour", "day", "week", "month", "year", "all"], "day") %}
|
||||||
<input id="sort_submit" type="submit" value="→">
|
</select>
|
||||||
</select>{% endif %}
|
<button id="sort_submit" class="submit">
|
||||||
|
<svg width="15" viewBox="0 0 110 100" fill="none" stroke-width="10" stroke-linecap="round">
|
||||||
|
<path d="M20 50 H100" />
|
||||||
|
<path d="M75 15 L100 50 L75 85" />
|
||||||
|
→
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div id="posts">
|
<div id="posts">
|
||||||
{% for post in posts %}
|
{% for post in posts %}
|
||||||
{% if !(post.flags.nsfw && prefs.hide_nsfw == "on") %}
|
{% if !(post.flags.nsfw && prefs.show_nsfw != "on") %}
|
||||||
<hr class="sep" />
|
<hr class="sep" />
|
||||||
<div class="post {% if post.flags.stickied %}stickied{% endif %} {% if prefs.layout == "card" && post.post_type == "image" %}card_post{% endif %}">
|
<div class="post {% if post.flags.stickied %}stickied{% endif %}">
|
||||||
<div class="post_left">
|
<p class="post_header">
|
||||||
<p class="post_score">{{ post.score }}</p>
|
<a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a>
|
||||||
{% if post.flags.nsfw %}<div class="nsfw">NSFW</div>{% endif %}
|
<span class="dot">•</span>
|
||||||
</div>
|
<a class="post_author" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
|
||||||
<div class="post_right">
|
<span class="dot">•</span>
|
||||||
<div class="post_text">
|
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
||||||
<p class="post_header">
|
</p>
|
||||||
<a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a>
|
<p class="post_title">
|
||||||
<span class="dot">•</span>
|
{% if post.flair.flair_parts.len() > 0 %}
|
||||||
<a class="post_author" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
|
<small class="post_flair" style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }};">{% call utils::render_flair(post.flair.flair_parts) %}</small>
|
||||||
<span class="dot">•</span>
|
|
||||||
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
|
||||||
</p>
|
|
||||||
<p class="post_title">
|
|
||||||
{% if post.flair.flair_parts.len() > 0 %}
|
|
||||||
<small class="post_flair" style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }}">{% call utils::render_flair(post.flair.flair_parts) %}</small>
|
|
||||||
{% endif %}
|
|
||||||
<a href="{{ post.permalink }}">{{ post.title }}</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- POST MEDIA/THUMBNAIL -->
|
|
||||||
{% if prefs.layout == "card" && post.post_type == "image" %}
|
|
||||||
<img class="post_media" src="{{ post.media }}"/>
|
|
||||||
{% else if post.post_type != "self" %}
|
|
||||||
<a class="post_thumbnail {% if post.thumbnail == "" %}no_thumbnail{% endif %}" href="{% if post.post_type == "link" %}{{ post.media }}{% else %}{{ post.permalink }}{% endif %}">
|
|
||||||
{% if post.thumbnail == "" %}
|
|
||||||
<svg viewBox="0 0 100 106" width="50" height="53" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M35,15h-15a10,10 0,0,0 0,20h25a10,10 0,0,0 10,-10m-12.5,0a10, 10 0,0,1 10, -10h25a10,10 0,0,1 0,20h-15" fill="none" stroke-width="5" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
{% else %}
|
|
||||||
<img src="{{ post.thumbnail }}">
|
|
||||||
{% endif %}
|
|
||||||
<span>{% if post.post_type == "link" %}{{ post.domain }}{% else %}{{ post.post_type }}{% endif %}</span>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<a href="{{ post.permalink }}">{{ post.title }}</a>{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
|
||||||
|
</p>
|
||||||
|
<!-- POST MEDIA/THUMBNAIL -->
|
||||||
|
{% if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "image" %}
|
||||||
|
<a href="{{ post.media.url }}" style="display:contents" >
|
||||||
|
<svg class="post_media {% if post.media.height / post.media.width < 2 %}short{% endif %}"
|
||||||
|
width="{{ post.media.width }}px"
|
||||||
|
height="{{ post.media.height }}px"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<image width="100%" height="100%" href="{{ post.media.url }}"/>
|
||||||
|
<desc>
|
||||||
|
<img alt="Post image" src="{{ post.media.url }}"/>
|
||||||
|
</dev>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
{% else if post.post_type != "self" %}
|
||||||
|
<a class="post_thumbnail {% if post.thumbnail.url.is_empty() %}no_thumbnail{% endif %}" href="{% if post.post_type == "link" %}{{ post.media.url }}{% else %}{{ post.permalink }}{% endif %}">
|
||||||
|
{% if post.thumbnail.url.is_empty() %}
|
||||||
|
<svg viewBox="0 0 100 106" width="140" height="53" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title>Thumbnail</title>
|
||||||
|
<path d="M35,15h-15a10,10 0,0,0 0,20h25a10,10 0,0,0 10,-10m-12.5,0a10, 10 0,0,1 10, -10h25a10,10 0,0,1 0,20h-15" fill="none" stroke-width="5" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
{% else %}
|
||||||
|
<svg width="{{ post.thumbnail.width }}px" height="{{ post.thumbnail.height }}px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<image alt="Thumbnail" width="100%" height="100%" href="{{ post.thumbnail.url }}"/>
|
||||||
|
</svg>
|
||||||
|
{% endif %}
|
||||||
|
<span>{% if post.post_type == "link" %}{{ post.domain }}{% else %}{{ post.post_type }}{% endif %}</span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="post_score">{{ post.score }}<span class="label"> Upvotes</span></div>
|
||||||
|
<div class="post_footer">
|
||||||
|
<a href="{{ post.permalink }}" class="post_comments">{{ post.comments }} comments</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -95,7 +115,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div id="sub_meta">
|
<div id="sub_meta">
|
||||||
<img id="sub_icon" src="{{ sub.icon }}">
|
<img id="sub_icon" src="{{ sub.icon }}" alt="Icon for r/{{ sub.name }}">
|
||||||
<p id="sub_title">{{ sub.title }}</p>
|
<p id="sub_title">{{ sub.title }}</p>
|
||||||
<p id="sub_name">r/{{ sub.name }}</p>
|
<p id="sub_name">r/{{ sub.name }}</p>
|
||||||
<p id="sub_description">{{ sub.description }}</p>
|
<p id="sub_description">{{ sub.description }}</p>
|
||||||
@ -105,6 +125,17 @@
|
|||||||
<div>{{ sub.members }}</div>
|
<div>{{ sub.members }}</div>
|
||||||
<div>{{ sub.active }}</div>
|
<div>{{ sub.active }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="sub_subscription">
|
||||||
|
{% if prefs.subs.contains(sub.name) %}
|
||||||
|
<form action="/r/{{ sub.name }}/unsubscribe" method="POST">
|
||||||
|
<button class="unsubscribe">Unsubscribe</button>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<form action="/r/{{ sub.name }}/subscribe" method="POST">
|
||||||
|
<button class="subscribe">Subscribe</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<details class="panel" id="sidebar">
|
<details class="panel" id="sidebar">
|
||||||
|
@ -7,6 +7,10 @@
|
|||||||
|
|
||||||
{% block title %}{{ user.name.replace("u/", "") }} (u/{{ user.name }}) - Libreddit{% endblock %}
|
{% block title %}{{ user.name.replace("u/", "") }} (u/{{ user.name }}) - Libreddit{% endblock %}
|
||||||
|
|
||||||
|
{% block subscriptions %}
|
||||||
|
{% call utils::sub_list("") %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<main>
|
<main>
|
||||||
<div id="column_one">
|
<div id="column_one">
|
||||||
@ -15,54 +19,66 @@
|
|||||||
{% call utils::options(sort.0, ["hot", "new", "top"], "") %}
|
{% call utils::options(sort.0, ["hot", "new", "top"], "") %}
|
||||||
</select>{% if sort.0 == "top" %}<select id="timeframe" name="t">
|
</select>{% if sort.0 == "top" %}<select id="timeframe" name="t">
|
||||||
{% call utils::options(sort.1, ["hour", "day", "week", "month", "year", "all"], "all") %}
|
{% call utils::options(sort.1, ["hour", "day", "week", "month", "year", "all"], "all") %}
|
||||||
</select>{% endif %}<input id="sort_submit" type="submit" value="→">
|
</select>{% endif %}<button id="sort_submit" class="submit">
|
||||||
|
<svg width="15" viewBox="0 0 110 100" fill="none" stroke-width="10" stroke-linecap="round">
|
||||||
|
<path d="M20 50 H100" />
|
||||||
|
<path d="M75 15 L100 50 L75 85" />
|
||||||
|
→
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div id="posts">
|
<div id="posts">
|
||||||
{% for post in posts %}
|
{% for post in posts %}
|
||||||
|
|
||||||
{% if post.flags.nsfw && prefs.hide_nsfw == "on" %}
|
{% if post.flags.nsfw && prefs.show_nsfw != "on" %}
|
||||||
{% else if post.title != "Comment" %}
|
{% else if post.title != "Comment" %}
|
||||||
<div class="post {% if post.flags.stickied %}stickied{% endif %} {% if prefs.layout == "card" && post.post_type == "image" %}card_post{% endif %}">
|
<div class="post {% if post.flags.stickied %}stickied{% endif %}">
|
||||||
<div class="post_left">
|
<p class="post_header">
|
||||||
<p class="post_score">{{ post.score }}</p>
|
<a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a>
|
||||||
{% if post.flags.nsfw %}<div class="nsfw">NSFW</div>{% endif %}
|
<span class="dot">•</span>
|
||||||
</div>
|
<a class="post_author" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
|
||||||
<div class="post_right">
|
<span class="dot">•</span>
|
||||||
<div class="post_text">
|
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
||||||
<p class="post_header">
|
</p>
|
||||||
<a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a>
|
<p class="post_title">
|
||||||
{% if post.author.flair.flair_parts.len() > 0 %}
|
{% if post.flair.flair_parts.len() > 0 %}
|
||||||
<small class="author_flair">{% call utils::render_flair(post.author.flair.flair_parts) %}</small>
|
<small class="post_flair" style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }};">{% call utils::render_flair(post.flair.flair_parts) %}</small>
|
||||||
{% endif %}
|
|
||||||
<span class="dot">•</span>
|
|
||||||
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
|
||||||
</p>
|
|
||||||
<p class="post_title">
|
|
||||||
{% if post.flair.background_color == "Comment" %}
|
|
||||||
{% else if post.flair.background_color == "" %}
|
|
||||||
{% else %}
|
|
||||||
<small class="post_flair" style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }}">{% call utils::render_flair(post.flair.flair_parts) %}</small>
|
|
||||||
{% endif %}
|
|
||||||
<a href="{{ post.permalink }}">{{ post.title }}</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- POST MEDIA/THUMBNAIL -->
|
|
||||||
{% if prefs.layout == "card" && post.post_type == "image" %}
|
|
||||||
<img class="post_media" src="{{ post.media }}"/>
|
|
||||||
{% else if post.post_type != "self" %}
|
|
||||||
<a class="post_thumbnail {% if post.thumbnail == "" %}no_thumbnail{% endif %}" href="{% if post.post_type == "link" %}{{ post.media }}{% else %}{{ post.permalink }}{% endif %}">
|
|
||||||
{% if post.thumbnail == "" %}
|
|
||||||
<svg viewBox="0 0 100 106" width="50" height="53" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M35,15h-15a10,10 0,0,0 0,20h25a10,10 0,0,0 10,-10m-12.5,0a10, 10 0,0,1 10, -10h25a10,10 0,0,1 0,20h-15" fill="none" stroke-width="5" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
{% else %}
|
|
||||||
<img src="{{ post.thumbnail }}">
|
|
||||||
{% endif %}
|
|
||||||
<span>{% if post.post_type == "link" %}{{ post.domain }}{% else %}{{ post.post_type }}{% endif %}</span>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<a href="{{ post.permalink }}">{{ post.title }}</a>{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
|
||||||
|
</p>
|
||||||
|
<!-- POST MEDIA/THUMBNAIL -->
|
||||||
|
{% if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "image" %}
|
||||||
|
<a href="{{ post.media.url }}" style="display:contents" >
|
||||||
|
<svg class="post_media {% if post.media.height / post.media.width < 2 %}short{% endif %}"
|
||||||
|
width="{{ post.media.width }}px"
|
||||||
|
height="{{ post.media.height }}px"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<image width="100%" height="100%" href="{{ post.media.url }}"/>
|
||||||
|
<desc>
|
||||||
|
<img alt="Post image" src="{{ post.media.url }}"/>
|
||||||
|
</dev>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
{% else if post.post_type != "self" %}
|
||||||
|
<a class="post_thumbnail {% if post.thumbnail.url.is_empty() %}no_thumbnail{% endif %}" href="{% if post.post_type == "link" %}{{ post.media.url }}{% else %}{{ post.permalink }}{% endif %}">
|
||||||
|
{% if post.thumbnail.url.is_empty() %}
|
||||||
|
<svg viewBox="0 0 100 106" width="140" height="53" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title>Thumbnail</title>
|
||||||
|
<path d="M35,15h-15a10,10 0,0,0 0,20h25a10,10 0,0,0 10,-10m-12.5,0a10, 10 0,0,1 10, -10h25a10,10 0,0,1 0,20h-15" fill="none" stroke-width="5" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
{% else %}
|
||||||
|
<svg width="{{ post.thumbnail.width }}px" height="{{ post.thumbnail.height }}px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<image alt="Thumbnail" width="100%" height="100%" href="{{ post.thumbnail.url }}"/>
|
||||||
|
</svg>
|
||||||
|
{% endif %}
|
||||||
|
<span>{% if post.post_type == "link" %}{{ post.domain }}{% else %}{{ post.post_type }}{% endif %}</span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="post_score">{{ post.score }}<span class="label"> Upvotes</span></div>
|
||||||
|
<div class="post_footer">
|
||||||
|
<a href="{{ post.permalink }}" class="post_comments">{{ post.comments }} comments</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -16,20 +16,39 @@
|
|||||||
|
|
||||||
{% macro search(root, search) -%}
|
{% macro search(root, search) -%}
|
||||||
<form action="{% if root != "/r/" && !root.is_empty() %}{{ root }}{% endif %}/search/" id="searchbox">
|
<form action="{% if root != "/r/" && !root.is_empty() %}{{ root }}{% endif %}/search/" id="searchbox">
|
||||||
<input id="search" type="text" name="q" placeholder="Search" value="{{ search }}">
|
<input id="search" type="text" name="q" placeholder="Search" title="Search libreddit" value="{{ search }}">
|
||||||
{% if root != "/r/" && !root.is_empty() %}
|
{% if root != "/r/" && !root.is_empty() %}
|
||||||
<div id="inside">
|
<div id="inside">
|
||||||
<input type="checkbox" name="restrict_sr" id="restrict_sr">
|
<input type="checkbox" name="restrict_sr" id="restrict_sr">
|
||||||
<label for="restrict_sr" class="search_label">in {{ root }}</label>
|
<label for="restrict_sr" class="search_label">in {{ root }}</label>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<input type="submit" value="→">
|
<button class="submit">
|
||||||
|
<svg width="15" viewBox="0 0 110 100" fill="none" stroke-width="10" stroke-linecap="round">
|
||||||
|
<path d="M20 50 H100" />
|
||||||
|
<path d="M75 15 L100 50 L75 85" />
|
||||||
|
→
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{% macro render_flair(flair) -%}
|
{% macro render_flair(flair) -%}
|
||||||
{% for flair_part in flair %}
|
{% for flair_part in flair %}
|
||||||
{% if flair_part.flair_part_type == "emoji" %}<span class="emoji" style="background-image:url('{{ flair_part.value }}')"></span>
|
{% if flair_part.flair_part_type == "emoji" %}<span class="emoji" style="background-image:url('{{ flair_part.value }}');"></span>
|
||||||
{% else if flair_part.flair_part_type == "text" %}<span>{{ flair_part.value }}</span>{% endif %}
|
{% else if flair_part.flair_part_type == "text" %}<span>{{ flair_part.value }}</span>{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro sub_list(current) -%}
|
||||||
|
{% if prefs.subs.len() > 0 %}
|
||||||
|
<details id="subscriptions">
|
||||||
|
<summary>Subscriptions</summary>
|
||||||
|
<div id="sub_list">
|
||||||
|
{% for sub in prefs.subs %}
|
||||||
|
<a href="/r/{{ sub }}" {% if sub == current %}class="selected"{% endif %}>{{ sub }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
{% endif %}
|
||||||
|
{%- endmacro %}
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
{% call utils::search(["/r/", sub.as_str()].concat(), "") %}
|
{% call utils::search(["/r/", sub.as_str()].concat(), "") %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block subscriptions %}
|
||||||
|
{% call utils::sub_list(sub.as_str()) %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<main>
|
<main>
|
||||||
<div class="panel" id="column_one">
|
<div class="panel" id="column_one">
|
||||||
|
Reference in New Issue
Block a user