diff --git a/.gitignore b/.gitignore index c9a2389..3d7be86 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ mlmym +VERSION *.toml *.txt diff --git a/Dockerfile b/Dockerfile index 1bd6c58..9b9ab73 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,7 @@ WORKDIR /app COPY go.* ./ RUN go mod download COPY . ./ +RUN git describe --tag > VERSION RUN go build -v -o mlmym 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/templates /app/templates COPY --from=builder /app/public /app/public +COPY --from=builder /app/VERSION /app/VERSION CMD ["./mlmym", "--addr", "0.0.0.0:8080"] diff --git a/Makefile b/Makefile index 0ac9db1..1b88d82 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,18 @@ -.PHONY: dev reload serve style +.PHONY: dev reload serve VERSION -all: - $(MAKE) -j3 --no-print-directory dev +all: mlmym -dev: reload serve style +mlmym: VERSION + go build -v -o mlmym + +dev: + $(MAKE) -j2 --no-print-directory reload serve reload: - #websocketd --port=8080 watchexec -w public echo reload &>/dev/null - websocketd --loglevel=fatal --port=8009 watchexec --no-vcs-ignore -e html,css,js -d 500 -w public 'echo "$$WATCHEXEC_WRITTEN_PATH"' + websocketd --loglevel=fatal --port=8009 watchexec --no-vcs-ignore -e html,css,js 'echo "$$WATCHEXEC_WRITTEN_PATH"' -serve: - #python -m http.server --directory ./public 8081 &>/dev/null - DEBUG=true watchexec -e go -r "go run . --addr 0.0.0.0:8008 -w" +VERSION: + git describe --tag > $@ -style: - npm run watchcss > /dev/null 2>&1 +serve: VERSION + DEBUG=true watchexec --no-vcs-ignore -e go -r "go run . --addr 0.0.0.0:8008 -w" diff --git a/README.md b/README.md index ee99f9e..c2bde67 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,23 @@ a familiar desktop experience for [lemmy](https://join-lemmy.org). ![screenshot](https://raw.githubusercontent.com/rystaf/mlmym/main/screenshot1.png?raw=true) -### deployment +## deployment ```bash 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 ```bash 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 | + diff --git a/main.go b/main.go index a7aee4f..95e0f3d 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( "github.com/yuin/goldmark/extension" ) +var version string var watch = flag.Bool("w", false, "watch for file changes") var addr = flag.String("addr", ":80", "http service address") var md goldmark.Markdown @@ -55,7 +56,7 @@ func init() { )) templates = make(map[string]*template.Template) 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) glob, err := t.ParseGlob("templates/*") if err != nil { @@ -68,6 +69,9 @@ func init() { if os.Getenv("DEBUG") != "" { test() } + if data, err := os.ReadFile("VERSION"); err == nil { + version = string(data) + } } func test() { links := [][]string{ @@ -80,6 +84,7 @@ func test() { []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"}, diff --git a/public/noscript.css b/public/noscript.css new file mode 100644 index 0000000..8b24e2a --- /dev/null +++ b/public/noscript.css @@ -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; +} diff --git a/public/style.css b/public/style.css index 4cef8b7..7522f61 100644 --- a/public/style.css +++ b/public/style.css @@ -219,6 +219,27 @@ summary { font-size: 10px; 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 { color: #369; text-decoration: none; @@ -262,6 +283,7 @@ summary { } form.savecomment { margin: 0px 0px 10px 0px; + width: 500px; } .comment > .children > form.savecomment { margin: 0px 0px 10px 20px; @@ -270,9 +292,34 @@ form.savecomment { margin: 5px 0px 10px 15px; } .savecomment textarea { - width: 500px; + margin: 5px 0px; + width: 100%; 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 { color: #369; font-size: 10px; @@ -956,11 +1003,11 @@ nav .right a.mailbox { top: 4px; color: gray; } -nav .right a, .right input[type=submit] { +nav .right a, nav .right input[type=submit] { color: #369; text-decoration: none; } -.dark nav .right a, .dark .right input[type=submit]{ +.dark nav .right a, .dark nav .right input[type=submit]{ color: #dadada; } nav .right form, .comment form, form.link-btn { @@ -1057,9 +1104,13 @@ form.create input[type=file], form.create select { font-size: 13px; margin: 10px; } +.preferences div:last-child label { + text-align: left; + font-size: 10px; +} .preferences label{ display: inline-block; - width: 130px; + width: 150px; margin-right: 5px; text-align: right; diff --git a/public/utils.js b/public/utils.js index 04c8a92..df8c22a 100644 --- a/public/utils.js +++ b/public/utils.js @@ -10,8 +10,6 @@ function request(url, params, callback, errorcallback = function(){}) { var method = "GET" if (params) method = "POST" xmlHttp.open(method, url, true); - if (method = "POST") - xmlHttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xmlHttp.send(params); } 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) { e = e || window.event; var targ = e.currentTarget || e.srcElement || e; if (targ.nodeType == 3) targ = targ.parentNode; if (e.target.name=="submit") { e.preventDefault() - var form = e.target.parentNode + var form = uptil(e.target, function(el){ return el.tagName == "FORM" }) if (form) { data = new FormData(form) + data.set(e.target.name, e.target.value) + data.set("xhr", 1) 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) { targ = form } else { return } - params = new URLSearchParams(data).toString() - params += "&" + e.target.name + "=" + e.target.value - params += "&xhr=1" e.target.disabled = "disabled" - request(targ.target || "", params, + request(targ.action || "", data, function(res){ targ.outerHTML = res setup() @@ -217,11 +221,10 @@ function formSubmit(e) { var targ = e.currentTarget || e.srcElement || e; e.preventDefault() var data = new FormData(targ) - params = new URLSearchParams(data).toString() - params += "&" + e.submitter.name + "=" + e.submitter.value - params += "&xhr=1" + data.set(e.submitter.name, e.submitter.value) + data.set("xhr", "1") e.submitter.disabled = "disabled" - request(targ.target, params, + request(targ.target, data, function(res){ if (data.get("op") == "read_post") { document.getElementById("p"+data.get("postid")).remove() @@ -245,19 +248,25 @@ function toggleMyCommunities(e) { return false } mycommunities.className = "open" - mycommunities.innerHTML = "
loading
" - request(e.target.href + "&xhr=1", "", function(res) { - mycommunities.innerHTML = '
view all »' - mycommunities.innerHTML += res - }, function() { - mycommunities.className = "" - }) + if (mycommunities.innerHTML == "") { + mycommunities.innerHTML = "
loading
" + request(e.target.href + "&xhr=1", "", function(res) { + mycommunities.innerHTML = '
view all »' + mycommunities.innerHTML += res + }, function() { + mycommunities.className = "" + }) + } return false } function openSettings(e) { e.preventDefault() var settings = document.getElementById("settingspopup") + if (settings.className == "open") { + settings.className = "" + return false + } settings.className = "open" request(e.target.href + "?xhr=1", "", function(res) { settings.innerHTML = res @@ -287,8 +296,7 @@ function saveSettings(e) { var targ = e.currentTarget || e.srcElement || e; var data = new FormData(targ) e.preventDefault() - var params = new URLSearchParams(data).toString() - request(targ.target, params, function(res) { + request(targ.target, data, function(res) { ["endlessScrolling", "autoLoad"].map(function(x) { localStorage.setItem(x, data.get(x)=="on") }) @@ -343,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() { if (showimages = document.getElementById("se")) { showimages.addEventListener("click", showImages) @@ -363,6 +381,10 @@ function setup() { } 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") for (var i = 0; i < posts.length; i++) { posts[i].addEventListener("click", postClick) diff --git a/public/ws.js b/public/ws.js new file mode 100644 index 0000000..08ac7c3 --- /dev/null +++ b/public/ws.js @@ -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() +} diff --git a/routes.go b/routes.go index eea125b..d72d653 100644 --- a/routes.go +++ b/routes.go @@ -133,6 +133,8 @@ var funcMap = template.FuncMap{ var buf bytes.Buffer re := regexp.MustCompile(`\s---\s`) 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 { fmt.Println(err) return template.HTML(body) @@ -150,7 +152,7 @@ var funcMap = template.FuncMap{ return body } text := html2text.HTML2TextWithOptions(buf.String(), html2text.WithLinksInnerText()) - re := regexp.MustCompile(`\`) + 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, "") }, "contains": strings.Contains, @@ -164,10 +166,8 @@ var funcMap = template.FuncMap{ func LemmyLinkRewrite(input string, host string, lemmy_domain string) (body string) { body = input - // community bangs - body = RegReplace(body, `!([a-zA-Z0-9]+)@([a-zA-Z0-9\.\-]+)([ \n\r]+|<\/p>)`, `!$1@$2 `) // localize community and user links - body = RegReplace(body, `href="https:\/\/([a-zA-Z0-9\.\-]+)\/((c|u|comment|post)\/.*?)"`, `href="/$2@$1"`) + 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 == "" { @@ -180,9 +180,21 @@ func LemmyLinkRewrite(input string, host string, lemmy_domain string) (body stri 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"`) } - // remove redundant instance tag - re := regexp.MustCompile(`href="\/([a-zA-Z0-9\.\-]+)\/(c|u|post|comment)\/(.*?)@(.*?)"`) + + 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) @@ -196,11 +208,23 @@ func RegReplace(input string, match string, replace string) string { 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) { state := State{ - Host: Host, - Page: 1, - Status: http.StatusOK, + Host: Host, + Page: 1, + Status: http.StatusOK, + Version: version, + } + if watch != nil { + state.Watch = *watch } lemmyDomain := os.Getenv("LEMMY_DOMAIN") if lemmyDomain != "" { @@ -236,18 +260,27 @@ func Initialize(Host string, r *http.Request) (State, error) { state.Listing = getCookie(r, "DefaultListingType") state.Sort = getCookie(r, "DefaultSortType") state.CommentSort = getCookie(r, "DefaultCommentSortType") - state.Dark = getCookie(r, "Dark") != "" + if dark := getCookie(r, "Dark"); dark != "" { + state.Dark = dark != "0" + } else { + state.Dark = os.Getenv("DARK") != "" + } 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) if state.Sort == "" { - state.Sort = "Hot" + state.Sort = getenv("SORT", "Hot") } if state.CommentSort == "" { - state.CommentSort = "Hot" + state.CommentSort = getenv("COMMENT_SORT", "Hot") } if state.Listing == "" || state.Session == nil && state.Listing == "Subscribed" { - state.Listing = "All" + state.Listing = getenv("LISTING", "All") } return state, nil } @@ -491,6 +524,9 @@ func GetPost(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { state.Op = "edit_post" state.GetSite() } + if len(m["content"]) > 0 { + state.Content = m["content"][0] + } postid, _ := strconv.Atoi(ps.ByName("postid")) state.GetPost(postid) state.GetComments() @@ -535,6 +571,9 @@ func GetComment(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { if len(m["edit"]) > 0 { state.Op = "edit" } + if r.Method == "POST" && len(m["content"]) > 0 { + state.Content = m["content"][0] + } if len(m["source"]) > 0 { state.Op = "source" } @@ -544,6 +583,10 @@ func GetComment(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { } commentid, _ := strconv.Atoi(ps.ByName("commentid")) state.GetComment(commentid) + if state.XHR && len(m["content"]) > 0 { + Render(w, "create_comment.html", state) + return + } state.GetPost(state.PostID) Render(w, "index.html", state) } @@ -661,6 +704,7 @@ func Settings(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { Render(w, "index.html", state) return } + state.GetSite() switch r.Method { case "POST": for _, name := range []string{"DefaultSortType", "DefaultListingType", "DefaultCommentSortType"} { @@ -671,8 +715,7 @@ func Settings(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { setCookie(w, "", "Dark", "1") state.Dark = true } else { - deleteCookie(w, state.Host, "Dark") - deleteCookie(w, "", "Dark") + setCookie(w, "", "Dark", "0") state.Dark = false } if r.FormValue("shownsfw") != "" { @@ -690,6 +733,13 @@ func Settings(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { 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.Sort = r.FormValue("DefaultSortType") state.CommentSort = r.FormValue("DefaultCommentSortType") @@ -1185,9 +1235,20 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { parentid, _ := strconv.Atoi(r.FormValue("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 += ("![](https://" + state.Host + "/pictrs/image/" + pres.Files[0].Filename + ")") + } if r.FormValue("submit") == "save" { createComment := types.CreateComment{ - Content: r.FormValue("content"), + Content: content, PostID: state.PostID, } if state.CommentID > 0 { @@ -1209,6 +1270,22 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { } else { 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") != "" { w.Write([]byte{}) return @@ -1218,10 +1295,23 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { } case "edit_comment": 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 += ("![](https://" + state.Host + "/pictrs/image/" + pres.Files[0].Filename + ")") + } + if r.FormValue("submit") == "save" { resp, err := state.Client.EditComment(context.Background(), types.EditComment{ CommentID: commentid, - Content: types.NewOptional(r.FormValue("content")), + Content: types.NewOptional(content), }) if err != nil { fmt.Println(err) @@ -1230,6 +1320,29 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { r.URL.Fragment = "c" + commentid 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") != "" { state.XHR = true diff --git a/state.go b/state.go index 630a9e1..1e2085a 100644 --- a/state.go +++ b/state.go @@ -59,11 +59,14 @@ type Post struct { } type Session struct { - UserName string - UserID int + UserName string + UserID int + Communities []types.CommunityView } type State struct { + Watch bool + Version string Client *lemmy.Client HTTPClient *http.Client Session *Session @@ -95,11 +98,13 @@ type State struct { Op string Site *types.GetSiteResponse Query string + Content string SearchType string Captcha *types.CaptchaResponse Dark bool ShowNSFW bool HideInstanceNames bool + HideThumbnails bool } func (s State) Unknown() string { @@ -266,6 +271,18 @@ func (state *State) GetSite() { return } 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) { @@ -299,6 +316,9 @@ func (state *State) GetComment(commentid int) { state.Comments = append(state.Comments, comment) } } + if len(state.Comments) == 0 { + return + } ctx, err := state.GetContext(state.Context, state.Comments[0]) if err != nil { fmt.Println(err) @@ -531,16 +551,10 @@ func (state *State) Search(searchtype string) { if state.Page > 1 { return } - state.GetSite() - for _, c := range state.Site.MyUser.MustValue().Follows { - state.Communities = append(state.Communities, types.CommunityView{ - Community: c.Community, - Subscribed: "Subscribed", - }) + if state.Site == nil { + state.GetSite() } - sort.Slice(state.Communities, func(a, b int) bool { - return state.Communities[a].Community.Name < state.Communities[b].Community.Name - }) + state.Communities = state.Session.Communities return } resp, err := state.Client.Communities(context.Background(), types.ListCommunities{ diff --git a/templates/comment.html b/templates/comment.html index fb686b8..c33d77a 100644 --- a/templates/comment.html +++ b/templates/comment.html @@ -1,4 +1,4 @@ -
+
{{ if .State.Session }}
{{ if eq .Op "edit" }} -
- -
- -
- - - - - + {{ template "create_comment.html" .State }} {{ else }} -
{{ if and (eq .State.Op "reply") (eq .State.CommentID .P.Comment.ID)}} -
-
- -
- - - - -
+ {{ template "create_comment.html" .State }} {{ end}} {{ range $ci, $child := .C }}{{ template "comment.html" $child }}{{end}} {{ if and (ne .P.Counts.ChildCount .ChildCount) (not .State.Activities) (not .State.Query) }} @@ -128,4 +114,4 @@
{{end}}
-
+
diff --git a/templates/create_comment.html b/templates/create_comment.html new file mode 100644 index 0000000..081d8eb --- /dev/null +++ b/templates/create_comment.html @@ -0,0 +1,45 @@ +
+
+ +
+
+ +
+ {{ if eq .Op "edit" }} + + + {{ else }} + + {{ end }} + + + {{ if or .Op .Content }} + + {{ end }} + + {{ if .Content }} +
+
+

Preview

+
+ {{ markdown .Host .Content }} +
+
+
+ {{ end }} +
diff --git a/templates/frontpage.html b/templates/frontpage.html index 14f746f..bce44e6 100644 --- a/templates/frontpage.html +++ b/templates/frontpage.html @@ -2,26 +2,12 @@ {{ 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}} - + {{ template "nav.html" . -}} @@ -59,6 +45,6 @@ {{ template "sidebar.html" . }} {{ end }} - + diff --git a/templates/login.html b/templates/login.html index 8a3356c..2ecb7bd 100644 --- a/templates/login.html +++ b/templates/login.html @@ -2,7 +2,7 @@ {{ host .Host }}: sign up or log in - + diff --git a/templates/main.html b/templates/main.html index a110926..a8c78d9 100644 --- a/templates/main.html +++ b/templates/main.html @@ -3,23 +3,11 @@ {{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}} - + {{ template "nav.html" . -}} @@ -99,13 +87,9 @@
{{ if and .Session (ne .Op "edit_post") }} -
-
- -
- - -
+
+ {{ template "create_comment.html" .}} +
{{ end }} {{ end }} {{ end}} @@ -140,7 +124,10 @@ {{ end }} {{ end }} - + + {{ if .Watch }} + + {{ end }} {{ template "sidebar.html" . }} {{ end }} diff --git a/templates/nav.html b/templates/nav.html index d093b83..f455ace 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -1,3 +1,4 @@ +{{ $state := . }}