Improve data sync #190
@@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<ul class="contribution-list">
|
<ul class="contribution-list {{if @loading 'loading'}}">
|
||||||
{{#each this.contributionsFiltered as |contribution|}}
|
{{#each this.contributionsFiltered as |contribution|}}
|
||||||
<li role="button" data-contribution-id={{contribution.id}}
|
<li role="button" data-contribution-id={{contribution.id}}
|
||||||
{{action "openContributionDetails" contribution}}
|
{{action "openContributionDetails" contribution}}
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import Component from '@ember/component';
|
|||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
|
tagName: '',
|
||||||
|
|
|||||||
|
|
||||||
router: service(),
|
router: service(),
|
||||||
|
|
||||||
tagName: 'table',
|
|
||||||
classNames: 'contributor-list',
|
|
||||||
|
|
||||||
selectedContributorId: null,
|
selectedContributorId: null,
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@@ -17,5 +15,4 @@ export default Component.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
<tbody>
|
<table class="contributor-list {{if @loading 'loading'}}">
|
||||||
{{#each @contributorList as |c|}}
|
<thead>
|
||||||
<tr role="button"
|
</thead>
|
||||||
onclick={{action "openContributorDetails" c.contributor}}
|
<tbody>
|
||||||
class="{{if (is-current-user c.contributor) "current-user"}} {{if (eq c.contributor.id @selectedContributorId) "selected"}}">
|
{{#each @contributorList as |c|}}
|
||||||
<td class="person">
|
<tr role="button"
|
||||||
<UserAvatar @contributor={{c.contributor}} /> {{c.contributor.name}}
|
onclick={{action "openContributorDetails" c.contributor}}
|
||||||
</td>
|
class="{{if (is-current-user c.contributor) "current-user"}} {{if (eq c.contributor.id @selectedContributorId) "selected"}}">
|
||||||
<td class="kredits">
|
<td class="person">
|
||||||
<span class="amount">
|
<UserAvatar @contributor={{c.contributor}} /> {{c.contributor.name}}
|
||||||
{{#if @showUnconfirmedKredits}}
|
</td>
|
||||||
{{c.amountTotal}}
|
<td class="kredits">
|
||||||
{{else}}
|
<span class="amount">
|
||||||
{{c.amountConfirmed}}
|
{{#if @showUnconfirmedKredits}}
|
||||||
{{/if}}
|
{{c.amountTotal}}
|
||||||
</span>
|
{{else}}
|
||||||
<span class="symbol">₭S</span>
|
{{c.amountConfirmed}}
|
||||||
</td>
|
{{/if}}
|
||||||
</tr>
|
</span>
|
||||||
{{/each}}
|
<span class="symbol">₭S</span>
|
||||||
</tbody>
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|||||||
@@ -24,10 +24,16 @@ export default Route.extend({
|
|||||||
this.kredits.addContractEventHandlers();
|
this.kredits.addContractEventHandlers();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (this.kredits.contributorsNeedFetch) {
|
if (this.kredits.contributorsNeedSync) {
|
||||||
schedule('afterRender', this.kredits, this.kredits.fetchContributors);
|
schedule('afterRender', this.kredits.syncContributors,
|
||||||
|
this.kredits.syncContributors.perform);
|
||||||
}
|
}
|
||||||
schedule('afterRender', this.kredits, this.kredits.fetchContributions);
|
if (this.kredits.contributionsNeedSync) {
|
||||||
|
schedule('afterRender', this.kredits.syncContributions,
|
||||||
|
this.kredits.syncContributions.perform);
|
||||||
|
}
|
||||||
|
schedule('afterRender', this.kredits.fetchMissingContributions,
|
||||||
|
this.kredits.fetchMissingContributions.perform);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+102
-17
@@ -1,6 +1,5 @@
|
|||||||
import ethers from 'ethers';
|
import ethers from 'ethers';
|
||||||
import Kredits from 'kredits-contracts';
|
import Kredits from 'kredits-contracts';
|
||||||
import RSVP from 'rsvp';
|
|
||||||
|
|
||||||
import Service from '@ember/service';
|
import Service from '@ember/service';
|
||||||
import EmberObject from '@ember/object';
|
import EmberObject from '@ember/object';
|
||||||
@@ -9,6 +8,8 @@ import { alias, notEmpty } from '@ember/object/computed';
|
|||||||
import { isEmpty, isPresent } from '@ember/utils';
|
import { isEmpty, isPresent } from '@ember/utils';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
|
import { task, taskGroup } from 'ember-concurrency';
|
||||||
|
|
||||||
import groupBy from 'kredits-web/utils/group-by';
|
import groupBy from 'kredits-web/utils/group-by';
|
||||||
import processContributorData from 'kredits-web/utils/process-contributor-data';
|
import processContributorData from 'kredits-web/utils/process-contributor-data';
|
||||||
import processContributionData from 'kredits-web/utils/process-contribution-data';
|
import processContributionData from 'kredits-web/utils/process-contribution-data';
|
||||||
@@ -21,7 +22,6 @@ import Contribution from 'kredits-web/models/contribution'
|
|||||||
export default Service.extend({
|
export default Service.extend({
|
||||||
|
|
||||||
browserCache: service(),
|
browserCache: service(),
|
||||||
contributorsNeedFetch: false,
|
|
||||||
|
|
||||||
currentBlock: null,
|
currentBlock: null,
|
||||||
currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded.
|
currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded.
|
||||||
@@ -34,6 +34,10 @@ export default Service.extend({
|
|||||||
currentUserIsCore: alias('currentUser.isCore'),
|
currentUserIsCore: alias('currentUser.isCore'),
|
||||||
hasAccounts: notEmpty('currentUserAccounts'),
|
hasAccounts: notEmpty('currentUserAccounts'),
|
||||||
|
|
||||||
|
// When data was loaded from cache, we need to fetch updates from the network
|
||||||
|
contributorsNeedSync: false,
|
||||||
|
contributionsNeedSync: false,
|
||||||
|
|
||||||
contributionsUnconfirmed: computed('contributions.[]', 'currentBlock', function() {
|
contributionsUnconfirmed: computed('contributions.[]', 'currentBlock', function() {
|
||||||
return this.contributions.filter(contribution => {
|
return this.contributions.filter(contribution => {
|
||||||
return contribution.confirmedAt > this.currentBlock;
|
return contribution.confirmedAt > this.currentBlock;
|
||||||
@@ -90,7 +94,7 @@ export default Service.extend({
|
|||||||
getEthProvider () {
|
getEthProvider () {
|
||||||
let ethProvider;
|
let ethProvider;
|
||||||
|
|
||||||
return new RSVP.Promise(async (resolve) => {
|
return new Promise(resolve => {
|
||||||
function instantiateWithoutAccount () {
|
function instantiateWithoutAccount () {
|
||||||
console.debug('[kredits] Creating new instance from npm module class');
|
console.debug('[kredits] Creating new instance from npm module class');
|
||||||
console.debug(`[kredits] providerURL: ${config.web3ProviderUrl}`);
|
console.debug(`[kredits] providerURL: ${config.web3ProviderUrl}`);
|
||||||
@@ -179,7 +183,7 @@ export default Service.extend({
|
|||||||
const numCachedContributors = await this.browserCache.contributors.length();
|
const numCachedContributors = await this.browserCache.contributors.length();
|
||||||
if (numCachedContributors > 0) {
|
if (numCachedContributors > 0) {
|
||||||
await this.loadContributorsFromCache();
|
await this.loadContributorsFromCache();
|
||||||
this.set('contributorsNeedFetch', true);
|
this.set('contributorsNeedSync', true);
|
||||||
} else {
|
} else {
|
||||||
await this.fetchContributors();
|
await this.fetchContributors();
|
||||||
}
|
}
|
||||||
@@ -187,6 +191,7 @@ export default Service.extend({
|
|||||||
const numCachedContributions = await this.browserCache.contributions.length();
|
const numCachedContributions = await this.browserCache.contributions.length();
|
||||||
if (numCachedContributions > 0) {
|
if (numCachedContributions > 0) {
|
||||||
await this.loadContributionsFromCache();
|
await this.loadContributionsFromCache();
|
||||||
|
this.set('contributionsNeedSync', true);
|
||||||
} else {
|
} else {
|
||||||
await this.fetchContributions({ page: { size: 30 } });
|
await this.fetchContributions({ page: { size: 30 } });
|
||||||
}
|
}
|
||||||
@@ -226,12 +231,9 @@ export default Service.extend({
|
|||||||
console.debug(`[kredits] Fetching all contributors from the network`);
|
console.debug(`[kredits] Fetching all contributors from the network`);
|
||||||
return this.kredits.Contributor.all()
|
return this.kredits.Contributor.all()
|
||||||
.then(contributors => {
|
.then(contributors => {
|
||||||
return contributors.map(data => {
|
return contributors.forEach(data => {
|
||||||
const contributor = Contributor.create(processContributorData(data));
|
this.loadContributorFromData(data);
|
||||||
const loadedContributor = this.contributors.findBy('id', contributor.id);
|
return;
|
||||||
if (loadedContributor) { this.contributors.removeObject(loadedContributor); }
|
|
||||||
this.contributors.pushObject(contributor);
|
|
||||||
return contributor;
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -239,6 +241,14 @@ export default Service.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
loadContributorFromData(data) {
|
||||||
|
const contributor = Contributor.create(processContributorData(data));
|
||||||
|
const loadedContributor = this.contributors.findBy('id', contributor.id);
|
||||||
|
if (loadedContributor) { this.contributors.removeObject(loadedContributor); }
|
||||||
|
this.contributors.pushObject(contributor);
|
||||||
|
return contributor;
|
||||||
|
},
|
||||||
|
|
||||||
async cacheLoadedContributors () {
|
async cacheLoadedContributors () {
|
||||||
for (const c of this.contributors) {
|
for (const c of this.contributors) {
|
||||||
await this.browserCache.contributors.setItem(c.id, c.serialize());
|
await this.browserCache.contributors.setItem(c.id, c.serialize());
|
||||||
@@ -255,6 +265,10 @@ export default Service.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
syncContributors: task(function * () {
|
||||||
|
yield this.fetchContributors();
|
||||||
|
}),
|
||||||
|
|
||||||
addContribution (attributes) {
|
addContribution (attributes) {
|
||||||
console.debug('[kredits] add contribution', attributes);
|
console.debug('[kredits] add contribution', attributes);
|
||||||
|
|
||||||
@@ -275,11 +289,7 @@ export default Service.extend({
|
|||||||
return this.kredits.Contribution.all(options)
|
return this.kredits.Contribution.all(options)
|
||||||
.then(contributions => {
|
.then(contributions => {
|
||||||
return contributions.map(data => {
|
return contributions.map(data => {
|
||||||
const contribution = Contribution.create(processContributionData(data));
|
const contribution = this.loadContributionFromData(data);
|
||||||
contribution.set('contributor', this.contributors.findBy('id', data.contributorId.toString()));
|
|
||||||
const loadedContribution = this.contributions.findBy('id', contribution.id);
|
|
||||||
if (loadedContribution) { this.contributions.removeObject(loadedContribution); }
|
|
||||||
this.contributions.pushObject(contribution);
|
|
||||||
return contribution;
|
return contribution;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@@ -293,6 +303,15 @@ export default Service.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
loadContributionFromData(data) {
|
||||||
|
const contribution = Contribution.create(processContributionData(data));
|
||||||
|
contribution.set('contributor', this.contributors.findBy('id', data.contributorId.toString()));
|
||||||
|
const loadedContribution = this.contributions.findBy('id', contribution.id);
|
||||||
|
if (loadedContribution) { this.contributions.removeObject(loadedContribution); }
|
||||||
|
this.contributions.pushObject(contribution);
|
||||||
|
return contribution;
|
||||||
|
},
|
||||||
|
|
||||||
async cacheLoadedContributions () {
|
async cacheLoadedContributions () {
|
||||||
for (const c of this.contributions) {
|
for (const c of this.contributions) {
|
||||||
await this.browserCache.contributions.setItem(c.id, c.serialize());
|
await this.browserCache.contributions.setItem(c.id, c.serialize());
|
||||||
@@ -309,6 +328,72 @@ export default Service.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
contributionTasks: taskGroup().enqueue(),
|
||||||
|
|
||||||
|
syncContributions: task(function * () {
|
||||||
|
yield this.fetchNewContributions.perform();
|
||||||
|
yield this.syncUnconfirmedContributions.perform();
|
||||||
|
}).group('contributionTasks'),
|
||||||
|
|
||||||
|
fetchNewContributions: task(function * () {
|
||||||
|
const count = yield this.kredits.Contribution.functions.contributionsCount();
|
||||||
|
const lastKnownContributionId = Math.max.apply(null, this.contributions.mapBy('id'));
|
||||||
|
const toFetch = count - lastKnownContributionId;
|
||||||
|
|
||||||
|
if (toFetch > 0) {
|
||||||
|
console.debug(`[kredits] Fetching ${toFetch} new contributions`);
|
||||||
|
for (let id = lastKnownContributionId; id <= count; id++) {
|
||||||
|
const data = yield this.kredits.Contribution.getById(id);
|
||||||
|
const c = this.loadContributionFromData(data);
|
||||||
|
yield this.browserCache.contributions.setItem(c.id.toString(), c.serialize());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.debug(`[kredits] No new contributions to fetch`);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
fetchMissingContributions: task(function * () {
|
||||||
|
const count = yield this.kredits.Contribution.functions.contributionsCount();
|
||||||
|
const allIds = [...Array(count+1).keys()];
|
||||||
|
allIds.shift(); // remove first item, which is 0
|
||||||
|
const loadedContributions = new Set(this.contributions.mapBy('id'));
|
||||||
|
const toFetch = allIds.filter(id => !loadedContributions.has(id));
|
||||||
|
if (toFetch.length === 0) {
|
||||||
|
console.debug(`[kredits] No contributions left to fetch`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.debug(`[kredits] Fetching ${toFetch.length} past contributions`);
|
||||||
|
let countFetched = 0;
|
||||||
|
|
||||||
|
for (let id = count; id > 0; id--) {
|
||||||
|
if (loadedContributions.has(id)) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
const data = yield this.kredits.Contribution.getById(id);
|
||||||
|
const c = this.loadContributionFromData(data);
|
||||||
|
yield this.browserCache.contributions.setItem(c.id.toString(), c.serialize());
|
||||||
|
countFetched++;
|
||||||
|
if (countFetched % 20 === 0) {
|
||||||
|
console.debug(`[kredits] Fetched ${countFetched} more contributions`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.debug(`[kredits] Cached ${countFetched} past contributions`);
|
||||||
|
}).group('contributionTasks'),
|
||||||
|
|
||||||
|
syncUnconfirmedContributions: task(function * () {
|
||||||
|
if (this.contributionsUnconfirmed.length > 0) {
|
||||||
|
console.debug(`[kredits] Syncing unconfirmed contributions`);
|
||||||
|
for (const c of this.contributionsUnconfirmed) {
|
||||||
|
const data = yield this.kredits.Contribution.getById(c.id);
|
||||||
|
const contribution = this.loadContributionFromData(data);
|
||||||
|
yield this.browserCache.contributions.setItem(c.id.toString(), contribution.serialize());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.debug(`[kredits] No unconfirmed contributions to sync`);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
veto (contributionId) {
|
veto (contributionId) {
|
||||||
console.debug('[kredits] veto against', contributionId);
|
console.debug('[kredits] veto against', contributionId);
|
||||||
const contribution = this.contributions.findBy('id', contributionId);
|
const contribution = this.contributions.findBy('id', contributionId);
|
||||||
@@ -323,14 +408,14 @@ export default Service.extend({
|
|||||||
|
|
||||||
getCurrentUser: computed('kredits.provider', 'currentUserAccounts.[]', function() {
|
getCurrentUser: computed('kredits.provider', 'currentUserAccounts.[]', function() {
|
||||||
if (isEmpty(this.currentUserAccounts)) {
|
if (isEmpty(this.currentUserAccounts)) {
|
||||||
return RSVP.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
return this.kredits.Contributor
|
return this.kredits.Contributor
|
||||||
.functions.getContributorIdByAddress(this.currentUserAccounts.firstObject)
|
.functions.getContributorIdByAddress(this.currentUserAccounts.firstObject)
|
||||||
.then((id) => {
|
.then((id) => {
|
||||||
// check if the user is a contributor or not
|
// check if the user is a contributor or not
|
||||||
if (id === 0) {
|
if (id === 0) {
|
||||||
return RSVP.resolve();
|
return Promise.resolve();
|
||||||
} else {
|
} else {
|
||||||
return this.kredits.Contributor.getById(id);
|
return this.kredits.Contributor.getById(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
@mixin loading-border-top {
|
||||||
|
&::before {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
content: '';
|
||||||
|
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 40%, #68d7fb 60%, rgba(255, 255, 255, 0.2));
|
||||||
|
background-size: 200% 200%;
|
||||||
|
animation: kitt 2.5s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes kitt {
|
||||||
|
0%{ background-position: 0% 0%}
|
||||||
|
50%{ background-position: 100% 0%}
|
||||||
|
100%{ background-position: 0% 0%}
|
||||||
|
}
|
||||||
@@ -88,6 +88,7 @@ section {
|
|||||||
|
|
||||||
@import "buttons";
|
@import "buttons";
|
||||||
@import "forms";
|
@import "forms";
|
||||||
|
@import "sugar";
|
||||||
|
|
||||||
@import "components/contribution-list";
|
@import "components/contribution-list";
|
||||||
@import "components/contribution-details";
|
@import "components/contribution-details";
|
||||||
|
|||||||
@@ -114,4 +114,13 @@ ul.contribution-list {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.loading {
|
||||||
|
@include loading-border-top;
|
||||||
|
|
||||||
|
li {
|
||||||
|
&:first-of-type {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
table.contributor-list {
|
table.contributor-list {
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
@@ -48,4 +49,11 @@ table.contributor-list {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.loading {
|
||||||
|
@include loading-border-top;
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,8 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<ContributorList @contributorList={{this.kreditsToplist}}
|
<ContributorList @contributorList={{this.kreditsToplist}}
|
||||||
@showUnconfirmedKredits={{this.showUnconfirmedKredits}}
|
@showUnconfirmedKredits={{this.showUnconfirmedKredits}}
|
||||||
@selectedContributorId={{this.selectedContributorId}} />
|
@selectedContributorId={{this.selectedContributorId}}
|
||||||
|
@loading={{this.kredits.syncContributors.isRunning}} />
|
||||||
|
|
||||||
<p class="stats">
|
<p class="stats">
|
||||||
<span class="number">{{await this.kredits.totalKreditsEarned}}</span> kredits confirmed and issued to
|
<span class="number">{{await this.kredits.totalKreditsEarned}}</span> kredits confirmed and issued to
|
||||||
@@ -58,7 +59,9 @@
|
|||||||
{{#if this.contributionsUnconfirmed}}
|
{{#if this.contributionsUnconfirmed}}
|
||||||
<section id="contributions-unconfirmed">
|
<section id="contributions-unconfirmed">
|
||||||
<header class="with-nav">
|
<header class="with-nav">
|
||||||
<h2>Latest Contributions</h2>
|
<h2>
|
||||||
|
Latest Contributions
|
||||||
|
</h2>
|
||||||
<nav>
|
<nav>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
onclick={{action "toggleQuickFilterUnconfirmed"}}
|
onclick={{action "toggleQuickFilterUnconfirmed"}}
|
||||||
@@ -78,7 +81,8 @@
|
|||||||
@vetoContribution={{action "vetoContribution"}}
|
@vetoContribution={{action "vetoContribution"}}
|
||||||
@contractInteractionEnabled={{this.kredits.hasAccounts}}
|
@contractInteractionEnabled={{this.kredits.hasAccounts}}
|
||||||
@selectedContributionId={{this.selectedContributionId}}
|
@selectedContributionId={{this.selectedContributionId}}
|
||||||
@showQuickFilter={{this.showQuickFilterUnconfirmed}} />
|
@showQuickFilter={{this.showQuickFilterUnconfirmed}}
|
||||||
|
@loading={{this.kredits.syncContributions.isRunning}}/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
Generated
+11
@@ -10259,6 +10259,17 @@
|
|||||||
"semver": "^5.4.1"
|
"semver": "^5.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ember-concurrency": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/ember-concurrency/-/ember-concurrency-1.1.7.tgz",
|
||||||
|
"integrity": "sha512-PtEJvB4wG8e5CEHRC9ILl2BxF6U/xlMOhfCji/x7FxNFB9M230Du86LAy+4/yOozZHyoARVuazABPUj02P+DmQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ember-cli-babel": "^7.7.3",
|
||||||
|
"ember-compatibility-helpers": "^1.2.0",
|
||||||
|
"ember-maybe-import-regenerator": "^0.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ember-diff-attrs": {
|
"ember-diff-attrs": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/ember-diff-attrs/-/ember-diff-attrs-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/ember-diff-attrs/-/ember-diff-attrs-0.2.2.tgz",
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
"ember-cli-sass": "^10.0.1",
|
"ember-cli-sass": "^10.0.1",
|
||||||
"ember-cli-sri": "^2.1.1",
|
"ember-cli-sri": "^2.1.1",
|
||||||
"ember-cli-uglify": "^3.0.0",
|
"ember-cli-uglify": "^3.0.0",
|
||||||
|
"ember-concurrency": "^1.1.7",
|
||||||
"ember-export-application-global": "^2.0.1",
|
"ember-export-application-global": "^2.0.1",
|
||||||
"ember-fetch": "^8.0.1",
|
"ember-fetch": "^8.0.1",
|
||||||
"ember-flatpickr": "^2.16.2",
|
"ember-flatpickr": "^2.16.2",
|
||||||
|
|||||||
Reference in New Issue
Block a user
Since Octane, components don't have a tag like
divby default anymore. So this is not necessary.You mean Glimmer components. But this one hasn't been converted yet.