Compare commits

...

18 Commits

Author SHA1 Message Date
Râu Cao 6a72a5b418 2.0.0-beta.3 2023-01-15 13:18:42 +08:00
Râu Cao d90ddd464a Hide confirmed-in content when confirmed 2023-01-15 13:16:38 +08:00
Râu Cao 2626543f3e Fix linter errors 2023-01-15 13:09:39 +08:00
Râu Cao 088db24534 Merge pull request #207 from 67P/feature/optional_full_sync
Make full data sync optional, start via button
2023-01-15 12:36:59 +08:00
Râu Cao f7b7daa024 Merge pull request #210 from 67P/feature/confirmation-eta
Add confirmed-in component, show ETA for proposed reimbursements
2023-01-15 12:36:43 +08:00
Râu Cao bb11e2267b Merge pull request #211 from 67P/feature/expense-list
Expense list UI improvements
2023-01-15 12:35:17 +08:00
Râu Cao 7cd023a21b Update app/components/add-reimbursement/template.hbs
Co-authored-by: Manuel Wiedenmann <manuel@funkensturm.de>
2023-01-15 12:34:40 +08:00
Râu Cao 1095bf0218 Add more button colors, icon style for small buttons
Plus tag icon for the tag buttons
2023-01-13 15:58:39 +08:00
Râu Cao 138cec0389 Localize date in expense list 2023-01-13 15:58:39 +08:00
Râu Cao a93be41e08 Add expense-list component, DRY up code, add tags 2023-01-13 15:58:39 +08:00
Râu Cao 89f6fa0b5c Use component argument in tests 2023-01-13 15:57:50 +08:00
Râu Cao e2a80eafd1 Remove dev logs 2023-01-13 14:14:24 +08:00
Râu Cao 662e76979b Show confirmation ETA for proposed reimbursements 2023-01-11 14:19:03 +08:00
Râu Cao 6c7de97e38 Get duration in human time 2023-01-11 14:10:35 +08:00
Râu Cao fe27b010da Add confirmed-in component 2023-01-11 13:59:41 +08:00
Râu Cao ff716c68ea Update current block when new one is mined 2023-01-11 13:26:58 +08:00
Râu Cao 84dded2f10 Do not automatically push new release versions 2023-01-11 12:44:56 +08:00
Râu Cao f86190030f Make full data sync optional, start via button 2022-11-14 22:58:15 +01:00
29 changed files with 397 additions and 155 deletions
+6
View File
@@ -48,6 +48,12 @@ module.exports = {
"no-action" "no-action"
] ]
}, },
{
"moduleId": "app/components/expense-list/template",
"only": [
"no-invalid-role"
]
},
{ {
"moduleId": "app/components/topbar-account-panel/template", "moduleId": "app/components/topbar-account-panel/template",
"only": [ "only": [
+4 -20
View File
@@ -44,26 +44,10 @@
<h3>Expense items</h3> <h3>Expense items</h3>
{{#if this.expenses}} {{#if this.expenses}}
<ul class="expense-list"> <ExpenseList @expenses={{this.expenses}}
{{#each this.expenses as |expense|}} @removeExpenseItem={{this.removeExpenseItem}}
<li> @deletable={{true}} />
<div class="description" rowspan="2">
<h4>
<span class="date">{{expense.date}}:</span>
<span class="title">{{expense.title}}</span>
</h4>
<p class="description">{{expense.description}}</p>
</div>
<div class="amount">
{{fmt-fiat-currency expense.amount expense.currency}}
</div>
<div class="actions">
<button {{on "click" (fn this.removeExpenseItem expense)}}
class="danger small" type="button">delete</button>
</div>
</li>
{{/each}}
</ul>
<p class="actions"> <p class="actions">
<button {{on "click" this.showExpenseForm}} <button {{on "click" this.showExpenseForm}}
class="green small" type="button">+ Add another item</button> class="green small" type="button">+ Add another item</button>
+24
View File
@@ -0,0 +1,24 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import moment from 'moment';
export default class ConfirmedInComponent extends Component {
@service kredits;
get confirmedInBlocks () {
return this.args.confirmedAtBlock - this.kredits.currentBlock;
}
get confirmedInSeconds () {
// A new block is mined every 30 seconds on average
return this.confirmedInBlocks * 30;
}
get confirmedInHumanTime () {
return moment.duration(this.confirmedInSeconds, "seconds").humanize();
}
get isConfirmed () {
return this.confirmedInBlocks <= 0;
}
}
+3
View File
@@ -0,0 +1,3 @@
{{#unless this.isConfirmed}}
Confirming in <strong>{{this.confirmedInBlocks}}</strong> blocks (~ {{this.confirmedInHumanTime}})
{{/unless}}
+9
View File
@@ -0,0 +1,9 @@
import Component from '@glimmer/component';
export default class ExpenseListComponent extends Component {
get showDeleteButton () {
return !!this.args.deletable;
}
}
+29
View File
@@ -0,0 +1,29 @@
<ul class="expense-list">
{{#each @expenses as |expense|}}
<li class="expense-item">
<h4>
<span class="date">{{fmt-date-localized expense.date}}:</span>
<span class="title">{{expense.title}}</span>
</h4>
<div class="amount">
{{fmt-fiat-currency expense.amount expense.currency}}
</div>
<p class="description">
{{expense.description}}
</p>
<p class="tags">
{{#each expense.tags as |tag|}}
<button type="button" class="small yellow" role="none">
<IconTag />{{tag}}
</button>
{{/each}}
</p>
{{#if this.showDeleteButton}}
<div class="actions">
<button {{on "click" (fn @removeExpenseItem expense)}}
class="danger small" type="button">delete</button>
</div>
{{/if}}
</li>
{{/each}}
</ul>
+17 -25
View File
@@ -3,44 +3,36 @@
<li data-reimbursement-id={{reimbursement.id}} <li data-reimbursement-id={{reimbursement.id}}
class="{{item-status reimbursement}}"> class="{{item-status reimbursement}}">
<p class="meta"> <p class="meta">
<span class="title">
Expenses covered by {{reimbursement.contributor.name}}
</span>
<span class="recipient"> <span class="recipient">
<UserAvatar @contributor={{reimbursement.contributor}} /> <UserAvatar @contributor={{reimbursement.contributor}} />
</span> </span>
<span class="title">
Expenses covered by {{reimbursement.contributor.name}}
</span>
</p> </p>
<p class="token-amount"> <p class="token-amount">
<span class="amount"> <span class="amount">
{{sats-to-btc reimbursement.amount}}</span>&#8239;<span class="symbol">BTC</span> {{sats-to-btc reimbursement.amount}}</span>&#8239;<span class="symbol">BTC</span>
</p> </p>
<ul class="expense-list">
{{#each reimbursement.expenses as |expense|}} <ExpenseList @expenses={{reimbursement.expenses}} />
<li>
<div class="description" rowspan="2"> <div class="meta">
<h4> <p class="confirmation-eta">
<span class="date">{{expense.date}}:</span> <ConfirmedIn @confirmedAtBlock={{reimbursement.confirmedAt}} />
<span class="title">{{expense.title}}</span> </p>
</h4> <p class="actions">
<p class="description">{{expense.description}}</p>
</div>
<div class="amount">
{{fmt-fiat-currency expense.amount expense.currency}}
</div>
</li>
{{/each}}
</ul>
{{#if this.kredits.currentUserIsCore}}
<p>
<a href="{{this.ipfsGatewayUrl}}/{{reimbursement.ipfsHash}}" <a href="{{this.ipfsGatewayUrl}}/{{reimbursement.ipfsHash}}"
class="button small" target="_blank" rel="noopener noreferrer"> class="button small" target="_blank" rel="noopener noreferrer">
Inspect IPFS data Inspect IPFS data
</a> </a>
<button {{on "click" (fn this.veto reimbursement.id)}} {{#if this.kredits.currentUserIsCore}}
disabled={{reimbursement.vetoed}} <button {{on "click" (fn this.veto reimbursement.id)}}
class="button small danger" type="button">veto</button> disabled={{reimbursement.vetoed}}
class="button small danger" type="button">veto</button>
{{/if}}
</p> </p>
{{/if}} </div>
</li> </li>
{{/each}} {{/each}}
</ul> </ul>
+3 -1
View File
@@ -1,6 +1,6 @@
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { alias, not, sort } from '@ember/object/computed'; import { alias, gt, not, sort } from '@ember/object/computed';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
export default Controller.extend({ export default Controller.extend({
@@ -33,6 +33,8 @@ export default Controller.extend({
showQuickFilterUnconfirmed: false, showQuickFilterUnconfirmed: false,
showQuickFilterConfirmed: false, showQuickFilterConfirmed: false,
showFullContributionSync: gt('kredits.missingHistoricContributionsCount', 0),
showIntroText: computed('kredits.{hasAccounts,currentUser}', function(){ showIntroText: computed('kredits.{hasAccounts,currentUser}', function(){
return (!this.kredits.hasAccounts || !this.kredits.currentUser); return (!this.kredits.hasAccounts || !this.kredits.currentUser);
}), }),
+8
View File
@@ -0,0 +1,8 @@
import { helper } from '@ember/component/helper';
import getLocale from 'kredits-web/utils/get-locale';
export default helper(function(dateStr) {
const date = new Date(dateStr);
const locale = getLocale();
return new Intl.DateTimeFormat(locale).format(date);
});
+5 -2
View File
@@ -11,8 +11,11 @@ export default class DashboardRoute extends Route {
schedule('afterRender', this.kredits.syncContributions, schedule('afterRender', this.kredits.syncContributions,
this.kredits.syncContributions.perform); this.kredits.syncContributions.perform);
} }
schedule('afterRender', this.kredits.fetchMissingContributions, // TODO fetch automatically under a certain threshold
this.kredits.fetchMissingContributions.perform); // The browser might delete cached data and we don't need manual re-syncs
// depending on how little is missing
// schedule('afterRender', this.kredits.fetchMissingContributions,
// this.kredits.fetchMissingContributions.perform);
} }
} }
+20 -6
View File
@@ -48,6 +48,8 @@ export default Service.extend({
contributionsNeedSync: false, contributionsNeedSync: false,
reimbursementsNeedSync: false, reimbursementsNeedSync: false,
missingHistoricContributionsCount: 0,
init () { init () {
this._super(...arguments); this._super(...arguments);
this.set('contributors', []); this.set('contributors', []);
@@ -118,9 +120,12 @@ export default Service.extend({
}); });
await kredits.init(); await kredits.init();
this.set('kredits', kredits); this.set('kredits', kredits);
this.set('currentBlock', await kredits.provider.getBlockNumber()); this.set('currentBlock', await this.kredits.provider.getBlockNumber());
this.kredits.provider.on('block', blockNumber => {
console.debug('[kredits] New block mined:', blockNumber);
this.set('currentBlock', blockNumber)
});
if (this.currentUserAccounts && this.currentUserAccounts.length > 0) { if (this.currentUserAccounts && this.currentUserAccounts.length > 0) {
this.getCurrentUser.then(contributorData => { this.getCurrentUser.then(contributorData => {
@@ -227,12 +232,20 @@ export default Service.extend({
await this.loadObjectsFromCache('Contribution'); await this.loadObjectsFromCache('Contribution');
this.set('contributionsNeedSync', true); this.set('contributionsNeedSync', true);
} else { } else {
await this.fetchContributions({ page: { size: 30 } }); await this.fetchContributions({ page: { size: 40 } });
} }
await this.updateMissingHistoricContributionsCount();
return Promise.resolve(); return Promise.resolve();
}, },
async updateMissingHistoricContributionsCount () {
const contributionsCount = await this.kredits.Contribution.count;
this.set('missingHistoricContributionsCount', contributionsCount - this.contributions.length);
console.debug(`Missing ${this.missingHistoricContributionsCount} historic contributions (out of ${contributionsCount} overall)`)
},
addContributor (attributes) { addContributor (attributes) {
if (attributes.github_uid) { if (attributes.github_uid) {
const uidInt = parseInt(attributes.github_uid); const uidInt = parseInt(attributes.github_uid);
@@ -368,11 +381,12 @@ export default Service.extend({
syncContributions: task(function * () { syncContributions: task(function * () {
yield this.fetchNewContributions.perform(); yield this.fetchNewContributions.perform();
yield this.syncUnconfirmedContributions.perform(); yield this.syncUnconfirmedContributions.perform();
yield this.updateMissingHistoricContributionsCount();
this.set('contributionsNeedSync', false); this.set('contributionsNeedSync', false);
}).group('contributionTasks'), }).group('contributionTasks'),
fetchNewContributions: task(function * () { fetchNewContributions: task(function * () {
const count = yield this.kredits.Contribution.functions.contributionsCount(); const count = yield this.kredits.Contribution.count;
const lastKnownContributionId = Math.max.apply(null, this.contributions.mapBy('id')); const lastKnownContributionId = Math.max.apply(null, this.contributions.mapBy('id'));
const toFetch = count - lastKnownContributionId; const toFetch = count - lastKnownContributionId;
@@ -389,7 +403,7 @@ export default Service.extend({
}), }),
fetchMissingContributions: task(function * () { fetchMissingContributions: task(function * () {
const count = yield this.kredits.Contribution.functions.contributionsCount(); const count = yield this.kredits.Contribution.count;
const allIds = [...Array(count+1).keys()]; const allIds = [...Array(count+1).keys()];
allIds.shift(); // remove first item, which is 0 allIds.shift(); // remove first item, which is 0
const loadedContributions = new Set(this.contributions.mapBy('id')); const loadedContributions = new Set(this.contributions.mapBy('id'));
@@ -738,5 +752,5 @@ export default Service.extend({
this.contributors this.contributors
.findBy('address', to) .findBy('address', to)
.incrementProperty('balance', value); .incrementProperty('balance', value);
}, }
}); });
+34 -13
View File
@@ -31,32 +31,53 @@ button, input[type=submit], .button {
&.small { &.small {
font-size: 0.86rem; font-size: 0.86rem;
padding: 0.2rem 0.8rem; padding: 0.2rem 0.8rem;
svg {
width: 1em;
height: 1em;
vertical-align: middle;
margin-right: 0.4rem;
}
} }
&.danger:not(:disabled) { &.danger:not(:disabled) {
color: $red; color: $red;
background-color: rgba(40, 21, 21, 0.6); background-color: rgba(40, 21, 21, 0.6);
border-color: rgba(40, 21, 21, 1); border-color: rgba(40, 21, 21, 1);
&:hover { background-color: rgba(40, 21, 21, 0.8); }
&:hover { &:focus, &:active, &.active { border-color: $red; }
background-color: rgba(40, 21, 21, 0.8);
}
&:focus, &:active, &.active {
border-color: $red;
}
} }
&.green:not(:disabled) { &.green:not(:disabled) {
color: $green; color: $green;
background-color: rgba(21, 40, 21, 0.6); background-color: rgba(21, 40, 21, 0.6);
border-color: rgba(21, 40, 21, 1); border-color: rgba(21, 40, 21, 1);
&:hover { background-color: rgba(21, 40, 21, 0.8); }
&:focus, &:active, &.active { border-color: $green; }
}
&:hover { &.pink:not(:disabled) {
background-color: rgba(21, 40, 21, 0.8); color: $pink;
} background-color: rgba(40, 21, 40, 0.6);
&:focus, &:active, &.active { border-color: rgba(40, 21, 40, 1);
border-color: $green; &:hover { background-color: rgba(40, 21, 40, 0.8); }
} &:focus, &:active, &.active { border-color: $pink; }
}
&.purple:not(:disabled) {
color: $purple;
background-color: rgba(24, 21, 40, 0.6);
border-color: rgba(24, 21, 40, 1);
&:hover { background-color: rgba(24, 21, 40, 0.8); }
&:focus, &:active, &.active { border-color: $purple; }
}
&.yellow:not(:disabled) {
color: $yellow;
background-color: rgba(40, 40, 21, 0.6);
border-color: rgba(40, 40, 21, 1);
&:hover { background-color: rgba(40, 40, 21, 0.8); }
&:focus, &:active, &.active { border-color: $yellow; }
} }
&.icon { &.icon {
-4
View File
@@ -14,10 +14,6 @@ section#signup {
p { p {
font-size: 1.2rem; font-size: 1.2rem;
&.mg-bottom-md {
margin-bottom: 2rem;
}
&.label { &.label {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
font-size: 1rem; font-size: 1rem;
+6 -4
View File
@@ -62,15 +62,12 @@ main {
} }
} }
// TODO Remove after switch to Tailwind CSS
&.text-center { &.text-center {
text-align: center; text-align: center;
} }
p { p {
&.mg-bottom-md {
margin-bottom: 2rem;
}
&.actions { &.actions {
text-align: center; text-align: center;
padding-top: 2rem; padding-top: 2rem;
@@ -177,4 +174,9 @@ main section {
margin-bottom: 2rem; margin-bottom: 2rem;
} }
} }
// TODO Remove after switch to Tailwind CSS
.mb-4 { margin-bottom: 1rem; }
.mb-8 { margin-bottom: 2rem; }
} }
+17 -19
View File
@@ -17,32 +17,30 @@ ul.expense-list {
} }
} }
.description {
grid-row-start: 1;
grid-row-end: 3;
}
.amount {
justify-self: end;
// font-weight: normal;
}
.actions {
justify-self: end;
}
h4 { h4 {
font-size: 1.2rem; font-size: 1.2rem;
font-weight: normal; font-weight: normal;
line-height: 2rem; line-height: 2rem;
} }
p { .amount {
line-height: 2rem; justify-self: end;
}
&.description { .description {
font-size: 1rem; font-size: 1rem;
opacity: 0.7; opacity: 0.7;
grid-column-start: span 2;
}
.tags {
button {
margin-left: 0;
} }
} }
.actions {
justify-self: end;
}
} }
@@ -31,4 +31,24 @@ ul.reimbursement-list {
} }
} }
} }
div.meta {
grid-column-start: 1;
grid-column-end: 3;
margin-top: 0.6rem;
border-top: 1px solid $item-border-color;
display: flex;
p {
flex: 1;
padding: 1.6rem 0 1rem 0;
&.confirmation-eta {
}
&.actions {
text-align: right;
}
}
}
} }
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-tag"><path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"></path><line x1="7" y1="7" x2="7.01" y2="7"></line></svg>

After

Width:  |  Height:  |  Size: 355 B

+26
View File
@@ -105,6 +105,32 @@
@showQuickFilter={{this.showQuickFilterConfirmed}} /> @showQuickFilter={{this.showQuickFilterConfirmed}} />
</div> </div>
</section> </section>
{{#if this.showFullContributionSync}}
<section id="sync-all-contributions">
{{#if this.kredits.fetchMissingContributions.isIdle}}
<p class="mb-4">
There are
<strong>{{this.kredits.missingHistoricContributionsCount}}</strong>
earlier contributions, which are not currently loaded/displayed.
</p>
<p>
You can fetch all historic data in one go, and have it stored locally in
your browser:
<button type="button" {{on "click" (perform this.kredits.fetchMissingContributions)}} class="small">
fetch all data
</button>
</p>
{{else}}
<p class="mb-4">
Syncing data. Please be patient...
</p>
<p>
(You can leave this website anytime and sync missing data when you come back.)
</p>
{{/if}}
</section>
{{/if}}
</div> </div>
<div id="details"> <div id="details">
+1 -1
View File
@@ -2,7 +2,7 @@
<h2>Complete your contributor profile</h2> <h2>Complete your contributor profile</h2>
</header> </header>
<div class="content text-lg"> <div class="content text-lg">
<p class="mg-bottom-md"> <p class="mb-8">
Kredits allow you to take part in project governance, and to earn rewards for Kredits allow you to take part in project governance, and to earn rewards for
your contributions. For both, you will need an Ethereum wallet/account. your contributions. For both, you will need an Ethereum wallet/account.
</p> </p>
+5
View File
@@ -0,0 +1,5 @@
export default function() {
return (navigator.languages && navigator.languages.length) ?
navigator.languages[0] :
navigator.language;
}
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "kredits-web", "name": "kredits-web",
"version": "2.0.0-beta.2", "version": "2.0.0-beta.3",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "kredits-web", "name": "kredits-web",
"version": "2.0.0-beta.2", "version": "2.0.0-beta.3",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.19.1", "@babel/eslint-parser": "^7.19.1",
+1 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "kredits-web", "name": "kredits-web",
"version": "2.0.0-beta.2", "version": "2.0.0-beta.3",
"private": true, "private": true,
"description": "Contribution dashboard of the Kosmos project", "description": "Contribution dashboard of the Kosmos project",
"repository": "https://github.com/67P/kredits-web", "repository": "https://github.com/67P/kredits-web",
@@ -22,7 +22,6 @@
"build-prod": "rm -rf release/* && ember build -prod --output-path release", "build-prod": "rm -rf release/* && ember build -prod --output-path release",
"preversion": "npm test", "preversion": "npm test",
"version": "npm run build-prod && git add release/", "version": "npm run build-prod && git add release/",
"postversion": "git push origin master",
"deploy": "git push 5apps master" "deploy": "git push 5apps master"
}, },
"devDependencies": { "devDependencies": {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -8,10 +8,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="kredits-web/config/environment" content="%7B%22modulePrefix%22%3A%22kredits-web%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22auto%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_DEFAULT_ASYNC_OBSERVERS%22%3Atrue%2C%22_JQUERY_INTEGRATION%22%3Afalse%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22name%22%3A%22kredits-web%22%2C%22version%22%3A%222.0.0-beta.2%2B62b618b6%22%7D%2C%22web3ProviderUrl%22%3A%22https%3A%2F%2Frsk-testnet.kosmos.org%22%2C%22web3RequiredChainId%22%3A31%2C%22web3RequiredNetworkName%22%3A%22RSK%20Testnet%22%2C%22githubConnectUrl%22%3A%22https%3A%2F%2Fhal8000.chat.kosmos.org%2Fkredits%2Fsignup%2Fconnect%2Fgithub%22%2C%22githubSignupUrl%22%3A%22https%3A%2F%2Fhal8000.chat.kosmos.org%2Fkredits%2Fsignup%2Fgithub%22%2C%22ipfs%22%3A%7B%22host%22%3A%22ipfs.kosmos.org%22%2C%22port%22%3A%225444%22%2C%22protocol%22%3A%22https%22%2C%22gatewayUrl%22%3A%22https%3A%2F%2Fipfs.kosmos.org%2Fipfs%22%7D%2C%22tokens%22%3A%7B%22BTC%22%3A%220x2260fac5e5542a773aa44fbcfedf7c193bc2c599%22%7D%2C%22btcBalanceAPI%22%3A%22https%3A%2F%2Fapi.kosmos.org%2Fkredits%2Fonchain_btc_balance%22%2C%22corsProxy%22%3A%22https%3A%2F%2Fcors.5apps.com%2F%3Furi%3D%22%2C%22exportApplicationGlobal%22%3Afalse%7D" /> <meta name="kredits-web/config/environment" content="%7B%22modulePrefix%22%3A%22kredits-web%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22auto%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_DEFAULT_ASYNC_OBSERVERS%22%3Atrue%2C%22_JQUERY_INTEGRATION%22%3Afalse%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22name%22%3A%22kredits-web%22%2C%22version%22%3A%222.0.0-beta.3%2Bd90ddd46%22%7D%2C%22web3ProviderUrl%22%3A%22https%3A%2F%2Frsk-testnet.kosmos.org%22%2C%22web3RequiredChainId%22%3A31%2C%22web3RequiredNetworkName%22%3A%22RSK%20Testnet%22%2C%22githubConnectUrl%22%3A%22https%3A%2F%2Fhal8000.chat.kosmos.org%2Fkredits%2Fsignup%2Fconnect%2Fgithub%22%2C%22githubSignupUrl%22%3A%22https%3A%2F%2Fhal8000.chat.kosmos.org%2Fkredits%2Fsignup%2Fgithub%22%2C%22ipfs%22%3A%7B%22host%22%3A%22ipfs.kosmos.org%22%2C%22port%22%3A%225444%22%2C%22protocol%22%3A%22https%22%2C%22gatewayUrl%22%3A%22https%3A%2F%2Fipfs.kosmos.org%2Fipfs%22%7D%2C%22tokens%22%3A%7B%22BTC%22%3A%220x2260fac5e5542a773aa44fbcfedf7c193bc2c599%22%7D%2C%22btcBalanceAPI%22%3A%22https%3A%2F%2Fapi.kosmos.org%2Fkredits%2Fonchain_btc_balance%22%2C%22corsProxy%22%3A%22https%3A%2F%2Fcors.5apps.com%2F%3Furi%3D%22%2C%22exportApplicationGlobal%22%3Afalse%7D" />
<link integrity="" rel="stylesheet" href="/assets/vendor-ca96b2e19fc9f356e3dbad90c9f3f323.css"> <link integrity="" rel="stylesheet" href="/assets/vendor-ca96b2e19fc9f356e3dbad90c9f3f323.css">
<link integrity="" rel="stylesheet" href="/assets/kredits-web-429511daa3257050f23812f202a534ee.css"> <link integrity="" rel="stylesheet" href="/assets/kredits-web-f21602587acec9a1744c244385a83592.css">
@@ -25,7 +25,7 @@
<script src="/assets/vendor-4ee536ded971436a83d35253df25f2cb.js" integrity="sha256-yb+pC9iuZ8WcQwk9VuDNhSlWguL7BEABsEOEhBpwUC0= sha512-50LApx5XqeKHu/FMaljzqhAMPgkqSiy2rDhUcViYBcXWBBYmilFirup/iWvLiNCdfr5ct9wk7+tU9iShdSgDww==" ></script> <script src="/assets/vendor-4ee536ded971436a83d35253df25f2cb.js" integrity="sha256-yb+pC9iuZ8WcQwk9VuDNhSlWguL7BEABsEOEhBpwUC0= sha512-50LApx5XqeKHu/FMaljzqhAMPgkqSiy2rDhUcViYBcXWBBYmilFirup/iWvLiNCdfr5ct9wk7+tU9iShdSgDww==" ></script>
<script src="/assets/kredits-web-0a433c90d05ea3903308867e0d178f7f.js" integrity="sha256-kU8qbqT24o1tZj4xdYJzarfPFSfEmZr3JLu0g8pAmqk= sha512-gD6YmV3xDnwHJ2WU6yXNkUEiPScjdfM2S5eYwCrsfcsI4iJUG81z7IjUbwzstuZocKkCS5zt8Y84Hh84SdhSuw==" ></script> <script src="/assets/kredits-web-89113e4d5c371829e3d1b05e86cbab9b.js" integrity="sha256-aabl6JZt69T2BZIv4yXcRFfmhWCyEhVA2V4g1ZzkZ6s= sha512-aD0s+kWNsXdHJFxIkgncvwNZVhtARosjjKnbLE0w1ihSrvRoptWXV1SgySdZIHKxFgbNmJGA2oQOzhBKIoqvVg==" ></script>
</body> </body>
@@ -0,0 +1,49 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import createComponent from 'kredits-web/tests/helpers/create-component';
// import moment from 'moment';
module('Unit | Component | confirmed-in', function(hooks) {
setupTest(hooks);
test('it exists', function(assert) {
let component = createComponent('component:confirmed-in');
assert.ok(component);
});
test('#confirmedInBlocks', function(assert) {
const kredits = this.owner.lookup('service:kredits');
kredits.set('currentBlock', 419550);
let component = createComponent('component:confirmed-in');
component.args.confirmedAtBlock = 420000;
assert.equal(component.confirmedInBlocks, 450);
})
test('#confirmedInSeconds', function(assert) {
const kredits = this.owner.lookup('service:kredits');
kredits.set('currentBlock', 419550);
let component = createComponent('component:confirmed-in');
component.args.confirmedAtBlock = 420000;
assert.equal(component.confirmedInSeconds, 13500);
})
test('#confirmedInHumanTime', function(assert) {
const kredits = this.owner.lookup('service:kredits');
kredits.set('currentBlock', 419550);
let component = createComponent('component:confirmed-in');
component.args.confirmedAtBlock = 420000;
assert.equal(component.confirmedInHumanTime, '4 hours');
})
test('#isConfirmed', function(assert) {
const kredits = this.owner.lookup('service:kredits');
kredits.set('currentBlock', 430000);
let component = createComponent('component:confirmed-in');
component.args.confirmedAtBlock = 420000;
assert.ok(component.isConfirmed);
})
});