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:
parent
c3d9a5f846
commit
c86bc8e061
8 changed files with 371 additions and 34 deletions
|
@ -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%;
|
||||||
|
|
Loading…
Reference in a new issue