comment preview, image upload, versioned assets

This commit is contained in:
Ryan Stafford 2023-07-26 21:07:39 -04:00
parent 463b3fe49d
commit 6c80f67535
9 changed files with 179 additions and 92 deletions

View file

@ -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;
@ -956,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 {

View file

@ -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()
@ -217,11 +221,10 @@ 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") { if (data.get("op") == "read_post") {
document.getElementById("p"+data.get("postid")).remove() document.getElementById("p"+data.get("postid")).remove()
@ -349,6 +352,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)
@ -369,6 +382,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)

View file

@ -507,6 +507,9 @@ func GetPost(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
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()
@ -551,6 +554,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"
} }
@ -560,6 +566,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)
} }
@ -1201,9 +1211,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 += ("![](https://" + state.Host + "/pictrs/image/" + pres.Files[0].Filename + ")")
}
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 {
@ -1225,6 +1246,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
@ -1234,10 +1271,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 += ("![](https://" + state.Host + "/pictrs/image/" + pres.Files[0].Filename + ")")
}
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)
@ -1246,6 +1296,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

View file

@ -98,6 +98,7 @@ type State struct {
Op string Op string
Site *types.GetSiteResponse Site *types.GetSiteResponse
Query string Query string
Content string
SearchType string SearchType string
Captcha *types.CaptchaResponse Captcha *types.CaptchaResponse
Dark bool Dark bool

View file

@ -15,11 +15,11 @@
{{ 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>
<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}}"> <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 -}} {{- if .State.HideInstanceNames -}}
@ -33,19 +33,10 @@
* (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}}
[removed] [removed]
{{else}} {{else}}
@ -93,7 +84,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>
@ -110,15 +101,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) }}

View file

@ -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=28"> <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" . -}}
@ -59,6 +45,6 @@
{{ template "sidebar.html" . }} {{ template "sidebar.html" . }}
</main> </main>
{{ end }} {{ end }}
<script src="/_/static/utils.js?v=22"></script> <script src="/_/static/utils.js?v={{ .Version }}"></script>
</body> </body>
</html> </html>

View file

@ -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=28"> <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}}>

View file

@ -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=28"> <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" . -}}
@ -99,13 +87,9 @@
</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}}
@ -140,7 +124,7 @@
{{ end }} {{ end }}
{{ end }} {{ end }}
<script src="/_/static/utils.js?v=22"></script> <script src="/_/static/utils.js?v={{ .Version }}"></script>
{{ if .Watch }} {{ if .Watch }}
<script src="/_/static/ws.js"></script> <script src="/_/static/ws.js"></script>
{{ end }} {{ end }}

View file

@ -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=28"> <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">
@ -135,7 +131,7 @@
</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 }}