mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-10 05:24:18 +09:00
Adds per-event watch options, so a user can choose which repository notifications they receive. The watch button is now a menu with four modes: participating and mentions (the unwatched state, as on GitHub), all activity, ignore, and custom, which opens a dialog to pick issues, pull requests and releases. The same dialog is reachable from the gear icon on the watched repository list. Options apply to UI notifications and to mail. The watcher, star and fork counts moved inside their buttons, so all three share one shape, and the links to those lists moved to the repository sidebar. Assignees and requested reviewers now also receive UI notifications without having commented first. Buttons and dropdown: <img width="688" height="398" alt="image" src="https://github.com/user-attachments/assets/889b567d-086f-4a06-ac67-3a4205fc63b2" /> Modal: <img width="841" height="327" alt="Screenshot 2026-08-07 at 21 10 59" src="https://github.com/user-attachments/assets/3bedbff5-9011-4f1b-91a9-e8f5c212fb5d" /> New entries in sidebar: <img width="170" height="207" alt="Screenshot 2026-08-07 at 21 13 28" src="https://github.com/user-attachments/assets/5f5150cd-f42d-4543-bd0b-7334cf4aab0e" /> Fixes: https://github.com/go-gitea/gitea/issues/17238 Ref: https://github.com/go-gitea/gitea/issues/35492 --------- Co-authored-by: bircni <bircni@icloud.com> Co-authored-by: silverwind <me@silverwind.io>
172 lines
6.1 KiB
Go
172 lines
6.1 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"gitea.dev/models/db"
|
|
repo_model "gitea.dev/models/repo"
|
|
"gitea.dev/models/unittest"
|
|
user_model "gitea.dev/models/user"
|
|
"gitea.dev/modules/setting"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestIsWatching(t *testing.T) {
|
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
|
|
|
assert.True(t, repo_model.IsWatching(t.Context(), 1, 1))
|
|
assert.True(t, repo_model.IsWatching(t.Context(), 4, 1))
|
|
assert.True(t, repo_model.IsWatching(t.Context(), 11, 1))
|
|
|
|
assert.False(t, repo_model.IsWatching(t.Context(), 1, 5))
|
|
assert.False(t, repo_model.IsWatching(t.Context(), 8, 1))
|
|
assert.False(t, repo_model.IsWatching(t.Context(), unittest.NonexistentID, unittest.NonexistentID))
|
|
}
|
|
|
|
func TestGetWatchers(t *testing.T) {
|
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
watches, err := repo_model.GetWatchers(t.Context(), repo.ID)
|
|
assert.NoError(t, err)
|
|
// One watchers are inactive, thus minus 1
|
|
assert.Len(t, watches, repo.NumWatches-1)
|
|
for _, watch := range watches {
|
|
assert.Equal(t, repo.ID, watch.RepoID)
|
|
}
|
|
|
|
watches, err = repo_model.GetWatchers(t.Context(), unittest.NonexistentID)
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, watches)
|
|
}
|
|
|
|
func TestRepository_GetWatchers(t *testing.T) {
|
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
watchers, err := repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, watchers, repo.NumWatches)
|
|
for _, watcher := range watchers {
|
|
unittest.AssertExistsAndLoadBean(t, &repo_model.Watch{UserID: watcher.ID, RepoID: repo.ID})
|
|
}
|
|
|
|
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 9})
|
|
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, watchers)
|
|
}
|
|
|
|
func TestWatchIfAuto(t *testing.T) {
|
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
user12 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 12})
|
|
|
|
watchers, err := repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, watchers, repo.NumWatches)
|
|
|
|
setting.Service.AutoWatchOnChanges = false
|
|
|
|
prevCount := repo.NumWatches
|
|
|
|
// Must not add watch
|
|
assert.NoError(t, repo_model.WatchIfAuto(t.Context(), 8, 1, true))
|
|
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, watchers, prevCount)
|
|
|
|
// Should not add watch
|
|
assert.NoError(t, repo_model.WatchIfAuto(t.Context(), 10, 1, true))
|
|
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, watchers, prevCount)
|
|
|
|
setting.Service.AutoWatchOnChanges = true
|
|
|
|
// Must not add watch
|
|
assert.NoError(t, repo_model.WatchIfAuto(t.Context(), 8, 1, true))
|
|
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, watchers, prevCount)
|
|
|
|
// Should not add watch
|
|
assert.NoError(t, repo_model.WatchIfAuto(t.Context(), 12, 1, false))
|
|
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, watchers, prevCount)
|
|
|
|
// Should add watch
|
|
assert.NoError(t, repo_model.WatchIfAuto(t.Context(), 12, 1, true))
|
|
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, watchers, prevCount+1)
|
|
|
|
// Should remove watch, inhibit from adding auto
|
|
assert.NoError(t, repo_model.WatchRepo(t.Context(), user12, repo, false))
|
|
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, watchers, prevCount)
|
|
|
|
// Must not add watch
|
|
assert.NoError(t, repo_model.WatchIfAuto(t.Context(), 12, 1, true))
|
|
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, watchers, prevCount)
|
|
}
|
|
|
|
func TestClearRepoWatches(t *testing.T) {
|
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
|
|
|
const repoID int64 = 1
|
|
watchers, err := repo_model.GetRepoWatchers(t.Context(), repoID, db.ListOptions{Page: 1})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, watchers)
|
|
|
|
assert.NoError(t, repo_model.ClearRepoWatches(t.Context(), repoID))
|
|
|
|
watchers, err = repo_model.GetRepoWatchers(t.Context(), repoID, db.ListOptions{Page: 1})
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, watchers)
|
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
|
|
assert.Zero(t, repo.NumWatches)
|
|
}
|
|
|
|
func TestWatchOptions(t *testing.T) {
|
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
|
|
|
// repo 1 is watched by users 1, 4, 9 and 11, all with every event enabled
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
|
assert.NoError(t, repo_model.SetWatchOptions(t.Context(), user.ID, repo.ID, repo_model.WatchOptions{PullRequests: true}))
|
|
|
|
for watchType, expected := range map[repo_model.WatchType][]int64{
|
|
repo_model.WatchPullRequests: {1, 4, 9, 11},
|
|
repo_model.WatchIssues: {4, 9, 11},
|
|
repo_model.WatchReleases: {4, 9, 11},
|
|
} {
|
|
ids, err := repo_model.GetRepoWatchersIDs(t.Context(), repo.ID, watchType)
|
|
assert.NoError(t, err)
|
|
assert.ElementsMatch(t, expected, ids, watchType)
|
|
}
|
|
|
|
// the options of one user must not show up for another
|
|
watches, err := repo_model.GetUserWatches(t.Context(), 4, []int64{repo.ID})
|
|
assert.NoError(t, err)
|
|
assert.True(t, watches[repo.ID].Issues)
|
|
|
|
// watching again resets a custom selection
|
|
assert.NoError(t, repo_model.WatchRepo(t.Context(), user, repo, false))
|
|
assert.NoError(t, repo_model.WatchRepo(t.Context(), user, repo, true))
|
|
watch, err := repo_model.GetWatch(t.Context(), user.ID, repo.ID)
|
|
assert.NoError(t, err)
|
|
assert.True(t, watch.PullRequests && watch.Issues && watch.Releases)
|
|
}
|