mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 19:06:18 +01:00 
			
		
		
		
	Add paging and archive/private repository filtering to dashboard list (#11321)
* Add archived options to SearchRepository Signed-off-by: Andrew Thornton <art27@cantab.net> * Add only-private search Signed-off-by: Andrew Thornton <art27@cantab.net> * Add filter options and paging to dashboard repository page Signed-off-by: Andrew Thornton <art27@cantab.net> * swagger generate Signed-off-by: Andrew Thornton <art27@cantab.net> * fix-swagger-again Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @mrsdizzie also remember state Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
		| @@ -140,6 +140,7 @@ type SearchRepoOptions struct { | |||||||
| 	PriorityOwnerID int64 | 	PriorityOwnerID int64 | ||||||
| 	OrderBy         SearchOrderBy | 	OrderBy         SearchOrderBy | ||||||
| 	Private         bool // Include private repositories in results | 	Private         bool // Include private repositories in results | ||||||
|  | 	OnlyPrivate     bool // Include only private repositories in results | ||||||
| 	StarredByID     int64 | 	StarredByID     int64 | ||||||
| 	AllPublic       bool // Include also all public repositories of users and public organisations | 	AllPublic       bool // Include also all public repositories of users and public organisations | ||||||
| 	AllLimited      bool // Include also all public repositories of limited organisations | 	AllLimited      bool // Include also all public repositories of limited organisations | ||||||
| @@ -159,6 +160,10 @@ type SearchRepoOptions struct { | |||||||
| 	// True -> include just mirrors | 	// True -> include just mirrors | ||||||
| 	// False -> include just non-mirrors | 	// False -> include just non-mirrors | ||||||
| 	Mirror util.OptionalBool | 	Mirror util.OptionalBool | ||||||
|  | 	// None -> include archived AND non-archived | ||||||
|  | 	// True -> include just archived | ||||||
|  | 	// False -> include just non-archived | ||||||
|  | 	Archived util.OptionalBool | ||||||
| 	// only search topic name | 	// only search topic name | ||||||
| 	TopicOnly bool | 	TopicOnly bool | ||||||
| 	// include description in keyword search | 	// include description in keyword search | ||||||
| @@ -205,14 +210,26 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { | |||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		// Not looking at private organisations | 		// Not looking at private organisations | ||||||
| 		// We should be able to see all non-private repositories that either: | 		// We should be able to see all non-private repositories that | ||||||
| 		cond = cond.And(builder.Eq{"is_private": false}) | 		// isn't in a private or limited organisation. | ||||||
| 		accessCond := builder.Or( | 		cond = cond.And( | ||||||
| 			//   A. Aren't in organisations  __OR__ | 			builder.Eq{"is_private": false}, | ||||||
| 			builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})), | 			builder.NotIn("owner_id", builder.Select("id").From("`user`").Where( | ||||||
| 			//   B. Isn't a private or limited organisation. | 				builder.And( | ||||||
| 			builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate})))) | 					builder.Eq{"type": UserTypeOrganization}, | ||||||
| 		cond = cond.And(accessCond) | 					builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}), | ||||||
|  | 				)))) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if opts.OnlyPrivate { | ||||||
|  | 		cond = cond.And( | ||||||
|  | 			builder.Or( | ||||||
|  | 				builder.Eq{"is_private": true}, | ||||||
|  | 				builder.In("owner_id", builder.Select("id").From("`user`").Where( | ||||||
|  | 					builder.And( | ||||||
|  | 						builder.Eq{"type": UserTypeOrganization}, | ||||||
|  | 						builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}), | ||||||
|  | 					))))) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if opts.Template != util.OptionalBoolNone { | 	if opts.Template != util.OptionalBoolNone { | ||||||
| @@ -299,6 +316,10 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { | |||||||
| 		cond = cond.And(accessibleRepositoryCondition(opts.Actor)) | 		cond = cond.And(accessibleRepositoryCondition(opts.Actor)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if opts.Archived != util.OptionalBoolNone { | ||||||
|  | 		cond = cond.And(builder.Eq{"is_archived": opts.Archived == util.OptionalBoolTrue}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	switch opts.HasMilestones { | 	switch opts.HasMilestones { | ||||||
| 	case util.OptionalBoolTrue: | 	case util.OptionalBoolTrue: | ||||||
| 		cond = cond.And(builder.Gt{"num_milestones": 0}) | 		cond = cond.And(builder.Gt{"num_milestones": 0}) | ||||||
|   | |||||||
| @@ -206,6 +206,17 @@ my_orgs = My Organizations | |||||||
| my_mirrors = My Mirrors | my_mirrors = My Mirrors | ||||||
| view_home = View %s | view_home = View %s | ||||||
| search_repos = Find a repository… | search_repos = Find a repository… | ||||||
|  | filter = Other Filters | ||||||
|  |  | ||||||
|  | show_archived = Archived | ||||||
|  | show_both_archived_unarchived = Showing both archived and unarchived | ||||||
|  | show_only_archived = Showing only archived | ||||||
|  | show_only_unarchived = Showing only unarchived | ||||||
|  |  | ||||||
|  | show_private = Private | ||||||
|  | show_both_private_public = Showing both public and private | ||||||
|  | show_only_private = Showing only private | ||||||
|  | show_only_public = Showing only public | ||||||
|  |  | ||||||
| issues.in_your_repos = In your repositories | issues.in_your_repos = In your repositories | ||||||
|  |  | ||||||
|   | |||||||
| @@ -78,10 +78,18 @@ func Search(ctx *context.APIContext) { | |||||||
| 	//   in: query | 	//   in: query | ||||||
| 	//   description: include private repositories this user has access to (defaults to true) | 	//   description: include private repositories this user has access to (defaults to true) | ||||||
| 	//   type: boolean | 	//   type: boolean | ||||||
|  | 	// - name: onlyPrivate | ||||||
|  | 	//   in: query | ||||||
|  | 	//   description: only include private repositories this user has access to (defaults to false) | ||||||
|  | 	//   type: boolean | ||||||
| 	// - name: template | 	// - name: template | ||||||
| 	//   in: query | 	//   in: query | ||||||
| 	//   description: include template repositories this user has access to (defaults to true) | 	//   description: include template repositories this user has access to (defaults to true) | ||||||
| 	//   type: boolean | 	//   type: boolean | ||||||
|  | 	// - name: archived | ||||||
|  | 	//   in: query | ||||||
|  | 	//   description: show only archived, non-archived or all repositories (defaults to all) | ||||||
|  | 	//   type: boolean | ||||||
| 	// - name: mode | 	// - name: mode | ||||||
| 	//   in: query | 	//   in: query | ||||||
| 	//   description: type of repository to search for. Supported values are | 	//   description: type of repository to search for. Supported values are | ||||||
| @@ -125,6 +133,7 @@ func Search(ctx *context.APIContext) { | |||||||
| 		TopicOnly:          ctx.QueryBool("topic"), | 		TopicOnly:          ctx.QueryBool("topic"), | ||||||
| 		Collaborate:        util.OptionalBoolNone, | 		Collaborate:        util.OptionalBoolNone, | ||||||
| 		Private:            ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")), | 		Private:            ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")), | ||||||
|  | 		OnlyPrivate:        ctx.IsSigned && ctx.QueryBool("onlyPrivate"), | ||||||
| 		Template:           util.OptionalBoolNone, | 		Template:           util.OptionalBoolNone, | ||||||
| 		StarredByID:        ctx.QueryInt64("starredBy"), | 		StarredByID:        ctx.QueryInt64("starredBy"), | ||||||
| 		IncludeDescription: ctx.QueryBool("includeDesc"), | 		IncludeDescription: ctx.QueryBool("includeDesc"), | ||||||
| @@ -156,6 +165,10 @@ func Search(ctx *context.APIContext) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if ctx.Query("archived") != "" { | ||||||
|  | 		opts.Archived = util.OptionalBoolOf(ctx.QueryBool("archived")) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	var sortMode = ctx.Query("sort") | 	var sortMode = ctx.Query("sort") | ||||||
| 	if len(sortMode) > 0 { | 	if len(sortMode) > 0 { | ||||||
| 		var sortOrder = ctx.Query("order") | 		var sortOrder = ctx.Query("order") | ||||||
|   | |||||||
| @@ -1769,12 +1769,24 @@ | |||||||
|             "name": "private", |             "name": "private", | ||||||
|             "in": "query" |             "in": "query" | ||||||
|           }, |           }, | ||||||
|  |           { | ||||||
|  |             "type": "boolean", | ||||||
|  |             "description": "only include private repositories this user has access to (defaults to false)", | ||||||
|  |             "name": "onlyPrivate", | ||||||
|  |             "in": "query" | ||||||
|  |           }, | ||||||
|           { |           { | ||||||
|             "type": "boolean", |             "type": "boolean", | ||||||
|             "description": "include template repositories this user has access to (defaults to true)", |             "description": "include template repositories this user has access to (defaults to true)", | ||||||
|             "name": "template", |             "name": "template", | ||||||
|             "in": "query" |             "in": "query" | ||||||
|           }, |           }, | ||||||
|  |           { | ||||||
|  |             "type": "boolean", | ||||||
|  |             "description": "show only archived, non-archived or all repositories (defaults to all)", | ||||||
|  |             "name": "archived", | ||||||
|  |             "in": "query" | ||||||
|  |           }, | ||||||
|           { |           { | ||||||
|             "type": "string", |             "type": "string", | ||||||
|             "description": "type of repository to search for. Supported values are \"fork\", \"source\", \"mirror\" and \"collaborative\"", |             "description": "type of repository to search for. Supported values are \"fork\", \"source\", \"mirror\" and \"collaborative\"", | ||||||
|   | |||||||
| @@ -35,9 +35,46 @@ | |||||||
| 				{{end}} | 				{{end}} | ||||||
| 			</h4> | 			</h4> | ||||||
| 			<div class="ui attached secondary segment repos-search"> | 			<div class="ui attached secondary segment repos-search"> | ||||||
| 				<div class="ui fluid icon input" :class="{loading: isLoading}"> | 				<div class="ui fluid right action left icon input" :class="{loading: isLoading}"> | ||||||
| 					<input @input="searchRepos(reposFilter)" v-model="searchQuery" ref="search" placeholder="{{.i18n.Tr "home.search_repos"}}"> | 					<input @input="searchRepos(reposFilter)" v-model="searchQuery" ref="search" placeholder="{{.i18n.Tr "home.search_repos"}}"> | ||||||
| 					<i class="search icon"></i> | 					<i class="search icon"></i> | ||||||
|  | 					<div class="ui dropdown button" title="{{.i18n.Tr "home.filter"}}"> | ||||||
|  | 						<i class="icon filter"></i> | ||||||
|  | 						<div class="menu"> | ||||||
|  | 							<div class="item"> | ||||||
|  | 								<a @click="toggleArchivedFilter()"> | ||||||
|  | 									<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.i18n.Tr "home.show_both_archived_unarchived"}}" v-if="archivedFilter === 'both'"> | ||||||
|  | 										<input type="checkbox"> | ||||||
|  | 										<label><i class="archive icon archived-icon"></i>{{.i18n.Tr "home.show_archived"}}</label> | ||||||
|  | 									</div> | ||||||
|  | 									<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.i18n.Tr "home.show_only_unarchived"}}" v-if="archivedFilter === 'unarchived'"> | ||||||
|  | 										<input type="checkbox"> | ||||||
|  | 										<label><i class="archive icon archived-icon"></i>{{.i18n.Tr "home.show_archived"}}</label> | ||||||
|  | 									</div> | ||||||
|  | 									<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.i18n.Tr "home.show_only_archived"}}" v-if="archivedFilter === 'archived'"> | ||||||
|  | 										<input type="checkbox"> | ||||||
|  | 										<label><i class="archive icon archived-icon"></i>{{.i18n.Tr "home.show_archived"}}</label> | ||||||
|  | 									</div> | ||||||
|  | 								</a> | ||||||
|  | 							</div> | ||||||
|  | 							<div class="item"> | ||||||
|  | 								<a @click="togglePrivateFilter()"> | ||||||
|  | 									<div class="ui checkbox" id="privateFilterCheckbox" title="{{.i18n.Tr "home.show_both_private_public"}}" v-if="privateFilter === 'both'"> | ||||||
|  | 										<input type="checkbox"> | ||||||
|  | 										<label><svg class="svg octicon-lock" width="16" height="16" aria-hidden="true"><use xlink:href="#octicon-lock" /></svg>{{.i18n.Tr "home.show_private"}}</label> | ||||||
|  | 									</div> | ||||||
|  | 									<div class="ui checkbox" id="privateFilterCheckbox" title="{{.i18n.Tr "home.show_only_public"}}" v-if="privateFilter === 'public'"> | ||||||
|  | 										<input type="checkbox"> | ||||||
|  | 										<label><svg class="svg octicon-lock" width="16" height="16" aria-hidden="true"><use xlink:href="#octicon-lock" /></svg>{{.i18n.Tr "home.show_private"}}</label> | ||||||
|  | 									</div> | ||||||
|  | 									<div class="ui checkbox" id="privateFilterCheckbox" title="{{.i18n.Tr "home.show_only_private"}}" v-if="privateFilter === 'private'"> | ||||||
|  | 										<input type="checkbox"> | ||||||
|  | 										<label><svg class="svg octicon-lock" width="16" height="16" aria-hidden="true"><use xlink:href="#octicon-lock" /></svg>{{.i18n.Tr "home.show_private"}}</label> | ||||||
|  | 									</div> | ||||||
|  | 								</a> | ||||||
|  | 							</div> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<div class="ui secondary tiny pointing borderless menu center aligned grid repos-filter"> | 				<div class="ui secondary tiny pointing borderless menu center aligned grid repos-filter"> | ||||||
| 					<a class="item" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')"> | 					<a class="item" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')"> | ||||||
| @@ -64,7 +101,7 @@ | |||||||
| 			</div> | 			</div> | ||||||
| 			<div class="ui attached table segment"> | 			<div class="ui attached table segment"> | ||||||
| 				<ul class="repo-owner-name-list"> | 				<ul class="repo-owner-name-list"> | ||||||
| 					<li v-for="repo in repos" :class="{'private': repo.private}" v-show="showRepo(repo, reposFilter)"> | 					<li v-for="repo in repos" :class="{'private': repo.private}" v-show="showRepo(repo)"> | ||||||
| 						<a :href="suburl + '/' + repo.full_name"> | 						<a :href="suburl + '/' + repo.full_name"> | ||||||
| 							<svg :class="'svg ' + repoClass(repo)" width="16" height="16" aria-hidden="true"><use :xlink:href="'#' + repoClass(repo)" /></svg> | 							<svg :class="'svg ' + repoClass(repo)" width="16" height="16" aria-hidden="true"><use :xlink:href="'#' + repoClass(repo)" /></svg> | ||||||
| 							<strong class="text truncate item-name">${repo.full_name}</strong> | 							<strong class="text truncate item-name">${repo.full_name}</strong> | ||||||
| @@ -75,7 +112,27 @@ | |||||||
| 						</a> | 						</a> | ||||||
| 					</li> | 					</li> | ||||||
| 					<li v-if="showMoreReposLink"> | 					<li v-if="showMoreReposLink"> | ||||||
| 						<a :href="moreReposLink">{{.i18n.Tr "home.show_more_repos"}}</a> | 						<div class="center"> | ||||||
|  | 							<div class="ui borderless pagination menu narrow"> | ||||||
|  | 								<a class="item navigation" :class="{'disabled': page === 1}" | ||||||
|  | 									@click="changePage(1)" title="{{$.i18n.Tr "admin.first_page"}}"> | ||||||
|  | 									<i class="angle double left icon"></i> | ||||||
|  | 								</a> | ||||||
|  | 								<a class="item navigation" :class="{'disabled': page === 1}" | ||||||
|  | 									@click="changePage(page - 1)" title="{{$.i18n.Tr "repo.issues.previous"}}"> | ||||||
|  | 									<i class="left arrow icon"></i> | ||||||
|  | 								</a> | ||||||
|  | 								<a class="active item">${page}</a> | ||||||
|  | 								<a class="item navigation" :class="{'disabled': page === finalPage}" | ||||||
|  | 									@click="changePage(page + 1)" title="{{$.i18n.Tr "repo.issues.next"}}"> | ||||||
|  | 									<i class="icon right arrow"></i> | ||||||
|  | 								</a> | ||||||
|  | 								<a class="item navigation" :class="{'disabled': page === finalPage}" | ||||||
|  | 									@click="changePage(finalPage)" title="{{$.i18n.Tr "admin.last_page"}}"> | ||||||
|  | 									<i class="angle double right icon"></i> | ||||||
|  | 								</a> | ||||||
|  | 							</div> | ||||||
|  | 						</div> | ||||||
| 					</li> | 					</li> | ||||||
| 				</ul> | 				</ul> | ||||||
| 			</div> | 			</div> | ||||||
|   | |||||||
| @@ -2662,33 +2662,70 @@ function initVueComponents() { | |||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     data() { |     data() { | ||||||
|  |       const params = new URLSearchParams(window.location.search); | ||||||
|  |  | ||||||
|  |       let tab = params.get('repo-search-tab'); | ||||||
|  |       if (!tab) { | ||||||
|  |         tab = 'repos'; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       let reposFilter = params.get('repo-search-filter'); | ||||||
|  |       if (!reposFilter) { | ||||||
|  |         reposFilter = 'all'; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       let privateFilter = params.get('repo-search-private'); | ||||||
|  |       if (!privateFilter) { | ||||||
|  |         privateFilter = 'both'; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       let archivedFilter = params.get('repo-search-archived'); | ||||||
|  |       if (!archivedFilter) { | ||||||
|  |         archivedFilter = 'both'; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       let searchQuery = params.get('repo-search-query'); | ||||||
|  |       if (!searchQuery) { | ||||||
|  |         searchQuery = ''; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       let page = 1; | ||||||
|  |       try { | ||||||
|  |         page = parseInt(params.get('repo-search-page')); | ||||||
|  |       } catch { | ||||||
|  |         // noop | ||||||
|  |       } | ||||||
|  |       if (!page) { | ||||||
|  |         page = 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|       return { |       return { | ||||||
|         tab: 'repos', |         tab, | ||||||
|         repos: [], |         repos: [], | ||||||
|         reposTotalCount: 0, |         reposTotalCount: 0, | ||||||
|         reposFilter: 'all', |         reposFilter, | ||||||
|         searchQuery: '', |         archivedFilter, | ||||||
|  |         privateFilter, | ||||||
|  |         page, | ||||||
|  |         finalPage: 1, | ||||||
|  |         searchQuery, | ||||||
|         isLoading: false, |         isLoading: false, | ||||||
|         staticPrefix: StaticUrlPrefix, |         staticPrefix: StaticUrlPrefix, | ||||||
|  |         counts: {}, | ||||||
|         repoTypes: { |         repoTypes: { | ||||||
|           all: { |           all: { | ||||||
|             count: 0, |  | ||||||
|             searchMode: '', |             searchMode: '', | ||||||
|           }, |           }, | ||||||
|           forks: { |           forks: { | ||||||
|             count: 0, |  | ||||||
|             searchMode: 'fork', |             searchMode: 'fork', | ||||||
|           }, |           }, | ||||||
|           mirrors: { |           mirrors: { | ||||||
|             count: 0, |  | ||||||
|             searchMode: 'mirror', |             searchMode: 'mirror', | ||||||
|           }, |           }, | ||||||
|           sources: { |           sources: { | ||||||
|             count: 0, |  | ||||||
|             searchMode: 'source', |             searchMode: 'source', | ||||||
|           }, |           }, | ||||||
|           collaborative: { |           collaborative: { | ||||||
|             count: 0, |  | ||||||
|             searchMode: 'collaborative', |             searchMode: 'collaborative', | ||||||
|           }, |           }, | ||||||
|         } |         } | ||||||
| @@ -2697,21 +2734,26 @@ function initVueComponents() { | |||||||
|  |  | ||||||
|     computed: { |     computed: { | ||||||
|       showMoreReposLink() { |       showMoreReposLink() { | ||||||
|         return this.repos.length > 0 && this.repos.length < this.repoTypes[this.reposFilter].count; |         return this.repos.length > 0 && this.repos.length < this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`]; | ||||||
|       }, |       }, | ||||||
|       searchURL() { |       searchURL() { | ||||||
|         return `${this.suburl}/api/v1/repos/search?sort=updated&order=desc&uid=${this.uid}&q=${this.searchQuery |         return `${this.suburl}/api/v1/repos/search?sort=updated&order=desc&uid=${this.uid}&q=${this.searchQuery | ||||||
|         }&limit=${this.searchLimit}&mode=${this.repoTypes[this.reposFilter].searchMode |         }&page=${this.page}&limit=${this.searchLimit}&mode=${this.repoTypes[this.reposFilter].searchMode | ||||||
|         }${this.reposFilter !== 'all' ? '&exclusive=1' : ''}`; |         }${this.reposFilter !== 'all' ? '&exclusive=1' : '' | ||||||
|  |         }${this.archivedFilter === 'archived' ? '&archived=true' : ''}${this.archivedFilter === 'unarchived' ? '&archived=false' : '' | ||||||
|  |         }${this.privateFilter === 'private' ? '&onlyPrivate=true' : ''}${this.privateFilter === 'public' ? '&private=false' : '' | ||||||
|  |         }`; | ||||||
|       }, |       }, | ||||||
|       repoTypeCount() { |       repoTypeCount() { | ||||||
|         return this.repoTypes[this.reposFilter].count; |         return this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`]; | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     mounted() { |     mounted() { | ||||||
|       this.searchRepos(this.reposFilter); |       this.searchRepos(this.reposFilter); | ||||||
|  |       $(this.$el).find('.poping.up').popup(); | ||||||
|  |       $(this.$el).find('.dropdown').dropdown(); | ||||||
|  |       this.setCheckboxes(); | ||||||
|       const self = this; |       const self = this; | ||||||
|       Vue.nextTick(() => { |       Vue.nextTick(() => { | ||||||
|         self.$refs.search.focus(); |         self.$refs.search.focus(); | ||||||
| @@ -2721,17 +2763,178 @@ function initVueComponents() { | |||||||
|     methods: { |     methods: { | ||||||
|       changeTab(t) { |       changeTab(t) { | ||||||
|         this.tab = t; |         this.tab = t; | ||||||
|  |         this.updateHistory(); | ||||||
|  |       }, | ||||||
|  |  | ||||||
|  |       setCheckboxes() { | ||||||
|  |         switch (this.archivedFilter) { | ||||||
|  |           case 'unarchived': | ||||||
|  |             $('#archivedFilterCheckbox').checkbox('set unchecked'); | ||||||
|  |             break; | ||||||
|  |           case 'archived': | ||||||
|  |             $('#archivedFilterCheckbox').checkbox('set checked'); | ||||||
|  |             break; | ||||||
|  |           case 'both': | ||||||
|  |             $('#archivedFilterCheckbox').checkbox('set indeterminate'); | ||||||
|  |             break; | ||||||
|  |           default: | ||||||
|  |             this.archivedFilter = 'both'; | ||||||
|  |             $('#archivedFilterCheckbox').checkbox('set indeterminate'); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         switch (this.privateFilter) { | ||||||
|  |           case 'public': | ||||||
|  |             $('#privateFilterCheckbox').checkbox('set unchecked'); | ||||||
|  |             break; | ||||||
|  |           case 'private': | ||||||
|  |             $('#privateFilterCheckbox').checkbox('set checked'); | ||||||
|  |             break; | ||||||
|  |           case 'both': | ||||||
|  |             $('#privateFilterCheckbox').checkbox('set indeterminate'); | ||||||
|  |             break; | ||||||
|  |           default: | ||||||
|  |             this.privateFilter = 'both'; | ||||||
|  |             $('#privateFilterCheckbox').checkbox('set indeterminate'); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|       }, |       }, | ||||||
|  |  | ||||||
|       changeReposFilter(filter) { |       changeReposFilter(filter) { | ||||||
|         this.reposFilter = filter; |         this.reposFilter = filter; | ||||||
|         this.repos = []; |         this.repos = []; | ||||||
|         this.repoTypes[filter].count = 0; |         this.page = 1; | ||||||
|         this.searchRepos(filter); |         Vue.set(this.counts, `${filter}:${this.archivedFilter}:${this.privateFilter}`, 0); | ||||||
|  |         this.searchRepos(); | ||||||
|       }, |       }, | ||||||
|  |  | ||||||
|       showRepo(repo, filter) { |       updateHistory() { | ||||||
|         switch (filter) { |         const params = new URLSearchParams(window.location.search); | ||||||
|  |  | ||||||
|  |         if (this.tab === 'repos') { | ||||||
|  |           params.delete('repo-search-tab'); | ||||||
|  |         } else { | ||||||
|  |           params.set('repo-search-tab', this.tab); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.reposFilter === 'all') { | ||||||
|  |           params.delete('repo-search-filter'); | ||||||
|  |         } else { | ||||||
|  |           params.set('repo-search-filter', this.reposFilter); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.privateFilter === 'both') { | ||||||
|  |           params.delete('repo-search-private'); | ||||||
|  |         } else { | ||||||
|  |           params.set('repo-search-private', this.privateFilter); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.archivedFilter === 'both') { | ||||||
|  |           params.delete('repo-search-archived'); | ||||||
|  |         } else { | ||||||
|  |           params.set('repo-search-archived', this.archivedFilter); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.searchQuery === '') { | ||||||
|  |           params.delete('repo-search-query'); | ||||||
|  |         } else { | ||||||
|  |           params.set('repo-search-query', this.searchQuery); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.page === 1) { | ||||||
|  |           params.delete('repo-search-page'); | ||||||
|  |         } else { | ||||||
|  |           params.set('repo-search-page', `${this.page}`); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         window.history.replaceState({}, '', `?${params.toString()}`); | ||||||
|  |       }, | ||||||
|  |  | ||||||
|  |       toggleArchivedFilter() { | ||||||
|  |         switch (this.archivedFilter) { | ||||||
|  |           case 'both': | ||||||
|  |             this.archivedFilter = 'unarchived'; | ||||||
|  |             break; | ||||||
|  |           case 'unarchived': | ||||||
|  |             this.archivedFilter = 'archived'; | ||||||
|  |             break; | ||||||
|  |           case 'archived': | ||||||
|  |             this.archivedFilter = 'both'; | ||||||
|  |             break; | ||||||
|  |           default: | ||||||
|  |             this.archivedFilter = 'both'; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         this.page = 1; | ||||||
|  |         this.repos = []; | ||||||
|  |         this.setCheckboxes(); | ||||||
|  |         Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, 0); | ||||||
|  |         this.searchRepos(); | ||||||
|  |       }, | ||||||
|  |  | ||||||
|  |       togglePrivateFilter() { | ||||||
|  |         switch (this.privateFilter) { | ||||||
|  |           case 'both': | ||||||
|  |             this.privateFilter = 'public'; | ||||||
|  |             break; | ||||||
|  |           case 'public': | ||||||
|  |             this.privateFilter = 'private'; | ||||||
|  |             break; | ||||||
|  |           case 'private': | ||||||
|  |             this.privateFilter = 'both'; | ||||||
|  |             break; | ||||||
|  |           default: | ||||||
|  |             this.privateFilter = 'both'; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         this.page = 1; | ||||||
|  |         this.repos = []; | ||||||
|  |         this.setCheckboxes(); | ||||||
|  |         Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, 0); | ||||||
|  |         this.searchRepos(); | ||||||
|  |       }, | ||||||
|  |  | ||||||
|  |  | ||||||
|  |       changePage(page) { | ||||||
|  |         this.page = page; | ||||||
|  |         if (this.page > this.finalPage) { | ||||||
|  |           this.page = this.finalPage; | ||||||
|  |         } | ||||||
|  |         if (this.page < 1) { | ||||||
|  |           this.page = 1; | ||||||
|  |         } | ||||||
|  |         this.repos = []; | ||||||
|  |         Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, 0); | ||||||
|  |         this.searchRepos(); | ||||||
|  |       }, | ||||||
|  |  | ||||||
|  |       showArchivedRepo(repo) { | ||||||
|  |         switch (this.archivedFilter) { | ||||||
|  |           case 'both': | ||||||
|  |             return true; | ||||||
|  |           case 'unarchived': | ||||||
|  |             return !repo.archived; | ||||||
|  |           case 'archived': | ||||||
|  |             return repo.archived; | ||||||
|  |           default: | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |  | ||||||
|  |       showPrivateRepo(repo) { | ||||||
|  |         switch (this.privateFilter) { | ||||||
|  |           case 'both': | ||||||
|  |             return true; | ||||||
|  |           case 'public': | ||||||
|  |             return !repo.private; | ||||||
|  |           case 'private': | ||||||
|  |             return repo.private; | ||||||
|  |           default: | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |  | ||||||
|  |       showFilteredRepo(repo) { | ||||||
|  |         switch (this.reposFilter) { | ||||||
|           case 'sources': |           case 'sources': | ||||||
|             return repo.owner.id === this.uid && !repo.mirror && !repo.fork; |             return repo.owner.id === this.uid && !repo.mirror && !repo.fork; | ||||||
|           case 'forks': |           case 'forks': | ||||||
| @@ -2745,12 +2948,16 @@ function initVueComponents() { | |||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|  |  | ||||||
|       searchRepos(reposFilter) { |       showRepo(repo) { | ||||||
|  |         return this.showArchivedRepo(repo) && this.showPrivateRepo(repo) && this.showFilteredRepo(repo); | ||||||
|  |       }, | ||||||
|  |  | ||||||
|  |       searchRepos() { | ||||||
|         const self = this; |         const self = this; | ||||||
|  |  | ||||||
|         this.isLoading = true; |         this.isLoading = true; | ||||||
|  |  | ||||||
|         const searchedMode = this.repoTypes[reposFilter].searchMode; |         const searchedMode = this.repoTypes[this.reposFilter].searchMode; | ||||||
|         const searchedURL = this.searchURL; |         const searchedURL = this.searchURL; | ||||||
|         const searchedQuery = this.searchQuery; |         const searchedQuery = this.searchQuery; | ||||||
|  |  | ||||||
| @@ -2758,10 +2965,12 @@ function initVueComponents() { | |||||||
|           if (searchedURL === self.searchURL) { |           if (searchedURL === self.searchURL) { | ||||||
|             self.repos = result.data; |             self.repos = result.data; | ||||||
|             const count = request.getResponseHeader('X-Total-Count'); |             const count = request.getResponseHeader('X-Total-Count'); | ||||||
|             if (searchedQuery === '' && searchedMode === '') { |             if (searchedQuery === '' && searchedMode === '' && self.archivedFilter === 'both') { | ||||||
|               self.reposTotalCount = count; |               self.reposTotalCount = count; | ||||||
|             } |             } | ||||||
|             self.repoTypes[reposFilter].count = count; |             Vue.set(self.counts, `${self.reposFilter}:${self.archivedFilter}:${self.privateFilter}`, count); | ||||||
|  |             self.finalPage = Math.floor(count / self.searchLimit) + 1; | ||||||
|  |             self.updateHistory(); | ||||||
|           } |           } | ||||||
|         }).always(() => { |         }).always(() => { | ||||||
|           if (searchedURL === self.searchURL) { |           if (searchedURL === self.searchURL) { | ||||||
|   | |||||||
| @@ -318,11 +318,11 @@ code, | |||||||
| } | } | ||||||
|  |  | ||||||
| .ui { | .ui { | ||||||
|     &.left { |     &.left:not(.action) { | ||||||
|         float: left; |         float: left; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     &.right { |     &.right:not(.action) { | ||||||
|         float: right; |         float: right; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -727,6 +727,15 @@ code, | |||||||
|                 display: none; |                 display: none; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         &.narrow .item { | ||||||
|  |             padding-left: 8px; | ||||||
|  |             padding-right: 8px; | ||||||
|  |             min-width: 1em; | ||||||
|  |             text-align: center; | ||||||
|  |             .icon { | ||||||
|  |                 margin-right: 0; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -174,6 +174,11 @@ | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         #privateFilterCheckbox .svg { | ||||||
|  |             color: #888888; | ||||||
|  |             margin-right: .25rem; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         .repo-owner-name-list { |         .repo-owner-name-list { | ||||||
|             .item-name { |             .item-name { | ||||||
|                 max-width: 70%; |                 max-width: 70%; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user