Compare commits
30 Commits
v2.0.0-beta.3
...
v2.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
205d2d2afc
|
|||
| 895bf569ff | |||
|
dbedf1dbe8
|
|||
| 26f2f2afe9 | |||
|
6c456b1184
|
|||
|
03e7f14d1c
|
|||
|
e94e747ba1
|
|||
|
3679412b3c
|
|||
|
ea15e69d79
|
|||
|
8cc3da40b7
|
|||
|
bdeab512b6
|
|||
| 97ebebe28c | |||
|
d5edb93ec5
|
|||
|
0ecc4079c2
|
|||
|
3f080cf553
|
|||
|
0bdb4d2021
|
|||
|
1e7d8491f9
|
|||
|
4356288497
|
|||
|
87bf8ff2df
|
|||
|
26975636a0
|
|||
| dad56a7b31 | |||
|
d069fa02ab
|
|||
|
d219abc358
|
|||
|
13989bf973
|
|||
|
b3ab69d599
|
|||
|
fe3816501c
|
|||
| 854402aa73 | |||
|
abc19c715b
|
|||
|
b6434ac8d2
|
|||
|
4788b8c51c
|
@@ -0,0 +1,30 @@
|
||||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
---
|
||||
language: node_js
|
||||
node_js:
|
||||
- "14"
|
||||
- "16"
|
||||
|
||||
sudo: false
|
||||
dist: precise
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
addons:
|
||||
chrome: stable
|
||||
|
||||
cache:
|
||||
npm: false
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
@@ -1,9 +1,15 @@
|
||||
[](https://travis-ci.org/67P/kredits-web)
|
||||
[](https://github.com/67P/kredits-web/actions/workflows/ci.yml?query=branch%3Amaster)
|
||||
|
||||
# kredits-web
|
||||
# Kredits Web
|
||||
|
||||
This README outlines the details of collaborating on this Ember application.
|
||||
A short introduction of this app could easily go here.
|
||||
This is the main Web UI for Kosmos [Kredits](https://wiki.kosmos.org/Kredits).
|
||||
It provides an overview of contributors and contributions, as well as the
|
||||
community's budget, expenses, and reimbursements.
|
||||
|
||||
It is an unhosted Web app, fetching all data from
|
||||
[Rootstock](https://rootstock.io/) and [IPFS](https://ipfs.tech/), and caching
|
||||
it in the browser's local database. As such, you can run the app directly from
|
||||
the `/release` directory on any Web server that can serve static assets.
|
||||
|
||||
## Development
|
||||
|
||||
@@ -24,9 +30,9 @@ You will need the following things properly installed on your computer.
|
||||
|
||||
### Building/running for development
|
||||
|
||||
* `ember serve` - by default kredits-web connects to the Ethreum Kovan network
|
||||
* Visit your app at [http://localhost:4200](http://localhost:4200).
|
||||
* Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests).
|
||||
* `npm start` - by default Kredits Web connects to the Rootstock testnet network
|
||||
* Visit the app at [http://localhost:4200](http://localhost:4200).
|
||||
* Visit the tests at [http://localhost:4200/tests](http://localhost:4200/tests).
|
||||
|
||||
See [working with locally deployed contracts](https://github.com/67P/kredits-web#working-with-locally-deployed-contracts) for details on how to develop with locally deployed contracts.
|
||||
|
||||
@@ -48,7 +54,7 @@ Make use of the many generators for code, try `ember help generate` for more det
|
||||
### Building
|
||||
|
||||
* `ember build` (development)
|
||||
* `ember build --environment production` (production)
|
||||
* `npm run build` (production)
|
||||
|
||||
### Deploying
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<form onsubmit={{action "submit"}}>
|
||||
<p>
|
||||
<label for="c-account">Ethereum account</label>
|
||||
<label for="c-account">Rootstock account</label>
|
||||
<Input @type="text"
|
||||
@value={{this.account}}
|
||||
name="account" id="c-account"
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { alias } from '@ember/object/computed';
|
||||
|
||||
export default class BudgetBalancesComponent extends Component {
|
||||
@service communityFunds;
|
||||
@alias('communityFunds.balances') balances;
|
||||
|
||||
get balancesSorted () {
|
||||
return this.communityFunds.balances
|
||||
.sortBy('confirmed_balance').reverse();
|
||||
}
|
||||
|
||||
get loading () {
|
||||
return !this.communityFunds.balancesLoaded;
|
||||
|
||||
@@ -7,11 +7,19 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each this.balances as |balance|}}
|
||||
{{#each this.balancesSorted as |balance|}}
|
||||
<tr>
|
||||
<th>{{balance.token.symbol}}</th>
|
||||
<td class="amount">{{balance.confirmed_balance}}</td>
|
||||
<td class="fiat-amount">~{{balance.balanceUSD}} USD</td>
|
||||
<th>
|
||||
<img src={{balance.token.icon}}
|
||||
alt={{balance.token.description}}
|
||||
title={{balance.token.description}} />
|
||||
</th>
|
||||
<td class="amount">
|
||||
{{fmt-number balance.confirmed_balance}} <span class="unit">sats</span>
|
||||
</td>
|
||||
<td class="fiat-amount">
|
||||
~{{balance.balanceUSD}} USD
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<nav id="main-menu">
|
||||
<ul>
|
||||
<li><LinkTo @route="dashboard">Dashboard</LinkTo></li>
|
||||
<li><LinkTo @route="budget">Budget</LinkTo></li>
|
||||
<li><LinkTo @route="about">About</LinkTo></li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -1,45 +1,37 @@
|
||||
import Component from '@ember/component';
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { computed } from '@ember/object';
|
||||
import { action } from '@ember/object';
|
||||
import { isPresent } from '@ember/utils';
|
||||
|
||||
export default Component.extend({
|
||||
export default class TopbarAccountPanelComponent extends Component {
|
||||
@service router;
|
||||
@service kredits;
|
||||
|
||||
tagName: '',
|
||||
@tracked setupInProgress = false;
|
||||
|
||||
kredits: service(),
|
||||
router: service(),
|
||||
|
||||
setupInProgress: false,
|
||||
|
||||
userHasEthereumWallet: computed(function() {
|
||||
get userHasWallet () {
|
||||
return isPresent(window.ethereum);
|
||||
}),
|
||||
|
||||
showConnectButton: computed('userHasEthereumWallet',
|
||||
'kredits.hasAccounts', function() {
|
||||
return this.userHasEthereumWallet &&
|
||||
!this.kredits.hasAccounts;
|
||||
}),
|
||||
|
||||
actions: {
|
||||
|
||||
signup() {
|
||||
this.router.transitionTo('signup');
|
||||
},
|
||||
|
||||
async connectAccount() {
|
||||
try {
|
||||
await window.ethereum.enable();
|
||||
this.set('setupInProgress', true);
|
||||
await this.kredits.setup();
|
||||
this.set('setupInProgress', false);
|
||||
} catch (error) {
|
||||
this.set('setupInProgress', false);
|
||||
console.log('Opening Ethereum wallet failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
get walletConnected () {
|
||||
return this.userHasWallet && this.kredits.hasAccounts;
|
||||
}
|
||||
|
||||
get walletDisconnected () {
|
||||
return this.userHasWallet && !this.kredits.hasAccounts;
|
||||
}
|
||||
|
||||
@action
|
||||
signup () {
|
||||
this.router.transitionTo('signup');
|
||||
}
|
||||
|
||||
@action
|
||||
async connectWallet () {
|
||||
this.setupInProgress = true;
|
||||
await this.kredits.connectWallet();
|
||||
this.setupInProgress = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
{{#if this.setupInProgress}}
|
||||
Connecting account...
|
||||
{{else}}
|
||||
{{#if (and this.kredits.hasAccounts this.kredits.currentUser)}}
|
||||
{{#if (and this.walletConnected this.kredits.currentUser)}}
|
||||
{{this.kredits.currentUser.name}}
|
||||
{{#if this.kredits.currentUserIsCore}}
|
||||
<span class="core-flag">(core)</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<span class="anonymous">Anonymous</span>
|
||||
<button onclick={{action "signup"}} class="small" type="button">Sign up</button>
|
||||
{{#if this.showConnectButton}}
|
||||
<button onclick={{action "connectAccount"}} class="small green" type="button">Connect account</button>
|
||||
<button onclick={{action "signup"}} id="signup"
|
||||
class="small" type="button">Sign up</button>
|
||||
{{#if this.walletDisconnected}}
|
||||
<button onclick={{action "connectWallet"}} id="connect"
|
||||
class="small green" type="button">Connect wallet</button>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default Controller.extend({
|
||||
kredits: service(),
|
||||
});
|
||||
export default class ApplicationController extends Controller {
|
||||
@service kredits;
|
||||
}
|
||||
|
||||
@@ -35,10 +35,6 @@ export default Controller.extend({
|
||||
|
||||
showFullContributionSync: gt('kredits.missingHistoricContributionsCount', 0),
|
||||
|
||||
showIntroText: computed('kredits.{hasAccounts,currentUser}', function(){
|
||||
return (!this.kredits.hasAccounts || !this.kredits.currentUser);
|
||||
}),
|
||||
|
||||
actions: {
|
||||
|
||||
vetoContribution (contributionId) {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import config from 'kredits-web/config/environment';
|
||||
import { isAddress } from 'web3-utils';
|
||||
|
||||
export default class AccountController extends Controller {
|
||||
@service kredits;
|
||||
|
||||
@tracked accountAddress = null;
|
||||
|
||||
get isValidEthAccount () {
|
||||
return isAddress(this.accountAddress);
|
||||
}
|
||||
|
||||
get signupButtonDisabled () {
|
||||
return !this.isValidEthAccount;
|
||||
}
|
||||
|
||||
@action
|
||||
completeSignup () {
|
||||
const payload = {
|
||||
accessToken: this.kredits.githubAccessToken,
|
||||
account: this.accountAddress
|
||||
}
|
||||
|
||||
fetch(config.githubSignupUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
alert('Creating profile failed. We have been notified about this error and will take a look soon. Sorry!');
|
||||
console.warn('Creating contributor profile failed:',
|
||||
JSON.parse(data.error.body).error.message)
|
||||
return false;
|
||||
} else {
|
||||
console.log('[signup/account] Created contributor:', data);
|
||||
|
||||
this.kredits.githubAccessToken = null;
|
||||
this.accountAddress = null;
|
||||
|
||||
this.transitionToRoute('signup.complete');
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { computed } from '@ember/object';
|
||||
import { alias, not } from '@ember/object/computed';
|
||||
import { isAddress } from 'web3-utils';
|
||||
import { inject as service } from '@ember/service';
|
||||
import config from 'kredits-web/config/environment';
|
||||
|
||||
export default Controller.extend({
|
||||
|
||||
kredits: service(),
|
||||
|
||||
ethAddress: null,
|
||||
githubAccessToken: alias('kredits.githubAccessToken'),
|
||||
|
||||
isValidEthAccount: computed('ethAddress', function() {
|
||||
return isAddress(this.ethAddress);
|
||||
}),
|
||||
|
||||
signupButtonDisabled: not('isValidEthAccount'),
|
||||
|
||||
actions: {
|
||||
|
||||
completeSignup () {
|
||||
const payload = {
|
||||
accessToken: this.githubAccessToken,
|
||||
account: this.ethAddress
|
||||
}
|
||||
|
||||
fetch(config.githubSignupUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
console.log('Created contributor:', data);
|
||||
|
||||
this.setProperties({
|
||||
githubAccessToken: null,
|
||||
ethAddress: null
|
||||
});
|
||||
|
||||
this.transitionToRoute('signup.complete');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,14 +1,12 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { action } from '@ember/object';
|
||||
import config from 'kredits-web/config/environment';
|
||||
|
||||
export default Controller.extend({
|
||||
|
||||
actions: {
|
||||
|
||||
connectGithub () {
|
||||
window.location = config.githubConnectUrl;
|
||||
}
|
||||
export default class IndexController extends Controller {
|
||||
|
||||
@action
|
||||
connectGithub () {
|
||||
window.location = config.githubConnectUrl;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { helper } from '@ember/component/helper';
|
||||
|
||||
export default helper(function fmtNumber(number) {
|
||||
const lang = navigator.language || navigator.userLanguage;
|
||||
return number.toLocaleString(lang);
|
||||
});
|
||||
+2
-4
@@ -11,7 +11,6 @@ Router.map(function() {
|
||||
this.route('contributors', function() {
|
||||
this.route('show', { path: ':id' });
|
||||
});
|
||||
|
||||
this.route('contributions', function() {
|
||||
this.route('show', { path: ':id' });
|
||||
});
|
||||
@@ -26,16 +25,15 @@ Router.map(function() {
|
||||
});
|
||||
this.route('signup', function() {
|
||||
this.route('github');
|
||||
this.route('eth-account');
|
||||
this.route('account');
|
||||
this.route('complete');
|
||||
});
|
||||
this.route('budget', function() {
|
||||
this.route('expenses');
|
||||
|
||||
this.route('reimbursements', function() {});
|
||||
});
|
||||
|
||||
this.route('reimbursements', function() {
|
||||
this.route('new');
|
||||
});
|
||||
this.route('about');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class AboutRoute extends Route {
|
||||
}
|
||||
@@ -8,7 +8,13 @@ export default Route.extend({
|
||||
contributors: alias('kredits.contributors'),
|
||||
|
||||
model (params) {
|
||||
return this.contributors.findBy('id', params.id);
|
||||
const contributor = this.contributors.findBy('id', params.id);
|
||||
|
||||
if (contributor) {
|
||||
return contributor;
|
||||
} else {
|
||||
return this.kredits.fetchContributor(params.id);
|
||||
}
|
||||
},
|
||||
|
||||
setupController (controller, model) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { isEmpty } from '@ember/utils';
|
||||
|
||||
export default class SignupAccountRoute extends Route {
|
||||
@service kredits;
|
||||
|
||||
async setupController (controller) {
|
||||
if (!window.ethereum) return;
|
||||
|
||||
if (this.kredits.hasAccounts) {
|
||||
controller.accountAddress = this.kredits.currentUserAccounts.firstObject;
|
||||
} else {
|
||||
return this.kredits.connectWallet();
|
||||
}
|
||||
}
|
||||
|
||||
redirect () {
|
||||
if (isEmpty(this.kredits.githubAccessToken)) {
|
||||
this.transitionTo('signup.index');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { isEmpty } from '@ember/utils';
|
||||
|
||||
export default Route.extend({
|
||||
|
||||
kredits: service(),
|
||||
|
||||
redirect () {
|
||||
this._super(...arguments);
|
||||
|
||||
if (isEmpty(this.kredits.githubAccessToken)) {
|
||||
this.transitionTo('signup.index');
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -22,7 +22,7 @@ export default Route.extend({
|
||||
|
||||
this.kredits.set('githubAccessToken', accessToken);
|
||||
|
||||
this.transitionTo('signup.eth-account');
|
||||
this.transitionTo('signup.account');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -4,8 +4,8 @@ import config from 'kredits-web/config/environment';
|
||||
|
||||
function createStore(name) {
|
||||
let networkName;
|
||||
if (config.web3RequiredNetworkName) {
|
||||
networkName = config.web3RequiredNetworkName.toLocaleLowerCase().replace(' ', '-');
|
||||
if (config.web3NetworkName) {
|
||||
networkName = config.web3NetworkName.toLocaleLowerCase().replace(' ', '-');
|
||||
} else {
|
||||
networkName = 'custom';
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Service, { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { A } from '@ember/array';
|
||||
import { task } from 'ember-concurrency-decorators';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import Service, { inject as service } from '@ember/service';
|
||||
import config from 'kredits-web/config/environment';
|
||||
|
||||
export default class CommunityFundsService extends Service {
|
||||
@@ -12,9 +12,20 @@ export default class CommunityFundsService extends Service {
|
||||
|
||||
@task
|
||||
*fetchBalances () {
|
||||
yield fetch(config.btcBalanceAPI).then(res => res.json())
|
||||
.then(res => {
|
||||
return this.processBalances(res);
|
||||
const promises = [];
|
||||
const balances = config.communityFundsAPI.balances;
|
||||
|
||||
for (const item of Object.keys(balances)) {
|
||||
const c = balances[item];
|
||||
promises.push(
|
||||
this.fetchBalance(c.url)
|
||||
.then(res => { return this.processBalance(res, c) })
|
||||
)
|
||||
}
|
||||
|
||||
yield Promise.all(promises)
|
||||
.then(() => {
|
||||
this.balancesLoaded = true;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(`[community-funds] Fetching balances failed:`);
|
||||
@@ -22,18 +33,21 @@ export default class CommunityFundsService extends Service {
|
||||
});
|
||||
}
|
||||
|
||||
async processBalances (res) {
|
||||
async fetchBalance(url) {
|
||||
return fetch(url).then(res => res.json());
|
||||
}
|
||||
|
||||
async processBalance (res, config) {
|
||||
await this.exchangeRates.fetchRates();
|
||||
|
||||
// Format and round the approximate USD value
|
||||
const lang = navigator.language || navigator.userLanguage;
|
||||
const balanceUSD = res.confirmed_balance * this.exchangeRates.btcusd;
|
||||
const balanceUSD = (res.confirmed_balance / 100000000) * this.exchangeRates.btcusd;
|
||||
res.balanceUSD = Math.round(balanceUSD).toLocaleString(lang);
|
||||
|
||||
this.balances.pushObject({
|
||||
...res,
|
||||
...{ token: { name: 'BTC', symbol: 'BTC'} }
|
||||
...{ token: { icon: `/img/${config.icon}`, symbol: config.symbol, description: config.description } }
|
||||
});
|
||||
|
||||
this.balancesLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,13 @@ export default class ExchangeRatesService extends Service {
|
||||
@tracked btceur = 0;
|
||||
@tracked btcusd = 0;
|
||||
|
||||
get exchangeRatesLoaded () {
|
||||
return (this.btceur !== 0) && (this.btcusd !== 0);
|
||||
}
|
||||
|
||||
async fetchRates (source='bitstamp') {
|
||||
if (this.exchangeRatesLoaded) return;
|
||||
|
||||
switch(source) {
|
||||
case 'bitstamp':
|
||||
this.btceur = await fetchFromBitstamp('btceur');
|
||||
|
||||
+52
-24
@@ -5,7 +5,7 @@ import Service from '@ember/service';
|
||||
import EmberObject from '@ember/object';
|
||||
import { computed } from '@ember/object';
|
||||
import { alias, filterBy, notEmpty, sort } from '@ember/object/computed';
|
||||
import { isEmpty, isPresent } from '@ember/utils';
|
||||
import { isEmpty } from '@ember/utils';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
import { task, taskGroup } from 'ember-concurrency';
|
||||
@@ -15,6 +15,7 @@ import processContributorData from 'kredits-web/utils/process-contributor-data';
|
||||
import processContributionData from 'kredits-web/utils/process-contribution-data';
|
||||
import processReimbursementData from 'kredits-web/utils/process-reimbursement-data';
|
||||
import formatKredits from 'kredits-web/utils/format-kredits';
|
||||
import switchNetwork from 'kredits-web/utils/switch-network';
|
||||
|
||||
import config from 'kredits-web/config/environment';
|
||||
import Contributor from 'kredits-web/models/contributor';
|
||||
@@ -55,6 +56,21 @@ export default Service.extend({
|
||||
this.set('contributors', []);
|
||||
this.set('contributions', []);
|
||||
this.set('reimbursements', []);
|
||||
|
||||
if (window.ethereum) {
|
||||
window.ethereum.on('chainChanged', this.handleUserChainChanged);
|
||||
window.ethereum.on('accountsChanged', this.handleAccountsChanged);
|
||||
}
|
||||
},
|
||||
|
||||
handleUserChainChanged (chainId) {
|
||||
console.log('User-provided chain ID changed to', chainId);
|
||||
window.location.reload();
|
||||
},
|
||||
|
||||
handleAccountsChanged (accounts) {
|
||||
console.log('User-provided accounts changed to', accounts);
|
||||
window.location.reload();
|
||||
},
|
||||
|
||||
// This is called in the application route's beforeModel(). So it is
|
||||
@@ -64,7 +80,7 @@ export default Service.extend({
|
||||
let ethProvider;
|
||||
|
||||
return new Promise(resolve => {
|
||||
function instantiateWithoutAccount () {
|
||||
function instantiateWithoutWallet () {
|
||||
console.debug('[kredits] Creating new instance from npm module class');
|
||||
console.debug(`[kredits] providerURL: ${config.web3ProviderUrl}`);
|
||||
ethProvider = new ethers.providers.JsonRpcProvider(config.web3ProviderUrl);
|
||||
@@ -74,44 +90,50 @@ export default Service.extend({
|
||||
});
|
||||
}
|
||||
|
||||
async function instantiateWithAccount (web3Provider, context) {
|
||||
async function instantiateWithWallet (web3Provider, context) {
|
||||
console.debug('[kredits] Using user-provided Web3 instance, e.g. from Metamask');
|
||||
ethProvider = new ethers.providers.Web3Provider(web3Provider);
|
||||
ethProvider = new ethers.providers.Web3Provider(web3Provider);
|
||||
const network = await ethProvider.getNetwork();
|
||||
const accounts = await ethProvider.listAccounts();
|
||||
const chainId = config.web3ChainId;
|
||||
|
||||
const network = await ethProvider.getNetwork();
|
||||
if (isPresent(config.web3RequiredChainId) &&
|
||||
network.chainId !== config.web3RequiredChainId) {
|
||||
return instantiateWithoutAccount();
|
||||
}
|
||||
if (isEmpty(accounts)) return instantiateWithoutWallet();
|
||||
|
||||
ethProvider.listAccounts().then(accounts => {
|
||||
if (network.chainId !== chainId) {
|
||||
return switchNetwork();
|
||||
} else {
|
||||
context.set('currentUserAccounts', accounts);
|
||||
const ethSigner = accounts.length === 0 ? null : ethProvider.getSigner();
|
||||
resolve({
|
||||
ethProvider,
|
||||
ethSigner
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (window.ethereum) {
|
||||
if (window.ethereum.isConnected()) {
|
||||
instantiateWithAccount(window.ethereum, this);
|
||||
} else {
|
||||
instantiateWithoutAccount();
|
||||
}
|
||||
}
|
||||
// Legacy dapp browsers...
|
||||
else if (window.web3) {
|
||||
instantiateWithAccount(window.web3.currentProvider, this);
|
||||
}
|
||||
// Non-dapp browsers...
|
||||
else {
|
||||
instantiateWithoutAccount();
|
||||
instantiateWithWallet(window.ethereum, this);
|
||||
} else {
|
||||
instantiateWithoutWallet();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async connectWallet () {
|
||||
const provider = new ethers.providers.Web3Provider(window.ethereum);
|
||||
const network = await provider.getNetwork();
|
||||
const chainId = config.web3ChainId;
|
||||
const chainIdHex = `0x${Number(chainId).toString(16)}`;
|
||||
|
||||
try {
|
||||
await window.ethereum.request({ method: 'eth_requestAccounts' });
|
||||
if (network.chainId !== chainId) await switchNetwork(chainIdHex);
|
||||
} catch (err) {
|
||||
console.log('Connecting wallet failed:', err);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
async setup () {
|
||||
const kredits = await this.getEthProvider().then(providerAndSigner => {
|
||||
return new Kredits(providerAndSigner.ethProvider, providerAndSigner.ethSigner, {
|
||||
@@ -274,6 +296,12 @@ export default Service.extend({
|
||||
});
|
||||
},
|
||||
|
||||
async fetchContributor (id) {
|
||||
console.debug(`[kredits] Fetching contributor from the network`);
|
||||
return this.kredits.Contributor.getById(id)
|
||||
.then(data => this.loadContributorFromData(data))
|
||||
},
|
||||
|
||||
fetchContributors () {
|
||||
console.debug(`[kredits] Fetching all contributors from the network`);
|
||||
return this.kredits.Contributor.all()
|
||||
|
||||
+21
-35
@@ -1,3 +1,7 @@
|
||||
body {
|
||||
margin-bottom: 10rem;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem 2rem;
|
||||
|
||||
@@ -14,7 +18,7 @@ main {
|
||||
"contributions";
|
||||
}
|
||||
|
||||
&#budget {
|
||||
&#budget, &#about {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-row-gap: 2rem;
|
||||
@@ -56,13 +60,12 @@ main {
|
||||
|
||||
&.text-lg {
|
||||
p {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5em;
|
||||
font-size: 1.35rem;
|
||||
margin-bottom: 2rem;
|
||||
line-height: 150%;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Remove after switch to Tailwind CSS
|
||||
&.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -74,40 +77,23 @@ main {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#intro {
|
||||
padding: 2rem;
|
||||
background-color: rgba(0,0,0,.2);
|
||||
font-size: 1.6rem;
|
||||
&.text {
|
||||
h2 {
|
||||
margin: 4rem 0 2rem 0;
|
||||
|
||||
@include media-max(small) {
|
||||
padding: 2rem 1rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
&:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: inherit;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5em;
|
||||
font-size: 1.2rem;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
p {
|
||||
font-size: 1.35rem;
|
||||
margin-bottom: 2rem;
|
||||
line-height: 150%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
|
||||
&:hover, &:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +114,7 @@ main {
|
||||
}
|
||||
}
|
||||
|
||||
&#budget {
|
||||
&#budget, &#about {
|
||||
grid-column-gap: 3rem;
|
||||
grid-template-columns: 2fr 4fr 2fr;
|
||||
grid-template-areas:
|
||||
|
||||
@@ -98,6 +98,7 @@ section {
|
||||
@import "components/contributor-profile";
|
||||
@import "components/expense-list";
|
||||
@import "components/external-account-link";
|
||||
@import "components/main-navigation-menu";
|
||||
@import "components/loading-spinner";
|
||||
@import "components/reimbursement-list";
|
||||
@import "components/topbar";
|
||||
|
||||
@@ -16,8 +16,12 @@ section#funds {
|
||||
}
|
||||
|
||||
th, td {
|
||||
font-size: 1.2rem;
|
||||
vertical-align: text-bottom;
|
||||
|
||||
img {
|
||||
max-height: 1.5rem;
|
||||
max-width: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
@@ -35,8 +39,13 @@ section#funds {
|
||||
}
|
||||
|
||||
&.fiat-amount {
|
||||
font-size: 1.2rem;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
|
||||
span.unit {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
nav#main-menu {
|
||||
padding: 2rem;
|
||||
background-color: rgba(0,0,0,.2);
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 3rem;
|
||||
font-size: 1.5rem;
|
||||
|
||||
@include media-max(small) {
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
|
||||
&.active, &:hover {
|
||||
color: $yellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
h1 {
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<main id="about">
|
||||
<div id="aside">
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<section class="text">
|
||||
<h2>What is this?</h2>
|
||||
<p>
|
||||
You have found the contribution dashboard and budget management app of the
|
||||
<a href="https://kosmos.org" target="_blank" rel="noreferrer noopener">Kosmos</a>
|
||||
open-source co-operative.
|
||||
</p>
|
||||
<p>
|
||||
We use this app to track what people contribute to all parts of our
|
||||
projects (not just code), as well as for managing our community's budget.
|
||||
</p>
|
||||
<h2>Why?</h2>
|
||||
<p>
|
||||
We are trying out a new form of co-operative, native to the Internet,
|
||||
creating and sharing digital resources the same way traditional
|
||||
co-operatives share physical ones.
|
||||
</p>
|
||||
<p>
|
||||
By knowing roughly how much people contribute, we are able to reward
|
||||
co-operative members who contribute their time with money contributed
|
||||
by the ones who don't.
|
||||
</p>
|
||||
<p>
|
||||
In addition to open-source grant payouts, all contributors can also propose
|
||||
reimbursements for specific expenses they covered on behalf of the
|
||||
community.
|
||||
</p>
|
||||
<h2>How?</h2>
|
||||
<p>
|
||||
Instead of notaries, lawyers, courts, or banks, we record data and manage
|
||||
certain decisions on a decentralized ledger called
|
||||
<a href="https://rootstock.io/" target="_blank" rel="noreferrer noopener">Rootstock</a>,
|
||||
which is cryptographically tied to the bitcoin timechain.
|
||||
</p>
|
||||
<p>
|
||||
By doing this, we can ensure community control over the budget, as well as
|
||||
full transparency and verifiability of everything that happens. It also
|
||||
allows us to quickly, cheaply, and reliably send our open-source grants to
|
||||
anyone who's contributing, no matter where they are on this beautiful
|
||||
planet.
|
||||
</p>
|
||||
<h2>Kredits</h2>
|
||||
<p>
|
||||
All contributions are rewarded with so-called kredits. They are both
|
||||
credits in the traditional sense of public attribution, as well as a
|
||||
measure of how much and how regularly someone has added value to the
|
||||
community and its products and services.
|
||||
</p>
|
||||
<p>
|
||||
Kredits are considered for example for grant payouts, as well as
|
||||
permissions for certain actions like vetos or votes. They may also be used
|
||||
to access <a href="https://kosmos.org/services/">hosted services</a> for free,
|
||||
or to unlock additional features on otherwise free services.
|
||||
</p>
|
||||
<h2>Getting started</h2>
|
||||
<p>
|
||||
We'd be delighted to welcome you as a new contributor!
|
||||
If you'd like to start collecting kredits for your contributions, you can
|
||||
<LinkTo @route="signup">create a contributor profile</LinkTo> now.
|
||||
</p>
|
||||
<p>
|
||||
Next, you could learn more about
|
||||
<a href="https://community.kosmos.org/t/how-kredits-for-contributions-are-proposed-and-confirmed/176">how kredits are proposed and issued</a>.
|
||||
If you want to dive deeper into how this all works, head over to the
|
||||
<a href="https://wiki.kosmos.org/Kredits">Kredits documentation</a>
|
||||
page on our (soon to be phased out) wiki.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div id="empty">
|
||||
</div>
|
||||
</main>
|
||||
@@ -1,6 +1,8 @@
|
||||
<header id="topbar">
|
||||
<h1><LinkTo @route="dashboard">Kosmos Kredits</LinkTo></h1>
|
||||
<h1><LinkTo @route="dashboard">Kredits</LinkTo></h1>
|
||||
<TopbarAccountPanel />
|
||||
</header>
|
||||
|
||||
<MainNavigationMenu />
|
||||
|
||||
{{outlet}}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
{{#if this.reimbursementsConfirmed}}
|
||||
<section id="expenses-confirmed">
|
||||
<header class="with-nav">
|
||||
<h2>Confirmed Expenses</h2>
|
||||
<h2>Confirmed Reimbursements</h2>
|
||||
<nav>
|
||||
<LinkTo @route="reimbursements.new" @title="Submit a reimbursement" class="button small green">add</LinkTo>
|
||||
</nav>
|
||||
|
||||
+30
-42
@@ -1,19 +1,3 @@
|
||||
{{#if this.showIntroText}}
|
||||
<div id="intro" class={{if this.showDetailsPane "with-details"}}>
|
||||
<h2>
|
||||
Welcome to the contribution dashboard of the
|
||||
<a href="https://kosmos.org" target="_blank" rel="noreferrer noopener">Kosmos</a> project!
|
||||
</h2>
|
||||
<p>
|
||||
If you want to learn more about what the numbers mean and how this works,
|
||||
check out the
|
||||
<a href="https://wiki.kosmos.org/Kredits" target="_blank" rel="noreferrer noopener">Kredits documentation</a>.
|
||||
If you want to start earning kredits for your contributions,
|
||||
<LinkTo @route="signup">create a contributor profile</LinkTo>.
|
||||
</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<main id="dashboard" class={{if this.showDetailsPane "with-details"}}>
|
||||
|
||||
<div id="stats">
|
||||
@@ -45,14 +29,16 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="contributions-by-type">
|
||||
<header>
|
||||
<h2>Contributions by type</h2>
|
||||
</header>
|
||||
<div class="content">
|
||||
<ChartContributionsByType @contributions={{this.contributions}} />
|
||||
</div>
|
||||
</section>
|
||||
{{#if this.contributionsConfirmed}}
|
||||
<section id="contributions-by-type">
|
||||
<header>
|
||||
<h2>Contributions by type</h2>
|
||||
</header>
|
||||
<div class="content">
|
||||
<ChartContributionsByType @contributions={{this.contributions}} />
|
||||
</div>
|
||||
</section>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div id="contributions">
|
||||
@@ -87,24 +73,26 @@
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
<section id="contributions-confirmed">
|
||||
<header class="with-nav">
|
||||
<h2>Confirmed Contributions</h2>
|
||||
<nav>
|
||||
<button type="button"
|
||||
onclick={{action "toggleQuickFilterConfirmed"}}
|
||||
class="small {{if this.showQuickFilterConfirmed "active"}}">
|
||||
filter
|
||||
</button>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="content">
|
||||
<ContributionList @contributions={{this.contributionsConfirmedSorted}}
|
||||
@vetoContribution={{action "vetoContribution"}}
|
||||
@selectedContributionId={{this.selectedContributionId}}
|
||||
@showQuickFilter={{this.showQuickFilterConfirmed}} />
|
||||
</div>
|
||||
</section>
|
||||
{{#if this.contributionsConfirmed}}
|
||||
<section id="contributions-confirmed">
|
||||
<header class="with-nav">
|
||||
<h2>Confirmed Contributions</h2>
|
||||
<nav>
|
||||
<button type="button"
|
||||
onclick={{action "toggleQuickFilterConfirmed"}}
|
||||
class="small {{if this.showQuickFilterConfirmed "active"}}">
|
||||
filter
|
||||
</button>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="content">
|
||||
<ContributionList @contributions={{this.contributionsConfirmedSorted}}
|
||||
@vetoContribution={{action "vetoContribution"}}
|
||||
@selectedContributionId={{this.selectedContributionId}}
|
||||
@showQuickFilter={{this.showQuickFilterConfirmed}} />
|
||||
</div>
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showFullContributionSync}}
|
||||
<section id="sync-all-contributions">
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
<div class="actions">
|
||||
<p>
|
||||
<a href="https://rinkeby.etherscan.io/address/{{this.model.account}}" class="button small" target="_blank" rel="noopener noreferrer">Inspect Ethereum transactions</a>
|
||||
<a href="https://explorer.testnet.rsk.co/address/{{this.model.account}}" class="button small" target="_blank" rel="noopener noreferrer">Inspect Rootstock transactions</a>
|
||||
{{#if this.model.ipfsHash}}
|
||||
<a href="{{this.ipfsGatewayUrl}}/{{this.model.ipfsHash}}" class="button small" target="_blank" rel="noopener noreferrer">Inspect IPFS profile</a>
|
||||
{{/if}}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<header>
|
||||
<h2>Complete your contributor profile</h2>
|
||||
</header>
|
||||
<div class="content text-lg">
|
||||
<p class="mb-8">
|
||||
Kredits allow you to to earn rewards for your contributions, in the form of
|
||||
dynamic open-source grants. As a regular contributor, you can also take
|
||||
part in the community's project governance and finances.
|
||||
</p>
|
||||
<p>
|
||||
In order to interact with the system you will need a
|
||||
<a href="https://rootstock.io" target="_blank" rel="noopener noreferrer">Rootstock</a>
|
||||
wallet/account.
|
||||
</p>
|
||||
<form>
|
||||
<p>
|
||||
<label>
|
||||
Rootstock address:<br>
|
||||
<Input @type="text" @value={{this.accountAddress}} placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4" class={{if this.isValidEthAccount "valid" ""}} />
|
||||
</label>
|
||||
</p>
|
||||
</form>
|
||||
<p class="actions">
|
||||
<button {{on "click" (fn this.completeSignup)}}
|
||||
disabled={{this.signupButtonDisabled}}
|
||||
type="button">
|
||||
Complete my profile
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
@@ -7,7 +7,8 @@
|
||||
</p>
|
||||
<p>
|
||||
Why not say hi to your fellow contributors
|
||||
<a href="https://wiki.kosmos.org/Main_Page#Community_.2F_Getting_in_touch_.2F_Getting_involved">in one of our chat rooms</a>?.
|
||||
<a href="https://wiki.kosmos.org/Main_Page#Community_.2F_Getting_in_touch_.2F_Getting_involved"
|
||||
target="_blank" rel="noopener noreferrer">in one of our chat rooms</a>?.
|
||||
</p>
|
||||
<p class="actions">
|
||||
<LinkTo @route="dashboard" class="button small">Return to dashboard</LinkTo>
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<header>
|
||||
<h2>Complete your contributor profile</h2>
|
||||
</header>
|
||||
<div class="content text-lg">
|
||||
<p class="mb-8">
|
||||
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.
|
||||
</p>
|
||||
<form>
|
||||
<p>
|
||||
<label>
|
||||
Ethereum account:<br>
|
||||
<Input @type="text" @value={{this.ethAddress}} placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4" class={{if this.isValidEthAccount "valid" ""}} />
|
||||
</label>
|
||||
</p>
|
||||
</form>
|
||||
<p class="actions">
|
||||
<button disabled={{this.signupButtonDisabled}}
|
||||
onclick={{action "completeSignup"}}
|
||||
type="button">
|
||||
Complete my profile
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,29 @@
|
||||
import config from 'kredits-web/config/environment';
|
||||
|
||||
export default async function () {
|
||||
const networkName = config.web3NetworkName;
|
||||
const chainId = config.web3ChainId;
|
||||
const chainIdHex = `0x${Number(chainId).toString(16)}`;
|
||||
|
||||
try {
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_switchEthereumChain',
|
||||
params: [{ chainId: chainIdHex }]
|
||||
});
|
||||
} catch (err) {
|
||||
// This error code indicates that the chain has not been added to MetaMask
|
||||
if (err.code === 4902) {
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [{
|
||||
chainId: chainIdHex,
|
||||
chainName: networkName,
|
||||
rpcUrls: [ config.web3ProviderUrl ],
|
||||
nativeCurrency: { name: 'tRBTC', symbol: 'tRBTC', decimals: 18 }
|
||||
}]
|
||||
});
|
||||
} else {
|
||||
console.warn('Failed to switch chains:', err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-6
@@ -26,11 +26,11 @@ module.exports = function(environment) {
|
||||
},
|
||||
|
||||
web3ProviderUrl: 'https://rsk-testnet.kosmos.org',
|
||||
web3RequiredChainId: 31,
|
||||
web3RequiredNetworkName: 'RSK Testnet',
|
||||
web3ChainId: 31,
|
||||
web3NetworkName: 'RSK Testnet',
|
||||
|
||||
githubConnectUrl: 'https://hal8000.chat.kosmos.org/kredits/signup/connect/github',
|
||||
githubSignupUrl: 'https://hal8000.chat.kosmos.org/kredits/signup/github',
|
||||
githubConnectUrl: 'https://hal8000.kosmos.chat/kredits/signup/connect/github',
|
||||
githubSignupUrl: 'https://hal8000.kosmos.chat/kredits/signup/github',
|
||||
|
||||
ipfs: {
|
||||
host: 'ipfs.kosmos.org',
|
||||
@@ -45,7 +45,22 @@ module.exports = function(environment) {
|
||||
'BTC': '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599'
|
||||
},
|
||||
|
||||
btcBalanceAPI: 'https://api.kosmos.org/kredits/onchain_btc_balance',
|
||||
communityFundsAPI: {
|
||||
balances: {
|
||||
onchain: {
|
||||
icon: 'icon-btc.png',
|
||||
symbol: 'BTC',
|
||||
description: 'BTC on chain',
|
||||
url: 'https://api.kosmos.org/btcpay/onchain_btc_balance'
|
||||
},
|
||||
lightning: {
|
||||
icon: 'icon-btc-lightning.png',
|
||||
symbol: 'BTC',
|
||||
description: 'BTC on Lightning Network',
|
||||
url: 'https://api.kosmos.org/btcpay/lightning_btc_balance'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
corsProxy: 'https://cors.5apps.com/?uri='
|
||||
};
|
||||
@@ -66,6 +81,9 @@ module.exports = function(environment) {
|
||||
protocol: 'http',
|
||||
gatewayUrl: 'http://localhost:8080/ipfs'
|
||||
};
|
||||
|
||||
ENV.communityFundsAPI.balances.onchain.url = 'http://localhost:3000/api/btcpay/onchain_btc_balance';
|
||||
ENV.communityFundsAPI.balances.lightning.url = 'http://localhost:3000/api/btcpay/lightning_btc_balance';
|
||||
}
|
||||
|
||||
if (environment === 'test') {
|
||||
@@ -82,7 +100,12 @@ module.exports = function(environment) {
|
||||
|
||||
if (process.env.WEB3_PROVIDER_URL) {
|
||||
ENV.web3ProviderUrl = process.env.WEB3_PROVIDER_URL;
|
||||
ENV.web3RequiredChainId = null;
|
||||
}
|
||||
if (process.env.WEB3_CHAIN_ID) {
|
||||
ENV.web3ChainId = parseInt(process.env.WEB3_CHAIN_ID);
|
||||
}
|
||||
if (process.env.WEB3_NETWORK_NAME) {
|
||||
ENV.web3NetworkName = process.env.WEB3_NETWORK_NAME;
|
||||
}
|
||||
|
||||
return ENV;
|
||||
|
||||
Generated
+9
-9
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "kredits-web",
|
||||
"version": "2.0.0-beta.3",
|
||||
"version": "2.1.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "kredits-web",
|
||||
"version": "2.0.0-beta.3",
|
||||
"version": "2.1.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
@@ -14,7 +14,7 @@
|
||||
"@ember/optional-features": "^1.3.0",
|
||||
"@glimmer/component": "^1.0.0",
|
||||
"@glimmer/tracking": "^1.0.0",
|
||||
"@kredits/contracts": "^7.0.1",
|
||||
"@kredits/contracts": "^7.3.0",
|
||||
"babel-preset-es2015": "^6.22.0",
|
||||
"broccoli-asset-rev": "^3.0.0",
|
||||
"ember-auto-import": "^1.12.1",
|
||||
@@ -3067,9 +3067,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@kredits/contracts": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@kredits/contracts/-/contracts-7.0.1.tgz",
|
||||
"integrity": "sha512-4IhNQoPc1BCT63NKyDOmV+ryb82FKNbIqfDyTdant6WTI9lfkIAnI8Et+ynQmqxx3Efez5hqoDZtEhCijcuIng==",
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@kredits/contracts/-/contracts-7.3.0.tgz",
|
||||
"integrity": "sha512-7Bo2LFEqGIK39G54AStas1RWouBOp+r7Vcv2vP1uLbY01Usea8SHvd2OjLP6nFgsQa0n/rto9oremHv5IAylrQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@kosmos/schemas": "^3.1.0",
|
||||
@@ -26066,9 +26066,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@kredits/contracts": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@kredits/contracts/-/contracts-7.0.1.tgz",
|
||||
"integrity": "sha512-4IhNQoPc1BCT63NKyDOmV+ryb82FKNbIqfDyTdant6WTI9lfkIAnI8Et+ynQmqxx3Efez5hqoDZtEhCijcuIng==",
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@kredits/contracts/-/contracts-7.3.0.tgz",
|
||||
"integrity": "sha512-7Bo2LFEqGIK39G54AStas1RWouBOp+r7Vcv2vP1uLbY01Usea8SHvd2OjLP6nFgsQa0n/rto9oremHv5IAylrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@kosmos/schemas": "^3.1.0",
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kredits-web",
|
||||
"version": "2.0.0-beta.3",
|
||||
"version": "2.1.0",
|
||||
"private": true,
|
||||
"description": "Contribution dashboard of the Kosmos project",
|
||||
"repository": "https://github.com/67P/kredits-web",
|
||||
@@ -17,7 +17,7 @@
|
||||
"start": "ember serve",
|
||||
"test": "npm-run-all lint:* test:*",
|
||||
"test:ember": "ember test",
|
||||
"start:local": "WEB3_PROVIDER_URL=http://localhost:8545 ember serve",
|
||||
"start:local": "WEB3_PROVIDER_URL=http://localhost:8545 WEB3_CHAIN_ID=1337 WEB3_NETWORK_NAME='Hardhat Devchain' ember serve",
|
||||
"build": "ember build --environment=production",
|
||||
"build-prod": "rm -rf release/* && ember build -prod --output-path release",
|
||||
"preversion": "npm test",
|
||||
@@ -30,7 +30,7 @@
|
||||
"@ember/optional-features": "^1.3.0",
|
||||
"@glimmer/component": "^1.0.0",
|
||||
"@glimmer/tracking": "^1.0.0",
|
||||
"@kredits/contracts": "^7.0.1",
|
||||
"@kredits/contracts": "^7.3.0",
|
||||
"babel-preset-es2015": "^6.22.0",
|
||||
"broccoli-asset-rev": "^3.0.0",
|
||||
"ember-auto-import": "^1.12.1",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+418
-274
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
+4
-4
@@ -8,10 +8,10 @@
|
||||
<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.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" />
|
||||
<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.1.0%2B895bf569%22%7D%2C%22web3ProviderUrl%22%3A%22https%3A%2F%2Frsk-testnet.kosmos.org%22%2C%22web3ChainId%22%3A31%2C%22web3NetworkName%22%3A%22RSK%20Testnet%22%2C%22githubConnectUrl%22%3A%22https%3A%2F%2Fhal8000.kosmos.chat%2Fkredits%2Fsignup%2Fconnect%2Fgithub%22%2C%22githubSignupUrl%22%3A%22https%3A%2F%2Fhal8000.kosmos.chat%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%22communityFundsAPI%22%3A%7B%22balances%22%3A%7B%22onchain%22%3A%7B%22icon%22%3A%22icon-btc.png%22%2C%22symbol%22%3A%22BTC%22%2C%22description%22%3A%22BTC%20on%20chain%22%2C%22url%22%3A%22https%3A%2F%2Fapi.kosmos.org%2Fbtcpay%2Fonchain_btc_balance%22%7D%2C%22lightning%22%3A%7B%22icon%22%3A%22icon-btc-lightning.png%22%2C%22symbol%22%3A%22BTC%22%2C%22description%22%3A%22BTC%20on%20Lightning%20Network%22%2C%22url%22%3A%22https%3A%2F%2Fapi.kosmos.org%2Fbtcpay%2Flightning_btc_balance%22%7D%7D%7D%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/kredits-web-f21602587acec9a1744c244385a83592.css">
|
||||
<link integrity="" rel="stylesheet" href="/assets/kredits-web-950f3a97cf1dfead67fdfdc4f5dc1cfb.css">
|
||||
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
<body>
|
||||
|
||||
|
||||
<script src="/assets/vendor-4ee536ded971436a83d35253df25f2cb.js" integrity="sha256-yb+pC9iuZ8WcQwk9VuDNhSlWguL7BEABsEOEhBpwUC0= sha512-50LApx5XqeKHu/FMaljzqhAMPgkqSiy2rDhUcViYBcXWBBYmilFirup/iWvLiNCdfr5ct9wk7+tU9iShdSgDww==" ></script>
|
||||
<script src="/assets/kredits-web-89113e4d5c371829e3d1b05e86cbab9b.js" integrity="sha256-aabl6JZt69T2BZIv4yXcRFfmhWCyEhVA2V4g1ZzkZ6s= sha512-aD0s+kWNsXdHJFxIkgncvwNZVhtARosjjKnbLE0w1ihSrvRoptWXV1SgySdZIHKxFgbNmJGA2oQOzhBKIoqvVg==" ></script>
|
||||
<script src="/assets/vendor-4d87b3e0995c5bb18e46836a089900a4.js" integrity="sha256-5WmWY05Kd+n98Sw0GwuaWodmDVBZ2yVGGtC/2owVfQE= sha512-tiGPA5zvfhnSC9LPTmpvXJmOu30yhsdPqSiZJMPGmE/ez/xJLMaM29vjs4Cs74cDf6fX4uYSGRIu1KfOyGpJYA==" ></script>
|
||||
<script src="/assets/kredits-web-f9beb4aee95d824a1f7bfea226863afa.js" integrity="sha256-wxo3LUhCG27AJ4/5vcWxTz0OpwmqAnK/Vi0AqbftMT0= sha512-tcrBE46PDwENIVYg4/O4j9n+GB7CffqOhXNDIcY1AdxY8mi6F/TNUrDMxFIV0S3BtDWXVyk1zqxPckOylCQc3g==" ></script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
@@ -3,37 +3,90 @@ import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
function stubWalletApi () {
|
||||
window.ethereum = {
|
||||
on: function() { return true; },
|
||||
request: function() { return Promise.resolve(); }
|
||||
}
|
||||
}
|
||||
|
||||
module('Integration | Component | topbar-account-panel', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('unknown user without wallet (or no permission to get wallet/account info)', async function(assert) {
|
||||
test('Unknown user without wallet', async function(assert) {
|
||||
await render(hbs`<TopbarAccountPanel />`);
|
||||
|
||||
assert.ok(this.element.textContent.trim().match(/^Anonymous/));
|
||||
assert.ok(
|
||||
this.element.textContent.trim().match(/^Anonymous/),
|
||||
'shows current user as anonymous'
|
||||
);
|
||||
assert.equal(
|
||||
this.element.querySelectorAll('button#signup').length, 1,
|
||||
'signup button is visible'
|
||||
);
|
||||
assert.equal(
|
||||
this.element.querySelectorAll('button#connect').length, 0,
|
||||
'connect button is not visible'
|
||||
);
|
||||
});
|
||||
|
||||
test('unknown user with Ethereum wallet', async function(assert) {
|
||||
test('Unknown user with disconnected wallet', async function(assert) {
|
||||
stubWalletApi();
|
||||
let service = this.owner.lookup('service:kredits');
|
||||
service.set('currentUserAccounts', []);
|
||||
await render(hbs`<TopbarAccountPanel />`);
|
||||
|
||||
assert.ok(
|
||||
this.element.textContent.trim().match(/^Anonymous/),
|
||||
'shows current user as anonymous'
|
||||
);
|
||||
assert.equal(
|
||||
this.element.querySelectorAll('button#signup').length, 1,
|
||||
'signup button is visible'
|
||||
);
|
||||
assert.equal(
|
||||
this.element.querySelectorAll('button#connect').length, 1,
|
||||
'connect button is visible'
|
||||
);
|
||||
});
|
||||
|
||||
test('Unknown user with connected wallet', async function(assert) {
|
||||
stubWalletApi();
|
||||
let service = this.owner.lookup('service:kredits');
|
||||
service.set('currentUserAccounts', [{ foo: 'bar' }]);
|
||||
await render(hbs`<TopbarAccountPanel />`);
|
||||
|
||||
assert.ok(this.element.textContent.trim().match(/^Anonymous/));
|
||||
assert.ok(
|
||||
this.element.textContent.trim().match(/^Anonymous/),
|
||||
'shows current user as anonymous'
|
||||
);
|
||||
assert.equal(
|
||||
this.element.querySelectorAll('button#signup').length, 1,
|
||||
'signup button is visible'
|
||||
);
|
||||
assert.equal(
|
||||
this.element.querySelectorAll('button#connect').length, 0,
|
||||
'connect button is not visible'
|
||||
);
|
||||
});
|
||||
|
||||
test('known contributor', async function(assert) {
|
||||
test('Known contributor', async function(assert) {
|
||||
stubWalletApi();
|
||||
let service = this.owner.lookup('service:kredits');
|
||||
service.set('currentUserAccounts', [{ foo: 'bar' }]);
|
||||
service.set('currentUser', {
|
||||
name: 'Dorian Nakamoto',
|
||||
isCore: false
|
||||
});
|
||||
service.set('currentUser', { name: 'Dorian Nakamoto', isCore: false });
|
||||
await render(hbs`<TopbarAccountPanel />`);
|
||||
|
||||
assert.dom(this.element).hasText('Dorian Nakamoto');
|
||||
|
||||
service.set('currentUser.isCore', true);
|
||||
await render(hbs`<TopbarAccountPanel />`);
|
||||
|
||||
assert.equal(this.element.querySelectorAll('span.core-flag').length, 1);
|
||||
assert.dom(this.element).hasText(
|
||||
'Dorian Nakamoto', 'shows current user\'s name'
|
||||
);
|
||||
assert.equal(
|
||||
this.element.querySelectorAll('button#signup').length, 0,
|
||||
'signup button is not visible'
|
||||
);
|
||||
assert.equal(
|
||||
this.element.querySelectorAll('button#connect').length, 0,
|
||||
'connect button is not visible'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Route | signup/eth-account', function(hooks) {
|
||||
module('Unit | Route | signup/account', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function(assert) {
|
||||
let route = this.owner.lookup('route:signup/eth-account');
|
||||
let route = this.owner.lookup('route:signup/account');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user