diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json
index b24d96526f..6305a8a8ed 100644
--- a/options/locale_next/locale_en-US.json
+++ b/options/locale_next/locale_en-US.json
@@ -4,6 +4,12 @@
"home.explore_repos": "Explore repositories",
"home.explore_users": "Explore users",
"home.explore_orgs": "Explore organizations",
+ "stars.list.none": "No one starred this repo.",
+ "watch.list.none": "No one is watching this repo.",
+ "followers.incoming.list.self.none": "No one is following your profile.",
+ "followers.incoming.list.none": "No one is following this user.",
+ "followers.outgoing.list.self.none": "You are not following anyone.",
+ "followers.outgoing.list.none": "%s isn't following anyone.",
"relativetime.now": "now",
"relativetime.future": "in future",
"relativetime.mins": {
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 219de722d2..de508509dc 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -1,5 +1,6 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
// Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
@@ -1240,6 +1241,7 @@ func RenderUserCards(ctx *context.Context, total int, getter func(opts db.ListOp
func Watchers(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.watchers")
ctx.Data["CardsTitle"] = ctx.Tr("repo.watchers")
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("watch.list.none")
ctx.Data["PageIsWatchers"] = true
RenderUserCards(ctx, ctx.Repo.Repository.NumWatches, func(opts db.ListOptions) ([]*user_model.User, error) {
@@ -1251,6 +1253,7 @@ func Watchers(ctx *context.Context) {
func Stars(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.stargazers")
ctx.Data["CardsTitle"] = ctx.Tr("repo.stargazers")
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("stars.list.none")
ctx.Data["PageIsStargazers"] = true
RenderUserCards(ctx, ctx.Repo.Repository.NumStars, func(opts db.ListOptions) ([]*user_model.User, error) {
return repo_model.GetStargazers(ctx, ctx.Repo.Repository, opts)
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index 5132b1da5c..8068c1c6bc 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -1,5 +1,6 @@
// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
+// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
@@ -170,10 +171,20 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
ctx.Data["Cards"] = followers
total = int(numFollowers)
ctx.Data["CardsTitle"] = ctx.TrN(total, "user.followers.title.one", "user.followers.title.few")
+ if ctx.IsSigned && ctx.ContextUser.ID == ctx.Doer.ID {
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("followers.incoming.list.self.none")
+ } else {
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("followers.incoming.list.none")
+ }
case "following":
ctx.Data["Cards"] = following
total = int(numFollowing)
ctx.Data["CardsTitle"] = ctx.TrN(total, "user.following.title.one", "user.following.title.few")
+ if ctx.IsSigned && ctx.ContextUser.ID == ctx.Doer.ID {
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("followers.outgoing.list.self.none")
+ } else {
+ ctx.Data["CardsNoneMsg"] = ctx.Tr("followers.outgoing.list.none", ctx.ContextUser.Name)
+ }
case "activity":
date := ctx.FormString("date")
pagingNum = setting.UI.FeedPagingNum
diff --git a/templates/repo/user_cards.tmpl b/templates/repo/user_cards.tmpl
index fbd4ef0cee..fc5a30bd45 100644
--- a/templates/repo/user_cards.tmpl
+++ b/templates/repo/user_cards.tmpl
@@ -4,29 +4,33 @@
{{.CardsTitle}}
{{end}}
-
- {{range .Cards}}
- -
-
- {{ctx.AvatarUtils.Avatar .}}
-
-
-
-
-
- {{end}}
-
+
+ {{end}}
+
+ {{else if .CardsNoneMsg}}
+
{{.CardsNoneMsg}}
+ {{end}}
{{template "base/paginate" .}}
diff --git a/tests/integration/repo_starwatch_test.go b/tests/integration/repo_starwatch_test.go
index 3bcfa29fe3..85100eca30 100644
--- a/tests/integration/repo_starwatch_test.go
+++ b/tests/integration/repo_starwatch_test.go
@@ -17,7 +17,7 @@ import (
"github.com/stretchr/testify/assert"
)
-func testRepoStarringOrWatching(t *testing.T, action, listURI string) {
+func testRepoStarringOrWatching(t *testing.T, action, listURI string, expectEmpty bool) {
t.Helper()
defer tests.PrepareTestEnv(t)()
@@ -50,6 +50,12 @@ func testRepoStarringOrWatching(t *testing.T, action, listURI string) {
htmlDoc = NewHTMLParser(t, resp.Body)
htmlDoc.AssertElement(t, ".user-cards .list .card > a[href='/user5']", true)
+ if expectEmpty {
+ // Verify which user-cards elements are present
+ htmlDoc.AssertElement(t, ".user-cards > .list", true)
+ htmlDoc.AssertElement(t, ".user-cards > div", false)
+ }
+
// Unstar/unwatch the repo as user5
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/user2/repo1/action/%s", oppositeAction), map[string]string{
"_csrf": GetCSRF(t, session, "/user2/repo1"),
@@ -73,15 +79,22 @@ func testRepoStarringOrWatching(t *testing.T, action, listURI string) {
// Verify that "user5" is not among the stargazers/watchers
htmlDoc = NewHTMLParser(t, resp.Body)
- htmlDoc.AssertElement(t, ".user-cards .list .item.ui.segment > a[href='/user5']", false)
+ htmlDoc.AssertElement(t, ".user-cards .list .item.ui.segment > a[href='/user2']", false)
+
+ if expectEmpty {
+ // Verify which user-cards elements are present
+ htmlDoc.AssertElement(t, ".user-cards > .list", false)
+ htmlDoc.AssertElement(t, ".user-cards > div", true)
+ }
}
func TestRepoStarUnstarUI(t *testing.T) {
- testRepoStarringOrWatching(t, "star", "stars")
+ testRepoStarringOrWatching(t, "star", "stars", true)
}
func TestRepoWatchUnwatchUI(t *testing.T) {
- testRepoStarringOrWatching(t, "watch", "watchers")
+ testRepoStarringOrWatching(t, "watch", "watchers", false)
+ // Empty list state is not checked because repo is watched by many users
}
func TestDisabledStars(t *testing.T) {
diff --git a/tests/integration/user_profile_follows_test.go b/tests/integration/user_profile_follows_test.go
index 08bd58db16..b4216865b7 100644
--- a/tests/integration/user_profile_follows_test.go
+++ b/tests/integration/user_profile_follows_test.go
@@ -1,5 +1,5 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
+// SPDX-License-Identifier: GPL-3.0-or-later
package integration
@@ -32,6 +32,7 @@ func TestUserProfileFollows(t *testing.T) {
followingLink := "#profile-avatar-card a[href='/user1?tab=following']"
listHeader := ".user-cards h2"
listItems := ".user-cards .list"
+ listMsg := ".user-cards > div"
// = No follows =
@@ -44,7 +45,8 @@ func TestUserProfileFollows(t *testing.T) {
// Verify that user1 has no followers
testSelectorEquals(t, page, followersLink, "0 followers")
testSelectorEquals(t, page, listHeader, "Followers")
- testListCount(t, page, listItems, followCount)
+ page.AssertElement(t, listItems, false)
+ page.AssertElement(t, listMsg, true)
// Request the profile of user1, the Following tab
response = user1.MakeRequest(t, NewRequest(t, "GET", "/user1?tab=following"), http.StatusOK)
@@ -53,7 +55,8 @@ func TestUserProfileFollows(t *testing.T) {
// Verify that user1 does not follow anyone
testSelectorEquals(t, page, followingLink, "0 following")
testSelectorEquals(t, page, listHeader, "Following")
- testListCount(t, page, listItems, followCount)
+ page.AssertElement(t, listItems, false)
+ page.AssertElement(t, listMsg, true)
// Make user1 and user2 follow each other
testUserFollowUser(t, user1, "user2")
@@ -71,6 +74,7 @@ func TestUserProfileFollows(t *testing.T) {
testSelectorEquals(t, page, followersLink, "1 follower")
testSelectorEquals(t, page, listHeader, "Follower")
testListCount(t, page, listItems, followCount)
+ page.AssertElement(t, listMsg, false)
// Request the profile of user1, the Following tab
response = user1.MakeRequest(t, NewRequest(t, "GET", "/user1?tab=following"), http.StatusOK)
@@ -80,6 +84,7 @@ func TestUserProfileFollows(t *testing.T) {
testSelectorEquals(t, page, followingLink, "1 following")
testSelectorEquals(t, page, listHeader, "Following")
testListCount(t, page, listItems, followCount)
+ page.AssertElement(t, listMsg, false)
// Make user1 and user3 follow each other
testUserFollowUser(t, user1, "user5")
@@ -97,6 +102,7 @@ func TestUserProfileFollows(t *testing.T) {
testSelectorEquals(t, page, followersLink, "2 followers")
testSelectorEquals(t, page, listHeader, "Followers")
testListCount(t, page, listItems, followCount)
+ page.AssertElement(t, listMsg, false)
// Request the profile of user1, the Following tab
response = user1.MakeRequest(t, NewRequest(t, "GET", "/user1?tab=following"), http.StatusOK)
@@ -106,6 +112,7 @@ func TestUserProfileFollows(t *testing.T) {
testSelectorEquals(t, page, followingLink, "2 following")
testSelectorEquals(t, page, listHeader, "Following")
testListCount(t, page, listItems, followCount)
+ page.AssertElement(t, listMsg, false)
}
// testUserFollowUser simply follows a user `following` by session of user `follower`