mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 06:24:11 +01:00 
			
		
		
		
	Add pages to view watched repos and subscribed issues/PRs (#17156)
Adds GitHub-like pages to view watched repos and subscribed issues/PRs This is my second try to fix this, but it is better than the first since it doesn't uses a filter option which could be slow when accessing `/issues` or `/pulls` and it shows both pulls and issues (the first try is #17053). Closes #16111 Replaces and closes #17053  Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		
							parent
							
								
									3b6a7e5c8a
								
							
						
					
					
						commit
						08609d439d
					
				@ -1186,6 +1186,7 @@ type IssuesOptions struct { //nolint
 | 
			
		||||
	PosterID           int64
 | 
			
		||||
	MentionedID        int64
 | 
			
		||||
	ReviewRequestedID  int64
 | 
			
		||||
	SubscriberID       int64
 | 
			
		||||
	MilestoneIDs       []int64
 | 
			
		||||
	ProjectID          int64
 | 
			
		||||
	ProjectBoardID     int64
 | 
			
		||||
@ -1299,6 +1300,10 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
 | 
			
		||||
		applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.SubscriberID > 0 {
 | 
			
		||||
		applySubscribedCondition(sess, opts.SubscriberID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(opts.MilestoneIDs) > 0 {
 | 
			
		||||
		sess.In("issue.milestone_id", opts.MilestoneIDs)
 | 
			
		||||
	}
 | 
			
		||||
@ -1463,6 +1468,36 @@ func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64)
 | 
			
		||||
			reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func applySubscribedCondition(sess *xorm.Session, subscriberID int64) *xorm.Session {
 | 
			
		||||
	return sess.And(
 | 
			
		||||
		builder.
 | 
			
		||||
			NotIn("issue.id",
 | 
			
		||||
				builder.Select("issue_id").
 | 
			
		||||
					From("issue_watch").
 | 
			
		||||
					Where(builder.Eq{"is_watching": false, "user_id": subscriberID}),
 | 
			
		||||
			),
 | 
			
		||||
	).And(
 | 
			
		||||
		builder.Or(
 | 
			
		||||
			builder.In("issue.id", builder.
 | 
			
		||||
				Select("issue_id").
 | 
			
		||||
				From("issue_watch").
 | 
			
		||||
				Where(builder.Eq{"is_watching": true, "user_id": subscriberID}),
 | 
			
		||||
			),
 | 
			
		||||
			builder.In("issue.id", builder.
 | 
			
		||||
				Select("issue_id").
 | 
			
		||||
				From("comment").
 | 
			
		||||
				Where(builder.Eq{"poster_id": subscriberID}),
 | 
			
		||||
			),
 | 
			
		||||
			builder.Eq{"issue.poster_id": subscriberID},
 | 
			
		||||
			builder.In("issue.repo_id", builder.
 | 
			
		||||
				Select("id").
 | 
			
		||||
				From("watch").
 | 
			
		||||
				Where(builder.Eq{"user_id": subscriberID, "mode": true}),
 | 
			
		||||
			),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CountIssuesByRepo map from repoID to number of issues matching the options
 | 
			
		||||
func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
 | 
			
		||||
	e := db.GetEngine(db.DefaultContext)
 | 
			
		||||
 | 
			
		||||
@ -3034,6 +3034,9 @@ pin = Pin notification
 | 
			
		||||
mark_as_read = Mark as read
 | 
			
		||||
mark_as_unread = Mark as unread
 | 
			
		||||
mark_all_as_read = Mark all as read
 | 
			
		||||
subscriptions = Subscriptions
 | 
			
		||||
watching = Watching
 | 
			
		||||
no_subscriptions = No subscriptions
 | 
			
		||||
 | 
			
		||||
[gpg]
 | 
			
		||||
default_key=Signed with default key
 | 
			
		||||
 | 
			
		||||
@ -13,16 +13,23 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	activities_model "code.gitea.io/gitea/models/activities"
 | 
			
		||||
	"code.gitea.io/gitea/models/db"
 | 
			
		||||
	issues_model "code.gitea.io/gitea/models/issues"
 | 
			
		||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
			
		||||
	"code.gitea.io/gitea/modules/base"
 | 
			
		||||
	"code.gitea.io/gitea/modules/context"
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	"code.gitea.io/gitea/modules/structs"
 | 
			
		||||
	"code.gitea.io/gitea/modules/util"
 | 
			
		||||
	issue_service "code.gitea.io/gitea/services/issue"
 | 
			
		||||
	pull_service "code.gitea.io/gitea/services/pull"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	tplNotification    base.TplName = "user/notification/notification"
 | 
			
		||||
	tplNotificationDiv base.TplName = "user/notification/notification_div"
 | 
			
		||||
	tplNotification              base.TplName = "user/notification/notification"
 | 
			
		||||
	tplNotificationDiv           base.TplName = "user/notification/notification_div"
 | 
			
		||||
	tplNotificationSubscriptions base.TplName = "user/notification/notification_subscriptions"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetNotificationCount is the middleware that sets the notification count in the context
 | 
			
		||||
@ -197,6 +204,208 @@ func NotificationPurgePost(c *context.Context) {
 | 
			
		||||
	c.Redirect(setting.AppSubURL+"/notifications", http.StatusSeeOther)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NotificationSubscriptions returns the list of subscribed issues
 | 
			
		||||
func NotificationSubscriptions(c *context.Context) {
 | 
			
		||||
	page := c.FormInt("page")
 | 
			
		||||
	if page < 1 {
 | 
			
		||||
		page = 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sortType := c.FormString("sort")
 | 
			
		||||
	c.Data["SortType"] = sortType
 | 
			
		||||
 | 
			
		||||
	state := c.FormString("state")
 | 
			
		||||
	if !util.IsStringInSlice(state, []string{"all", "open", "closed"}, true) {
 | 
			
		||||
		state = "all"
 | 
			
		||||
	}
 | 
			
		||||
	c.Data["State"] = state
 | 
			
		||||
	var showClosed util.OptionalBool
 | 
			
		||||
	switch state {
 | 
			
		||||
	case "all":
 | 
			
		||||
		showClosed = util.OptionalBoolNone
 | 
			
		||||
	case "closed":
 | 
			
		||||
		showClosed = util.OptionalBoolTrue
 | 
			
		||||
	case "open":
 | 
			
		||||
		showClosed = util.OptionalBoolFalse
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var issueTypeBool util.OptionalBool
 | 
			
		||||
	issueType := c.FormString("issueType")
 | 
			
		||||
	switch issueType {
 | 
			
		||||
	case "issues":
 | 
			
		||||
		issueTypeBool = util.OptionalBoolFalse
 | 
			
		||||
	case "pulls":
 | 
			
		||||
		issueTypeBool = util.OptionalBoolTrue
 | 
			
		||||
	default:
 | 
			
		||||
		issueTypeBool = util.OptionalBoolNone
 | 
			
		||||
	}
 | 
			
		||||
	c.Data["IssueType"] = issueType
 | 
			
		||||
 | 
			
		||||
	var labelIDs []int64
 | 
			
		||||
	selectedLabels := c.FormString("labels")
 | 
			
		||||
	c.Data["Labels"] = selectedLabels
 | 
			
		||||
	if len(selectedLabels) > 0 && selectedLabels != "0" {
 | 
			
		||||
		var err error
 | 
			
		||||
		labelIDs, err = base.StringsToInt64s(strings.Split(selectedLabels, ","))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.ServerError("StringsToInt64s", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	count, err := issues_model.CountIssues(&issues_model.IssuesOptions{
 | 
			
		||||
		SubscriberID: c.Doer.ID,
 | 
			
		||||
		IsClosed:     showClosed,
 | 
			
		||||
		IsPull:       issueTypeBool,
 | 
			
		||||
		LabelIDs:     labelIDs,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.ServerError("CountIssues", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	issues, err := issues_model.Issues(&issues_model.IssuesOptions{
 | 
			
		||||
		ListOptions: db.ListOptions{
 | 
			
		||||
			PageSize: setting.UI.IssuePagingNum,
 | 
			
		||||
			Page:     page,
 | 
			
		||||
		},
 | 
			
		||||
		SubscriberID: c.Doer.ID,
 | 
			
		||||
		SortType:     sortType,
 | 
			
		||||
		IsClosed:     showClosed,
 | 
			
		||||
		IsPull:       issueTypeBool,
 | 
			
		||||
		LabelIDs:     labelIDs,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.ServerError("Issues", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	commitStatuses, lastStatus, err := pull_service.GetIssuesAllCommitStatus(c, issues)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.ServerError("GetIssuesAllCommitStatus", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c.Data["CommitLastStatus"] = lastStatus
 | 
			
		||||
	c.Data["CommitStatuses"] = commitStatuses
 | 
			
		||||
	c.Data["Issues"] = issues
 | 
			
		||||
 | 
			
		||||
	c.Data["IssueRefEndNames"], c.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, "")
 | 
			
		||||
 | 
			
		||||
	commitStatus, err := pull_service.GetIssuesLastCommitStatus(c, issues)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.ServerError("GetIssuesLastCommitStatus", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c.Data["CommitStatus"] = commitStatus
 | 
			
		||||
 | 
			
		||||
	issueList := issues_model.IssueList(issues)
 | 
			
		||||
	approvalCounts, err := issueList.GetApprovalCounts(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.ServerError("ApprovalCounts", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c.Data["ApprovalCounts"] = func(issueID int64, typ string) int64 {
 | 
			
		||||
		counts, ok := approvalCounts[issueID]
 | 
			
		||||
		if !ok || len(counts) == 0 {
 | 
			
		||||
			return 0
 | 
			
		||||
		}
 | 
			
		||||
		reviewTyp := issues_model.ReviewTypeApprove
 | 
			
		||||
		if typ == "reject" {
 | 
			
		||||
			reviewTyp = issues_model.ReviewTypeReject
 | 
			
		||||
		} else if typ == "waiting" {
 | 
			
		||||
			reviewTyp = issues_model.ReviewTypeRequest
 | 
			
		||||
		}
 | 
			
		||||
		for _, count := range counts {
 | 
			
		||||
			if count.Type == reviewTyp {
 | 
			
		||||
				return count.Count
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Data["Status"] = 1
 | 
			
		||||
	c.Data["Title"] = c.Tr("notification.subscriptions")
 | 
			
		||||
 | 
			
		||||
	// redirect to last page if request page is more than total pages
 | 
			
		||||
	pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)
 | 
			
		||||
	if pager.Paginater.Current() < page {
 | 
			
		||||
		c.Redirect(fmt.Sprintf("/notifications/subscriptions?page=%d", pager.Paginater.Current()))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	pager.AddParam(c, "sort", "SortType")
 | 
			
		||||
	pager.AddParam(c, "state", "State")
 | 
			
		||||
	c.Data["Page"] = pager
 | 
			
		||||
 | 
			
		||||
	c.HTML(http.StatusOK, tplNotificationSubscriptions)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NotificationWatching returns the list of watching repos
 | 
			
		||||
func NotificationWatching(c *context.Context) {
 | 
			
		||||
	page := c.FormInt("page")
 | 
			
		||||
	if page < 1 {
 | 
			
		||||
		page = 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var orderBy db.SearchOrderBy
 | 
			
		||||
	c.Data["SortType"] = c.FormString("sort")
 | 
			
		||||
	switch c.FormString("sort") {
 | 
			
		||||
	case "newest":
 | 
			
		||||
		orderBy = db.SearchOrderByNewest
 | 
			
		||||
	case "oldest":
 | 
			
		||||
		orderBy = db.SearchOrderByOldest
 | 
			
		||||
	case "recentupdate":
 | 
			
		||||
		orderBy = db.SearchOrderByRecentUpdated
 | 
			
		||||
	case "leastupdate":
 | 
			
		||||
		orderBy = db.SearchOrderByLeastUpdated
 | 
			
		||||
	case "reversealphabetically":
 | 
			
		||||
		orderBy = db.SearchOrderByAlphabeticallyReverse
 | 
			
		||||
	case "alphabetically":
 | 
			
		||||
		orderBy = db.SearchOrderByAlphabetically
 | 
			
		||||
	case "moststars":
 | 
			
		||||
		orderBy = db.SearchOrderByStarsReverse
 | 
			
		||||
	case "feweststars":
 | 
			
		||||
		orderBy = db.SearchOrderByStars
 | 
			
		||||
	case "mostforks":
 | 
			
		||||
		orderBy = db.SearchOrderByForksReverse
 | 
			
		||||
	case "fewestforks":
 | 
			
		||||
		orderBy = db.SearchOrderByForks
 | 
			
		||||
	default:
 | 
			
		||||
		c.Data["SortType"] = "recentupdate"
 | 
			
		||||
		orderBy = db.SearchOrderByRecentUpdated
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	repos, count, err := repo_model.SearchRepository(&repo_model.SearchRepoOptions{
 | 
			
		||||
		ListOptions: db.ListOptions{
 | 
			
		||||
			PageSize: setting.UI.User.RepoPagingNum,
 | 
			
		||||
			Page:     page,
 | 
			
		||||
		},
 | 
			
		||||
		Actor:              c.Doer,
 | 
			
		||||
		Keyword:            c.FormTrim("q"),
 | 
			
		||||
		OrderBy:            orderBy,
 | 
			
		||||
		Private:            c.IsSigned,
 | 
			
		||||
		WatchedByID:        c.Doer.ID,
 | 
			
		||||
		Collaborate:        util.OptionalBoolFalse,
 | 
			
		||||
		TopicOnly:          c.FormBool("topic"),
 | 
			
		||||
		IncludeDescription: setting.UI.SearchRepoDescription,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.ServerError("ErrSearchRepository", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	total := int(count)
 | 
			
		||||
	c.Data["Total"] = total
 | 
			
		||||
	c.Data["Repos"] = repos
 | 
			
		||||
 | 
			
		||||
	// redirect to last page if request page is more than total pages
 | 
			
		||||
	pager := context.NewPagination(total, setting.UI.User.RepoPagingNum, page, 5)
 | 
			
		||||
	pager.SetDefaultParams(c)
 | 
			
		||||
	c.Data["Page"] = pager
 | 
			
		||||
 | 
			
		||||
	c.Data["Status"] = 2
 | 
			
		||||
	c.Data["Title"] = c.Tr("notification.watching")
 | 
			
		||||
 | 
			
		||||
	c.HTML(http.StatusOK, tplNotificationSubscriptions)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAvailable returns the notification counts
 | 
			
		||||
func NewAvailable(ctx *context.Context) {
 | 
			
		||||
	ctx.JSON(http.StatusOK, structs.NotificationCount{New: activities_model.CountUnread(ctx, ctx.Doer.ID)})
 | 
			
		||||
 | 
			
		||||
@ -1269,6 +1269,8 @@ func RegisterRoutes(m *web.Route) {
 | 
			
		||||
 | 
			
		||||
	m.Group("/notifications", func() {
 | 
			
		||||
		m.Get("", user.Notifications)
 | 
			
		||||
		m.Get("/subscriptions", user.NotificationSubscriptions)
 | 
			
		||||
		m.Get("/watching", user.NotificationWatching)
 | 
			
		||||
		m.Post("/status", user.NotificationStatusPost)
 | 
			
		||||
		m.Post("/purge", user.NotificationPurgePost)
 | 
			
		||||
		m.Get("/new", user.NewAvailable)
 | 
			
		||||
 | 
			
		||||
@ -171,6 +171,10 @@
 | 
			
		||||
							{{.locale.Tr "your_starred"}}
 | 
			
		||||
						</a>
 | 
			
		||||
					{{end}}
 | 
			
		||||
					<a class="item" href="{{AppSubUrl}}/notifications/subscriptions">
 | 
			
		||||
						{{svg "octicon-bell"}}
 | 
			
		||||
						{{.locale.Tr "notification.subscriptions"}}<!-- Subscriptions -->
 | 
			
		||||
					</a>
 | 
			
		||||
					<a class="{{if .PageIsUserSettings}}active{{end}} item" href="{{AppSubUrl}}/user/settings">
 | 
			
		||||
						{{svg "octicon-tools"}}
 | 
			
		||||
						{{.locale.Tr "your_settings"}}<!-- Your settings -->
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										79
									
								
								templates/user/notification/notification_subscriptions.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								templates/user/notification/notification_subscriptions.tmpl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
			
		||||
{{template "base/head" .}}
 | 
			
		||||
<div class="page-content user notification" id="notification_subscriptions" data-params="{{.Page.GetParams}}" data-sequence-number="{{.SequenceNumber}}">
 | 
			
		||||
	<div class="ui container">
 | 
			
		||||
		<div class="ui top attached tabular menu">
 | 
			
		||||
			<a href="{{AppSubUrl}}/notifications/subscriptions" class="{{if eq .Status 1}}active {{end}}item">
 | 
			
		||||
				{{.locale.Tr "notification.subscriptions"}}
 | 
			
		||||
			</a>
 | 
			
		||||
			<a href="{{AppSubUrl}}/notifications/watching" class="{{if eq .Status 2}}active {{end}}item">
 | 
			
		||||
				{{.locale.Tr "notification.watching"}}
 | 
			
		||||
			</a>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="ui bottom attached active tab segment">
 | 
			
		||||
			{{if eq .Status 1}}
 | 
			
		||||
				<div id="issue-filters" class="ui stackable grid">
 | 
			
		||||
					<div class="six wide column">
 | 
			
		||||
						<div class="ui compact tiny menu">
 | 
			
		||||
							<a class="{{if eq .State "all"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state=all&issueType={{$.IssueType}}&labels={{$.Labels}}">
 | 
			
		||||
								{{.locale.Tr "all"}}
 | 
			
		||||
							</a>
 | 
			
		||||
							<a class="{{if eq .State "open"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state=open&issueType={{$.IssueType}}&labels={{$.Labels}}">
 | 
			
		||||
								{{svg "octicon-issue-opened" 16 "mr-3"}}
 | 
			
		||||
								{{.locale.Tr "repo.issues.open_title"}}
 | 
			
		||||
							</a>
 | 
			
		||||
							<a class="{{if eq .State "closed"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state=closed&issueType={{$.IssueType}}&labels={{$.Labels}}">
 | 
			
		||||
								{{svg "octicon-issue-closed" 16 "mr-3"}}
 | 
			
		||||
								{{.locale.Tr "repo.issues.closed_title"}}
 | 
			
		||||
							</a>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="seven wide right aligned right floated column">
 | 
			
		||||
						<div class="ui right aligned secondary filter stackable menu labels">
 | 
			
		||||
							<!-- Type -->
 | 
			
		||||
								<div class="ui dropdown type jump item">
 | 
			
		||||
									<span class="text">
 | 
			
		||||
										{{.locale.Tr "repo.issues.filter_type"}}
 | 
			
		||||
										{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 | 
			
		||||
									</span>
 | 
			
		||||
									<div class="menu">
 | 
			
		||||
										<a class="{{if or (eq .IssueType "all") (not .IssueType)}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state={{$.State}}&issueType=all&labels={{$.Labels}}">{{.locale.Tr "all"}}</a>
 | 
			
		||||
										<a class="{{if eq .IssueType "issues"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state={{$.State}}&issueType=issues&labels={{$.Labels}}">{{.locale.Tr "issues"}}</a>
 | 
			
		||||
										<a class="{{if eq .IssueType "pulls"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state={{$.State}}&issueType=pulls&labels={{$.Labels}}">{{.locale.Tr "pull_requests"}}</a>
 | 
			
		||||
									</div>
 | 
			
		||||
								</div>
 | 
			
		||||
 | 
			
		||||
							<!-- Sort -->
 | 
			
		||||
							<div class="ui dropdown type jump item">
 | 
			
		||||
								<span class="text">
 | 
			
		||||
									{{.locale.Tr "repo.issues.filter_sort"}}
 | 
			
		||||
									{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 | 
			
		||||
								</span>
 | 
			
		||||
								<div class="menu">
 | 
			
		||||
									<a class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="{{$.Link}}?sort=latest&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{.locale.Tr "repo.issues.filter_sort.latest"}}</a>
 | 
			
		||||
									<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="{{$.Link}}?sort=oldest&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{.locale.Tr "repo.issues.filter_sort.oldest"}}</a>
 | 
			
		||||
									<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?sort=recentupdate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{.locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
 | 
			
		||||
									<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?sort=leastupdate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{.locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
 | 
			
		||||
									<a class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="{{$.Link}}?sort=mostcomment&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{.locale.Tr "repo.issues.filter_sort.mostcomment"}}</a>
 | 
			
		||||
									<a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="{{$.Link}}?sort=leastcomment&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{.locale.Tr "repo.issues.filter_sort.leastcomment"}}</a>
 | 
			
		||||
									<a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="{{$.Link}}?sort=nearduedate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{.locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
 | 
			
		||||
									<a class="{{if eq .SortType "farduedate"}}active {{end}}item" href="{{$.Link}}?sort=farduedate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{.locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
 | 
			
		||||
								</div>
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
				{{if eq (len .Issues) 0}}
 | 
			
		||||
					<div class="ui divider"></div>
 | 
			
		||||
					{{.locale.Tr "notification.no_subscriptions"}}
 | 
			
		||||
				{{else}}
 | 
			
		||||
					{{template "shared/issuelist" mergeinto . "listType" "dashboard"}}
 | 
			
		||||
				{{end}}
 | 
			
		||||
			{{else}}
 | 
			
		||||
				{{template "explore/repo_search" .}}
 | 
			
		||||
				{{template "explore/repo_list" .}}
 | 
			
		||||
				{{template "base/paginate" .}}
 | 
			
		||||
			{{end}}
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
{{template "base/footer" .}}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user