post/user blocking

This commit is contained in:
Ryan Stafford 2023-09-03 12:48:04 -04:00
parent a96b5b1cd2
commit 08996db8b2
12 changed files with 216 additions and 29 deletions

View file

@ -56,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", "create_comment.html"} { for _, name := range []string{"index.html", "login.html", "frontpage.html", "root.html", "settings.html", "xhr.html", "create_comment.html", "block.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 {

View file

@ -408,9 +408,12 @@ form.nsfw div {
.gray { .gray {
color: #808080; color: #808080;
} }
.loading { .morecomments .loading {
color: red !important; color: red !important;
} }
.blockpopup .loading {
padding: 2px;
}
.error { .error {
color: red; color: red;
font-size: 13px; font-size: 13px;
@ -462,7 +465,7 @@ form.nsfw div {
.buttons li { .buttons li {
display: inline; display: inline;
} }
.buttons, .buttons input { .buttons, .buttons > form input, .buttons li form input {
font-weight: bold; font-weight: bold;
font-size: 10px; font-size: 10px;
padding: 0; padding: 0;
@ -481,20 +484,32 @@ form.nsfw div {
border-left: 2px solid #c5c1ad; border-left: 2px solid #c5c1ad;
padding: 0 8px; padding: 0 8px;
} }
.buttons a, .buttons form input, .comment .buttons form input { .buttons a, .buttons > form input, .comment .buttons form input {
text-decoration: none; text-decoration: none;
color: #888; color: #888;
display: inline-block; display: inline-block;
margin-right: 5px !important; margin-right: 5px !important;
} }
.buttons a:hover, .title a:hover, .buttons form input:hover, .comment .buttons form input:hover { .buttons a:hover, .title a:hover, .buttons > form input:hover, .comment .buttons form input:hover {
text-decoration: underline; text-decoration: underline;
} }
.entry { .entry {
overflow: hidden;
color: #888; color: #888;
overflow:hidden;
}
.entry .buttons .blockpopup {
display: inline-block;
position: absolute;
border: 1px solid #888;
z-index: 100;
background-color: white;
}
form.blockpost {
margin: 2px;
}
.blockpost div:last-child input {
margin: 4px 2px 4px 4px;
} }
.expando-button { .expando-button {
width: 23px; width: 23px;
height: 23px; height: 23px;
@ -816,7 +831,7 @@ h1, h2 {
.dark .moderators a { .dark .moderators a {
color: #6a98af; color: #6a98af;
} }
.community { main > .community {
margin: 20px 50px; margin: 20px 50px;
max-width: 840px; max-width: 840px;
position: relative; position: relative;

View file

@ -51,13 +51,23 @@ function commentClick(e) {
if (e.target.value == "preview") { if (e.target.value == "preview") {
targ = form 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 }
e.target.disabled = "disabled" e.target.disabled = "disabled"
request(targ.action || "", data, request(targ.action || "", data,
function(res){ function(res){
if (data.get("op") == "block_user") {
var submitter = targ.getElementsByClassName("creator")[0].href
var comments = Array.prototype.slice.call(document.getElementsByClassName("comment"))
for (var i = 0; i < comments.length; i++) {
var submitter2 = comments[i].getElementsByClassName("creator")[0].href
if (submitter2 == submitter) {
comments[i].remove()
}
}
return
}
targ.outerHTML = res targ.outerHTML = res
setup() setup()
}, },
@ -223,6 +233,10 @@ function formSubmit(e) {
var data = new FormData(targ) var data = new FormData(targ)
data.set(e.submitter.name, e.submitter.value) data.set(e.submitter.name, e.submitter.value)
data.set("xhr", "1") data.set("xhr", "1")
if (data.get("submit") == "cancel") {
targ.remove()
return
}
e.submitter.disabled = "disabled" e.submitter.disabled = "disabled"
request(targ.target, data, request(targ.target, data,
function(res){ function(res){
@ -230,6 +244,24 @@ function formSubmit(e) {
document.getElementById("p"+data.get("postid")).remove() document.getElementById("p"+data.get("postid")).remove()
return return
} }
if (data.get("op") == "block_post") {
var post = document.getElementById("p"+data.get("postid"))
var user = post.getElementsByClassName("submitter")[0].href
var community = post.getElementsByClassName("community")[0].href
var posts = Array.prototype.slice.call(document.getElementsByClassName("post"))
for (var i = 0; i < posts.length; i++) {
var user2 = posts[i].getElementsByClassName("submitter")[0].href
var community2 = posts[i].getElementsByClassName("community")[0].href
if (data.get("blockcommunity") != null && community2 == community) {
posts[i].remove()
}
if (data.get("blockuser") != null && user2 == user) {
posts[i].remove()
}
}
targ.remove()
return
}
targ.outerHTML = res targ.outerHTML = res
setup() setup()
}, },
@ -388,7 +420,7 @@ function setup() {
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)
var forms = posts[i].getElementsByClassName("link-btn") var forms = posts[i].getElementsByTagName("form")
for (var f = 0; f < forms.length; f++) { for (var f = 0; f < forms.length; f++) {
forms[f].addEventListener("submit", formSubmit) forms[f].addEventListener("submit", formSubmit)
} }
@ -406,6 +438,34 @@ function setup() {
for (var i = 0; i < comments.length; i++) { for (var i = 0; i < comments.length; i++) {
comments[i].addEventListener("click", commentClick) comments[i].addEventListener("click", commentClick)
} }
var links = document.getElementsByTagName("a")
for (var i = 0; i < links.length; i++) {
if (links[i].rel == "xhr") {
links[i].addEventListener("click", xhrLink)
}
}
}
function xhrLink(e) {
e = e || window.event;
e.preventDefault();
var targ = e.currentTarget || e.srcElement || e;
var t = []
if (targ.target != "") {
t = document.getElementsByName(targ.target)
}
if (t.length) {
t[0].innerHTML = '<div class="loading">loading</div>'
}
request(targ.href+"?xhr", "",
function(res){
if (t.length) {
t[0].innerHTML = res
}
setup()
},
function(res){
})
return false;
} }
setup() setup()

View file

@ -295,7 +295,7 @@ func GetTemplate(name string) (*template.Template, error) {
} }
t, ok := templates[name] t, ok := templates[name]
if !ok { if !ok {
return nil, errors.New("template not found") return nil, errors.New("template not found: " + name)
} }
return t, nil return t, nil
} }
@ -493,6 +493,7 @@ func GetPost(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
Render(w, "index.html", state) Render(w, "index.html", state)
return return
} }
// redirect /post/remote_id@instance to /post/local_id
if path := strings.Split(ps.ByName("postid"), "@"); len(path) > 1 { if path := strings.Split(ps.ByName("postid"), "@"); len(path) > 1 {
apid := ResolveId(r, "post", path[0], path[1]) apid := ResolveId(r, "post", path[0], path[1])
if apid != "" { if apid != "" {
@ -529,6 +530,11 @@ func GetPost(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
} }
postid, _ := strconv.Atoi(ps.ByName("postid")) postid, _ := strconv.Atoi(ps.ByName("postid"))
state.GetPost(postid) state.GetPost(postid)
if ps.ByName("op") == "block" {
state.Op = "block"
Render(w, "block.html", state)
return
}
state.GetComments() state.GetComments()
Render(w, "index.html", state) Render(w, "index.html", state)
} }
@ -597,6 +603,9 @@ func GetUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
return return
} }
state.GetUser(ps.ByName("username")) state.GetUser(ps.ByName("username"))
if state.Site == nil {
state.GetSite()
}
Render(w, "index.html", state) Render(w, "index.html", state)
} }
func GetMessageForm(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { func GetMessageForm(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
@ -931,6 +940,20 @@ func UserOp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
CommunityID: communityid, CommunityID: communityid,
Block: false, Block: false,
}) })
case "block_user":
personId, _ := strconv.Atoi(r.FormValue("user_id"))
if personId == 0 {
state.GetUser(ps.ByName("username"))
personId = state.User.PersonView.Person.ID
}
state.Client.BlockPerson(context.Background(), types.BlockPerson{
PersonID: personId,
Block: r.FormValue("submit") == "block",
})
if r.FormValue("xhr") == "1" {
w.Write([]byte{})
return
}
case "logout": case "logout":
deleteCookie(w, state.Host, "jwt") deleteCookie(w, state.Host, "jwt")
deleteCookie(w, state.Host, "user") deleteCookie(w, state.Host, "user")
@ -1166,6 +1189,21 @@ 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 "block_post":
postid, _ := strconv.Atoi(r.FormValue("postid"))
state.GetPost(postid)
if r.FormValue("blockcommunity") != "" && len(state.Posts) > 0 {
state.Client.BlockCommunity(context.Background(), types.BlockCommunity{
CommunityID: state.Posts[0].Post.CommunityID,
Block: true,
})
}
if r.FormValue("blockuser") != "" && len(state.Posts) > 0 {
state.Client.BlockPerson(context.Background(), types.BlockPerson{
PersonID: state.Posts[0].Post.CreatorID,
Block: true,
})
}
case "read_post": case "read_post":
postid, _ := strconv.Atoi(r.FormValue("postid")) postid, _ := strconv.Atoi(r.FormValue("postid"))
post := types.MarkPostAsRead{ post := types.MarkPostAsRead{
@ -1399,6 +1437,7 @@ func GetRouter() *httprouter.Router {
router.GET("/:host/c/:community/edit", middleware(GetCreateCommunity)) router.GET("/:host/c/:community/edit", middleware(GetCreateCommunity))
router.GET("/:host/post/:postid", middleware(GetPost)) router.GET("/:host/post/:postid", middleware(GetPost))
router.POST("/:host/post/:postid", middleware(UserOp)) router.POST("/:host/post/:postid", middleware(UserOp))
router.GET("/:host/post/:postid/:op", middleware(GetPost))
router.GET("/:host/comment/:commentid", middleware(GetComment)) router.GET("/:host/comment/:commentid", middleware(GetComment))
router.GET("/:host/comment/:commentid/:op", middleware(GetComment)) router.GET("/:host/comment/:commentid/:op", middleware(GetComment))
router.POST("/:host/comment/:commentid", middleware(UserOp)) router.POST("/:host/comment/:commentid", middleware(UserOp))

View file

@ -107,6 +107,18 @@ type State struct {
HideThumbnails bool HideThumbnails bool
} }
func (s State) UserBlocked() bool {
if s.User == nil || s.Site == nil || !s.Site.MyUser.IsValid() {
return false
}
for _, p := range s.Site.MyUser.MustValue().PersonBlocks {
if p.Target.ID == s.User.PersonView.Person.ID {
return true
}
}
return false
}
func (s State) Unknown() string { func (s State) Unknown() string {
fmt.Println(fmt.Sprintf("%v", s.Error)) fmt.Println(fmt.Sprintf("%v", s.Error))
re := regexp.MustCompile(`(.*?)@(.*?)@`) re := regexp.MustCompile(`(.*?)@(.*?)@`)

23
templates/block.html Normal file
View file

@ -0,0 +1,23 @@
{{ if not .XHR }}
{{ template "header.html" . }}
{{ template "nav.html" . }}
{{ end }}
<form method="POST" class="blockpost"{{ if not .XHR }} action="./"{{ end }}>
<div>
<input type="checkbox" id="blockuser" name="blockuser" checked>
<label for="blockuser">u/{{ fullname (index .Posts 0).Creator }}</label>
</div>
<div>
<input type="checkbox" id="blockcommunity" name="blockcommunity">
<label for="blockcommunity">c/{{ fullcname .Community.CommunityView.Community }}</label>
</div>
<div>
<input type="hidden" name="op" value="block_post">
<input type="hidden" name="postid" value="{{(index .Posts 0).Post.ID}}">
<input type="submit" value="block" name="submit">
<input type="submit" value="cancel" name="submit">
</div>
</form>
{{ if not .XHR }}
{{ template "footer.html" . }}
{{ end }}

View file

@ -21,7 +21,7 @@
[-] [-]
{{- 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 class="creator{{ if .P.Comment.Distinguished}}{{if .P.Creator.Admin}} admin{{end}} distinguished{{ else if .Submitter }} submitter{{end}}" href="/{{.State.Host}}/u/{{fullname .P.Creator}}">
{{- if .State.HideInstanceNames -}} {{- if .State.HideInstanceNames -}}
{{ .P.Creator.Name }} {{ .P.Creator.Name }}
{{- else -}} {{- else -}}
@ -77,10 +77,17 @@
<input type="submit" name="submit" value="unsave"> <input type="submit" name="submit" value="unsave">
{{ else }} {{ else }}
<input type="submit" name="submit" value="save"> <input type="submit" name="submit" value="save">
{{ end }} {{ end }}
</form> </form>
</li> </li>
<li>
<form class="link-btn" method="POST">
<input type="hidden" name="commentid" value="{{.P.Comment.ID}}">
<input type="hidden" name="op" value="block_user">
<input type="hidden" name="user_id" value="{{.P.Creator.ID}}">
<input type="submit" name="submit" value="block">
</form>
</li>
<li> <li>
<a class="reply" for="c{{.P.Comment.ID}}" href="/{{.State.Host}}/comment/{{.P.Comment.ID}}?reply"> <a class="reply" for="c{{.P.Comment.ID}}" href="/{{.State.Host}}/comment/{{.P.Comment.ID}}?reply">
reply reply

7
templates/footer.html Normal file
View file

@ -0,0 +1,7 @@
<script src="/_/static/utils.js?v={{ .Version }}"></script>
{{ if .Watch }}
<script src="/_/static/ws.js"></script>
{{ end }}
</main>
</body>
</html>

11
templates/header.html Normal file
View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<head>
<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" />
<link rel="shortcut icon" href="/{{.Host}}/icon.jpg">
<link rel="stylesheet" href="/_/static/style.css?v={{ .Version }}">
</head>
<body{{ if .Dark }} class="dark"{{end}}>
<noscript>
<link rel="stylesheet" href="/_/static/noscript.css?v={{ .Version }}">
</noscript>

View file

@ -41,11 +41,11 @@
<div id="settingspopup"></div> <div id="settingspopup"></div>
<div class="spacer"> <div class="spacer">
<a href="/{{ .Host}}/"> <a href="/{{ .Host}}/">
<img class="icon" src="{{ if .Site }}{{ .Site.SiteView.Site.Icon.String }}{{else}}/{{ .Host}}/icon.jpg{{end}}"> <img class="icon" src="{{ if and .Site .Site.SiteView.Site.Icon.IsValid }}{{ .Site.SiteView.Site.Icon.String }}{{else}}/{{ .Host}}/icon.jpg{{end}}">
</a> </a>
</div> </div>
{{- if .Community }} {{- if .Community }}
<a class="title" href="/{{ .Host}}//c/{{fullcname .Community.CommunityView.Community}}">{{fullcname .Community.CommunityView.Community}}</a> <a class="title" href="/{{ .Host}}/c/{{fullcname .Community.CommunityView.Community}}">{{fullcname .Community.CommunityView.Community}}</a>
{{ else if .User }} {{ else if .User }}
<a class="title" href="/{{ .Host}}/u/{{fullname .User.PersonView.Person}}">{{fullname .User.PersonView.Person}}</a> <a class="title" href="/{{ .Host}}/u/{{fullname .User.PersonView.Person}}">{{fullname .User.PersonView.Person}}</a>
{{ else }} {{ else }}
@ -60,7 +60,8 @@
<span>: search</span> <span>: search</span>
{{ end }} {{ end }}
<ul> <ul>
{{ if and .User (not .Query)}} {{- if eq .Op "block" }}<li class="selected"><a href="/{{ .Host }}/post/{{ (index .Posts 0).Post.ID}}">block</a></li>
{{ else 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>

View file

@ -51,7 +51,7 @@
{{- end -}} {{- end -}}
</a> </a>
to to
<a href="/{{ .State.Host }}/c/{{ fullcname .Community }}"> <a class="community" href="/{{ .State.Host }}/c/{{ fullcname .Community }}">
c/{{ if .State.HideInstanceNames -}} c/{{ if .State.HideInstanceNames -}}
{{ .Community.Name }} {{ .Community.Name }}
{{ else -}} {{ else -}}
@ -63,6 +63,10 @@
{{ 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> <a href="{{ .Post.ApID}}">fedilink</a>
{{ if .State.Session }}
<a href="/{{ .State.Host }}/post/{{ .Post.ID }}/block" rel="xhr" target="block{{ .Post.ID }}">block</a>
{{ end }}
<span class="blockpopup" name="block{{.Post.ID}}"></span>
{{ 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">

View file

@ -11,8 +11,26 @@
<input type="hidden" name="sort" value="New"> <input type="hidden" name="sort" value="New">
</form> </form>
{{ if not .Session -}}
<form class="login" method="post">
<input name="username" type="text" placeholder="username" maxlength="20">
<input name="password" type="password" placeholder="password">
<div>
<input type="submit" name="op" value="login">
</div>
</form>
{{ end }}
{{ if .User }} {{ if .User }}
<h1>{{ .User.PersonView.Person.Name }}</h1> <h1>{{ .User.PersonView.Person.Name }}</h1>
{{ if .Session }}
<div>
<form method="POST" class="block {{ if .UserBlocked }}unblock{{end}}">
<input name="submit" type="submit" value="{{ if .UserBlocked}}unblock{{else}}block{{end}}">
<input type="hidden" name="op" value="block_user">
</form>
</div>
{{ end }}
<b>{{ .User.PersonView.Counts.PostScore}}</b> post score <br> <b>{{ .User.PersonView.Counts.PostScore}}</b> post score <br>
<b>{{ .User.PersonView.Counts.CommentScore}}</b> comment score <br> <b>{{ .User.PersonView.Counts.CommentScore}}</b> comment score <br>
<div class="age"> <div class="age">
@ -29,16 +47,6 @@
{{ end }} {{ end }}
{{ end }} {{ end }}
{{ if not .Session -}}
<form class="login" method="post">
<input name="username" type="text" placeholder="username" maxlength="20">
<input name="password" type="password" placeholder="password">
<div>
<input type="submit" name="op" value="login">
</div>
</form>
{{ end }}
{{ if and .PostID .Posts }} {{ if and .PostID .Posts }}
<div class="stats"> <div class="stats">
this post was submitted on {{ (index .Posts 0).Post.Published.Time.Format "01 Jan 2006" }} this post was submitted on {{ (index .Posts 0).Post.Published.Time.Format "01 Jan 2006" }}
@ -55,7 +63,7 @@
</div> </div>
{{ end }} {{ end }}
{{ if and .Site (not .Community) }} {{ if and .Site (not .Community) (not .User) }}
{{ if .Site.SiteView.Site.Banner.IsValid }} {{ if .Site.SiteView.Site.Banner.IsValid }}
<div class="banner" style="background-image: url({{ .Site.SiteView.Site.Banner }})"></div> <div class="banner" style="background-image: url({{ .Site.SiteView.Site.Banner }})"></div>
{{ else if .Site.SiteView.Site.Icon.IsValid }} {{ else if .Site.SiteView.Site.Icon.IsValid }}