Compare commits
34 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a96b5b1cd2 | ||
|
|
d8292bb9be | ||
|
|
3e1e3868f4 | ||
|
|
a91b08547b | ||
|
|
624b7e4847 | ||
|
|
c935ffabea | ||
|
|
f5a423f2d5 | ||
|
|
c5a53c79da | ||
|
|
669749c12e | ||
|
|
dd58cb7e55 | ||
|
|
2bea76c7f0 | ||
|
|
a689604470 | ||
|
|
6c80f67535 | ||
|
|
463b3fe49d | ||
|
|
3fe31bd5b3 | ||
|
|
1858f1aec0 | ||
|
|
dd4f76a393 | ||
|
|
f7c2910b07 | ||
|
|
556cc785f5 | ||
|
|
d5a96181d2 | ||
|
|
3ebf9b3793 | ||
|
|
3ce346d815 | ||
|
|
9525c1ff4d | ||
|
|
b6ce673292 | ||
|
|
ed7d9422d7 | ||
|
|
5d778161ec | ||
|
|
1eec1a7274 | ||
|
|
8c7aabab72 | ||
|
|
0f4242e61e | ||
|
|
9782144dcb | ||
|
|
23d08a730c | ||
|
|
2d8a3d2315 | ||
|
|
1dd8476fae | ||
|
|
8d31cbada5 |
26 changed files with 954 additions and 276 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
mlmym
|
mlmym
|
||||||
|
VERSION
|
||||||
*.toml
|
*.toml
|
||||||
*.txt
|
*.txt
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ WORKDIR /app
|
||||||
COPY go.* ./
|
COPY go.* ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
COPY . ./
|
COPY . ./
|
||||||
|
RUN git describe --tag > VERSION
|
||||||
RUN go build -v -o mlmym
|
RUN go build -v -o mlmym
|
||||||
|
|
||||||
FROM debian:bullseye-slim
|
FROM debian:bullseye-slim
|
||||||
|
|
@ -14,4 +15,5 @@ RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -
|
||||||
COPY --from=builder /app/mlmym /app/mlmym
|
COPY --from=builder /app/mlmym /app/mlmym
|
||||||
COPY --from=builder /app/templates /app/templates
|
COPY --from=builder /app/templates /app/templates
|
||||||
COPY --from=builder /app/public /app/public
|
COPY --from=builder /app/public /app/public
|
||||||
|
COPY --from=builder /app/VERSION /app/VERSION
|
||||||
CMD ["./mlmym", "--addr", "0.0.0.0:8080"]
|
CMD ["./mlmym", "--addr", "0.0.0.0:8080"]
|
||||||
|
|
|
||||||
23
Makefile
23
Makefile
|
|
@ -1,17 +1,18 @@
|
||||||
.PHONY: dev reload serve style
|
.PHONY: dev reload serve VERSION
|
||||||
|
|
||||||
all:
|
all: mlmym
|
||||||
$(MAKE) -j3 --no-print-directory dev
|
|
||||||
|
|
||||||
dev: reload serve style
|
mlmym: VERSION
|
||||||
|
go build -v -o mlmym
|
||||||
|
|
||||||
|
dev:
|
||||||
|
$(MAKE) -j2 --no-print-directory reload serve
|
||||||
|
|
||||||
reload:
|
reload:
|
||||||
#websocketd --port=8080 watchexec -w public echo reload &>/dev/null
|
websocketd --loglevel=fatal --port=8009 watchexec --no-vcs-ignore -e html,css,js 'echo "$$WATCHEXEC_WRITTEN_PATH"'
|
||||||
websocketd --loglevel=fatal --port=8009 watchexec --no-vcs-ignore -e html,css,js -d 500 -w public 'echo "$$WATCHEXEC_WRITTEN_PATH"'
|
|
||||||
|
|
||||||
serve:
|
VERSION:
|
||||||
#python -m http.server --directory ./public 8081 &>/dev/null
|
git describe --tag > $@
|
||||||
watchexec -e go -r "go run . --addr 0.0.0.0:8008 -w"
|
|
||||||
|
|
||||||
style:
|
serve: VERSION
|
||||||
npm run watchcss > /dev/null 2>&1
|
DEBUG=true watchexec --no-vcs-ignore -e go -r "go run . --addr 0.0.0.0:8008 -w"
|
||||||
|
|
|
||||||
13
README.md
13
README.md
|
|
@ -3,14 +3,23 @@ a familiar desktop experience for [lemmy](https://join-lemmy.org).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### deployment
|
## deployment
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it -p "8080:8080" ghcr.io/rystaf/mlmym:latest
|
docker run -it -p "8080:8080" ghcr.io/rystaf/mlmym:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### config
|
## config
|
||||||
Set the environment variable `LEMMY_DOMAIN` to run in single instance mode
|
Set the environment variable `LEMMY_DOMAIN` to run in single instance mode
|
||||||
```bash
|
```bash
|
||||||
docker run -it -e LEMMY_DOMAIN='lemmydomain.com' -p "8080:8080" ghcr.io/rystaf/mlmym:latest
|
docker run -it -e LEMMY_DOMAIN='lemmydomain.com' -p "8080:8080" ghcr.io/rystaf/mlmym:latest
|
||||||
```
|
```
|
||||||
|
#### default user settings
|
||||||
|
| environment variable | default |
|
||||||
|
| -------------------- | ------- |
|
||||||
|
| DARK | false |
|
||||||
|
| HIDE_THUMBNAILS | false |
|
||||||
|
| LISTING | All |
|
||||||
|
| SORT | Hot |
|
||||||
|
| COMMENT_SORT | Hot |
|
||||||
|
|
||||||
|
|
|
||||||
24
go.mod
24
go.mod
|
|
@ -3,16 +3,16 @@ module mlmym
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/julienschmidt/httprouter v1.3.0
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/k3a/html2text v1.2.1
|
||||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
github.com/rystaf/go-lemmy v0.0.0-20230720221045-c6d79b98e968
|
||||||
github.com/gorilla/sessions v1.2.1 // indirect
|
github.com/yuin/goldmark v1.5.4
|
||||||
github.com/gorilla/websocket v1.4.2 // indirect
|
golang.org/x/text v0.10.0
|
||||||
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
)
|
||||||
github.com/k3a/html2text v1.2.1 // indirect
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230704005320-c4b010dd339b // indirect
|
require (
|
||||||
github.com/yuin/goldmark v1.5.4 // indirect
|
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
||||||
go.elara.ws/go-lemmy v0.17.3 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
golang.org/x/text v0.10.0 // indirect
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
31
go.sum
31
go.sum
|
|
@ -2,45 +2,28 @@ github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+M
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
|
||||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
|
||||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/k3a/html2text v1.2.1 h1:nvnKgBvBR/myqrwfLuiqecUtaK1lB9hGziIJKatNFVY=
|
github.com/k3a/html2text v1.2.1 h1:nvnKgBvBR/myqrwfLuiqecUtaK1lB9hGziIJKatNFVY=
|
||||||
github.com/k3a/html2text v1.2.1/go.mod h1:ieEXykM67iT8lTvEWBh6fhpH4B23kB9OMKPdIBmgUqA=
|
github.com/k3a/html2text v1.2.1/go.mod h1:ieEXykM67iT8lTvEWBh6fhpH4B23kB9OMKPdIBmgUqA=
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230622213726-c394de37235c h1:VxOcsDMWaqoBKbhoiSBxPl1zZ62YZ/VAW2nxlBRJiow=
|
github.com/rystaf/go-lemmy v0.0.0-20230720221045-c6d79b98e968 h1:wfyB6wQzYMH2U8xQvdamExbyCyPhe4o8HP47FMZM5Jk=
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230622213726-c394de37235c/go.mod h1:nRSkTD+ARAHXtqlSPdf5q3hjHLP1ALsS1m5D3o86o+4=
|
github.com/rystaf/go-lemmy v0.0.0-20230720221045-c6d79b98e968/go.mod h1:nRSkTD+ARAHXtqlSPdf5q3hjHLP1ALsS1m5D3o86o+4=
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230622214853-5f2ab0756865 h1:xitFpcTOSP8RlZWR569yY75B2/7WX08rQQVG+0Mi4SA=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230622214853-5f2ab0756865/go.mod h1:nRSkTD+ARAHXtqlSPdf5q3hjHLP1ALsS1m5D3o86o+4=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230622215253-d38b61ec174f h1:EueAC5v+8oX9xK9bT36Tpgbz+c66wUZx5zmyxePurbw=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230622215253-d38b61ec174f/go.mod h1:nRSkTD+ARAHXtqlSPdf5q3hjHLP1ALsS1m5D3o86o+4=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230622222647-983d49e1d285 h1:tihBOF3ejTXzYVftaflwqRAXnaY4W9q3iNiE3YMF+D8=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230622222647-983d49e1d285/go.mod h1:nRSkTD+ARAHXtqlSPdf5q3hjHLP1ALsS1m5D3o86o+4=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230622230518-ee2cfdf288a4 h1:++T5SoZzghtfNJprWlXiRSpPPdnMSSZgIWWAnPoGx/w=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230622230518-ee2cfdf288a4/go.mod h1:nRSkTD+ARAHXtqlSPdf5q3hjHLP1ALsS1m5D3o86o+4=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230623185656-962f9bf8359d h1:ORS2KIBuT+wBn4wJncF1SoLDCVCAUPHASHpQ+Y3TnRI=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230623185656-962f9bf8359d/go.mod h1:nRSkTD+ARAHXtqlSPdf5q3hjHLP1ALsS1m5D3o86o+4=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230623191111-7ff8c74b1935 h1:zmzUz6PGRB8yQTT6BRaZNTgNlrk6L7e72dzTnWJTw+I=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230623191111-7ff8c74b1935/go.mod h1:nRSkTD+ARAHXtqlSPdf5q3hjHLP1ALsS1m5D3o86o+4=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230623191350-f39e3c8bdcb5 h1:MoI87uid2KqpLdUMZGK2HBOuxJMnPOJaar/4Og2PshM=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230623191350-f39e3c8bdcb5/go.mod h1:nRSkTD+ARAHXtqlSPdf5q3hjHLP1ALsS1m5D3o86o+4=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230704005320-c4b010dd339b h1:6z+gOUUvKwKQfgqEbxXS229gjr5V3HYg9bYbL9VHFdQ=
|
|
||||||
github.com/rystaf/go-lemmy v0.0.0-20230704005320-c4b010dd339b/go.mod h1:nRSkTD+ARAHXtqlSPdf5q3hjHLP1ALsS1m5D3o86o+4=
|
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
|
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
|
||||||
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.elara.ws/go-lemmy v0.17.3 h1:644k23BS2xqKJHJ9cHd8eyt1INpb5myqsBQQL2chBiA=
|
|
||||||
go.elara.ws/go-lemmy v0.17.3/go.mod h1:rurQND/HT3yWfX/T4w+hb6vEwRAeAlV+9bSGFkkx5rA=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
|
|
||||||
45
main.go
45
main.go
|
|
@ -7,12 +7,14 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
"github.com/yuin/goldmark/extension"
|
"github.com/yuin/goldmark/extension"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var version string
|
||||||
var watch = flag.Bool("w", false, "watch for file changes")
|
var watch = flag.Bool("w", false, "watch for file changes")
|
||||||
var addr = flag.String("addr", ":80", "http service address")
|
var addr = flag.String("addr", ":80", "http service address")
|
||||||
var md goldmark.Markdown
|
var md goldmark.Markdown
|
||||||
|
|
@ -54,7 +56,7 @@ func init() {
|
||||||
))
|
))
|
||||||
templates = make(map[string]*template.Template)
|
templates = make(map[string]*template.Template)
|
||||||
if !*watch {
|
if !*watch {
|
||||||
for _, name := range []string{"index.html", "login.html", "frontpage.html", "root.html", "settings.html", "xhr.html"} {
|
for _, name := range []string{"index.html", "login.html", "frontpage.html", "root.html", "settings.html", "xhr.html", "create_comment.html"} {
|
||||||
t := template.New(name).Funcs(funcMap)
|
t := template.New(name).Funcs(funcMap)
|
||||||
glob, err := t.ParseGlob("templates/*")
|
glob, err := t.ParseGlob("templates/*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -64,6 +66,47 @@ func init() {
|
||||||
templates[name] = glob
|
templates[name] = glob
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if os.Getenv("DEBUG") != "" {
|
||||||
|
test()
|
||||||
|
}
|
||||||
|
if data, err := os.ReadFile("VERSION"); err == nil {
|
||||||
|
version = string(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func test() {
|
||||||
|
links := [][]string{
|
||||||
|
[]string{"https://lemmy.local/u/dude", "/lemmy.local/u/dude", "/u/dude"},
|
||||||
|
[]string{"https://lemmy.local/u/dude@lemmy.local", "/lemmy.local/u/dude", "/u/dude"},
|
||||||
|
[]string{"/u/dude", "/lemmy.local/u/dude", "/u/dude"},
|
||||||
|
[]string{"/u/dude@lemmy.world", "/lemmy.local/u/dude@lemmy.world", "/u/dude@lemmy.world"},
|
||||||
|
[]string{"/u/dude@lemmy.local", "/lemmy.local/u/dude", "/u/dude"},
|
||||||
|
[]string{"https://lemmy.world/c/dude", "/lemmy.local/c/dude@lemmy.world", "/c/dude@lemmy.world"},
|
||||||
|
[]string{"https://lemmy.world/u/dude", "/lemmy.local/u/dude@lemmy.world", "/u/dude@lemmy.world"},
|
||||||
|
[]string{"https://lemmy.world/u/dude@lemmy.world", "/lemmy.local/u/dude@lemmy.world", "/u/dude@lemmy.world"},
|
||||||
|
[]string{"https://lemmy.world/post/123", "/lemmy.local/post/123@lemmy.world", "/post/123@lemmy.world"},
|
||||||
|
[]string{"https://lemmy.world/post/123#123", "https://lemmy.world/post/123#123", "https://lemmy.world/post/123#123"},
|
||||||
|
[]string{"/post/123", "/lemmy.local/post/123", "/post/123"},
|
||||||
|
[]string{"/comment/123", "/lemmy.local/comment/123", "/comment/123"},
|
||||||
|
[]string{"https://lemmy.local/comment/123", "/lemmy.local/comment/123", "/comment/123"},
|
||||||
|
}
|
||||||
|
for _, url := range links {
|
||||||
|
output := LemmyLinkRewrite(`href="`+url[0]+`"`, "lemmy.local", "")
|
||||||
|
success := (output == (`href="` + url[1] + `"`))
|
||||||
|
if !success {
|
||||||
|
fmt.Println("\n!!!! multi instance link rewrite failure !!!!")
|
||||||
|
fmt.Println(url)
|
||||||
|
fmt.Println(output)
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
output = LemmyLinkRewrite(`href="`+url[0]+`"`, ".", "lemmy.local")
|
||||||
|
success = (output == (`href="` + url[2] + `"`))
|
||||||
|
if !success {
|
||||||
|
fmt.Println("\n!!!! single instance link rewrite failure !!!!")
|
||||||
|
fmt.Println(success, url)
|
||||||
|
fmt.Println(output)
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
func middleware(n httprouter.Handle) httprouter.Handle {
|
func middleware(n httprouter.Handle) httprouter.Handle {
|
||||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
|
|
||||||
17
public/noscript.css
Normal file
17
public/noscript.css
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
.scripting,
|
||||||
|
.expando-button,
|
||||||
|
.minimize,
|
||||||
|
#showimages,
|
||||||
|
#lmc,
|
||||||
|
.hidechildren {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.post .expando .image img {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
div.pager {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.savecomment input[type=file] {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
110
public/style.css
110
public/style.css
|
|
@ -219,6 +219,27 @@ summary {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
.preview h3 {
|
||||||
|
background-color: #f0f3fc;
|
||||||
|
border: 0px solid #e6e6e6;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
color: black;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.dark .preview h3 {
|
||||||
|
background-color: #333;
|
||||||
|
border-color: #333;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
.preview .comment {
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
.preview .comment .content {
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
.meta a, .activity .meta a {
|
.meta a, .activity .meta a {
|
||||||
color: #369;
|
color: #369;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
@ -262,6 +283,7 @@ summary {
|
||||||
}
|
}
|
||||||
form.savecomment {
|
form.savecomment {
|
||||||
margin: 0px 0px 10px 0px;
|
margin: 0px 0px 10px 0px;
|
||||||
|
width: 500px;
|
||||||
}
|
}
|
||||||
.comment > .children > form.savecomment {
|
.comment > .children > form.savecomment {
|
||||||
margin: 0px 0px 10px 20px;
|
margin: 0px 0px 10px 20px;
|
||||||
|
|
@ -270,9 +292,34 @@ form.savecomment {
|
||||||
margin: 5px 0px 10px 15px;
|
margin: 5px 0px 10px 15px;
|
||||||
}
|
}
|
||||||
.savecomment textarea {
|
.savecomment textarea {
|
||||||
width: 500px;
|
margin: 5px 0px;
|
||||||
|
width: 100%;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.savecomment .upload label div {
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
width: 32px;
|
||||||
|
position: relative;
|
||||||
|
background-color: #999;
|
||||||
|
color: #000;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.savecomment .upload input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.savecomment .right {
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
.savecomment .right a {
|
||||||
|
line-height: 28px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.comment .meta a.minimize {
|
.comment .meta a.minimize {
|
||||||
color: #369;
|
color: #369;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
|
|
@ -292,6 +339,7 @@ form.savecomment {
|
||||||
.children .morecomments {
|
.children .morecomments {
|
||||||
}
|
}
|
||||||
.morecomments {
|
.morecomments {
|
||||||
|
height: 20px;
|
||||||
clear: left;
|
clear: left;
|
||||||
margin: 0px 0px 10px 0px;
|
margin: 0px 0px 10px 0px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
|
|
@ -304,11 +352,11 @@ form.savecomment {
|
||||||
.morecomments a:hover {
|
.morecomments a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
.member {
|
.member, .block {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom:3px;
|
margin-bottom:3px;
|
||||||
}
|
}
|
||||||
.member input {
|
.member input, .block input {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
@ -320,10 +368,10 @@ form.savecomment {
|
||||||
bottom: 1px;
|
bottom: 1px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.join input {
|
.join input, .block input {
|
||||||
background-color: green;
|
background-color: green;
|
||||||
}
|
}
|
||||||
.leave input {
|
.leave input, .block.unblock input {
|
||||||
background-color: #cf6165;
|
background-color: #cf6165;
|
||||||
}
|
}
|
||||||
.pending input {
|
.pending input {
|
||||||
|
|
@ -501,21 +549,52 @@ form.nsfw div {
|
||||||
position: relative;
|
position: relative;
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
#settingspopup {
|
#mycommunities, #settingspopup {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: 1px solid #888;
|
border: 1px solid #888;
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
#mycommunities {
|
||||||
|
top: 17px;
|
||||||
|
padding: 5px 0px;
|
||||||
|
border-width: 0px 1px 1px 0px;
|
||||||
|
}
|
||||||
|
#mycommunities div {
|
||||||
|
margin: 0px 5px;
|
||||||
|
}
|
||||||
|
#mycommunities a:first-child {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
#mycommunities a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #369;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 9px;
|
||||||
|
display: block;
|
||||||
|
padding: 0px 3px;
|
||||||
|
}
|
||||||
|
.dark #mycommunities a {
|
||||||
|
color: #8cb3d9;
|
||||||
|
}
|
||||||
|
.dark #mycommunities a:hover {
|
||||||
|
background-color: #3e3e3e;
|
||||||
|
}
|
||||||
|
#mycommunities a:hover {
|
||||||
|
background-color: #c7def7;
|
||||||
|
}
|
||||||
|
#settingspopup {
|
||||||
right: 10px;
|
right: 10px;
|
||||||
top: 45px;
|
top: 45px;
|
||||||
}
|
}
|
||||||
#settingspopup form {
|
#settingspopup form {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
.dark #settingspopup {
|
.dark #settingspopup, .dark #mycommunities {
|
||||||
background-color: #262626;
|
background-color: #262626;
|
||||||
}
|
}
|
||||||
#settingspopup.open {
|
#settingspopup.open, #mycommunities.open {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.expando.open{
|
.expando.open{
|
||||||
|
|
@ -806,11 +885,11 @@ nav .communities a.more {
|
||||||
color: orangered !important;
|
color: orangered !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a {
|
nav .communities a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
nav > a:hover {
|
nav .title a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -924,11 +1003,11 @@ nav .right a.mailbox {
|
||||||
top: 4px;
|
top: 4px;
|
||||||
color: gray;
|
color: gray;
|
||||||
}
|
}
|
||||||
nav .right a, .right input[type=submit] {
|
nav .right a, nav .right input[type=submit] {
|
||||||
color: #369;
|
color: #369;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
.dark nav .right a, .dark .right input[type=submit]{
|
.dark nav .right a, .dark nav .right input[type=submit]{
|
||||||
color: #dadada;
|
color: #dadada;
|
||||||
}
|
}
|
||||||
nav .right form, .comment form, form.link-btn {
|
nav .right form, .comment form, form.link-btn {
|
||||||
|
|
@ -1025,9 +1104,14 @@ form.create input[type=file], form.create select {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
.preferences div:last-child label {
|
||||||
|
text-align: left;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
.preferences label{
|
.preferences label{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100px;
|
width: 150px;
|
||||||
|
margin-right: 5px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ function request(url, params, callback, errorcallback = function(){}) {
|
||||||
var method = "GET"
|
var method = "GET"
|
||||||
if (params) method = "POST"
|
if (params) method = "POST"
|
||||||
xmlHttp.open(method, url, true);
|
xmlHttp.open(method, url, true);
|
||||||
if (method = "POST")
|
|
||||||
xmlHttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
|
||||||
xmlHttp.send(params);
|
xmlHttp.send(params);
|
||||||
}
|
}
|
||||||
function postClick(e) {
|
function postClick(e) {
|
||||||
|
|
@ -34,25 +32,31 @@ function postClick(e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function uptil (el, f) {
|
||||||
|
if (el) return f(el) ? el : uptil(el.parentNode, f)
|
||||||
|
}
|
||||||
function commentClick(e) {
|
function commentClick(e) {
|
||||||
e = e || window.event;
|
e = e || window.event;
|
||||||
var targ = e.currentTarget || e.srcElement || e;
|
var targ = e.currentTarget || e.srcElement || e;
|
||||||
if (targ.nodeType == 3) targ = targ.parentNode;
|
if (targ.nodeType == 3) targ = targ.parentNode;
|
||||||
if (e.target.name=="submit") {
|
if (e.target.name=="submit") {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
var form = e.target.parentNode
|
var form = uptil(e.target, function(el){ return el.tagName == "FORM" })
|
||||||
if (form) {
|
if (form) {
|
||||||
data = new FormData(form)
|
data = new FormData(form)
|
||||||
|
data.set(e.target.name, e.target.value)
|
||||||
|
data.set("xhr", 1)
|
||||||
if (("c"+data.get("commentid")) == targ.id) {
|
if (("c"+data.get("commentid")) == targ.id) {
|
||||||
|
targ.action = form.action
|
||||||
|
if (e.target.value == "preview") {
|
||||||
|
targ = form
|
||||||
|
}
|
||||||
|
console.log("ok")
|
||||||
} else if (("c"+data.get("parentid")) == targ.id) {
|
} else if (("c"+data.get("parentid")) == targ.id) {
|
||||||
targ = form
|
targ = form
|
||||||
} else { return }
|
} else { return }
|
||||||
params = new URLSearchParams(data).toString()
|
|
||||||
params += "&" + e.target.name + "=" + e.target.value
|
|
||||||
params += "&xhr=1"
|
|
||||||
e.target.disabled = "disabled"
|
e.target.disabled = "disabled"
|
||||||
request(targ.target || "", params,
|
request(targ.action || "", data,
|
||||||
function(res){
|
function(res){
|
||||||
targ.outerHTML = res
|
targ.outerHTML = res
|
||||||
setup()
|
setup()
|
||||||
|
|
@ -145,7 +149,7 @@ function loadMoreComments(e) {
|
||||||
e.target.parentNode.outerHTML = res + '<div class="morecomments"><a id="lmc" href="" data-page="'+(parseInt(page)+1)+'">load more comments</a></div>'
|
e.target.parentNode.outerHTML = res + '<div class="morecomments"><a id="lmc" href="" data-page="'+(parseInt(page)+1)+'">load more comments</a></div>'
|
||||||
setup()
|
setup()
|
||||||
} else {
|
} else {
|
||||||
e.target.parentNode.outerHTML = ""
|
e.target.parentNode.innerHTML = ""
|
||||||
}
|
}
|
||||||
}, function() {
|
}, function() {
|
||||||
e.target.innerHTML = "loading failed"
|
e.target.innerHTML = "loading failed"
|
||||||
|
|
@ -217,12 +221,15 @@ function formSubmit(e) {
|
||||||
var targ = e.currentTarget || e.srcElement || e;
|
var targ = e.currentTarget || e.srcElement || e;
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
var data = new FormData(targ)
|
var data = new FormData(targ)
|
||||||
params = new URLSearchParams(data).toString()
|
data.set(e.submitter.name, e.submitter.value)
|
||||||
params += "&" + e.submitter.name + "=" + e.submitter.value
|
data.set("xhr", "1")
|
||||||
params += "&xhr=1"
|
|
||||||
e.submitter.disabled = "disabled"
|
e.submitter.disabled = "disabled"
|
||||||
request(targ.target, params,
|
request(targ.target, data,
|
||||||
function(res){
|
function(res){
|
||||||
|
if (data.get("op") == "read_post") {
|
||||||
|
document.getElementById("p"+data.get("postid")).remove()
|
||||||
|
return
|
||||||
|
}
|
||||||
targ.outerHTML = res
|
targ.outerHTML = res
|
||||||
setup()
|
setup()
|
||||||
},
|
},
|
||||||
|
|
@ -233,9 +240,33 @@ function formSubmit(e) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleMyCommunities(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
var mycommunities = document.getElementById("mycommunities")
|
||||||
|
if (mycommunities.className.indexOf("open") > -1) {
|
||||||
|
mycommunities.className = ""
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mycommunities.className = "open"
|
||||||
|
if (mycommunities.innerHTML == "") {
|
||||||
|
mycommunities.innerHTML = "<div>loading</div>"
|
||||||
|
request(e.target.href + "&xhr=1", "", function(res) {
|
||||||
|
mycommunities.innerHTML = '<div><a href="'+e.target.href+'">view all »</a>'
|
||||||
|
mycommunities.innerHTML += res
|
||||||
|
}, function() {
|
||||||
|
mycommunities.className = ""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
function openSettings(e) {
|
function openSettings(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
var settings = document.getElementById("settingspopup")
|
var settings = document.getElementById("settingspopup")
|
||||||
|
if (settings.className == "open") {
|
||||||
|
settings.className = ""
|
||||||
|
return false
|
||||||
|
}
|
||||||
settings.className = "open"
|
settings.className = "open"
|
||||||
request(e.target.href + "?xhr=1", "", function(res) {
|
request(e.target.href + "?xhr=1", "", function(res) {
|
||||||
settings.innerHTML = res
|
settings.innerHTML = res
|
||||||
|
|
@ -265,8 +296,7 @@ function saveSettings(e) {
|
||||||
var targ = e.currentTarget || e.srcElement || e;
|
var targ = e.currentTarget || e.srcElement || e;
|
||||||
var data = new FormData(targ)
|
var data = new FormData(targ)
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
var params = new URLSearchParams(data).toString()
|
request(targ.target, data, function(res) {
|
||||||
request(targ.target, params, function(res) {
|
|
||||||
["endlessScrolling", "autoLoad"].map(function(x) {
|
["endlessScrolling", "autoLoad"].map(function(x) {
|
||||||
localStorage.setItem(x, data.get(x)=="on")
|
localStorage.setItem(x, data.get(x)=="on")
|
||||||
})
|
})
|
||||||
|
|
@ -321,6 +351,16 @@ function toggleImages(open) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function insertImg(e) {
|
||||||
|
e = e || window.event;
|
||||||
|
var form = uptil(e.target, function(el){ return el.tagName == "FORM" })
|
||||||
|
form.querySelector("input[value=preview]").click()
|
||||||
|
var inputs = form.getElementsByTagName("input")
|
||||||
|
for (var i = 0; i < inputs.length; i++) {
|
||||||
|
inputs[i].disabled = "disabled"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setup() {
|
function setup() {
|
||||||
if (showimages = document.getElementById("se")) {
|
if (showimages = document.getElementById("se")) {
|
||||||
showimages.addEventListener("click", showImages)
|
showimages.addEventListener("click", showImages)
|
||||||
|
|
@ -328,6 +368,9 @@ function setup() {
|
||||||
if (settings = document.getElementById("opensettings")) {
|
if (settings = document.getElementById("opensettings")) {
|
||||||
settings.addEventListener("click", openSettings)
|
settings.addEventListener("click", openSettings)
|
||||||
}
|
}
|
||||||
|
if (settings = document.getElementById("openmycommunities")) {
|
||||||
|
settings.addEventListener("click", toggleMyCommunities)
|
||||||
|
}
|
||||||
if (hidechildren = document.getElementById("hidechildren")){
|
if (hidechildren = document.getElementById("hidechildren")){
|
||||||
hidechildren.addEventListener("click", hideAllChildComments)
|
hidechildren.addEventListener("click", hideAllChildComments)
|
||||||
}
|
}
|
||||||
|
|
@ -338,6 +381,10 @@ function setup() {
|
||||||
}
|
}
|
||||||
lmc.addEventListener("click", loadMoreComments)
|
lmc.addEventListener("click", loadMoreComments)
|
||||||
}
|
}
|
||||||
|
var imgUpload = document.getElementsByClassName("imgupload")
|
||||||
|
for (var i = 0; i < imgUpload.length; i++) {
|
||||||
|
imgUpload[i].addEventListener("change", insertImg)
|
||||||
|
}
|
||||||
var posts = document.getElementsByClassName("post")
|
var posts = document.getElementsByClassName("post")
|
||||||
for (var i = 0; i < posts.length; i++) {
|
for (var i = 0; i < posts.length; i++) {
|
||||||
posts[i].addEventListener("click", postClick)
|
posts[i].addEventListener("click", postClick)
|
||||||
|
|
|
||||||
33
public/ws.js
Normal file
33
public/ws.js
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
var ok = false
|
||||||
|
var t = 800
|
||||||
|
var ws
|
||||||
|
var prot = location.protocol == 'https:' ? "wss://":"ws://"
|
||||||
|
let port = ":8009"
|
||||||
|
|
||||||
|
function start(){
|
||||||
|
let url = prot + document.location.hostname + port + document.location.pathname + document.location.search;
|
||||||
|
console.log('connecting to ', url);
|
||||||
|
ws = new WebSocket(url);
|
||||||
|
ws.onopen = function(){
|
||||||
|
console.log("open");
|
||||||
|
t = 800
|
||||||
|
}
|
||||||
|
ws.onmessage = function(msg){
|
||||||
|
console.log("reload:", msg.data)
|
||||||
|
if (msg.data == "") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
ws.onclose = function(){
|
||||||
|
console.log("close");
|
||||||
|
setTimeout(function(){
|
||||||
|
//start()
|
||||||
|
if (t < 10 * 1000) t += 200
|
||||||
|
}, t);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
console.log("ws");
|
||||||
|
if (typeof WebSocket != 'undefined') {
|
||||||
|
start()
|
||||||
|
}
|
||||||
339
routes.go
339
routes.go
|
|
@ -32,12 +32,12 @@ var funcMap = template.FuncMap{
|
||||||
}
|
}
|
||||||
return host
|
return host
|
||||||
},
|
},
|
||||||
"proxy": func(s string) string {
|
"localize": func(s string) string {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
return "/" + u.Host + u.Path
|
return "." + u.Path + "@" + u.Host
|
||||||
},
|
},
|
||||||
"printer": func(n any) string {
|
"printer": func(n any) string {
|
||||||
p := message.NewPrinter(language.English)
|
p := message.NewPrinter(language.English)
|
||||||
|
|
@ -119,9 +119,9 @@ var funcMap = template.FuncMap{
|
||||||
if re.MatchString(p.URL.String()) {
|
if re.MatchString(p.URL.String()) {
|
||||||
return p.URL.String() + "?format=jpg&thumbnail=96"
|
return p.URL.String() + "?format=jpg&thumbnail=96"
|
||||||
}
|
}
|
||||||
re = regexp.MustCompile(`^https:\/\/i.imgur.com\/([a-zA-Z0-9]+)\.([a-z]+)$`)
|
re = regexp.MustCompile(`^https:\/\/(i\.)?imgur.com\/([a-zA-Z0-9]{5,})(\.[a-zA-Z0-9]+)?`)
|
||||||
if re.MatchString(p.URL.String()) {
|
if re.MatchString(p.URL.String()) {
|
||||||
return re.ReplaceAllString(p.URL.String(), "https://i.imgur.com/${1}s.$2")
|
return re.ReplaceAllString(p.URL.String(), "https://i.imgur.com/${2}s.jpg")
|
||||||
}
|
}
|
||||||
if p.URL.IsValid() {
|
if p.URL.IsValid() {
|
||||||
return "/_/static/link.png"
|
return "/_/static/link.png"
|
||||||
|
|
@ -133,23 +133,17 @@ var funcMap = template.FuncMap{
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
re := regexp.MustCompile(`\s---\s`)
|
re := regexp.MustCompile(`\s---\s`)
|
||||||
body = re.ReplaceAllString(body, "\n***\n")
|
body = re.ReplaceAllString(body, "\n***\n")
|
||||||
|
// community bangs
|
||||||
|
body = RegReplace(body, `([^\[])!([a-zA-Z0-9_]+)@([a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)+)`, `$1[!$2@$3](/c/$2@$3)`)
|
||||||
if err := md.Convert([]byte(body), &buf); err != nil {
|
if err := md.Convert([]byte(body), &buf); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return template.HTML(body)
|
return template.HTML(body)
|
||||||
}
|
}
|
||||||
converted := buf.String()
|
body = buf.String()
|
||||||
converted = strings.Replace(converted, `<img `, `<img loading="lazy" `, -1)
|
body = strings.Replace(body, `<img `, `<img loading="lazy" `, -1)
|
||||||
if os.Getenv("LEMMY_DOMAIN") == "" {
|
body = LemmyLinkRewrite(body, host, os.Getenv("LEMMY_DOMAIN"))
|
||||||
re := regexp.MustCompile(`href="https:\/\/([a-zA-Z0-9\.\-]+\/(c\/[a-zA-Z0-9]+|(post|comment)\/\d+))`)
|
body = RegReplace(body, `::: ?spoiler (.*?)\n([\S\s]*?):::`, "<details><summary>$1</summary>$2</details>")
|
||||||
converted = re.ReplaceAllString(converted, `href="/$1`)
|
return template.HTML(body)
|
||||||
}
|
|
||||||
re = regexp.MustCompile(`href="\/(c\/[a-zA-Z0-9\-]+|(post|comment)\/\d+)`)
|
|
||||||
converted = re.ReplaceAllString(converted, `href="/`+host+`/$1`)
|
|
||||||
re = regexp.MustCompile(` !([a-zA-Z0-9]+)@([a-zA-Z0-9\.\-]+) `)
|
|
||||||
converted = re.ReplaceAllString(converted, ` <a href="/$2/c/$1">!$1@$2</a> `)
|
|
||||||
re = regexp.MustCompile(`::: spoiler (.*?)\n([\S\s]*?):::`)
|
|
||||||
converted = re.ReplaceAllString(converted, "<details><summary>$1</summary>$2</details>")
|
|
||||||
return template.HTML(converted)
|
|
||||||
},
|
},
|
||||||
"rmmarkdown": func(body string) string {
|
"rmmarkdown": func(body string) string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
@ -158,7 +152,7 @@ var funcMap = template.FuncMap{
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
text := html2text.HTML2TextWithOptions(buf.String(), html2text.WithLinksInnerText())
|
text := html2text.HTML2TextWithOptions(buf.String(), html2text.WithLinksInnerText())
|
||||||
re := regexp.MustCompile(`\<https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)\>`)
|
re := regexp.MustCompile(`\<(https?:\/\/|mailto)(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)\>`)
|
||||||
return re.ReplaceAllString(text, "")
|
return re.ReplaceAllString(text, "")
|
||||||
},
|
},
|
||||||
"contains": strings.Contains,
|
"contains": strings.Contains,
|
||||||
|
|
@ -170,11 +164,67 @@ var funcMap = template.FuncMap{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LemmyLinkRewrite(input string, host string, lemmy_domain string) (body string) {
|
||||||
|
body = input
|
||||||
|
// localize community and user links
|
||||||
|
body = RegReplace(body, `href="https:\/\/([a-zA-Z0-9\.\-]+)\/((c|u|comment|post)\/[^#\?]*?)"`, `href="/$2@$1"`)
|
||||||
|
// remove extra instance tag
|
||||||
|
body = RegReplace(body, `href="(https:\/)?(\/[a-zA-Z0-9\.\-]+)?\/((c|u)\/[a-zA-Z0-9]+@[a-zA-Z0-9\.\-]+)@([a-zA-Z0-9\.\-]+)"`, `href="/$3"`)
|
||||||
|
if lemmy_domain == "" {
|
||||||
|
// add domain to relative links
|
||||||
|
body = RegReplace(body, `href="\/((c|u|post|comment)\/(.*?)")`, `href="/`+host+`/$1`)
|
||||||
|
// convert links to relative
|
||||||
|
body = RegReplace(body, `href="https:\/\/([a-zA-Z0-9\.\-]+\/((c|u|post|comment)\/[a-zA-Z0-9]+"))`, `href="/$1`)
|
||||||
|
} else {
|
||||||
|
// convert local links to relative
|
||||||
|
body = RegReplace(body, `href="https:\/\/`+lemmy_domain+`\/(c\/[a-zA-Z0-9]+"|(c|u|post|comment)\/(.*?)")`, `href="/$1`)
|
||||||
|
body = RegReplace(body, `href="(.*)@`+lemmy_domain+`"`, `href="$1"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
re := regexp.MustCompile(`href="\/?([a-zA-Z0-9\.\-]*)\/(c|u|post|comment)\/(.*?)@(.*?)"`)
|
||||||
|
// assume "old." subdomain is mlmym and remove
|
||||||
|
matches := re.FindAllStringSubmatch(body, -1)
|
||||||
|
for _, match := range matches {
|
||||||
|
if match[4][0:4] == "old." {
|
||||||
|
s := 1
|
||||||
|
if match[1] == "" {
|
||||||
|
s += 1
|
||||||
|
}
|
||||||
|
body = strings.Replace(body, match[0], `href="/`+strings.Join(match[s:4], "/")+"@"+match[4][4:]+`"`, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// remove redundant instance tag
|
||||||
|
matches = re.FindAllStringSubmatch(body, -1)
|
||||||
|
for _, match := range matches {
|
||||||
|
if match[1] == match[4] {
|
||||||
|
body = strings.Replace(body, match[0], `href="/`+strings.Join(match[1:4], "/")+`"`, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegReplace(input string, match string, replace string) string {
|
||||||
|
re := regexp.MustCompile(match)
|
||||||
|
return re.ReplaceAllString(input, replace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getenv(key, fallback string) string {
|
||||||
|
value := os.Getenv(key)
|
||||||
|
if len(value) == 0 {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
func Initialize(Host string, r *http.Request) (State, error) {
|
func Initialize(Host string, r *http.Request) (State, error) {
|
||||||
state := State{
|
state := State{
|
||||||
Host: Host,
|
Host: Host,
|
||||||
Page: 1,
|
Page: 1,
|
||||||
Status: http.StatusOK,
|
Status: http.StatusOK,
|
||||||
|
Version: version,
|
||||||
|
}
|
||||||
|
if watch != nil {
|
||||||
|
state.Watch = *watch
|
||||||
}
|
}
|
||||||
lemmyDomain := os.Getenv("LEMMY_DOMAIN")
|
lemmyDomain := os.Getenv("LEMMY_DOMAIN")
|
||||||
if lemmyDomain != "" {
|
if lemmyDomain != "" {
|
||||||
|
|
@ -209,14 +259,28 @@ func Initialize(Host string, r *http.Request) (State, error) {
|
||||||
}
|
}
|
||||||
state.Listing = getCookie(r, "DefaultListingType")
|
state.Listing = getCookie(r, "DefaultListingType")
|
||||||
state.Sort = getCookie(r, "DefaultSortType")
|
state.Sort = getCookie(r, "DefaultSortType")
|
||||||
state.Dark = getCookie(r, "Dark") != ""
|
state.CommentSort = getCookie(r, "DefaultCommentSortType")
|
||||||
|
if dark := getCookie(r, "Dark"); dark != "" {
|
||||||
|
state.Dark = dark != "0"
|
||||||
|
} else {
|
||||||
|
state.Dark = os.Getenv("DARK") != ""
|
||||||
|
}
|
||||||
state.ShowNSFW = getCookie(r, "ShowNSFW") != ""
|
state.ShowNSFW = getCookie(r, "ShowNSFW") != ""
|
||||||
|
state.HideInstanceNames = getCookie(r, "HideInstanceNames") != ""
|
||||||
|
if hide := getCookie(r, "HideThumbnails"); hide != "" {
|
||||||
|
state.HideThumbnails = hide != "0"
|
||||||
|
} else {
|
||||||
|
state.HideThumbnails = os.Getenv("HIDE_THUMBNAILS") != ""
|
||||||
|
}
|
||||||
state.ParseQuery(r.URL.RawQuery)
|
state.ParseQuery(r.URL.RawQuery)
|
||||||
if state.Sort == "" {
|
if state.Sort == "" {
|
||||||
state.Sort = "Hot"
|
state.Sort = getenv("SORT", "Hot")
|
||||||
|
}
|
||||||
|
if state.CommentSort == "" {
|
||||||
|
state.CommentSort = getenv("COMMENT_SORT", "Hot")
|
||||||
}
|
}
|
||||||
if state.Listing == "" || state.Session == nil && state.Listing == "Subscribed" {
|
if state.Listing == "" || state.Session == nil && state.Listing == "Subscribed" {
|
||||||
state.Listing = "All"
|
state.Listing = getenv("LISTING", "All")
|
||||||
}
|
}
|
||||||
return state, nil
|
return state, nil
|
||||||
}
|
}
|
||||||
|
|
@ -385,17 +449,84 @@ func GetFrontpage(w http.ResponseWriter, r *http.Request, ps httprouter.Params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCommunities(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
r.URL.Path = "/search"
|
||||||
|
if ps.ByName("host") != "" {
|
||||||
|
r.URL.Path = "/" + ps.ByName("host") + "/search"
|
||||||
|
}
|
||||||
|
r.URL.RawQuery = "searchtype=Communities&sort=TopMonth"
|
||||||
|
http.Redirect(w, r, r.URL.String(), 301)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveId(r *http.Request, class string, id string, host string) string {
|
||||||
|
remoteAddr := r.RemoteAddr
|
||||||
|
if r.Header.Get("CF-Connecting-IP") != "" {
|
||||||
|
remoteAddr = r.Header.Get("CF-Connecting-IP")
|
||||||
|
}
|
||||||
|
client := http.Client{Transport: NewAddHeaderTransport(remoteAddr)}
|
||||||
|
c, err := lemmy.NewWithClient("https://"+host, &client)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
idn, _ := strconv.Atoi(id)
|
||||||
|
if class == "post" {
|
||||||
|
resp, err := c.Post(context.Background(), types.GetPost{
|
||||||
|
ID: types.NewOptional(idn),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return resp.PostView.Post.ApID
|
||||||
|
}
|
||||||
|
resp, err := c.Comment(context.Background(), types.GetComment{
|
||||||
|
ID: idn,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return resp.CommentView.Comment.ApID
|
||||||
|
}
|
||||||
|
|
||||||
func GetPost(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
func GetPost(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
state, err := Initialize(ps.ByName("host"), r)
|
state, err := Initialize(ps.ByName("host"), r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Render(w, "index.html", state)
|
Render(w, "index.html", state)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if path := strings.Split(ps.ByName("postid"), "@"); len(path) > 1 {
|
||||||
|
apid := ResolveId(r, "post", path[0], path[1])
|
||||||
|
if apid != "" {
|
||||||
|
resp, err := state.Client.ResolveObject(context.Background(), types.ResolveObject{
|
||||||
|
Q: apid,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
dest := apid
|
||||||
|
if os.Getenv("LEMMY_DOMAIN") == "" {
|
||||||
|
dest = RegReplace(dest, `https:\/\/([a-zA-Z0-9\.\-]+\/post\/\d+)`, `/$1`)
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, dest, 302)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
post, _ := resp.Post.Value()
|
||||||
|
if post.Post.ID > 0 {
|
||||||
|
dest := RegReplace(r.URL.String(), `(([a-zA-Z0-9\.\-]+)?/post/)([a-zA-Z0-9\-\.@]+)`, `$1`)
|
||||||
|
dest += strconv.Itoa(post.Post.ID)
|
||||||
|
http.Redirect(w, r, dest, 302)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
http.Redirect(w, r, apid, 302)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
m, _ := url.ParseQuery(r.URL.RawQuery)
|
m, _ := url.ParseQuery(r.URL.RawQuery)
|
||||||
if len(m["edit"]) > 0 {
|
if len(m["edit"]) > 0 {
|
||||||
state.Op = "edit_post"
|
state.Op = "edit_post"
|
||||||
state.GetSite()
|
state.GetSite()
|
||||||
}
|
}
|
||||||
|
if len(m["content"]) > 0 {
|
||||||
|
state.Content = m["content"][0]
|
||||||
|
}
|
||||||
postid, _ := strconv.Atoi(ps.ByName("postid"))
|
postid, _ := strconv.Atoi(ps.ByName("postid"))
|
||||||
state.GetPost(postid)
|
state.GetPost(postid)
|
||||||
state.GetComments()
|
state.GetComments()
|
||||||
|
|
@ -407,6 +538,32 @@ func GetComment(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
Render(w, "index.html", state)
|
Render(w, "index.html", state)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if path := strings.Split(ps.ByName("commentid"), "@"); len(path) > 1 {
|
||||||
|
apid := ResolveId(r, "comment", path[0], path[1])
|
||||||
|
if apid != "" {
|
||||||
|
resp, err := state.Client.ResolveObject(context.Background(), types.ResolveObject{
|
||||||
|
Q: apid,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
dest := apid
|
||||||
|
if os.Getenv("LEMMY_DOMAIN") == "" {
|
||||||
|
dest = RegReplace(dest, `https:\/\/([a-zA-Z0-9\.\-]+\/comment\/\d+)`, `/$1`)
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, dest, 302)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
comment, _ := resp.Comment.Value()
|
||||||
|
if comment.Comment.ID > 0 {
|
||||||
|
dest := RegReplace(r.URL.String(), `(([a-zA-Z0-9\.\-]+)?/comment/)([a-zA-Z0-9\-\.@]+)`, `$1`)
|
||||||
|
dest += strconv.Itoa(comment.Comment.ID)
|
||||||
|
http.Redirect(w, r, dest, 302)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
http.Redirect(w, r, apid, 302)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
m, _ := url.ParseQuery(r.URL.RawQuery)
|
m, _ := url.ParseQuery(r.URL.RawQuery)
|
||||||
if len(m["reply"]) > 0 {
|
if len(m["reply"]) > 0 {
|
||||||
state.Op = "reply"
|
state.Op = "reply"
|
||||||
|
|
@ -414,6 +571,9 @@ func GetComment(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
if len(m["edit"]) > 0 {
|
if len(m["edit"]) > 0 {
|
||||||
state.Op = "edit"
|
state.Op = "edit"
|
||||||
}
|
}
|
||||||
|
if r.Method == "POST" && len(m["content"]) > 0 {
|
||||||
|
state.Content = m["content"][0]
|
||||||
|
}
|
||||||
if len(m["source"]) > 0 {
|
if len(m["source"]) > 0 {
|
||||||
state.Op = "source"
|
state.Op = "source"
|
||||||
}
|
}
|
||||||
|
|
@ -423,6 +583,10 @@ func GetComment(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
}
|
}
|
||||||
commentid, _ := strconv.Atoi(ps.ByName("commentid"))
|
commentid, _ := strconv.Atoi(ps.ByName("commentid"))
|
||||||
state.GetComment(commentid)
|
state.GetComment(commentid)
|
||||||
|
if state.XHR && len(m["content"]) > 0 {
|
||||||
|
Render(w, "create_comment.html", state)
|
||||||
|
return
|
||||||
|
}
|
||||||
state.GetPost(state.PostID)
|
state.GetPost(state.PostID)
|
||||||
Render(w, "index.html", state)
|
Render(w, "index.html", state)
|
||||||
}
|
}
|
||||||
|
|
@ -540,9 +704,10 @@ func Settings(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
Render(w, "index.html", state)
|
Render(w, "index.html", state)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
state.GetSite()
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case "POST":
|
case "POST":
|
||||||
for _, name := range []string{"DefaultSortType", "DefaultListingType"} {
|
for _, name := range []string{"DefaultSortType", "DefaultListingType", "DefaultCommentSortType"} {
|
||||||
deleteCookie(w, state.Host, name)
|
deleteCookie(w, state.Host, name)
|
||||||
setCookie(w, "", name, r.FormValue(name))
|
setCookie(w, "", name, r.FormValue(name))
|
||||||
}
|
}
|
||||||
|
|
@ -550,8 +715,7 @@ func Settings(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
setCookie(w, "", "Dark", "1")
|
setCookie(w, "", "Dark", "1")
|
||||||
state.Dark = true
|
state.Dark = true
|
||||||
} else {
|
} else {
|
||||||
deleteCookie(w, state.Host, "Dark")
|
setCookie(w, "", "Dark", "0")
|
||||||
deleteCookie(w, "", "Dark")
|
|
||||||
state.Dark = false
|
state.Dark = false
|
||||||
}
|
}
|
||||||
if r.FormValue("shownsfw") != "" {
|
if r.FormValue("shownsfw") != "" {
|
||||||
|
|
@ -562,8 +726,23 @@ func Settings(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
deleteCookie(w, "", "ShowNSFW")
|
deleteCookie(w, "", "ShowNSFW")
|
||||||
state.ShowNSFW = false
|
state.ShowNSFW = false
|
||||||
}
|
}
|
||||||
|
if r.FormValue("hideInstanceNames") != "" {
|
||||||
|
setCookie(w, "", "HideInstanceNames", "1")
|
||||||
|
state.HideInstanceNames = true
|
||||||
|
} else {
|
||||||
|
deleteCookie(w, "", "HideInstanceNames")
|
||||||
|
state.HideInstanceNames = false
|
||||||
|
}
|
||||||
|
if r.FormValue("hideThumbnails") != "" {
|
||||||
|
setCookie(w, "", "HideThumbnails", "1")
|
||||||
|
state.HideInstanceNames = true
|
||||||
|
} else {
|
||||||
|
setCookie(w, "", "HideThumbnails", "0")
|
||||||
|
state.HideInstanceNames = false
|
||||||
|
}
|
||||||
state.Listing = r.FormValue("DefaultListingType")
|
state.Listing = r.FormValue("DefaultListingType")
|
||||||
state.Sort = r.FormValue("DefaultSortType")
|
state.Sort = r.FormValue("DefaultSortType")
|
||||||
|
state.CommentSort = r.FormValue("DefaultCommentSortType")
|
||||||
// TODO save user settings
|
// TODO save user settings
|
||||||
case "GET":
|
case "GET":
|
||||||
if state.Session != nil {
|
if state.Session != nil {
|
||||||
|
|
@ -740,6 +919,18 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
CommunityID: communityid,
|
CommunityID: communityid,
|
||||||
Follow: true,
|
Follow: true,
|
||||||
})
|
})
|
||||||
|
case "block":
|
||||||
|
communityid, _ := strconv.Atoi(r.FormValue("communityid"))
|
||||||
|
state.Client.BlockCommunity(context.Background(), types.BlockCommunity{
|
||||||
|
CommunityID: communityid,
|
||||||
|
Block: true,
|
||||||
|
})
|
||||||
|
case "unblock":
|
||||||
|
communityid, _ := strconv.Atoi(r.FormValue("communityid"))
|
||||||
|
state.Client.BlockCommunity(context.Background(), types.BlockCommunity{
|
||||||
|
CommunityID: communityid,
|
||||||
|
Block: false,
|
||||||
|
})
|
||||||
case "logout":
|
case "logout":
|
||||||
deleteCookie(w, state.Host, "jwt")
|
deleteCookie(w, state.Host, "jwt")
|
||||||
deleteCookie(w, state.Host, "user")
|
deleteCookie(w, state.Host, "user")
|
||||||
|
|
@ -755,10 +946,15 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(fmt.Sprintf("%v", err), "missing_totp_token") {
|
if strings.Contains(fmt.Sprintf("%v", err), "missing_totp_token") {
|
||||||
state.Op = "2fa"
|
state.Op = "2fa"
|
||||||
Render(w, "login.html", state)
|
}
|
||||||
return
|
state.GetSite()
|
||||||
|
if state.Site != nil && state.Site.SiteView.LocalSite.CaptchaEnabled {
|
||||||
|
state.GetCaptcha()
|
||||||
}
|
}
|
||||||
state.Status = http.StatusUnauthorized
|
state.Status = http.StatusUnauthorized
|
||||||
|
state.Error = err
|
||||||
|
Render(w, "login.html", state)
|
||||||
|
return
|
||||||
} else if resp.JWT.IsValid() {
|
} else if resp.JWT.IsValid() {
|
||||||
state.GetUser(r.FormValue("username"))
|
state.GetUser(r.FormValue("username"))
|
||||||
if state.User != nil {
|
if state.User != nil {
|
||||||
|
|
@ -970,6 +1166,22 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
r.URL.Path = "/" + state.Host + "/c/" + resp.PostView.Community.Name
|
r.URL.Path = "/" + state.Host + "/c/" + resp.PostView.Community.Name
|
||||||
r.URL.RawQuery = ""
|
r.URL.RawQuery = ""
|
||||||
}
|
}
|
||||||
|
case "read_post":
|
||||||
|
postid, _ := strconv.Atoi(r.FormValue("postid"))
|
||||||
|
post := types.MarkPostAsRead{
|
||||||
|
PostID: postid,
|
||||||
|
Read: true,
|
||||||
|
}
|
||||||
|
if r.FormValue("submit") == "mark unread" {
|
||||||
|
post.Read = false
|
||||||
|
}
|
||||||
|
_, err := state.Client.MarkPostAsRead(context.Background(), post)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
} else if r.FormValue("xhr") != "" {
|
||||||
|
w.Write([]byte{})
|
||||||
|
return
|
||||||
|
}
|
||||||
case "vote_post":
|
case "vote_post":
|
||||||
var score int16
|
var score int16
|
||||||
score = 1
|
score = 1
|
||||||
|
|
@ -1023,9 +1235,20 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
parentid, _ := strconv.Atoi(r.FormValue("parentid"))
|
parentid, _ := strconv.Atoi(r.FormValue("parentid"))
|
||||||
state.GetComment(parentid)
|
state.GetComment(parentid)
|
||||||
}
|
}
|
||||||
|
content := r.FormValue("content")
|
||||||
|
file, handler, err := r.FormFile("file")
|
||||||
|
if err == nil {
|
||||||
|
pres, err := state.UploadImage(file, handler)
|
||||||
|
if err != nil {
|
||||||
|
state.Error = err
|
||||||
|
Render(w, "index.html", state)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
content += ("")
|
||||||
|
}
|
||||||
if r.FormValue("submit") == "save" {
|
if r.FormValue("submit") == "save" {
|
||||||
createComment := types.CreateComment{
|
createComment := types.CreateComment{
|
||||||
Content: r.FormValue("content"),
|
Content: content,
|
||||||
PostID: state.PostID,
|
PostID: state.PostID,
|
||||||
}
|
}
|
||||||
if state.CommentID > 0 {
|
if state.CommentID > 0 {
|
||||||
|
|
@ -1047,6 +1270,22 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
} else if r.FormValue("submit") == "preview" {
|
||||||
|
q := r.URL.Query()
|
||||||
|
q.Set("content", content)
|
||||||
|
q.Set("reply", "")
|
||||||
|
if r.FormValue("xhr") != "" {
|
||||||
|
q.Set("xhr", "1")
|
||||||
|
}
|
||||||
|
r.URL.RawQuery = q.Encode()
|
||||||
|
if ps.ByName("postid") != "" {
|
||||||
|
GetPost(w, r, ps)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ps.ByName("commentid") != "" {
|
||||||
|
GetComment(w, r, ps)
|
||||||
|
return
|
||||||
|
}
|
||||||
} else if r.FormValue("xhr") != "" {
|
} else if r.FormValue("xhr") != "" {
|
||||||
w.Write([]byte{})
|
w.Write([]byte{})
|
||||||
return
|
return
|
||||||
|
|
@ -1056,10 +1295,23 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
}
|
}
|
||||||
case "edit_comment":
|
case "edit_comment":
|
||||||
commentid, _ := strconv.Atoi(r.FormValue("commentid"))
|
commentid, _ := strconv.Atoi(r.FormValue("commentid"))
|
||||||
|
q := r.URL.Query()
|
||||||
|
content := r.FormValue("content")
|
||||||
|
file, handler, err := r.FormFile("file")
|
||||||
|
if err == nil {
|
||||||
|
pres, err := state.UploadImage(file, handler)
|
||||||
|
if err != nil {
|
||||||
|
state.Error = err
|
||||||
|
Render(w, "index.html", state)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
content += ("")
|
||||||
|
}
|
||||||
|
|
||||||
if r.FormValue("submit") == "save" {
|
if r.FormValue("submit") == "save" {
|
||||||
resp, err := state.Client.EditComment(context.Background(), types.EditComment{
|
resp, err := state.Client.EditComment(context.Background(), types.EditComment{
|
||||||
CommentID: commentid,
|
CommentID: commentid,
|
||||||
Content: types.NewOptional(r.FormValue("content")),
|
Content: types.NewOptional(content),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
|
@ -1068,6 +1320,29 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
r.URL.Fragment = "c" + commentid
|
r.URL.Fragment = "c" + commentid
|
||||||
r.URL.RawQuery = ""
|
r.URL.RawQuery = ""
|
||||||
}
|
}
|
||||||
|
} else if r.FormValue("submit") == "preview" {
|
||||||
|
q.Set("content", content)
|
||||||
|
q.Set("edit", "")
|
||||||
|
if r.FormValue("xhr") != "" {
|
||||||
|
q.Set("xhr", "1")
|
||||||
|
}
|
||||||
|
r.URL.RawQuery = q.Encode()
|
||||||
|
if ps.ByName("commentid") != "" {
|
||||||
|
GetComment(w, r, ps)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if r.FormValue("submit") == "cancel" {
|
||||||
|
if ps.ByName("commentid") != "" {
|
||||||
|
if r.FormValue("xhr") != "" {
|
||||||
|
q.Set("xhr", "1")
|
||||||
|
}
|
||||||
|
r.URL.RawQuery = q.Encode()
|
||||||
|
GetComment(w, r, ps)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if r.FormValue("xhr") != "" {
|
||||||
|
w.Write([]byte{})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if r.FormValue("xhr") != "" {
|
if r.FormValue("xhr") != "" {
|
||||||
state.XHR = true
|
state.XHR = true
|
||||||
|
|
@ -1136,6 +1411,7 @@ func GetRouter() *httprouter.Router {
|
||||||
router.POST("/:host/create_post", middleware(UserOp))
|
router.POST("/:host/create_post", middleware(UserOp))
|
||||||
router.GET("/:host/create_community", middleware(GetCreateCommunity))
|
router.GET("/:host/create_community", middleware(GetCreateCommunity))
|
||||||
router.POST("/:host/create_community", middleware(UserOp))
|
router.POST("/:host/create_community", middleware(UserOp))
|
||||||
|
router.GET("/:host/communities", middleware(GetCommunities))
|
||||||
} else {
|
} else {
|
||||||
router.ServeFiles("/_/static/*filepath", http.Dir("public"))
|
router.ServeFiles("/_/static/*filepath", http.Dir("public"))
|
||||||
router.GET("/", middleware(GetFrontpage))
|
router.GET("/", middleware(GetFrontpage))
|
||||||
|
|
@ -1167,6 +1443,7 @@ func GetRouter() *httprouter.Router {
|
||||||
router.POST("/create_post", middleware(UserOp))
|
router.POST("/create_post", middleware(UserOp))
|
||||||
router.GET("/create_community", middleware(GetCreateCommunity))
|
router.GET("/create_community", middleware(GetCreateCommunity))
|
||||||
router.POST("/create_community", middleware(UserOp))
|
router.POST("/create_community", middleware(UserOp))
|
||||||
|
router.GET("/communities", middleware(GetCommunities))
|
||||||
}
|
}
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
|
||||||
180
state.go
180
state.go
|
|
@ -11,6 +11,8 @@ import (
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -57,47 +59,85 @@ type Post struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
UserName string
|
UserName string
|
||||||
UserID int
|
UserID int
|
||||||
|
Communities []types.CommunityView
|
||||||
}
|
}
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
Client *lemmy.Client
|
Watch bool
|
||||||
HTTPClient *http.Client
|
Version string
|
||||||
Session *Session
|
Client *lemmy.Client
|
||||||
Status int
|
HTTPClient *http.Client
|
||||||
Error error
|
Session *Session
|
||||||
Alert string
|
Status int
|
||||||
Host string
|
Error error
|
||||||
CommunityName string
|
Alert string
|
||||||
Community *types.GetCommunityResponse
|
Host string
|
||||||
TopCommunities []types.CommunityView
|
CommunityName string
|
||||||
Communities []types.CommunityView
|
Community *types.GetCommunityResponse
|
||||||
UnreadCount int64
|
TopCommunities []types.CommunityView
|
||||||
Sort string
|
Communities []types.CommunityView
|
||||||
Listing string
|
UnreadCount int64
|
||||||
Page int
|
Sort string
|
||||||
Parts []string
|
CommentSort string
|
||||||
Posts []Post
|
Listing string
|
||||||
Comments []Comment
|
Page int
|
||||||
Activities []Activity
|
Parts []string
|
||||||
CommentCount int
|
Posts []Post
|
||||||
PostID int
|
Comments []Comment
|
||||||
CommentID int
|
Activities []Activity
|
||||||
Context int
|
CommentCount int
|
||||||
UserName string
|
PostID int
|
||||||
User *types.GetPersonDetailsResponse
|
CommentID int
|
||||||
Now int64
|
Context int
|
||||||
XHR bool
|
UserName string
|
||||||
Op string
|
User *types.GetPersonDetailsResponse
|
||||||
Site *types.GetSiteResponse
|
Now int64
|
||||||
Query string
|
XHR bool
|
||||||
SearchType string
|
Op string
|
||||||
Captcha *types.CaptchaResponse
|
Site *types.GetSiteResponse
|
||||||
Dark bool
|
Query string
|
||||||
ShowNSFW bool
|
Content string
|
||||||
|
SearchType string
|
||||||
|
Captcha *types.CaptchaResponse
|
||||||
|
Dark bool
|
||||||
|
ShowNSFW bool
|
||||||
|
HideInstanceNames bool
|
||||||
|
HideThumbnails bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s State) Unknown() string {
|
||||||
|
fmt.Println(fmt.Sprintf("%v", s.Error))
|
||||||
|
re := regexp.MustCompile(`(.*?)@(.*?)@`)
|
||||||
|
if strings.Contains(fmt.Sprintf("%v", s.Error), "couldnt_find_community") {
|
||||||
|
matches := re.FindAllStringSubmatch(s.CommunityName+"@", -1)
|
||||||
|
if len(matches) < 1 || len(matches[0]) < 3 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if matches[0][2] != s.Host {
|
||||||
|
remote := "/" + matches[0][2] + "/c/" + matches[0][1]
|
||||||
|
if os.Getenv("LEMMY_DOMAIN") != "" {
|
||||||
|
remote = "https:/" + remote
|
||||||
|
}
|
||||||
|
return remote
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.Contains(fmt.Sprintf("%v", s.Error), "couldnt_find_that_username_or_email") {
|
||||||
|
matches := re.FindAllStringSubmatch(s.UserName+"@", -1)
|
||||||
|
if len(matches) < 1 || len(matches[0]) < 3 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if matches[0][2] != s.Host {
|
||||||
|
remote := "/" + matches[0][2] + "/u/" + matches[0][1]
|
||||||
|
if os.Getenv("LEMMY_DOMAIN") != "" {
|
||||||
|
remote = "https:/" + remote
|
||||||
|
}
|
||||||
|
return remote
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
func (p State) SortBy(v string) string {
|
func (p State) SortBy(v string) string {
|
||||||
var q string
|
var q string
|
||||||
if p.Query != "" || p.SearchType == "Communities" {
|
if p.Query != "" || p.SearchType == "Communities" {
|
||||||
|
|
@ -163,6 +203,7 @@ func (state *State) ParseQuery(RawQuery string) {
|
||||||
}
|
}
|
||||||
if len(m["sort"]) > 0 {
|
if len(m["sort"]) > 0 {
|
||||||
state.Sort = m["sort"][0]
|
state.Sort = m["sort"][0]
|
||||||
|
state.CommentSort = m["sort"][0]
|
||||||
}
|
}
|
||||||
if len(m["communityname"]) > 0 {
|
if len(m["communityname"]) > 0 {
|
||||||
state.CommunityName = m["communityname"][0]
|
state.CommunityName = m["communityname"][0]
|
||||||
|
|
@ -221,17 +262,27 @@ func (state *State) GetCaptcha() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (state *State) GetSite() {
|
func (state *State) GetSite() {
|
||||||
token := state.Client.Token
|
|
||||||
state.Client.Token = ""
|
|
||||||
resp, err := state.Client.Site(context.Background(), types.GetSite{})
|
resp, err := state.Client.Site(context.Background(), types.GetSite{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
state.Status = http.StatusInternalServerError
|
state.Status = http.StatusInternalServerError
|
||||||
state.Host = "."
|
state.Host = "."
|
||||||
state.Error = errors.New("site unreachable")
|
state.Error = errors.New("unable to retrieve site")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
state.Client.Token = token
|
|
||||||
state.Site = resp
|
state.Site = resp
|
||||||
|
if !state.Site.MyUser.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, c := range state.Site.MyUser.MustValue().Follows {
|
||||||
|
state.Session.Communities = append(state.Session.Communities, types.CommunityView{
|
||||||
|
Community: c.Community,
|
||||||
|
Subscribed: "Subscribed",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Slice(state.Session.Communities, func(a, b int) bool {
|
||||||
|
return state.Session.Communities[a].Community.Name < state.Session.Communities[b].Community.Name
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (state *State) GetComment(commentid int) {
|
func (state *State) GetComment(commentid int) {
|
||||||
|
|
@ -241,7 +292,7 @@ func (state *State) GetComment(commentid int) {
|
||||||
state.CommentID = commentid
|
state.CommentID = commentid
|
||||||
cresp, err := state.Client.Comments(context.Background(), types.GetComments{
|
cresp, err := state.Client.Comments(context.Background(), types.GetComments{
|
||||||
ParentID: types.NewOptional(state.CommentID),
|
ParentID: types.NewOptional(state.CommentID),
|
||||||
Sort: types.NewOptional(types.CommentSortType(state.Sort)),
|
Sort: types.NewOptional(types.CommentSortType(state.CommentSort)),
|
||||||
Type: types.NewOptional(types.ListingType("All")),
|
Type: types.NewOptional(types.ListingType("All")),
|
||||||
Limit: types.NewOptional(int64(50)),
|
Limit: types.NewOptional(int64(50)),
|
||||||
})
|
})
|
||||||
|
|
@ -265,6 +316,9 @@ func (state *State) GetComment(commentid int) {
|
||||||
state.Comments = append(state.Comments, comment)
|
state.Comments = append(state.Comments, comment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(state.Comments) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
ctx, err := state.GetContext(state.Context, state.Comments[0])
|
ctx, err := state.GetContext(state.Context, state.Comments[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
|
@ -296,7 +350,7 @@ func (state *State) GetComments() {
|
||||||
}
|
}
|
||||||
cresp, err := state.Client.Comments(context.Background(), types.GetComments{
|
cresp, err := state.Client.Comments(context.Background(), types.GetComments{
|
||||||
PostID: types.NewOptional(state.PostID),
|
PostID: types.NewOptional(state.PostID),
|
||||||
Sort: types.NewOptional(types.CommentSortType(state.Sort)),
|
Sort: types.NewOptional(types.CommentSortType(state.CommentSort)),
|
||||||
Type: types.NewOptional(types.ListingType("All")),
|
Type: types.NewOptional(types.ListingType("All")),
|
||||||
Limit: types.NewOptional(int64(50)),
|
Limit: types.NewOptional(int64(50)),
|
||||||
Page: types.NewOptional(int64(state.Page)),
|
Page: types.NewOptional(int64(state.Page)),
|
||||||
|
|
@ -408,6 +462,7 @@ func (state *State) GetUser(username string) {
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
state.Error = err
|
||||||
state.Status = http.StatusInternalServerError
|
state.Status = http.StatusInternalServerError
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -492,7 +547,18 @@ func (state *State) GetPosts() {
|
||||||
|
|
||||||
func (state *State) Search(searchtype string) {
|
func (state *State) Search(searchtype string) {
|
||||||
if state.Query == "" && searchtype == "Communities" {
|
if state.Query == "" && searchtype == "Communities" {
|
||||||
|
if state.Listing == "Subscribed" {
|
||||||
|
if state.Page > 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if state.Site == nil {
|
||||||
|
state.GetSite()
|
||||||
|
}
|
||||||
|
state.Communities = state.Session.Communities
|
||||||
|
return
|
||||||
|
}
|
||||||
resp, err := state.Client.Communities(context.Background(), types.ListCommunities{
|
resp, err := state.Client.Communities(context.Background(), types.ListCommunities{
|
||||||
|
Type: types.NewOptional(types.ListingType(state.Listing)),
|
||||||
Sort: types.NewOptional(types.SortType(state.Sort)),
|
Sort: types.NewOptional(types.SortType(state.Sort)),
|
||||||
Limit: types.NewOptional(int64(25)),
|
Limit: types.NewOptional(int64(25)),
|
||||||
Page: types.NewOptional(int64(state.Page)),
|
Page: types.NewOptional(int64(state.Page)),
|
||||||
|
|
@ -507,7 +573,7 @@ func (state *State) Search(searchtype string) {
|
||||||
search := types.Search{
|
search := types.Search{
|
||||||
Q: state.Query,
|
Q: state.Query,
|
||||||
Sort: types.NewOptional(types.SortType(state.Sort)),
|
Sort: types.NewOptional(types.SortType(state.Sort)),
|
||||||
ListingType: types.NewOptional(types.ListingType("All")),
|
ListingType: types.NewOptional(types.ListingType(state.Listing)),
|
||||||
Type: types.NewOptional(types.SearchType(searchtype)),
|
Type: types.NewOptional(types.SearchType(searchtype)),
|
||||||
Limit: types.NewOptional(int64(25)),
|
Limit: types.NewOptional(int64(25)),
|
||||||
Page: types.NewOptional(int64(state.Page)),
|
Page: types.NewOptional(int64(state.Page)),
|
||||||
|
|
@ -559,22 +625,22 @@ func (state *State) GetPost(postid int) {
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.Status = http.StatusInternalServerError
|
state.Status = http.StatusInternalServerError
|
||||||
|
state.Error = err
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
state.Posts = []Post{Post{
|
|
||||||
PostView: resp.PostView,
|
|
||||||
State: state,
|
|
||||||
}}
|
|
||||||
if state.CommentID > 0 && len(state.Posts) > 0 {
|
|
||||||
state.Posts[0].Rank = -1
|
|
||||||
}
|
|
||||||
state.CommunityName = resp.PostView.Community.Name
|
|
||||||
cresp := types.GetCommunityResponse{
|
|
||||||
CommunityView: resp.CommunityView,
|
|
||||||
Moderators: resp.Moderators,
|
|
||||||
}
|
|
||||||
state.Community = &cresp
|
|
||||||
}
|
}
|
||||||
|
state.Posts = []Post{Post{
|
||||||
|
PostView: resp.PostView,
|
||||||
|
State: state,
|
||||||
|
}}
|
||||||
|
if state.CommentID > 0 && len(state.Posts) > 0 {
|
||||||
|
state.Posts[0].Rank = -1
|
||||||
|
}
|
||||||
|
state.CommunityName = resp.PostView.Community.Name
|
||||||
|
cresp := types.GetCommunityResponse{
|
||||||
|
CommunityView: resp.CommunityView,
|
||||||
|
Moderators: resp.Moderators,
|
||||||
|
}
|
||||||
|
state.Community = &cresp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (state *State) GetCommunity(communityName string) {
|
func (state *State) GetCommunity(communityName string) {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,12 @@
|
||||||
<a href="">{{$state.User.PersonView.Person.Name }}</a>
|
<a href="">{{$state.User.PersonView.Person.Name }}</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
in
|
in
|
||||||
<a href="/{{$state.Host}}/c/{{ fullcname $activity.Comment.P.Community }}">c/{{ $activity.Comment.P.Community.Name }}</a>
|
<a href="/{{$state.Host}}/c/{{ fullcname $activity.Comment.P.Community }}">
|
||||||
|
c/{{ if $state.HideInstanceNames -}}
|
||||||
|
{{ $activity.Comment.P.Community.Name }}</a>
|
||||||
|
{{ else -}}
|
||||||
|
{{ fullcname $activity.Comment.P.Community }}
|
||||||
|
{{ end }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{{ template "comment.html" $activity.Comment }}
|
{{ template "comment.html" $activity.Comment }}
|
||||||
|
|
@ -25,10 +30,22 @@
|
||||||
<b>message</b>
|
<b>message</b>
|
||||||
{{ if eq $activity.Message.Creator.ID $state.Session.UserID }}
|
{{ if eq $activity.Message.Creator.ID $state.Session.UserID }}
|
||||||
to
|
to
|
||||||
<a href="/{{$state.Host}}/u/{{fullname $activity.Message.Recipient}}">{{ $activity.Message.Recipient.Name }}</a>
|
<a href="/{{$state.Host}}/u/{{fullname $activity.Message.Recipient}}">
|
||||||
|
{{- if $state.HideInstanceNames -}}
|
||||||
|
{{ $activity.Message.Recipient.Name }}
|
||||||
|
{{- else -}}
|
||||||
|
{{ fullname $activity.Message.Recipient }}
|
||||||
|
{{- end -}}
|
||||||
|
</a>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
from
|
from
|
||||||
<a href="/{{$state.Host}}/u/{{fullname $activity.Message.Creator}}">{{ $activity.Message.Creator.Name }}</a>
|
<a href="/{{$state.Host}}/u/{{fullname $activity.Message.Creator}}">
|
||||||
|
{{- if $state.HideInstanceNames -}}
|
||||||
|
{{ $activity.Message.Creator.Name }}
|
||||||
|
{{- else -}}
|
||||||
|
{{ fullname $activity.Message.Creator }}
|
||||||
|
{{- end -}}
|
||||||
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
sent {{ humanize $activity.Message.PrivateMessage.Published.Time }}
|
sent {{ humanize $activity.Message.PrivateMessage.Published.Time }}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<div class="comment{{if or (lt .P.Counts.Score -5) .P.Comment.Deleted }} hidden{{end}}" id="c{{.P.Comment.ID}}">
|
<div class="comment{{if or (lt .P.Counts.Score -5) .P.Comment.Deleted .P.Comment.Removed }} hidden{{end}}" id="c{{.P.Comment.ID}}">
|
||||||
{{ if .State.Session }}
|
{{ if .State.Session }}
|
||||||
<div class="score">
|
<div class="score">
|
||||||
<form class="link-btn{{ if eq .P.MyVote.String "1"}} like{{ else if eq .P.MyVote.String "-1"}} dislike{{end}}" method="POST">
|
<form class="link-btn{{ if eq .P.MyVote.String "1"}} like{{ else if eq .P.MyVote.String "-1"}} dislike{{end}}" method="POST">
|
||||||
|
|
@ -15,44 +15,44 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<a class="minimize" href="" for="c{{.P.Comment.ID}}">
|
<a class="minimize" href="" for="c{{.P.Comment.ID}}">
|
||||||
{{if or (lt .P.Counts.Score -5) .P.Comment.Deleted }}
|
{{- if or (lt .P.Counts.Score -5) .P.Comment.Deleted -}}
|
||||||
[+]
|
[+]
|
||||||
{{ else }}
|
{{- else -}}
|
||||||
[-]
|
[-]
|
||||||
{{ end }}
|
{{- end -}}
|
||||||
|
</a>
|
||||||
|
<a {{ if .P.Comment.Distinguished}}class="{{if .P.Creator.Admin}}admin {{end}}distinguished"{{ else if .Submitter }}class="submitter"{{end}} href="/{{.State.Host}}/u/{{fullname .P.Creator}}">
|
||||||
|
{{- if .State.HideInstanceNames -}}
|
||||||
|
{{ .P.Creator.Name }}
|
||||||
|
{{- else -}}
|
||||||
|
{{ fullname .P.Creator }}
|
||||||
|
{{- end -}}
|
||||||
</a>
|
</a>
|
||||||
<a {{ if .P.Comment.Distinguished}}class="{{if .P.Creator.Admin}}admin {{end}}distinguished"{{ else if .Submitter }}class="submitter"{{end}} href="/{{.State.Host}}/u/{{fullname .P.Creator}}">{{fullname .P.Creator}}</a>
|
|
||||||
<b>{{.P.Counts.Score}} points</b> <span title="{{.P.Comment.Published.Time}}">{{ humanize .P.Comment.Published.Time }}</span>
|
<b>{{.P.Counts.Score}} points</b> <span title="{{.P.Comment.Published.Time}}">{{ humanize .P.Comment.Published.Time }}</span>
|
||||||
{{- if gt .P.Comment.Updated.Time.Unix .P.Comment.Published.Time.Unix -}}
|
{{- if gt .P.Comment.Updated.Time.Unix .P.Comment.Published.Time.Unix -}}
|
||||||
* (last edited <span title="{{.P.Comment.Updated.Time}}">{{ humanize .P.Comment.Updated.Time }}</span>)
|
* (last edited <span title="{{.P.Comment.Updated.Time}}">{{ humanize .P.Comment.Updated.Time }}</span>)
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="content">
|
||||||
{{ if eq .Op "edit" }}
|
{{ if eq .Op "edit" }}
|
||||||
<div class="content">
|
{{ template "create_comment.html" .State }}
|
||||||
<form class="savecomment" method="POST">
|
|
||||||
<div>
|
|
||||||
<textarea required name="content">{{ .P.Comment.Content }}</textarea>
|
|
||||||
</div>
|
|
||||||
<input type="hidden" name="commentid" value="{{.P.Comment.ID}}">
|
|
||||||
<input type="hidden" name="op" value="edit_comment">
|
|
||||||
<input name="submit" type="submit" value="save">
|
|
||||||
<input name="submit" type="submit" value="cancel">
|
|
||||||
</form>
|
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<div class="content">
|
{{if .P.Comment.Deleted}}
|
||||||
{{if .P.Comment.Deleted}}
|
[deleted]
|
||||||
[removed]
|
{{else if .P.Comment.Removed }}
|
||||||
{{else}}
|
[removed by mod]
|
||||||
|
{{else}}
|
||||||
<div {{ if and .Selected (not .State.XHR) (ne .State.Op "reply")}}class="highlight" {{end}}>
|
<div {{ if and .Selected (not .State.XHR) (ne .State.Op "reply")}}class="highlight" {{end}}>
|
||||||
{{ markdown .State.Host .P.Comment.Content }}
|
{{ markdown .State.Host .P.Comment.Content }}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{ if eq .Op "source" }}
|
{{ if eq .Op "source" }}
|
||||||
<div><textarea>{{.P.Comment.Content}}</textarea></div>
|
<div><textarea>{{.P.Comment.Content}}</textarea></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<ul class="buttons">
|
<ul class="buttons">
|
||||||
<li><a href="/{{.State.Host}}/comment/{{.P.Comment.ID}}">permalink</a></li>
|
<li><a href="/{{.State.Host}}/comment/{{.P.Comment.ID}}">permalink</a></li>
|
||||||
|
<li><a href="{{.P.Comment.ApID}}">fedilink</a></li>
|
||||||
{{ if ne .Op "source"}}
|
{{ if ne .Op "source"}}
|
||||||
<li><a class="source" for="c{{.P.Comment.ID}}" href="/{{.State.Host}}/comment/{{.P.Comment.ID}}?source">source</a></li>
|
<li><a class="source" for="c{{.P.Comment.ID}}" href="/{{.State.Host}}/comment/{{.P.Comment.ID}}?source">source</a></li>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
|
|
@ -87,7 +87,7 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if and .ParentID .State.CommentID }}
|
{{ if and .ParentID .State.CommentID (not .State.XHR) }}
|
||||||
<li>
|
<li>
|
||||||
<a href="/{{.State.Host}}/comment/{{.ParentID}}">parent</a>
|
<a href="/{{.State.Host}}/comment/{{.ParentID}}">parent</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -104,15 +104,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="children">
|
<div class="children">
|
||||||
{{ if and (eq .State.Op "reply") (eq .State.CommentID .P.Comment.ID)}}
|
{{ if and (eq .State.Op "reply") (eq .State.CommentID .P.Comment.ID)}}
|
||||||
<form class="savecomment" method="POST">
|
{{ template "create_comment.html" .State }}
|
||||||
<div>
|
|
||||||
<textarea name="content"></textarea>
|
|
||||||
</div>
|
|
||||||
<input type="hidden" name="parentid" value="{{.P.Comment.ID}}">
|
|
||||||
<input type="hidden" name="op" value="create_comment">
|
|
||||||
<input type="submit" name="submit" value="save">
|
|
||||||
<input type="submit" name="submit" value="cancel">
|
|
||||||
</form>
|
|
||||||
{{ end}}
|
{{ end}}
|
||||||
{{ range $ci, $child := .C }}{{ template "comment.html" $child }}{{end}}
|
{{ range $ci, $child := .C }}{{ template "comment.html" $child }}{{end}}
|
||||||
{{ if and (ne .P.Counts.ChildCount .ChildCount) (not .State.Activities) (not .State.Query) }}
|
{{ if and (ne .P.Counts.ChildCount .ChildCount) (not .State.Activities) (not .State.Query) }}
|
||||||
|
|
@ -122,4 +114,4 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
<div class="community">
|
<div class="community">
|
||||||
<form method="POST" class="member {{ membership .Subscribed }}">
|
<form method="POST" class="member {{ membership .Subscribed }}">
|
||||||
<input name="op" type="submit" value="{{ membership .Subscribed}}">
|
<input name="op" type="submit" value="{{ membership .Subscribed}}">
|
||||||
<input type="hidden" name="communityid" value ="{{.Community.ID}}">
|
<input type="hidden" name="communityid" value ="{{ .Community.ID }}">
|
||||||
</form>
|
</form>
|
||||||
<span class="title"><a href="{{proxy .Community.ActorID}}">c/{{fullcname .Community}}: {{.Community.Title}}</a></span>
|
<span class="title"><a href="{{ if .Community.Local }}./c/{{.Community.Name}}{{else}}{{ localize .Community.ActorID }}{{end}}">c/{{fullcname .Community}}: {{.Community.Title}}</a></span>
|
||||||
<div class="details">
|
<div class="details">
|
||||||
{{ if .Community.Description.IsValid }}
|
{{ if .Community.Description.IsValid }}
|
||||||
<div class="description">
|
<div class="description">
|
||||||
{{markdown "poop" .Community.Description.String}}
|
{{ markdown "" .Community.Description.String }}
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="gray">
|
<div class="gray">
|
||||||
{{printer .Counts.Subscribers}} subscribers,
|
{{ if .Counts.Subscribers }}{{ printer .Counts.Subscribers }} subscribers,{{end}}
|
||||||
a community founded {{ humanize .Community.Published.Time }}
|
a community founded {{ humanize .Community.Published.Time }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
45
templates/create_comment.html
Normal file
45
templates/create_comment.html
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<form class="savecomment" method="POST" enctype="multipart/form-data"
|
||||||
|
{{- if .CommentID }} action="/{{.Host}}/comment/{{.CommentID}}"
|
||||||
|
{{- else }} action="/{{.Host}}/post/{{.PostID}}"
|
||||||
|
{{- end -}}
|
||||||
|
>
|
||||||
|
<div class="upload">
|
||||||
|
<label title="upload photo"><div>📷</div>
|
||||||
|
<input class="imgupload" type="file" name="file" accept="image/*">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<textarea name="content">
|
||||||
|
{{- if .Content }}
|
||||||
|
{{- .Content -}}
|
||||||
|
{{ else if and (eq .Op "edit") .Comments }}
|
||||||
|
{{- (index .Comments 0).P.Comment.Content -}}
|
||||||
|
{{ end -}}
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
{{ if eq .Op "edit" }}
|
||||||
|
<input type="hidden" name="op" value="edit_comment">
|
||||||
|
<input type="hidden" name="commentid" value="{{.CommentID}}">
|
||||||
|
{{ else }}
|
||||||
|
<input type="hidden" name="op" value="create_comment">
|
||||||
|
{{ end }}
|
||||||
|
<input type="hidden" name="parentid" value="{{.CommentID}}">
|
||||||
|
<input type="submit" name="submit" value="save">
|
||||||
|
{{ if or .Op .Content }}
|
||||||
|
<input type="submit" name="submit" value="cancel">
|
||||||
|
{{ end }}
|
||||||
|
<div class="right">
|
||||||
|
<a href="https://join-lemmy.org/docs/users/02-media.html" target="_blank">formatting help</a>
|
||||||
|
<input name="submit" type="submit" value="preview">
|
||||||
|
</div>
|
||||||
|
{{ if .Content }}
|
||||||
|
<div class="preview">
|
||||||
|
<div class="comment">
|
||||||
|
<h3>Preview</h3>
|
||||||
|
<div class="content">
|
||||||
|
{{ markdown .Host .Content }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</form>
|
||||||
|
|
@ -2,26 +2,12 @@
|
||||||
<head>
|
<head>
|
||||||
<title>{{ if and .Community (ne .Community.CommunityView.Community.Title "")}}{{.Community.CommunityView.Community.Title}}{{else if ne .CommunityName ""}}/c/{{.CommunityName}}{{ else if .User}}overview for {{.User.PersonView.Person.Name}}{{else}}{{ host .Host }}{{end}}</title>
|
<title>{{ if and .Community (ne .Community.CommunityView.Community.Title "")}}{{.Community.CommunityView.Community.Title}}{{else if ne .CommunityName ""}}/c/{{.CommunityName}}{{ else if .User}}overview for {{.User.PersonView.Person.Name}}{{else}}{{ host .Host }}{{end}}</title>
|
||||||
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
|
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
|
||||||
<link rel="stylesheet" href="/_/static/style.css?v=23">
|
<link rel="stylesheet" href="/_/static/style.css?v={{ .Version }}">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</head>
|
</head>
|
||||||
<body {{ if .Dark }}class="dark"{{end}}>
|
<body {{ if .Dark }}class="dark"{{end}}>
|
||||||
<noscript>
|
<noscript>
|
||||||
<style>
|
<link rel="stylesheet" href="/_/static/noscript.css?v={{ .Version }}">
|
||||||
.expando-button,
|
|
||||||
.minimize,
|
|
||||||
#loadmore,
|
|
||||||
#showimages,
|
|
||||||
.hidechildren {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
div.pager {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.post .expando .image img {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
{{ template "nav.html" . -}}
|
{{ template "nav.html" . -}}
|
||||||
|
|
@ -29,12 +15,15 @@
|
||||||
{{ template "nsfw.html" }}
|
{{ template "nsfw.html" }}
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<main>
|
<main>
|
||||||
{{ if or (contains .Sort "Top") (and (not .PostID) (not .User) (not .Community) (not .Activities) (eq .Op ""))}}
|
{{ template "menu.html" . }}
|
||||||
{{ template "menu.html" . }}
|
|
||||||
{{ end}}
|
|
||||||
|
|
||||||
{{ if .Error }}
|
{{ if .Error }}
|
||||||
<div class="error">{{.Error}}</div>
|
<div class="error">
|
||||||
|
{{.Error}}.
|
||||||
|
{{ if .Unknown }}
|
||||||
|
try remote instance: <a href="{{ .Unknown }}">{{ .Unknown }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ range .Posts }}
|
{{ range .Posts }}
|
||||||
|
|
@ -56,6 +45,6 @@
|
||||||
{{ template "sidebar.html" . }}
|
{{ template "sidebar.html" . }}
|
||||||
</main>
|
</main>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<script src="/_/static/utils.js?v=19"></script>
|
<script src="/_/static/utils.js?v={{ .Version }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<head>
|
<head>
|
||||||
<title>{{ host .Host }}: sign up or log in</title>
|
<title>{{ host .Host }}: sign up or log in</title>
|
||||||
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
|
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
|
||||||
<link rel="stylesheet" href="/_/static/style.css?v=7">
|
<link rel="stylesheet" href="/_/static/style.css?v={{ .Version }}">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</head>
|
</head>
|
||||||
<body {{ if .Dark }}class="dark"{{end}}>
|
<body {{ if .Dark }}class="dark"{{end}}>
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
{{ if ne .Op "2fa" }}
|
{{ if ne .Op "2fa" }}
|
||||||
<div>
|
<div>
|
||||||
<h2>create a new account</h2>
|
<h2>create a new account</h2>
|
||||||
<form method="POST">
|
<form method="POST" action="/{{ .Host}}/login">
|
||||||
<label>
|
<label>
|
||||||
username
|
username
|
||||||
<div><input required name="username" type="text"></div>
|
<div><input required name="username" type="text"></div>
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,11 @@
|
||||||
<title>{{if and .Posts .PostID }}{{ (index .Posts 0).Post.Name}} : {{.CommunityName}}{{else if and .Community (ne .Community.CommunityView.Community.Title "")}}{{.Community.CommunityView.Community.Title}}{{else if ne .CommunityName ""}}/c/{{.CommunityName}}{{ else if .User}}overview for {{.User.PersonView.Person.Name}}{{else}}{{ host .Host }}{{end}}</title>
|
<title>{{if and .Posts .PostID }}{{ (index .Posts 0).Post.Name}} : {{.CommunityName}}{{else if and .Community (ne .Community.CommunityView.Community.Title "")}}{{.Community.CommunityView.Community.Title}}{{else if ne .CommunityName ""}}/c/{{.CommunityName}}{{ else if .User}}overview for {{.User.PersonView.Person.Name}}{{else}}{{ host .Host }}{{end}}</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
|
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
|
||||||
<link rel="stylesheet" href="/_/static/style.css?v=23">
|
<link rel="stylesheet" href="/_/static/style.css?v={{ .Version }}">
|
||||||
</head>
|
</head>
|
||||||
<body{{ if .Dark }} class="dark"{{end}}>
|
<body{{ if .Dark }} class="dark"{{end}}>
|
||||||
<noscript>
|
<noscript>
|
||||||
<style>
|
<link rel="stylesheet" href="/_/static/noscript.css?v={{ .Version }}">
|
||||||
.scripting,
|
|
||||||
.expando-button,
|
|
||||||
.minimize,
|
|
||||||
#showimages,
|
|
||||||
#lmc,
|
|
||||||
.hidechildren {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
.post .expando .image img {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
{{ template "nav.html" . -}}
|
{{ template "nav.html" . -}}
|
||||||
|
|
@ -27,12 +15,14 @@
|
||||||
{{ template "nsfw.html" }}
|
{{ template "nsfw.html" }}
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<main>
|
<main>
|
||||||
{{ if or (contains .Sort "Top") (and (not .PostID) (not .User) (not .Community) (not .Activities) (eq .Op ""))}}
|
{{ if or (.Query) (.SearchType) (and (not .PostID) (not .User) (not .Activities) (eq .Op ""))}}
|
||||||
{{ template "menu.html" . }}
|
{{ template "menu.html" . }}
|
||||||
{{ end}}
|
{{ end}}
|
||||||
|
|
||||||
{{ if or (ne .Query "") .Communities }}
|
{{ if or (ne .Query "") .Communities }}
|
||||||
<form class="search" method="GET">
|
<form class="search" method="GET">
|
||||||
|
<input type="hidden" name="sort" value="{{.Sort}}">
|
||||||
|
<input type="hidden" name="listingType" value="{{.Listing}}">
|
||||||
<div>search</div>
|
<div>search</div>
|
||||||
<div class="query">
|
<div class="query">
|
||||||
<input type="text" name="q" value="{{.Query}}">
|
<input type="text" name="q" value="{{.Query}}">
|
||||||
|
|
@ -56,7 +46,12 @@
|
||||||
{{ end}}
|
{{ end}}
|
||||||
|
|
||||||
{{ if .Error }}
|
{{ if .Error }}
|
||||||
<div class="error">{{.Error}}</div>
|
<div class="error">
|
||||||
|
{{.Error}}.
|
||||||
|
{{ if .Unknown }}
|
||||||
|
try remote instance: <a href="{{ .Unknown }}">{{ .Unknown }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ range .Communities }}
|
{{ range .Communities }}
|
||||||
|
|
@ -85,20 +80,16 @@
|
||||||
{{if .Comments}}{{if gt .Page 1}}(page {{ .Page }}) {{else if lt (index .Posts 0).Counts.Comments .CommentCount }}all{{else}}top{{end}} {{.CommentCount}} comments{{else}} no comments (yet){{end}}
|
{{if .Comments}}{{if gt .Page 1}}(page {{ .Page }}) {{else if lt (index .Posts 0).Counts.Comments .CommentCount }}all{{else}}top{{end}} {{.CommentCount}} comments{{else}} no comments (yet){{end}}
|
||||||
<div>
|
<div>
|
||||||
sorted by:
|
sorted by:
|
||||||
<a {{ if eq .Sort "Hot"}}class="selected"{{end}} href="{{ .SortBy "Hot"}}">hot</a>
|
<a {{ if eq .CommentSort "Hot"}}class="selected"{{end}} href="{{ .SortBy "Hot"}}">hot</a>
|
||||||
<a {{ if eq .Sort "Top"}}class="selected"{{end}} href="{{ .SortBy "Top"}}">top</a>
|
<a {{ if eq .CommentSort "Top"}}class="selected"{{end}} href="{{ .SortBy "Top"}}">top</a>
|
||||||
<a {{ if eq .Sort "New"}}class="selected"{{end}} href="{{ .SortBy "New"}}">new</a>
|
<a {{ if eq .CommentSort "New"}}class="selected"{{end}} href="{{ .SortBy "New"}}">new</a>
|
||||||
<a {{ if eq .Sort "Old"}}class="selected"{{end}} href="{{ .SortBy "Old"}}">old</a>
|
<a {{ if eq .CommentSort "Old"}}class="selected"{{end}} href="{{ .SortBy "Old"}}">old</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ if and .Session (ne .Op "edit_post") }}
|
{{ if and .Session (ne .Op "edit_post") }}
|
||||||
<form class="savecomment" method="POST">
|
<div class="create_comment">
|
||||||
<div>
|
{{ template "create_comment.html" .}}
|
||||||
<textarea required name="content" {{ if (index .Posts 0).Post.Deleted }} disabled {{end}}></textarea>
|
</div>
|
||||||
</div>
|
|
||||||
<input type="hidden" name="op" value="create_comment">
|
|
||||||
<input type="submit" name="submit" value="save"{{ if (index .Posts 0).Post.Deleted }} disabled {{end}}>
|
|
||||||
</form>
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end}}
|
{{ end}}
|
||||||
|
|
@ -133,7 +124,10 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
<script src="/_/static/utils.js?v=19"></script>
|
<script src="/_/static/utils.js?v={{ .Version }}"></script>
|
||||||
|
{{ if .Watch }}
|
||||||
|
<script src="/_/static/ws.js"></script>
|
||||||
|
{{ end }}
|
||||||
{{ template "sidebar.html" . }}
|
{{ template "sidebar.html" . }}
|
||||||
</main>
|
</main>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,18 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if contains .Sort "Top" }}
|
{{ if contains .Sort "Top" }}
|
||||||
links from past:
|
links from past:
|
||||||
|
<a {{ if eq .Sort "TopHour"}}class="selected"{{end}} href="{{ .SortBy "TopHour"}}">1h</a>
|
||||||
|
<span>-</span>
|
||||||
|
<a {{ if eq .Sort "TopSixHour"}}class="selected"{{end}} href="{{ .SortBy "TopSixHour"}}">6h</a>
|
||||||
|
<span>-</span>
|
||||||
|
<a {{ if eq .Sort "TopTwelveHour"}}class="selected"{{end}} href="{{ .SortBy "TopTwelveHour"}}">12h</a>
|
||||||
|
<span>-</span>
|
||||||
<a {{ if eq .Sort "TopDay"}}class="selected"{{end}} href="{{ .SortBy "TopDay"}}">day</a>
|
<a {{ if eq .Sort "TopDay"}}class="selected"{{end}} href="{{ .SortBy "TopDay"}}">day</a>
|
||||||
<span>-</span>
|
<span>-</span>
|
||||||
<a {{ if eq .Sort "TopMonth"}}class="selected"{{end}} href="{{ .SortBy "TopMonth"}}">month</a>
|
|
||||||
<span>-</span>
|
|
||||||
<a {{ if eq .Sort "TopWeek"}}class="selected"{{end}} href="{{ .SortBy "TopWeek"}}">week</a>
|
<a {{ if eq .Sort "TopWeek"}}class="selected"{{end}} href="{{ .SortBy "TopWeek"}}">week</a>
|
||||||
<span>-</span>
|
<span>-</span>
|
||||||
|
<a {{ if eq .Sort "TopMonth"}}class="selected"{{end}} href="{{ .SortBy "TopMonth"}}">month</a>
|
||||||
|
<span>-</span>
|
||||||
<a {{ if eq .Sort "TopYear"}}class="selected"{{end}} href="{{ .SortBy "TopYear"}}">year</a>
|
<a {{ if eq .Sort "TopYear"}}class="selected"{{end}} href="{{ .SortBy "TopYear"}}">year</a>
|
||||||
<span>-</span>
|
<span>-</span>
|
||||||
<a {{ if eq .Sort "TopAll"}}class="selected"{{end}} href="{{ .SortBy "TopAll"}}">all time</a>
|
<a {{ if eq .Sort "TopAll"}}class="selected"{{end}} href="{{ .SortBy "TopAll"}}">all time</a>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
|
{{ $state := . }}
|
||||||
<nav>
|
<nav>
|
||||||
<div class="communities">
|
<div class="communities">
|
||||||
|
{{ if .Session }}
|
||||||
|
<a id="openmycommunities" href="/{{.Host}}/search?searchtype=Communities&listingType=Subscribed&sort=TopMonth&page=0">my communities ▼</a>
|
||||||
|
{{ end }}
|
||||||
<a href="/{{.Host}}">home</a>
|
<a href="/{{.Host}}">home</a>
|
||||||
<span> - </span>
|
<span> - </span>
|
||||||
<a href="/{{.Host}}?listingType=All">all</a>
|
<a href="/{{.Host}}?listingType=All">all</a>
|
||||||
|
|
@ -9,7 +13,15 @@
|
||||||
<a href="/{{$host}}/c/{{fullcname $c.Community}}">{{$c.Community.Name}}</a>
|
<a href="/{{$host}}/c/{{fullcname $c.Community}}">{{$c.Community.Name}}</a>
|
||||||
<span> - </span>
|
<span> - </span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<a href="/{{$host}}/search?searchtype=Communities" class="more">more »</a>
|
<a href="/{{$host}}/search?searchtype=Communities&sort=TopMonth" class="more">more »</a>
|
||||||
|
</div>
|
||||||
|
<div id="mycommunities">
|
||||||
|
{{- if and .Session .Session.Communities }}
|
||||||
|
<a href="/{{.Host}}/search?searchtype=Communities&listingType=Subscribed&sort=TopMonth&page=0">view all »</a>
|
||||||
|
{{ range .Session.Communities }}
|
||||||
|
<a href="/{{ $state.Host}}/{{ if .Community.Local }}c/{{.Community.Name}}{{else}}{{ localize .Community.ActorID }}{{end}}">{{fullcname .Community }}</a>
|
||||||
|
{{ end }}
|
||||||
|
{{ end -}}
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
{{ if .Session }}
|
{{ if .Session }}
|
||||||
|
|
@ -48,14 +60,14 @@
|
||||||
<span>: search</span>
|
<span>: search</span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<ul>
|
<ul>
|
||||||
{{ if .User }}
|
{{ if and .User (not .Query)}}
|
||||||
<li {{if eq .Op "" }}class="selected"{{end}}><a href="?">overview</a></li>
|
<li {{if eq .Op "" }}class="selected"{{end}}><a href="?">overview</a></li>
|
||||||
{{ if and .Session (eq .User.PersonView.Person.ID .Session.UserID) }}
|
{{ if and .Session (eq .User.PersonView.Person.ID .Session.UserID) }}
|
||||||
<li {{if eq .Op "Saved"}}class="selected"{{end}}><a href="?view=Saved">saved</a></li>
|
<li {{if eq .Op "Saved"}}class="selected"{{end}}><a href="?view=Saved">saved</a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ else if .Comments -}}
|
{{ else if .Comments -}}
|
||||||
<li class="selected"><a href="">comments</a></li>
|
<li class="selected"><a href="">comments</a></li>
|
||||||
{{ else if .Activities }}
|
{{ else if and .Activities (not .Query) }}
|
||||||
<li class="selected"><a href="">mailbox</a></li>
|
<li class="selected"><a href="">mailbox</a></li>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<li{{ if eq .Sort "Hot" }} class="selected"{{end}}><a href="{{ .SortBy "Hot" }}">hot</a></li>
|
<li{{ if eq .Sort "Hot" }} class="selected"{{end}}><a href="{{ .SortBy "Hot" }}">hot</a></li>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{{ if and (ne .State.Op "vote_post") (ne .State.Op "save_post") }}
|
{{ if and (ne .State.Op "vote_post") (ne .State.Op "save_post") }}
|
||||||
<div class="post{{if .Post.Deleted}} deleted{{end}}{{ if .Post.FeaturedCommunity }} distinguished{{end}}{{if .Post.FeaturedLocal }} announcement{{end}}">
|
<div class="post{{if .Post.Deleted}} deleted{{end}}{{ if .Post.FeaturedCommunity }} distinguished{{end}}{{if .Post.FeaturedLocal }} announcement{{end}}" id="p{{.Post.ID}}">
|
||||||
{{ if gt .Rank 0 }}
|
{{ if gt .Rank 0 }}
|
||||||
<div class="rank"> {{ .Rank }} </div>
|
<div class="rank"> {{ .Rank }} </div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
@ -23,17 +23,19 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if and (ne .State.Op "vote_post") (ne .State.Op "save_post") }}
|
{{ if and (ne .State.Op "vote_post") (ne .State.Op "save_post") }}
|
||||||
</div>
|
</div>
|
||||||
|
{{ if not .State.HideThumbnails }}
|
||||||
<div class="thumb">
|
<div class="thumb">
|
||||||
<a class="url" href="{{ if .Post.URL.IsValid }}{{ .Post.URL }}{{ else }}/{{ .State.Host }}/post/{{ .Post.ID }}{{ end }}">
|
<a class="url" href="{{ if .Post.URL.IsValid }}{{ .Post.URL }}{{ else }}/{{ .State.Host }}/post/{{ .Post.ID }}{{ end }}">
|
||||||
<div {{ if and .Post.NSFW (not (and .State.Community .State.Community.CommunityView.Community.NSFW))}}class="img-blur"{{end}} style="background-image: url({{thumbnail .Post}})"></div>
|
<div {{ if and .Post.NSFW (not (and .State.Community .State.Community.CommunityView.Community.NSFW))}}class="img-blur"{{end}} style="background-image: url({{thumbnail .Post}})"></div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
<div class="entry">
|
<div class="entry">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<a class="url" href="{{ if .Post.URL.IsValid }}{{ .Post.URL }}{{ else }}/{{ .State.Host }}/post/{{ .Post.ID }}{{ end }}">{{ rmmarkdown .Post.Name }}</a>
|
<a class="url" href="{{ if .Post.URL.IsValid }}{{ .Post.URL }}{{ else }}/{{ .State.Host }}/post/{{ .Post.ID }}{{ end }}">{{ rmmarkdown .Post.Name }}</a>
|
||||||
({{ domain . }})
|
({{ domain . }})
|
||||||
</div>
|
</div>
|
||||||
<div class="expando-button {{ if and (not (and .Post.Body.IsValid .Post.Body.String )) (not (isImage .Post.URL.String)) }}hidden{{else if eq .Rank 0}}open{{ end }}"></div>
|
<div class="expando-button{{ if and (not (and .Post.Body.IsValid .Post.Body.String )) (not (isImage .Post.URL.String)) }} hidden{{else if eq .Rank 0}} open{{ end }}"></div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
submitted
|
submitted
|
||||||
<span title="{{.Post.Published.Time}}">{{ humanize .Post.Published.Time -}}</span>
|
<span title="{{.Post.Published.Time}}">{{ humanize .Post.Published.Time -}}</span>
|
||||||
|
|
@ -41,13 +43,26 @@
|
||||||
* (last edited <span title="{{.Post.Updated.Time}}">{{ humanize .Post.Updated.Time }}</span>)
|
* (last edited <span title="{{.Post.Updated.Time}}">{{ humanize .Post.Updated.Time }}</span>)
|
||||||
{{ end }}
|
{{ end }}
|
||||||
by
|
by
|
||||||
<a class="submitter{{ if .Creator.Admin}} admin{{end}}" href="/{{ .State.Host }}/u/{{ fullname .Creator }}">{{ fullname .Creator }}</a>
|
<a class="submitter{{ if .Creator.Admin}} admin{{end}}" href="/{{ .State.Host }}/u/{{ fullname .Creator }}">
|
||||||
|
{{- if .State.HideInstanceNames -}}
|
||||||
|
{{ .Creator.Name }}
|
||||||
|
{{- else -}}
|
||||||
|
{{ fullname .Creator }}
|
||||||
|
{{- end -}}
|
||||||
|
</a>
|
||||||
to
|
to
|
||||||
<a href="/{{ .State.Host }}/c/{{ fullcname .Community }}">c/{{ fullcname .Community}}</a>
|
<a href="/{{ .State.Host }}/c/{{ fullcname .Community }}">
|
||||||
|
c/{{ if .State.HideInstanceNames -}}
|
||||||
|
{{ .Community.Name }}
|
||||||
|
{{ else -}}
|
||||||
|
{{ fullcname .Community }}
|
||||||
|
{{ end }}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
{{ if .Post.NSFW }}<span class="nsfw">NSFW</span>{{end}}
|
{{ if .Post.NSFW }}<span class="nsfw">NSFW</span>{{end}}
|
||||||
<a href="/{{ .State.Host }}/post/{{ .Post.ID }}">{{ .Counts.Comments }} comments</a>
|
<a href="/{{ .State.Host }}/post/{{ .Post.ID }}">{{ .Counts.Comments }} comments</a>
|
||||||
|
<a href="{{ .Post.ApID}}">fedilink</a>
|
||||||
{{ if and .State.Session (eq .State.Session.UserID .Post.CreatorID) }}
|
{{ if and .State.Session (eq .State.Session.UserID .Post.CreatorID) }}
|
||||||
{{ if not .Post.Deleted }}<a href="/{{ .State.Host }}/post/{{ .Post.ID }}?edit">edit</a>{{end}}
|
{{ if not .Post.Deleted }}<a href="/{{ .State.Host }}/post/{{ .Post.ID }}?edit">edit</a>{{end}}
|
||||||
<form class="link-btn" method="POST">
|
<form class="link-btn" method="POST">
|
||||||
|
|
@ -79,11 +94,22 @@
|
||||||
{{ if .State.PostID }}
|
{{ if .State.PostID }}
|
||||||
<a id="hidechildren" class="scripting" href="">hide all child comments</a>
|
<a id="hidechildren" class="scripting" href="">hide all child comments</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ if and .State.Site .State.Site.MyUser.IsValid (not .State.Site.MyUser.MustValue.LocalUserView.LocalUser.ShowReadPosts) }}
|
||||||
|
<form class="link-btn" method="POST">
|
||||||
|
<input type="hidden" name="postid" value="{{.Post.ID }}">
|
||||||
|
<input type="hidden" name="op" value="read_post">
|
||||||
|
{{ if .Post.Deleted }}
|
||||||
|
<input type="submit" name="submit" value="mark unread">
|
||||||
|
{{ else }}
|
||||||
|
<input type="submit" name="submit" value="mark read">
|
||||||
|
{{ end }}
|
||||||
|
</form>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div></div>
|
<div></div>
|
||||||
<div class="clearleft"></div>
|
<div class="clearleft"></div>
|
||||||
<div class="expando {{ if eq .Rank 0 }}open{{ end}}">
|
<div class="expando{{ if eq .Rank 0 }} open{{ end}}">
|
||||||
{{ if (and .Post.Body.IsValid (ne .Post.Body.String "")) }}
|
{{ if (and .Post.Body.IsValid (ne .Post.Body.String "")) }}
|
||||||
<div class="md">{{ markdown .State.Host .Post.Body.String }}</div>
|
<div class="md">{{ markdown .State.Host .Post.Body.String }}</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,12 @@
|
||||||
<head>
|
<head>
|
||||||
<title>{{ host .Host }}: preferences</title>
|
<title>{{ host .Host }}: preferences</title>
|
||||||
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
|
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
|
||||||
<link rel="stylesheet" href="/_/static/style.css?v=15">
|
<link rel="stylesheet" href="/_/static/style.css?v={{ .Version }}">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</head>
|
</head>
|
||||||
<body {{ if .Dark}}class="dark"{{end}}>
|
<body {{ if .Dark}}class="dark"{{end}}>
|
||||||
<noscript>
|
<noscript>
|
||||||
<style>
|
<link rel="stylesheet" href="/_/static/noscript.css?v={{ .Version }}">
|
||||||
.scripting {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</noscript>
|
</noscript>
|
||||||
<nav>
|
<nav>
|
||||||
<div class="communities">
|
<div class="communities">
|
||||||
|
|
@ -74,7 +70,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>
|
<label>
|
||||||
default sort
|
default post sort
|
||||||
</label>
|
</label>
|
||||||
<select name="DefaultSortType">
|
<select name="DefaultSortType">
|
||||||
<option value="Hot"{{ if eq .Sort "Hot"}} selected{{end}}>Hot</option>
|
<option value="Hot"{{ if eq .Sort "Hot"}} selected{{end}}>Hot</option>
|
||||||
|
|
@ -93,6 +89,17 @@
|
||||||
<option value="TopAll"{{ if eq .Sort "TopAll"}} selected{{end}}>Top All Time</option></select>
|
<option value="TopAll"{{ if eq .Sort "TopAll"}} selected{{end}}>Top All Time</option></select>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
default comment sort
|
||||||
|
</label>
|
||||||
|
<select name="DefaultCommentSortType">
|
||||||
|
<option value="Hot"{{ if eq .CommentSort "Hot"}} selected{{end}}>Hot</option>
|
||||||
|
<option value="New"{{ if eq .CommentSort "New"}} selected{{end}}>New</option>
|
||||||
|
<option value="Old"{{ if eq .CommentSort "Old"}} selected{{end}}>Old</option>
|
||||||
|
<option value="Top"{{ if eq .CommentSort "Top"}} selected{{end}}>Top</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>
|
<label>
|
||||||
dark mode
|
dark mode
|
||||||
|
|
@ -112,13 +119,25 @@
|
||||||
<input type="checkbox" name="autoLoad">
|
<input type="checkbox" name="autoLoad">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label></label>
|
<label>
|
||||||
|
hide instance names
|
||||||
|
</label>
|
||||||
|
<input type="checkbox" name="hideInstanceNames" {{ if .HideInstanceNames }}checked{{end}}>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
hide thumbnails
|
||||||
|
</label>
|
||||||
|
<input type="checkbox" name="hideThumbnails" {{ if .HideThumbnails }}checked{{end}}>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>lemmy: {{ .Site.Version }}<br><a href="https://github.com/rystaf/mlmym">mlmym</a>: {{ .Version }}</label>
|
||||||
<input type="submit" value="save">
|
<input type="submit" value="save">
|
||||||
{{ if .XHR }}<input id="closesettings" type="submit" value="close">{{ end }}
|
{{ if .XHR }}<input id="closesettings" type="submit" value="close">{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{{ if not .XHR}}
|
{{ if not .XHR}}
|
||||||
<script src="/_/static/utils.js?v=8"></script>
|
<script src="/_/static/utils.js?v={{ .Version }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
{{ $host := .Host }}
|
{{ $host := .Host }}
|
||||||
<div class="{{ if .User }}user {{end}}side">
|
<div class="{{ if .User }}user {{end}}side">
|
||||||
|
|
||||||
{{ if not .SearchType }}
|
|
||||||
<form method="GET" action="/{{.Host}}/search">
|
<form method="GET" action="/{{.Host}}/search">
|
||||||
<input type="text" placeholder="search" name="q" value="{{.Query}}">
|
<input type="text" placeholder="search" name="q" value="">
|
||||||
{{ if .User }}
|
{{ if .User }}
|
||||||
<input type="hidden" name="username" value="{{.UserName}}">
|
<input type="hidden" name="username" value="{{.UserName}}">
|
||||||
{{ else if .Community }}
|
{{ else if .Community }}
|
||||||
<input type="hidden" name="communityname" value="{{fullcname .Community.CommunityView.Community}}">
|
<input type="hidden" name="communityname" value="{{fullcname .Community.CommunityView.Community}}">
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
<input type="hidden" name="sort" value="New">
|
||||||
</form>
|
</form>
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ if .User }}
|
{{ if .User }}
|
||||||
<h1>{{ .User.PersonView.Person.Name }}</h1>
|
<h1>{{ .User.PersonView.Person.Name }}</h1>
|
||||||
|
|
@ -91,8 +90,14 @@
|
||||||
<input name="op" type="submit" value="{{ membership .Community.CommunityView.Subscribed}}">
|
<input name="op" type="submit" value="{{ membership .Community.CommunityView.Subscribed}}">
|
||||||
<input name="communityid" type="hidden" value="{{ .Community.CommunityView.Community.ID }}">
|
<input name="communityid" type="hidden" value="{{ .Community.CommunityView.Community.ID }}">
|
||||||
</form>
|
</form>
|
||||||
|
<form method="POST" class="block {{ if .Community.CommunityView.Blocked }}unblock{{end}}">
|
||||||
|
<input name="op" type="submit" value="{{ if .Community.CommunityView.Blocked}}unblock{{else}}block{{end}}">
|
||||||
|
<input name="communityid" type="hidden" value="{{ .Community.CommunityView.Community.ID }}">
|
||||||
|
</form>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ .Community.CommunityView.Counts.Subscribers }} readers <br>
|
<div>
|
||||||
|
{{ .Community.CommunityView.Counts.Subscribers }} readers
|
||||||
|
</div>
|
||||||
<span class="green" title="Users active in the last day"></span>
|
<span class="green" title="Users active in the last day"></span>
|
||||||
{{ .Community.CommunityView.Counts.UsersActiveDay }} users here now
|
{{ .Community.CommunityView.Counts.UsersActiveDay }} users here now
|
||||||
{{ if and .Session (isMod .Community .Session.UserName) }}
|
{{ if and .Session (isMod .Community .Session.UserName) }}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,21 @@
|
||||||
|
{{ $state := . }}
|
||||||
{{ if or .PostID .CommentID }}
|
{{ if or .PostID .CommentID }}
|
||||||
{{ range $i, $comment := .Comments }}
|
{{ range $i, $comment := .Comments }}
|
||||||
{{ template "comment.html" $comment }}
|
{{ template "comment.html" $comment }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ else if .Activities }}
|
{{ else if .Activities }}
|
||||||
{{ template "activities.html" . }}
|
{{ template "activities.html" . }}
|
||||||
{{ else }}
|
{{ else if .Posts }}
|
||||||
{{ range $post := .Posts }}
|
{{ range $post := .Posts }}
|
||||||
{{ template "post.html" $post }}
|
{{ template "post.html" $post }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ else if .Communities }}
|
||||||
|
{{ range .Communities }}
|
||||||
|
{{ if not $state.Page }}
|
||||||
|
<a href="/{{ $state.Host}}/{{ if .Community.Local }}c/{{.Community.Name}}{{else}}{{ localize .Community.ActorID }}{{end}}">{{fullcname .Community }}</a>
|
||||||
|
{{ else }}
|
||||||
|
{{ template "community.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ else }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue