Compare commits

..

26 Commits

Author SHA1 Message Date
3cf787cf98 Fix #195 2021-04-14 21:53:17 -07:00
46e22cf74e Fix certificate error on ARM #193 2021-04-15 04:50:03 +00:00
5c2e134924 Include Cargo.lock. Fixes #191 2021-04-14 21:44:16 -07:00
c6244585fa Add database.red instance. Closes #194 2021-04-15 04:35:57 +00:00
9f1ba274eb Document ARM Docker deployment in README 2021-04-10 14:31:26 -07:00
93ed1c6f0c Fix Dockerfile healthchecks 2021-04-09 22:38:13 -07:00
6ce82c36fb Use alpine only for ARM builds 2021-04-09 18:59:04 -07:00
2974d92e30 Set correct Dockerfile for ARM builds 2021-04-09 18:27:12 -07:00
34dfcb2512 Revert ARM Dockerfile 2021-04-09 17:05:20 -07:00
6b42e97bda Test rust:alpine in ARM Dockerfile 2021-04-09 16:38:44 -07:00
49bfe4d27c Create dockerfile for arm64 2021-04-09 15:50:43 -07:00
c8965ae51b Switch Docker base image to "scratch" 2021-04-09 15:24:47 -07:00
0b64a52a63 Switch Docker builds in GitHub Actions to only ARM 2021-04-09 15:07:07 -07:00
a18db1e2b7 Properly pass preview queries to media proxy 2021-04-08 22:26:03 -07:00
3b53e5be4c Fix issue templates (#181)
* Fix comments bug report

* Fix comments feature request
2021-04-06 17:46:23 +00:00
42e8351285 Enhance Issue templates (#177)
* comment out in bugreport

* Comment out in featurerequest
2021-04-06 17:32:44 +00:00
b3e4b7bfae Add user following functionality 2021-04-06 10:23:05 -07:00
4a42a25ed3 Add libreddit.silkky.cloud. Closes #175 2021-04-05 21:57:06 +00:00
2bacaa163f Bump to v0.9 2021-04-01 18:00:18 -07:00
48c3a8c0d0 Added Dracula/Nord theme (#171)
* Added Dracula theme

* Updated accent and added Nord theme

* Updated accent and added Nord theme

* Added official foreground colors
2021-04-02 00:56:28 +00:00
c23d2dc50b Re-add unprivileged user to Dockerfile 2021-04-01 12:09:08 -07:00
46dbd88d91 New alpine-based Dockerfile with healthcheck 2021-03-31 13:05:22 -07:00
f0f484288e Fix server.rs function name 2021-03-31 13:03:44 -07:00
90d39b121f Example docker-compose 2021-03-31 13:03:18 -07:00
44dee302c9 Publish SHA512 checksums for future releases 2021-03-27 20:11:05 +00:00
c7f9386c01 Fix #169 2021-03-27 13:03:13 -07:00
21 changed files with 1662 additions and 64 deletions

View File

@ -1,24 +1,33 @@
--- ---
name: Bug report name: 🐛 Bug report
about: Create a report to help us improve about: Create a report to help us improve
title: Bug Report | [title] title: ''
labels: bug labels: bug
assignees: '' assignees: ''
--- ---
**Describe the bug** ## Describe the bug
A clear and concise description of what the bug is. <!--
A clear and concise description of what the bug is.
-->
**To reproduce** ## To reproduce
<!--
Steps to reproduce the behavior: Steps to reproduce the behavior:
1. Go to '...' 1. Go to '...'
2. Click on '....' 2. Click on '....'
3. Scroll down to '....' 3. Scroll down to '....'
4. See error 4. See error
-->
**Expected behavior** ## Expected behavior
A clear and concise description of what you expected to happen. <!--
A clear and concise description of what you expected to happen.
-->
**Additional context** ## Additional context
Add any other context about the problem here. <!--
Add any other context about the problem here.
-->

View File

@ -1,20 +1,28 @@
--- ---
name: Feature request name: 💡 Feature request
about: Suggest an idea for this project about: Suggest an idea for this project
title: Feature Request | [title] title: ''
labels: enhancement labels: enhancement
assignees: '' assignees: ''
--- ---
**Is your feature request related to a problem? Please describe.** ## Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] <!--
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-->
**Describe the solution you'd like** ## Describe the solution you'd like
A clear and concise description of what you want to happen. <!--
A clear and concise description of what you want to happen.
-->
**Describe alternatives you've considered** ## Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered. <!--
A clear and concise description of any alternative solutions or features you've considered.
-->
**Additional context** ## Additional context
Add any other context or screenshots about the feature request here. <!--
Add any other context or screenshots about the feature request here.
-->

View File

@ -1,4 +1,4 @@
name: Docker Multi-Architecture Build name: Docker ARM Build
on: on:
push: push:
@ -30,7 +30,7 @@ jobs:
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile.arm64
platforms: linux/amd64,linux/arm64 platforms: linux/arm64
push: true push: true
tags: spikecodes/libreddit:latest tags: spikecodes/libreddit:arm

View File

@ -33,6 +33,9 @@ jobs:
run: | run: |
echo "::set-output name=version::$(cargo metadata --format-version 1 --no-deps | jq .packages[0].version -r | sed 's/^/v/')" echo "::set-output name=version::$(cargo metadata --format-version 1 --no-deps | jq .packages[0].version -r | sed 's/^/v/')"
echo "::set-output name=tag::${GITHUB_REF#refs/*/}" echo "::set-output name=tag::${GITHUB_REF#refs/*/}"
- name: Calculate SHA512 checksum
run: sha512sum target/release/libreddit > libreddit.sha512
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
@ -40,7 +43,9 @@ jobs:
tag_name: ${{ steps.version.outputs.version }} tag_name: ${{ steps.version.outputs.version }}
name: ${{ steps.version.outputs.version }} - NAME name: ${{ steps.version.outputs.version }} - NAME
draft: true draft: true
files: target/release/libreddit files: |
target/release/libreddit
libreddit.sha512
body: | body: |
- CHANGES - CHANGES

3
.gitignore vendored
View File

@ -1,2 +1 @@
/target /target
Cargo.lock

1460
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ name = "libreddit"
description = " Alternative private front-end to Reddit" description = " Alternative private front-end to Reddit"
license = "AGPL-3.0" license = "AGPL-3.0"
repository = "https://github.com/spikecodes/libreddit" repository = "https://github.com/spikecodes/libreddit"
version = "0.8.1" version = "0.10.3"
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"] authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
edition = "2018" edition = "2018"
@ -20,6 +20,6 @@ hyper = { version = "0.14.5", features = ["full"] }
hyper-rustls = "0.22.1" hyper-rustls = "0.22.1"
route-recognizer = "0.3.0" route-recognizer = "0.3.0"
serde_json = "1.0.64" serde_json = "1.0.64"
tokio = { version = "1.4.0", features = ["full"] } tokio = { version = "1.5.0", features = ["full"] }
time = "0.2.26" time = "0.2.26"
url = "2.2.1" url = "2.2.1"

View File

@ -1,17 +1,36 @@
FROM rust:latest as builder ####################################################################################################
## Builder
####################################################################################################
FROM rust:alpine AS builder
RUN apk add --no-cache musl-dev
WORKDIR /libreddit
WORKDIR /usr/src/libreddit
COPY . . COPY . .
RUN cargo install --path .
RUN cargo build --target x86_64-unknown-linux-musl --release
FROM debian:buster-slim ####################################################################################################
## Final image
####################################################################################################
FROM alpine:latest
RUN apt-get update && apt-get install -y libcurl4 && rm -rf /var/lib/apt/lists/* # Import ca-certificates from builder
COPY --from=builder /usr/local/cargo/bin/libreddit /usr/local/bin/libreddit COPY --from=builder /usr/share/ca-certificates /usr/share/ca-certificates
RUN useradd --system --user-group --home-dir /nonexistent --no-create-home --shell /usr/sbin/nologin libreddit COPY --from=builder /etc/ssl/certs /etc/ssl/certs
# Copy our build
COPY --from=builder /libreddit/target/x86_64-unknown-linux-musl/release/libreddit /usr/local/bin/libreddit
# Use an unprivileged user.
RUN adduser --home /nonexistent --no-create-home --disabled-password libreddit
USER libreddit USER libreddit
# Tell Docker to expose port 8080
EXPOSE 8080 EXPOSE 8080
CMD ["libreddit"] # Run a healthcheck every minute to make sure Libreddit is functional
HEALTHCHECK --interval=1m --timeout=3s CMD wget --spider --q http://localhost:8080/settings || exit 1
CMD ["libreddit"]

36
Dockerfile.arm64 Normal file
View File

@ -0,0 +1,36 @@
####################################################################################################
## Builder
####################################################################################################
FROM rust:alpine AS builder
RUN apk add --no-cache g++
WORKDIR /usr/src/libreddit
COPY . .
RUN cargo install --path .
####################################################################################################
## Final image
####################################################################################################
FROM alpine:latest
# Import ca-certificates from builder
COPY --from=builder /usr/share/ca-certificates /usr/share/ca-certificates
COPY --from=builder /etc/ssl/certs /etc/ssl/certs
# Copy our build
COPY --from=builder /usr/local/cargo/bin/libreddit /usr/local/bin/libreddit
# Use an unprivileged user.
RUN adduser --home /nonexistent --no-create-home --disabled-password libreddit
USER libreddit
# Tell Docker to expose port 8080
EXPOSE 8080
# Run a healthcheck every minute to make sure Libreddit is functional
HEALTHCHECK --interval=1m --timeout=3s CMD wget --spider --q http://localhost:8080/settings || exit 1
CMD ["libreddit"]

View File

@ -36,6 +36,8 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new)
| [libreddit.40two.app](https://libreddit.40two.app) | 🇳🇱 NL | | | [libreddit.40two.app](https://libreddit.40two.app) | 🇳🇱 NL | |
| [reddit.invak.id](https://reddit.invak.id) | 🇧🇬 BG | | | [reddit.invak.id](https://reddit.invak.id) | 🇧🇬 BG | |
| [reddit.phii.me](https://reddit.phii.me) | 🇺🇸 US | | | [reddit.phii.me](https://reddit.phii.me) | 🇺🇸 US | |
| [libreddit.silkky.cloud](https://libreddit.silkky.cloud) | 🇫🇮 FI | |
| [libreddit.database.red](https://libreddit.database.red) | 🇺🇸 US | ✅ |
| [spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion](http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion) | 🇮🇳 IN | | | [spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion](http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion) | 🇮🇳 IN | |
| [fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion](http://fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion) | 🇩🇪 DE | | | [fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion](http://fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion) | 🇩🇪 DE | |
| [libreddit.himiko7xl2skojc6odi7hykl626gt4qki3vxdbv33u2u3af76d6k32ad.onion](http://libreddit.himiko7xl2skojc6odi7hykl626gt4qki3vxdbv33u2u3af76d6k32ad.onion) | 🇫🇮 FI | | | [libreddit.himiko7xl2skojc6odi7hykl626gt4qki3vxdbv33u2u3af76d6k32ad.onion](http://libreddit.himiko7xl2skojc6odi7hykl626gt4qki3vxdbv33u2u3af76d6k32ad.onion) | 🇫🇮 FI | |
@ -156,6 +158,8 @@ docker pull spikecodes/libreddit
docker run -d --name libreddit -p 80:8080 spikecodes/libreddit docker run -d --name libreddit -p 80:8080 spikecodes/libreddit
``` ```
To deploy on `arm64` platforms, simply replace `spikecodes/libreddit` in the commands above with `spikecodes/libreddit:arm`.
## 3) 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).

13
docker-compose.yml Normal file
View File

@ -0,0 +1,13 @@
version: "3.8"
services:
web:
build: .
restart: always
container_name: "libreddit"
ports:
- 8080:8080
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/settings"]
interval: 5m
timeout: 3s

View File

@ -7,7 +7,7 @@ use std::{result::Result, str::FromStr};
use crate::server::RequestExt; use crate::server::RequestExt;
pub async fn proxy(req: Request<Body>, format: &str) -> Result<Response<Body>, String> { pub async fn proxy(req: Request<Body>, format: &str) -> Result<Response<Body>, String> {
let mut url = format.to_string(); let mut url = format!("{}?{}", format, req.uri().query().unwrap_or_default());
for (name, value) in req.params().iter() { for (name, value) in req.params().iter() {
url = url.replace(&format!("{{{}}}", name), value); url = url.replace(&format!("{{{}}}", name), value);

View File

@ -164,7 +164,7 @@ async fn main() {
app.at("/img/:id").get(|r| proxy(r, "https://i.redd.it/{id}").boxed()); app.at("/img/:id").get(|r| proxy(r, "https://i.redd.it/{id}").boxed());
app.at("/thumb/:point/:id").get(|r| proxy(r, "https://{point}.thumbs.redditmedia.com/{id}").boxed()); app.at("/thumb/:point/:id").get(|r| proxy(r, "https://{point}.thumbs.redditmedia.com/{id}").boxed());
app.at("/emoji/:id/:name").get(|r| proxy(r, "https://emoji.redditmedia.com/{id}/{name}").boxed()); app.at("/emoji/:id/:name").get(|r| proxy(r, "https://emoji.redditmedia.com/{id}/{name}").boxed());
app.at("/preview/:loc/:id/:query").get(|r| proxy(r, "https://{loc}view.redd.it/{id}?{query}").boxed()); app.at("/preview/:loc/:id").get(|r| proxy(r, "https://{loc}view.redd.it/{id}").boxed());
app.at("/style/*path").get(|r| proxy(r, "https://styles.redditmedia.com/{path}").boxed()); app.at("/style/*path").get(|r| proxy(r, "https://styles.redditmedia.com/{path}").boxed());
app.at("/static/*path").get(|r| proxy(r, "https://www.redditstatic.com/{path}").boxed()); app.at("/static/*path").get(|r| proxy(r, "https://www.redditstatic.com/{path}").boxed());
@ -205,10 +205,10 @@ async fn main() {
.at("/r/:sub/w") .at("/r/:sub/w")
.get(|r| async move { Ok(redirect(format!("/r/{}/wiki", r.param("sub").unwrap_or_default()))) }.boxed()); .get(|r| async move { Ok(redirect(format!("/r/{}/wiki", r.param("sub").unwrap_or_default()))) }.boxed());
app app
.at("/r/:sub/w/:page") .at("/r/:sub/w/*page")
.get(|r| async move { Ok(redirect(format!("/r/{}/wiki/{}", r.param("sub").unwrap_or_default(), r.param("wiki").unwrap_or_default()))) }.boxed()); .get(|r| async move { Ok(redirect(format!("/r/{}/wiki/{}", r.param("sub").unwrap_or_default(), r.param("wiki").unwrap_or_default()))) }.boxed());
app.at("/r/:sub/wiki").get(|r| subreddit::wiki(r).boxed()); app.at("/r/:sub/wiki").get(|r| subreddit::wiki(r).boxed());
app.at("/r/:sub/wiki/:page").get(|r| subreddit::wiki(r).boxed()); app.at("/r/:sub/wiki/*page").get(|r| subreddit::wiki(r).boxed());
app.at("/r/:sub/about/sidebar").get(|r| subreddit::sidebar(r).boxed()); app.at("/r/:sub/about/sidebar").get(|r| subreddit::sidebar(r).boxed());
@ -223,10 +223,10 @@ async fn main() {
// View Reddit wiki // View Reddit wiki
app.at("/w").get(|_| async { Ok(redirect("/wiki".to_string())) }.boxed()); app.at("/w").get(|_| async { Ok(redirect("/wiki".to_string())) }.boxed());
app app
.at("/w/:page") .at("/w/*page")
.get(|r| async move { Ok(redirect(format!("/wiki/{}", r.param("page").unwrap_or_default()))) }.boxed()); .get(|r| async move { Ok(redirect(format!("/wiki/{}", r.param("page").unwrap_or_default()))) }.boxed());
app.at("/wiki").get(|r| subreddit::wiki(r).boxed()); app.at("/wiki").get(|r| subreddit::wiki(r).boxed());
app.at("/wiki/:page").get(|r| subreddit::wiki(r).boxed()); app.at("/wiki/*page").get(|r| subreddit::wiki(r).boxed());
// Search all of Reddit // Search all of Reddit
app.at("/search").get(|r| search::find(r).boxed()); app.at("/search").get(|r| search::find(r).boxed());

View File

@ -171,9 +171,9 @@ impl Server {
parammed.set_params(found.params().to_owned()); parammed.set_params(found.params().to_owned());
// Run the route's function // Run the route's function
let yeet = (found.handler().to_owned().to_owned())(parammed); let func = (found.handler().to_owned().to_owned())(parammed);
async move { async move {
let res: Result<Response<Body>, String> = yeet.await; let res: Result<Response<Body>, String> = func.await;
// Add default headers to response // Add default headers to response
res.map(|mut response| { res.map(|mut response| {
response.headers_mut().extend(headers); response.headers_mut().extend(headers);

View File

@ -50,7 +50,7 @@ pub async fn set(req: Request<Body>) -> Result<Response<Body>, String> {
let mut res = redirect("/settings".to_string()); let mut res = redirect("/settings".to_string());
let names = vec!["theme", "front_page", "layout", "wide", "comment_sort", "post_sort", "show_nsfw", "subscriptions"]; let names = vec!["theme", "front_page", "layout", "wide", "comment_sort", "post_sort", "show_nsfw"];
for name in names { for name in names {
match form.get(name) { match form.get(name) {

View File

@ -35,18 +35,19 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
let post_sort = req.cookie("post_sort").map_or_else(|| "hot".to_string(), |c| c.value().to_string()); let post_sort = req.cookie("post_sort").map_or_else(|| "hot".to_string(), |c| c.value().to_string());
let sort = req.param("sort").unwrap_or_else(|| req.param("id").unwrap_or(post_sort)); let sort = req.param("sort").unwrap_or_else(|| req.param("id").unwrap_or(post_sort));
let sub = req.param("sub").map_or( let sub = req.param("sub").unwrap_or(if front_page == "default" || front_page.is_empty() {
if front_page == "default" || front_page.is_empty() { if subscribed.is_empty() {
if subscribed.is_empty() { "popular".to_string()
"popular".to_string()
} else {
subscribed.to_owned()
}
} else { } else {
front_page.to_owned() subscribed.to_owned()
}, }
String::from, } else {
); front_page.to_owned()
});
if req.param("sub").is_some() && sub.starts_with("u_") {
return Ok(redirect(["/user/", &sub[2..]].concat()));
}
let path = format!("/r/{}/{}.json?{}&raw_json=1", sub, sort, req.uri().query().unwrap_or_default()); let path = format!("/r/{}/{}.json?{}&raw_json=1", sub, sort, req.uri().query().unwrap_or_default());

View File

@ -431,8 +431,8 @@ pub fn format_url(url: &str) -> String {
"a.thumbs.redditmedia.com" => capture(r"https://a\.thumbs\.redditmedia\.com/(.*)", "/thumb/a/", 1), "a.thumbs.redditmedia.com" => capture(r"https://a\.thumbs\.redditmedia\.com/(.*)", "/thumb/a/", 1),
"b.thumbs.redditmedia.com" => capture(r"https://b\.thumbs\.redditmedia\.com/(.*)", "/thumb/b/", 1), "b.thumbs.redditmedia.com" => capture(r"https://b\.thumbs\.redditmedia\.com/(.*)", "/thumb/b/", 1),
"emoji.redditmedia.com" => capture(r"https://emoji\.redditmedia\.com/(.*)/(.*)", "/emoji/", 2), "emoji.redditmedia.com" => capture(r"https://emoji\.redditmedia\.com/(.*)/(.*)", "/emoji/", 2),
"preview.redd.it" => capture(r"https://preview\.redd\.it/(.*)\?(.*)", "/preview/pre/", 2), "preview.redd.it" => capture(r"https://preview\.redd\.it/(.*)", "/preview/pre/", 1),
"external-preview.redd.it" => capture(r"https://external\-preview\.redd\.it/(.*)\?(.*)", "/preview/external-pre/", 2), "external-preview.redd.it" => capture(r"https://external\-preview\.redd\.it/(.*)", "/preview/external-pre/", 1),
"styles.redditmedia.com" => capture(r"https://styles\.redditmedia\.com/(.*)", "/style/", 1), "styles.redditmedia.com" => capture(r"https://styles\.redditmedia\.com/(.*)", "/style/", 1),
"www.redditstatic.com" => capture(r"https://www\.redditstatic\.com/(.*)", "/static/", 1), "www.redditstatic.com" => capture(r"https://www\.redditstatic\.com/(.*)", "/static/", 1),
_ => String::new(), _ => String::new(),

View File

@ -65,7 +65,33 @@
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1); --shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
} }
/* Dracula theme setting */
.dracula {
--accent: #bd93f9;
--green: #50fa7b;
--text: #f8f8f2;
--foreground: #3d4051;
--background: #282a36;
--outside: #44475a;
--post: #44475a;
--panel-border: 2px solid #44475a;
--highlighted: #4e5267;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Nord theme setting */
.nord {
--accent: #8fbcbb;
--green: #a3be8c;
--text: #eceff4;
--foreground: #3b4252;
--background: #2e3440;
--outside: #434c5e;
--post: #434c5e;
--panel-border: 2px solid #4c566a;
--highlighted: #3b4252;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* General */ /* General */
::selection { ::selection {
@ -281,7 +307,7 @@ aside {
/* Subscriptions */ /* Subscriptions */
#sub_subscription { #sub_subscription, #user_subscription {
margin-top: 20px; margin-top: 20px;
} }
@ -1231,4 +1257,4 @@ td, th {
padding: 7px 0px; padding: 7px 0px;
margin-right: -5px; margin-right: -5px;
} }
} }

View File

@ -15,7 +15,7 @@
<div id="theme"> <div id="theme">
<label for="theme">Theme:</label> <label for="theme">Theme:</label>
<select name="theme"> <select name="theme">
{% call utils::options(prefs.theme, ["system", "light", "dark", "black"], "system") %} {% call utils::options(prefs.theme, ["system", "light", "dark", "black", "dracula", "nord"], "system") %}
</select> </select>
</div> </div>
<p>Interface</p> <p>Interface</p>
@ -57,10 +57,10 @@
</form> </form>
{% if prefs.subscriptions.len() > 0 %} {% if prefs.subscriptions.len() > 0 %}
<div class="prefs" id="settings_subs"> <div class="prefs" id="settings_subs">
<p>Subscribed Subreddits</p> <p>Subscribed Feeds</p>
{% for sub in prefs.subscriptions %} {% for sub in prefs.subscriptions %}
<div> <div>
<span>{{ sub }}</span> <span>{% if sub.starts_with("u_") -%}{{ format!("u/{}", &sub[2..]) }}{% else -%}{{ format!("r/{}", sub) }}{% endif -%}</span>
<form action="/r/{{ sub }}/unsubscribe/?redirect=settings" method="POST"> <form action="/r/{{ sub }}/unsubscribe/?redirect=settings" method="POST">
<button class="unsubscribe">Unsubscribe</button> <button class="unsubscribe">Unsubscribe</button>
</form> </form>

View File

@ -75,6 +75,18 @@
<div>{{ user.karma }}</div> <div>{{ user.karma }}</div>
<div>{{ user.created }}</div> <div>{{ user.created }}</div>
</div> </div>
<div id="user_subscription">
{% let name = ["u_", user.name.as_str()].join("") %}
{% if prefs.subscriptions.contains(name) %}
<form action="/r/u_{{ user.name }}/unsubscribe" method="POST">
<button class="unsubscribe">Unfollow</button>
</form>
{% else %}
<form action="/r/u_{{ user.name }}/subscribe" method="POST">
<button class="subscribe">Follow</button>
</form>
{% endif %}
</div>
</div> </div>
</aside> </aside>
</main> </main>

View File

@ -58,7 +58,13 @@
{% macro post_in_list(post) -%} {% macro post_in_list(post) -%}
<div class="post {% if post.flags.stickied %}stickied{% endif %}"> <div class="post {% if post.flags.stickied %}stickied{% endif %}">
<p class="post_header"> <p class="post_header">
<a class="post_subreddit" href="/r/{{ post.community }}">r/{{ post.community }}</a> {% let community -%}
{% if post.community.starts_with("u_") -%}
{% let community = format!("u/{}", &post.community[2..]) -%}
{% else -%}
{% let community = format!("r/{}", post.community) -%}
{% endif -%}
<a class="post_subreddit" href="/{{ community }}">{{ community }}</a>
<span class="dot">&bull;</span> <span class="dot">&bull;</span>
<a class="post_author" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a> <a class="post_author" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
<span class="dot">&bull;</span> <span class="dot">&bull;</span>