From 86ba16a31752fdf3990c8be066a7da002b17dc08 Mon Sep 17 00:00:00 2001 From: Zachary Kuhn Date: Thu, 14 Sep 2017 10:52:15 -0400 Subject: [PATCH] Load a larger number of repositories and make changes to the site to accomodate this. --- config/spec-bundle.js | 1 + config/webpack.common.js | 3 +- src/app/app.module.ts | 3 + .../autocomplete-result.component.ts | 2 +- .../agency/agency.component.spec.ts | 5 +- .../explore-code/agency/agency.component.ts | 37 ++++-- .../explore-code/agency/agency.styles.scss | 12 ++ .../explore-code/agency/agency.template.html | 81 +++++++----- .../explore-code/repo/repo.component.spec.ts | 64 +++++---- .../explore-code/repo/repo.component.ts | 15 +-- .../explore-code/repo/repo.template.html | 125 +++--------------- .../repo-list-item.template.html | 12 +- .../repo-list/repo-list.component.ts | 8 +- .../repo-list/repo-list.styles.scss | 12 ++ .../repo-list/repo-list.template.html | 59 +++++---- .../search-results/search-results.styles.scss | 2 +- .../search-results.template.html | 2 +- .../explore-code/explore-code.routes.ts | 11 +- .../indexes/agencies-index.service.ts | 51 +++++++ src/app/services/indexes/index.ts | 2 + .../indexes/releases-index.service.ts | 64 +++++++++ src/app/services/repos/repos.service.ts | 26 ++-- .../search/lunr-search.service.spec.ts | 2 - .../services/search/lunr-search.service.ts | 98 ++++++-------- src/app/services/term/lunr-term.service.ts | 80 ++++++----- src/assets/agencies.json | 112 ++++++++++++++++ src/assets/releases.demo.json | 70 ++++++++++ src/assets/releases.json | 1 + src/assets/releasesIndex.json | 1 + src/polyfills.browser.ts | 1 + tsconfig.json | 3 +- 31 files changed, 620 insertions(+), 345 deletions(-) create mode 100644 src/app/services/indexes/agencies-index.service.ts create mode 100644 src/app/services/indexes/index.ts create mode 100644 src/app/services/indexes/releases-index.service.ts create mode 100644 src/assets/agencies.json create mode 100644 src/assets/releases.demo.json create mode 100644 src/assets/releases.json create mode 100644 src/assets/releasesIndex.json diff --git a/config/spec-bundle.js b/config/spec-bundle.js index 276f7f6d..5a591e30 100644 --- a/config/spec-bundle.js +++ b/config/spec-bundle.js @@ -15,6 +15,7 @@ Error.stackTraceLimit = Infinity; require('core-js/es6'); require('core-js/es7/reflect'); +require('core-js/es7/object'); // Typescript emit helpers polyfill require('ts-helpers'); diff --git a/config/webpack.common.js b/config/webpack.common.js index c37ca2bd..db3387cb 100644 --- a/config/webpack.common.js +++ b/config/webpack.common.js @@ -268,7 +268,8 @@ module.exports = function (options) { watchOptions: { aggregateTimeout: 300, poll: 1000 - } + }, + compress: true, } }); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index dd0a2cbf..5b965034 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -32,6 +32,7 @@ import { IsDefinedPipe } from './pipes/is-defined'; import { APP_COMPONENTS } from './utils/app-components'; import { AgencyService, AGENCIES } from './services/agency'; import { AgencyApiService } from './services/agency-api'; +import { AgenciesIndexService, ReleasesIndexService } from './services/indexes'; import { MobileService } from './services/mobile'; import { ModalService } from './services/modal'; import { RepoService } from './services/repo'; @@ -46,11 +47,13 @@ import { ElasticsearchTermService, LunrTermService, TermService } from './servic // Application wide providers const APP_PROVIDERS = [ ...APP_RESOLVER_PROVIDERS, + AgenciesIndexService, AgencyService, AgencyApiService, MobileService, ModalService, MonacoEditorService, + ReleasesIndexService, RepoService, ReposService, { provide: SearchService, useClass: LunrSearchService }, diff --git a/src/app/components/autocomplete-result/autocomplete-result.component.ts b/src/app/components/autocomplete-result/autocomplete-result.component.ts index 6d1e4284..a435c74b 100644 --- a/src/app/components/autocomplete-result/autocomplete-result.component.ts +++ b/src/app/components/autocomplete-result/autocomplete-result.component.ts @@ -16,7 +16,7 @@ export class AutocompleteResultComponent { if (this.result.agency) { // it's a repo with an agency attribute this.resource['iconId'] = 'assets/img/logos/agencies/' + this.result.agency + '-50x50.png'; this.resource['imageIcon'] = true; - this.resource['url'] = '/explore-code/repos/' + this.result.repoID; + this.resource['url'] = `/explore-code/agencies/${this.result.agency}/repos/${this.result.name}`; this.resource['name'] = this.result.name; } else { // it's an agency this.resource['iconId'] = 'assets/img/logos/agencies/' + this.result.id + '-50x50.png'; diff --git a/src/app/components/explore-code/agency/agency.component.spec.ts b/src/app/components/explore-code/agency/agency.component.spec.ts index 9683ff76..537767e8 100644 --- a/src/app/components/explore-code/agency/agency.component.spec.ts +++ b/src/app/components/explore-code/agency/agency.component.spec.ts @@ -3,6 +3,8 @@ import { Component } from '@angular/core'; import { HttpModule } from '@angular/http'; import { inject, TestBed } from '@angular/core/testing'; +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; + import { Observable } from 'rxjs/Rx'; import { AgencyComponent } from './index'; @@ -34,7 +36,8 @@ describe('AgencyComponent', () => { imports: [ HttpModule, RouterModule, - MetaModule.forRoot() + MetaModule.forRoot(), + InfiniteScrollModule, ], providers: [ AgencyService, diff --git a/src/app/components/explore-code/agency/agency.component.ts b/src/app/components/explore-code/agency/agency.component.ts index d08ba359..5d9f6b8c 100644 --- a/src/app/components/explore-code/agency/agency.component.ts +++ b/src/app/components/explore-code/agency/agency.component.ts @@ -18,9 +18,13 @@ import { MetaService } from '@ngx-meta/core'; export class AgencyComponent implements OnInit, OnDestroy { agency: Agency; public hasRepos: boolean = false; - public repos; + public repos = []; private eventSub: Subscription; private agencyReposSub: Subscription; + private allRepos = []; + private currentIndex = 0; + private pageSize = 20; + private isLoading = true; constructor( private agencyService: AgencyService, @@ -43,6 +47,10 @@ export class AgencyComponent implements OnInit, OnDestroy { let id = params['id']; this.agency = this.agencyService.getAgency(id); + this.repos = []; + this.allRepos = []; + this.currentIndex = 0; + this.isLoading = true; this.agencyRepos(); this.seoService.setTitle(this.agency.name, true); @@ -64,28 +72,29 @@ export class AgencyComponent implements OnInit, OnDestroy { agencyRepos() { this.agencyReposSub = this.reposService.getJsonFile(). subscribe((result) => { - if (result) { - this.repos = result['repos'].filter(repo => this.filterByAgency(repo)); + if (result && result.releases !== null && typeof result.releases === 'object') { + this.allRepos = Object.values(result.releases).filter(repo => this.filterByAgency(repo)) + .filter(repo => repo.permissions.usageType === 'openSource' || repo.permissions.usageType === 'governmentWideReuse'); + this.repos = this.allRepos.slice(0, this.repos.length || this.pageSize); + this.currentIndex = this.repos.length || this.pageSize; this.hasRepos = this.checkRepos(this.repos); + this.isLoading = false; } else { - console.log('Error.'); + } }); } checkRepos(repos) { - if (repos.length > 0) { - return true; - } else { - return false; - } + return repos.length > 0; } filterByAgency(repo) { - if (repo.agency !== undefined && repo.agency === this.agencyId()) { - return true; - } else { - return false; - } + return repo.agency !== undefined && repo.agency === this.agencyId(); + } + + onScroll() { + this.repos = [...this.repos, ...this.allRepos.slice(this.currentIndex, this.currentIndex + this.pageSize)]; + this.currentIndex = this.currentIndex + this.pageSize; } } diff --git a/src/app/components/explore-code/agency/agency.styles.scss b/src/app/components/explore-code/agency/agency.styles.scss index 6f3d4634..97a4eccf 100644 --- a/src/app/components/explore-code/agency/agency.styles.scss +++ b/src/app/components/explore-code/agency/agency.styles.scss @@ -6,6 +6,18 @@ font-size: 1.8em; margin-bottom: 0.2em; } + + .loading-dialog { + text-align: center; + + > p { + margin-top: 120px; + } + + > img { + margin-bottom: 120px; + } + } } .repo { diff --git a/src/app/components/explore-code/agency/agency.template.html b/src/app/components/explore-code/agency/agency.template.html index 9ce80042..3bd275bb 100644 --- a/src/app/components/explore-code/agency/agency.template.html +++ b/src/app/components/explore-code/agency/agency.template.html @@ -1,42 +1,53 @@

{{ agency.name }}

-
-

- {{ repos.length }} Featured {{ "Projects" | pluralize : repos.length }} -

- +
+

Loading Results...

+ United States of America Flag
-
-

- No repositories found. -

+
+
+

+ {{ allRepos.length }} Featured {{ "Projects" | pluralize : allRepos.length }} +

+ +
+ +
+

+ No repositories found. +

+
diff --git a/src/app/components/explore-code/repo/repo.component.spec.ts b/src/app/components/explore-code/repo/repo.component.spec.ts index 021e285e..b0b6ae4b 100644 --- a/src/app/components/explore-code/repo/repo.component.spec.ts +++ b/src/app/components/explore-code/repo/repo.component.spec.ts @@ -31,7 +31,7 @@ let id = '33202667'; class MockActivatedRoute extends ActivatedRoute { constructor() { super(); - this.params = Observable.of({id: id}); + this.params = Observable.of({agency_id: 'VA', id: id}); } } @@ -108,13 +108,13 @@ describe('RepoComponent', () => { (agencyService, reposService, metaService) => { let agency = { id: 'VA', name: 'Department of Veterans Affairs' }; spyOn(agencyService, 'getAgency').and.returnValue(agency); - let repos = undefined; + let repos = { releases: {} }; spyOn(reposService, 'getJsonFile').and.returnValue(Observable.of(repos)); // instantiate a new RepoComponent so that ngOnInit() doesn't get called let newRepoComponent = new RepoComponent(null, agencyService, reposService, null, metaService); // call getRepo() that invokes reposService.getJsonFile() - newRepoComponent.getRepo(''); + newRepoComponent.getRepo('VA', ''); expect(newRepoComponent.repo).toBeUndefined(); })); @@ -135,19 +135,20 @@ describe('RepoComponent', () => { })); /* Test repo.repository */ - it('should display repository in template if repo.repository property is set', + it('should display repository in template if repo.repositoryURL property is set', inject( [AgencyService, ReposService, SeoService], function (agencyService, reposService, seoService) { const agency = { id: 'VA', name: 'Department of Veterans Affairs' }; spyOn(agencyService, 'getAgency').and.returnValue(agency); - const repository = 'http://www.github.com/repository/'; + const repositoryURL = 'http://www.github.com/repository/'; const repo = createRepository({ name: 'A Fake repo name to show repo', - repository: repository, - homepage: 'http://code.gov/homepage/', - openSourceProject: 1, - governmentWideReuseProject: 0 + repositoryURL: repositoryURL, + homepageURL: 'http://code.gov/homepage/', + permissions: { + usageType: 'openSource' + } }); spyOn(reposService, 'getJsonFile').and.returnValue(Observable.of(repo)); @@ -156,7 +157,7 @@ describe('RepoComponent', () => { const anchors = fixture.nativeElement.querySelectorAll('.usa-button'); // 2nd child anchor is the repository (first one is homepage) - expect(anchors[1].href).toBe(repository); + expect(anchors[1].href).toBe(repositoryURL); } ) ); @@ -234,14 +235,14 @@ describe('RepoComponent', () => { )); /* Test repo.homepage */ - it('should NOT display repository homepage in template if repo.homepage property is undefined', + it('should NOT display repository homepage in template if repo.homepageURL property is undefined', inject([AgencyService, ReposService, SeoService], (agencyService, reposService, seoService) => { setupRepoPropertyTest( agencyService, reposService, seoService, - { homepage: undefined } + { homepageURL: undefined } ); @@ -252,14 +253,14 @@ describe('RepoComponent', () => { } )); - it('should NOT display repository homepage in template if repo.homepage property is null ', + it('should NOT display repository homepage in template if repo.homepageURL property is null ', inject([AgencyService, ReposService, SeoService], (agencyService, reposService, seoService) => { setupRepoPropertyTest( agencyService, reposService, seoService, - { homepage: null } + { homepageURL: null } ); fixture.detectChanges(); @@ -269,7 +270,7 @@ describe('RepoComponent', () => { } )); - it('should display repository homepage in template if repo.homepage property is defined', + it('should display repository homepage in template if repo.homepageURL property is defined', inject( [AgencyService, ReposService, SeoService], function (agencyService, reposService, seoService) { @@ -277,7 +278,7 @@ describe('RepoComponent', () => { agencyService, reposService, seoService, - { homepage: 'http://code.gov/', openSourceProject: 1 } + { homepageURL: 'http://code.gov/', permissions: { usageType: 'openSource' } } ); fixture.detectChanges(); @@ -307,12 +308,14 @@ describe('RepoComponent', () => { interface RepoProps { name?: string; description?: string; - homepage?: string; + homepageURL?: string; - repository?: string; + repositoryURL?: string; openSourceProject?: number; governmentWideReuseProject?: number; - + permissions?: { + usageType: string + }; } /** @@ -321,19 +324,24 @@ interface RepoProps { */ export function createRepository(repoProps: RepoProps) { return { - repos: [ - { - repoID : id, + releases: { + ['VA/' + id]: { + id : id, name: repoProps.name, description: repoProps.description, - codeLanguage: [{ language: 'JavaScript' }], + languages: [ 'JavaScript' ], agency: 'VA', - homepage: repoProps.homepage, - repository: repoProps.repository, - openSourceProject: repoProps.openSourceProject, - governmentWideReuseProject: repoProps.governmentWideReuseProject + homepageURL: repoProps.homepageURL, + repositoryURL: repoProps.repositoryURL, + permissions: { + usageType: 'openSource', + licenses: [{ + name: 'CCO', + URL: 'http://www.example.com', + }], + }, } - ] + } }; } diff --git a/src/app/components/explore-code/repo/repo.component.ts b/src/app/components/explore-code/repo/repo.component.ts index a330ab00..9b1b1263 100644 --- a/src/app/components/explore-code/repo/repo.component.ts +++ b/src/app/components/explore-code/repo/repo.component.ts @@ -29,10 +29,7 @@ export class RepoComponent implements OnInit, OnDestroy { ngOnInit() { this.eventSub = this.route.params.subscribe(params => { - - let id = params['id']; - - this.getRepo(id); + this.getRepo(params['agency_id'], params['id']); }); } @@ -41,11 +38,13 @@ export class RepoComponent implements OnInit, OnDestroy { if (this.repoSub) this.repoSub.unsubscribe(); } - getRepo(id) { + getRepo(agencyId, releaseId) { this.repoSub = this.reposService.getJsonFile(). subscribe((result) => { - if (result) { - this.repo = result['repos'].filter(repo => repo.repoID === id)[0]; + if (result.releases && Object.keys(result.releases).length) { + this.repo = { + ...result.releases[`${encodeURIComponent(agencyId)}/${encodeURIComponent(releaseId)}`] + }; // make a copy of the values so we don't modify them in memory this.repo.agency = this.agencyService.getAgency(this.repo.agency); this.seoService.setTitle(this.repo.name, true); this.seoService.setMetaDescription(this.repo.description); @@ -55,8 +54,6 @@ export class RepoComponent implements OnInit, OnDestroy { this.meta.setTag('twitter:title', `code.gov/${this.repo.name}`); this.meta.setTag('twitter:description', this.repo.description); this.meta.setTag('twitter:image', 'https://code.gov/assets/img/og.jpg'); - } else { - console.log('Error. Source code repositories not found'); } }); } diff --git a/src/app/components/explore-code/repo/repo.template.html b/src/app/components/explore-code/repo/repo.template.html index d7711d20..09ceca18 100644 --- a/src/app/components/explore-code/repo/repo.template.html +++ b/src/app/components/explore-code/repo/repo.template.html @@ -9,28 +9,24 @@

{{ repo.name }}

{{ repo.description }}

-
+ -
+
-
-
-
-
+

Highlights

-
-

Highlights

-
    -
  • -
    -
    - - -
    -

    Language

    -

    - This repo is built in - - {{ language.language }}. - -

    -
    -
    -
    - -
    -

    Language

    -

    - We’re not sure what this repo is built in. Try making a pull - request that updates this repo’s code.json file. -

    -
    -
  • -
  • -
    -
    - -
    -

    License

    -

    - This repo uses the - - {{repo.license_name}} license. - -

    -
    -
    -
    - -
    -

    License

    -

    - We’re not sure what license this repo has. Try making a pull - request that updates this repo’s code.json file. -

    -
    -
  • -
-
-
+

Activity