Skip to content

index: Replace loading spinner with list item placeholders #2457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions app/components/category-list.hbs

This file was deleted.

10 changes: 0 additions & 10 deletions app/components/crate-list-name-only.hbs

This file was deleted.

11 changes: 0 additions & 11 deletions app/components/crate-list-newest.hbs

This file was deleted.

5 changes: 0 additions & 5 deletions app/components/front-page-list.hbs

This file was deleted.

5 changes: 0 additions & 5 deletions app/components/front-page-list.module.css

This file was deleted.

16 changes: 7 additions & 9 deletions app/components/front-page-list/item.hbs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
<li>
<LinkTo @route={{@route}} @model={{@model}} local-class="link" ...attributes>
<div local-class="left">
<div local-class="title">{{@title}}</div>
{{#if @subtitle}}<div local-class="subtitle">{{@subtitle}}</div>{{/if}}
</div>
{{svg-jar "chevron-right" local-class="right"}}
</LinkTo>
</li>
<LinkTo @route={{@route}} @model={{@model}} local-class="link" ...attributes>
<div local-class="left">
<div local-class="title">{{@title}}</div>
{{#if @subtitle}}<div local-class="subtitle">{{@subtitle}}</div>{{/if}}
</div>
{{svg-jar "chevron-right" local-class="right"}}
</LinkTo>
7 changes: 7 additions & 0 deletions app/components/front-page-list/item/placeholder.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div local-class="link" ...attributes>
<div local-class="left">
<div local-class="title"></div>
{{#if @withSubtitle}}<div local-class="subtitle"></div>{{/if}}
</div>
{{svg-jar "chevron-right" local-class="right"}}
</div>
39 changes: 39 additions & 0 deletions app/components/front-page-list/item/placeholder.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.link {
display: flex;
align-items: center;
width: 100%;
min-height: 60px;
margin: 8px 0;
padding: 0 10px;
background-color: var(--main-bg-dark);
}

.left {
flex-grow: 1;
width: 0;
}

.title {
height: 16px;
width: 150px;
border-radius: 8px;
background: rgb(118, 131, 138);
opacity: 0.25;
}

.subtitle {
height: 13px;
width: 90px;
margin-top: 4px;
border-radius: 6.5px;
background: rgb(118, 131, 138);
opacity: 0.2;
}

.right {
flex-shrink: 0;
height: 16px;
width: auto;
margin-left: 10px;
color: rgb(118, 131, 138);
}
10 changes: 0 additions & 10 deletions app/components/keyword-list.hbs

This file was deleted.

30 changes: 29 additions & 1 deletion app/controllers/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
import Controller from '@ember/controller';
import { computed } from '@ember/object';
import { readOnly } from '@ember/object/computed';
import { inject as service } from '@ember/service';

import { task } from 'ember-concurrency';

export default Controller.extend({
hasData: true,
fetcher: service(),

model: readOnly('dataTask.lastSuccessful.value'),

hasData: computed('dataTask.{lastSuccessful,isRunning}', function () {
return this.get('dataTask.lastSuccessful') || !this.get('dataTask.isRunning');
}),

dataTask: task(function* () {
let data = yield this.fetcher.ajax('/api/v1/summary');

addCrates(this.store, data.new_crates);
addCrates(this.store, data.most_downloaded);
addCrates(this.store, data.just_updated);
addCrates(this.store, data.most_recently_downloaded);

return data;
}).drop(),
});

function addCrates(store, crates) {
for (let i = 0; i < crates.length; i++) {
crates[i] = store.push(store.normalize('crate', crates[i]));
}
}
3 changes: 3 additions & 0 deletions app/helpers/placeholders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { helper } from '@ember/component/helper';

export default helper(([count]) => new Array(count));
26 changes: 6 additions & 20 deletions app/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default Route.extend({
fetcher: service(),
fastboot: service(),

headTags() {
return [
Expand All @@ -16,26 +16,12 @@ export default Route.extend({
];
},

setupController(controller, model) {
this._super(controller, model);
setupController(controller) {
this.controllerFor('application').set('searchQuery', null);
},

model() {
return this.fetcher.ajax('/api/v1/summary');
},

// eslint-disable-next-line no-unused-vars
afterModel(model, transition) {
addCrates(this.store, model.new_crates);
addCrates(this.store, model.most_downloaded);
addCrates(this.store, model.just_updated);
addCrates(this.store, model.most_recently_downloaded);
let promise = controller.dataTask.perform();
if (this.fastboot.isFastBoot) {
this.fastboot.deferRendering(promise);
}
},
});

function addCrates(store, crates) {
for (let i = 0; i < crates.length; i++) {
crates[i] = store.push(store.normalize('crate', crates[i]));
}
}
12 changes: 4 additions & 8 deletions app/styles/index.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,9 @@
font-size: 105%;
line-height: 20px;
}

> section[aria-busy="true"] > h2::after {
content: '';
background-image: url('/assets/ajax-loader.gif');
display: inline-block;
height: 16px;
width: 16px;
}
}

.list {
list-style: none;
padding: 0;
}
145 changes: 133 additions & 12 deletions app/templates/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,149 @@
</div>

<div local-class='lists'>
<section data-test-new-crates aria-busy="{{this.dataTask.isRunning}}">
<section data-test-new-crates>
<h2>New Crates</h2>
<CrateListNewest @crates={{this.model.new_crates}} />
<ol local-class="list" aria-busy="{{this.dataTask.isRunning}}">
{{#if this.dataTask.isRunning}}
{{#each (placeholders 10)}}
<li>
<FrontPageList::Item::Placeholder @withSubtitle={{true}} />
</li>
{{/each}}
{{else}}
{{#each this.model.new_crates as |crate index|}}
<li>
<FrontPageList::Item
@route="crate"
@model={{crate.id}}
@title={{crate.name}}
@subtitle="v{{crate.newest_version}}"
data-test-crate-link={{index}}
/>
</li>
{{/each}}
{{/if}}
</ol>
</section>
<section data-test-most-downloaded aria-busy="{{this.dataTask.isRunning}}">

<section data-test-most-downloaded>
<h2>Most Downloaded</h2>
<CrateListNameOnly @crates={{this.model.most_downloaded}} />
<ol local-class="list" aria-busy="{{this.dataTask.isRunning}}">
{{#if this.dataTask.isRunning}}
{{#each (placeholders 10)}}
<li>
<FrontPageList::Item::Placeholder />
</li>
{{/each}}
{{else}}
{{#each this.model.most_downloaded as |crate index|}}
<li>
<FrontPageList::Item
@route="crate"
@model={{crate.id}}
@title={{crate.name}}
data-test-crate-link={{index}}
/>
</li>
{{/each}}
{{/if}}
</ol>
</section>
<section data-test-just-updated aria-busy="{{this.dataTask.isRunning}}">

<section data-test-just-updated>
<h2>Just Updated</h2>
<CrateListNewest @crates={{this.model.just_updated}} />
<ol local-class="list" aria-busy="{{this.dataTask.isRunning}}">
{{#if this.dataTask.isRunning}}
{{#each (placeholders 10)}}
<li>
<FrontPageList::Item::Placeholder @withSubtitle={{true}} />
</li>
{{/each}}
{{else}}
{{#each this.model.just_updated as |crate index|}}
<li>
<FrontPageList::Item
@route="crate"
@model={{crate.id}}
@title={{crate.name}}
@subtitle="v{{crate.newest_version}}"
data-test-crate-link={{index}}
/>
</li>
{{/each}}
{{/if}}
</ol>
</section>
<section data-test-most-recently-downloaded aria-busy="{{this.dataTask.isRunning}}">

<section data-test-most-recently-downloaded>
<h2>Most Recent Downloads</h2>
<CrateListNameOnly @crates={{this.model.most_recently_downloaded}} />
<ol local-class="list" aria-busy="{{this.dataTask.isRunning}}">
{{#if this.dataTask.isRunning}}
{{#each (placeholders 10)}}
<li>
<FrontPageList::Item::Placeholder />
</li>
{{/each}}
{{else}}
{{#each this.model.most_recently_downloaded as |crate index|}}
<li>
<FrontPageList::Item
@route="crate"
@model={{crate.id}}
@title={{crate.name}}
data-test-crate-link={{index}}
/>
</li>
{{/each}}
{{/if}}
</ol>
</section>
<section data-test-keywords aria-busy="{{this.dataTask.isRunning}}">

<section data-test-keywords>
<h2>Popular Keywords <LinkTo @route="keywords">(see all)</LinkTo></h2>
<KeywordList @keywords={{this.model.popular_keywords}} />
<ul local-class="list" aria-busy="{{this.dataTask.isRunning}}">
{{#if this.dataTask.isRunning}}
{{#each (placeholders 10)}}
<li>
<FrontPageList::Item::Placeholder @withSubtitle={{true}} />
</li>
{{/each}}
{{else}}
{{#each this.model.popular_keywords as |keyword|}}
<li>
<FrontPageList::Item
@route="keyword"
@model={{keyword}}
@title={{keyword.id}}
@subtitle="{{format-num keyword.crates_cnt}} crates"
/>
</li>
{{/each}}
{{/if}}
</ul>
</section>
<section data-test-categories aria-busy="{{this.dataTask.isRunning}}">

<section data-test-categories>
<h2>Popular Categories <LinkTo @route="categories">(see all)</LinkTo></h2>
<CategoryList @categories={{this.model.popular_categories}} />
<ul local-class="list" aria-busy="{{this.dataTask.isRunning}}">
{{#if this.dataTask.isRunning}}
{{#each (placeholders 10)}}
<li>
<FrontPageList::Item::Placeholder @withSubtitle={{true}} />
</li>
{{/each}}
{{else}}
{{#each this.model.popular_categories as |category|}}
<li>
<FrontPageList::Item
@route="category"
@model={{category.slug}}
@title={{category.category}}
@subtitle="{{format-num category.crates_cnt}} crates"
/>
</li>
{{/each}}
{{/if}}
</ul>
</section>
</div>