Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update the list of watchers and stargazers when clicking watch/unwatch or star/unstar #32570

Merged
merged 13 commits into from
Nov 22, 2024
4 changes: 2 additions & 2 deletions models/repo/star.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ func IsStaring(ctx context.Context, userID, repoID int64) bool {
}

// GetStargazers returns the users that starred the repo.
func GetStargazers(ctx context.Context, repo *Repository, opts db.ListOptions) ([]*user_model.User, error) {
sess := db.GetEngine(ctx).Where("star.repo_id = ?", repo.ID).
func GetStargazers(ctx context.Context, repoID int64, opts db.ListOptions) ([]*user_model.User, error) {
sess := db.GetEngine(ctx).Where("star.repo_id = ?", repoID).
Join("LEFT", "star", "`user`.id = star.uid")
if opts.Page > 0 {
sess = db.SetSessionPagination(sess, &opts)
Expand Down
6 changes: 3 additions & 3 deletions models/repo/star_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestRepository_GetStargazers(t *testing.T) {
// repo with stargazers
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
gazers, err := repo_model.GetStargazers(db.DefaultContext, repo, db.ListOptions{Page: 0})
gazers, err := repo_model.GetStargazers(db.DefaultContext, repo.ID, db.ListOptions{Page: 0})
assert.NoError(t, err)
if assert.Len(t, gazers, 1) {
assert.Equal(t, int64(2), gazers[0].ID)
Expand All @@ -50,7 +50,7 @@ func TestRepository_GetStargazers2(t *testing.T) {
// repo with stargazers
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
gazers, err := repo_model.GetStargazers(db.DefaultContext, repo, db.ListOptions{Page: 0})
gazers, err := repo_model.GetStargazers(db.DefaultContext, repo.ID, db.ListOptions{Page: 0})
assert.NoError(t, err)
assert.Len(t, gazers, 0)
}
Expand All @@ -69,7 +69,7 @@ func TestClearRepoStars(t *testing.T) {
assert.NoError(t, repo_model.ClearRepoStars(db.DefaultContext, repo.ID))
unittest.AssertNotExistsBean(t, &repo_model.Star{UID: user.ID, RepoID: repo.ID})

gazers, err := repo_model.GetStargazers(db.DefaultContext, repo, db.ListOptions{Page: 0})
gazers, err := repo_model.GetStargazers(db.DefaultContext, repo.ID, db.ListOptions{Page: 0})
assert.NoError(t, err)
assert.Len(t, gazers, 0)
}
2 changes: 1 addition & 1 deletion routers/api/v1/repo/star.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func ListStargazers(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"

stargazers, err := repo_model.GetStargazers(ctx, ctx.Repo.Repository, utils.GetListOptions(ctx))
stargazers, err := repo_model.GetStargazers(ctx, ctx.Repo.Repository.ID, utils.GetListOptions(ctx))
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetStargazers", err)
return
Expand Down
6 changes: 6 additions & 0 deletions routers/web/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ func Action(ctx *context.Context) {
ctx.Data["IsStaringRepo"] = repo_model.IsStaring(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
}

// we send the HX-Trigger header to trigger the refreshCards event, when the frontend receives a request with this header
// htmx triggers all elements that have the attribute hx-trigger="refreshCards from:body". This attribute is usually placed
// on containers that show a list of either stargazers or watchers. For a demonstration of the effects see the pull
// request description of https://github.com/go-gitea/gitea/pull/32570.
ctx.RespHeader().Add("HX-Trigger", "refreshCards")

switch ctx.PathParam(":action") {
case "watch", "unwatch", "star", "unstar":
// we have to reload the repository because NumStars or NumWatching (used in the templates) has just changed
Expand Down
30 changes: 16 additions & 14 deletions routers/web/repo/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const (
tplRepoHome base.TplName = "repo/home"
tplRepoViewList base.TplName = "repo/view_list"
tplWatchers base.TplName = "repo/watchers"
tplCards base.TplName = "repo/user_cards"
tplForks base.TplName = "repo/forks"
tplMigrating base.TplName = "repo/migrate/migrating"
)
Expand Down Expand Up @@ -1101,15 +1102,17 @@ func checkOutdatedBranch(ctx *context.Context) {
}

// RenderUserCards render a page show users according the input template
func RenderUserCards(ctx *context.Context, total int, getter func(opts db.ListOptions) ([]*user_model.User, error), tpl base.TplName) {
func RenderUserCards(ctx *context.Context, pageType string, total int, getter func(goctx gocontext.Context, repoID int64, opts db.ListOptions) ([]*user_model.User, error), tpl base.TplName) {
ctx.Data["Title"] = ctx.Tr(pageType)
ctx.Data["CardsTitle"] = ctx.Tr(pageType)
page := ctx.FormInt("page")
if page <= 0 {
page = 1
}
pager := context.NewPagination(total, setting.ItemsPerPage, page, 5)
ctx.Data["Page"] = pager

items, err := getter(db.ListOptions{
items, err := getter(ctx, ctx.Repo.Repository.ID, db.ListOptions{
Page: pager.Paginater.Current(),
PageSize: setting.ItemsPerPage,
})
Expand All @@ -1124,23 +1127,22 @@ func RenderUserCards(ctx *context.Context, total int, getter func(opts db.ListOp

// Watchers render repository's watch users
func Watchers(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.watchers")
ctx.Data["CardsTitle"] = ctx.Tr("repo.watchers")
ctx.Data["PageIsWatchers"] = true
RenderUserCards(ctx, "repo.watchers", ctx.Repo.Repository.NumWatches, repo_model.GetRepoWatchers, tplWatchers)
}

RenderUserCards(ctx, ctx.Repo.Repository.NumWatches, func(opts db.ListOptions) ([]*user_model.User, error) {
return repo_model.GetRepoWatchers(ctx, ctx.Repo.Repository.ID, opts)
}, tplWatchers)
// WatchersCards renders a repository's watchers user cards
func WatchersCards(ctx *context.Context) {
RenderUserCards(ctx, "repo.watchers", ctx.Repo.Repository.NumWatches, repo_model.GetRepoWatchers, tplCards)
}

// Stars render repository's starred users
func Stars(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.stargazers")
ctx.Data["CardsTitle"] = ctx.Tr("repo.stargazers")
ctx.Data["PageIsStargazers"] = true
delvh marked this conversation as resolved.
Show resolved Hide resolved
RenderUserCards(ctx, ctx.Repo.Repository.NumStars, func(opts db.ListOptions) ([]*user_model.User, error) {
return repo_model.GetStargazers(ctx, ctx.Repo.Repository, opts)
}, tplWatchers)
RenderUserCards(ctx, "repo.stargazers", ctx.Repo.Repository.NumStars, repo_model.GetStargazers, tplWatchers)
}

// StarsCards renders a repository's stargazers user cards
func StarsCards(ctx *context.Context) {
RenderUserCards(ctx, "repo.stargazers", ctx.Repo.Repository.NumStars, repo_model.GetStargazers, tplCards)
}

// Forks render repository's forked users
Expand Down
2 changes: 2 additions & 0 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1599,7 +1599,9 @@ func registerRoutes(m *web.Router) {

m.Group("/{username}/{reponame}", func() {
m.Get("/stars", repo.Stars)
m.Get("/stars/cards", repo.StarsCards)
m.Get("/watchers", repo.Watchers)
m.Get("/watchers/cards", repo.WatchersCards)
m.Get("/search", reqRepoCodeReader, repo.Search)
m.Post("/action/{action}", reqSignIn, repo.Action)
}, optSignIn, context.RepoAssignment, context.RepoRef())
Expand Down
13 changes: 12 additions & 1 deletion templates/repo/watchers.tmpl
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository watchers">
{{template "repo/header" .}}
<div class="ui container">
<div class="no-loading-indicator tw-hidden"></div>
<!-- this element reloads its content with htmx as soon as a response from the backend
includes the header "HX-Trigger: refreshCards". This usually happens when a user
watched/unwatched/starred/unstarred and we want their user card to appear/disappear.
To test go to the watchers page and click the watch button. The user cards should reload. -->
<div
hx-trigger="refreshCards from:body"
wxiaoguang marked this conversation as resolved.
Show resolved Hide resolved
hx-indicator=".no-loading-indicator"
hx-swap="innerHTML"
hx-get="{{$.Link}}/cards"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that it could use hx-select to partially reloading, then no need to add these /cards endpoints.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will try it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pushed 🤣

Managed to get it work in da86679, only a few changed lines now

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thing, it should use CurrentURL but not Link, because there could be "page" query parameters.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that looks great and the diff is smaller, thank you.

class="ui container"
>
{{template "repo/user_cards" .}}
</div>
</div>
Expand Down
Loading