Add following and unfollowing
This commit is contained in:
parent
d10445813c
commit
901e4dce0e
@ -121,6 +121,10 @@ func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
app.ChangeSession(w, r)
|
app.ChangeSession(w, r)
|
||||||
case "timeline":
|
case "timeline":
|
||||||
app.Timeline(w, r)
|
app.Timeline(w, r)
|
||||||
|
case "follow":
|
||||||
|
app.UserFollow(w, r)
|
||||||
|
case "unfollow":
|
||||||
|
app.UserUnfollow(w, r)
|
||||||
default:
|
default:
|
||||||
app.UserFeed(w, r)
|
app.UserFeed(w, r)
|
||||||
}
|
}
|
||||||
@ -431,3 +435,50 @@ func parse_form(req *http.Request, result interface{}) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *Application) UserFollow(w http.ResponseWriter, r *http.Request) {
|
||||||
|
app.traceLog.Printf("'UserFollow' handler (path: %q)", r.URL.Path)
|
||||||
|
|
||||||
|
if r.Method != "POST" {
|
||||||
|
http.Error(w, "Method not allowed", 405)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
app.error_400_with_message(w, "Bad URL: "+r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err := app.Profile.GetUserByHandle(scraper.UserHandle(parts[1]))
|
||||||
|
if err != nil {
|
||||||
|
app.error_404(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Profile.SetUserFollowed(&user, true)
|
||||||
|
|
||||||
|
app.buffered_render_basic_htmx(w, "following-button", user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *Application) UserUnfollow(w http.ResponseWriter, r *http.Request) {
|
||||||
|
app.traceLog.Printf("'UserUnfollow' handler (path: %q)", r.URL.Path)
|
||||||
|
|
||||||
|
if r.Method != "POST" {
|
||||||
|
http.Error(w, "Method not allowed", 405)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
app.error_400_with_message(w, "Bad URL: "+r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err := app.Profile.GetUserByHandle(scraper.UserHandle(parts[1]))
|
||||||
|
if err != nil {
|
||||||
|
app.error_404(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Profile.SetUserFollowed(&user, false)
|
||||||
|
app.buffered_render_basic_htmx(w, "following-button", user)
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/andybalholm/cascadia"
|
"github.com/andybalholm/cascadia"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -189,6 +190,55 @@ func TestTweetDetailInvalidNumber(t *testing.T) {
|
|||||||
require.Equal(resp.StatusCode, 400)
|
require.Equal(resp.StatusCode, 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Follow and unfollow
|
||||||
|
// -------------------
|
||||||
|
|
||||||
|
func TestFollowUnfollow(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
user, err := profile.GetUserByHandle("kwamurai")
|
||||||
|
require.NoError(err)
|
||||||
|
require.False(user.IsFollowed)
|
||||||
|
|
||||||
|
// Follow the user
|
||||||
|
resp := do_request(httptest.NewRequest("POST", "/follow/kwamurai", nil))
|
||||||
|
require.Equal(resp.StatusCode, 200)
|
||||||
|
|
||||||
|
root, err := html.Parse(resp.Body)
|
||||||
|
require.NoError(err)
|
||||||
|
button := cascadia.Query(root, selector("button"))
|
||||||
|
assert.Contains(button.Attr, html.Attribute{Key: "hx-post", Val: "/unfollow/kwamurai"})
|
||||||
|
assert.Equal(strings.TrimSpace(button.FirstChild.Data), "Unfollow")
|
||||||
|
|
||||||
|
user, err = profile.GetUserByHandle("kwamurai")
|
||||||
|
require.NoError(err)
|
||||||
|
require.True(user.IsFollowed)
|
||||||
|
|
||||||
|
// Unfollow the user
|
||||||
|
resp = do_request(httptest.NewRequest("POST", "/unfollow/kwamurai", nil))
|
||||||
|
require.Equal(resp.StatusCode, 200)
|
||||||
|
|
||||||
|
root, err = html.Parse(resp.Body)
|
||||||
|
require.NoError(err)
|
||||||
|
button = cascadia.Query(root, selector("button"))
|
||||||
|
assert.Contains(button.Attr, html.Attribute{Key: "hx-post", Val: "/follow/kwamurai"})
|
||||||
|
assert.Equal(strings.TrimSpace(button.FirstChild.Data), "Follow")
|
||||||
|
|
||||||
|
user, err = profile.GetUserByHandle("kwamurai")
|
||||||
|
require.NoError(err)
|
||||||
|
require.False(user.IsFollowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFollowUnfollowPostOnly(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
resp := do_request(httptest.NewRequest("GET", "/follow/kwamurai", nil))
|
||||||
|
require.Equal(resp.StatusCode, 405)
|
||||||
|
resp = do_request(httptest.NewRequest("GET", "/unfollow/kwamurai", nil))
|
||||||
|
require.Equal(resp.StatusCode, 405)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Static content
|
// Static content
|
||||||
// --------------
|
// --------------
|
||||||
|
|
||||||
|
@ -400,3 +400,12 @@ input[type="submit"] {
|
|||||||
.retweeted-by-label {
|
.retweeted-by-label {
|
||||||
margin: 0 0.2em;
|
margin: 0 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-bio {
|
||||||
|
margin: 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.followers-followees-container {
|
||||||
|
margin-top: 1em;
|
||||||
|
gap: 4em;
|
||||||
|
}
|
||||||
|
17
internal/webserver/tpl/includes/following_button.tpl
Normal file
17
internal/webserver/tpl/includes/following_button.tpl
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{{define "following-button"}}
|
||||||
|
{{if .IsFollowed}}
|
||||||
|
<button class="following-button"
|
||||||
|
hx-post="/unfollow/{{.Handle}}"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
Unfollow
|
||||||
|
</button>
|
||||||
|
{{else}}
|
||||||
|
<button class="following-button"
|
||||||
|
hx-post="/follow/{{.Handle}}"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
Follow
|
||||||
|
</button>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
@ -8,8 +8,10 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="user-feed-header-info-container">
|
<div class="user-feed-header-info-container">
|
||||||
|
<div class="row">
|
||||||
{{template "author-info" $user}}
|
{{template "author-info" $user}}
|
||||||
<button>{{if $user.IsFollowed}}Unfollow{{else}}Follow{{end}}</button>
|
{{template "following-button" $user}}
|
||||||
|
</div>
|
||||||
<div class="user-bio">
|
<div class="user-bio">
|
||||||
<span>{{$user.Bio}}</span>
|
<span>{{$user.Bio}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user