mirror of
https://github.com/cryptoadvance/specter-desktop.git
synced 2026-08-13 12:33:29 +02:00
Feature: initial devhelper extension (#1547)
* initial devhelper extension * change logo * adding fee-selection * moar structure and tx-table refactoring * error-message * improving handling * easier and more consistent * strange * moving devhelp to cryptoadvance.secterext * fix moving issues * a bit more explanation
This commit is contained in:
parent
ce07015375
commit
22042f4b12
21 changed files with 853 additions and 145 deletions
|
|
@ -168,6 +168,7 @@ class BaseConfig(object):
|
|||
EXTENSION_LIST = [
|
||||
"cryptoadvance.specter.services.swan.service",
|
||||
"cryptoadvance.specter.services.bitcoinreserve.service",
|
||||
"cryptoadvance.specterext.devhelp.service",
|
||||
]
|
||||
|
||||
# This is just a placeholder in order to be aware that you cannot set this
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
{% endif %}
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='helpers.js') }}"></script>
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
|
@ -263,7 +264,6 @@
|
|||
{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='helpers.js') }}"></script>
|
||||
|
||||
{% if not hwi_bridge %}
|
||||
{# url routes are not defined in hwi_bridge mode. `url_for` will fail. #}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,15 @@
|
|||
this.note = clone.querySelector(".note");
|
||||
this.info = clone.querySelector(".address-data-info");
|
||||
|
||||
|
||||
|
||||
this.note.innerText = `{{ _("Loading address") }}: ${this.address} {{ _("details") }}...`;
|
||||
|
||||
|
||||
// Attach the created element to the shadow dom
|
||||
shadow.appendChild(clone);
|
||||
}
|
||||
connectedCallback() {
|
||||
// Read input "data-*" attributes
|
||||
this.isVerifyQR = this.getAttribute('data-verify-qr') == 'True';
|
||||
this.isVerifyHwi = this.getAttribute('data-verify-hwi') == 'True';
|
||||
|
|
@ -51,12 +60,21 @@
|
|||
this.label = this.getAttribute('data-label');
|
||||
this.serviceId = this.getAttribute('data-service-id');
|
||||
this.wallet = this.getAttribute('data-address-wallet');
|
||||
this.fetchAddressData();
|
||||
}
|
||||
|
||||
static get observedAttributes() {return ['data-address-wallet', 'data-address']; }
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (name=="data-address") {
|
||||
this.address = newValue
|
||||
}
|
||||
if (name=="data-address-wallet") {
|
||||
this.wallet = newValue
|
||||
}
|
||||
this.info.innerHTML = "<br>"
|
||||
this.note.innerText = `{{ _("Loading address") }}: ${this.address} {{ _("details") }}...`;
|
||||
this.fetchAddressData();
|
||||
|
||||
// Attach the created element to the shadow dom
|
||||
shadow.appendChild(clone);
|
||||
}
|
||||
|
||||
async fetchAddressData() {
|
||||
|
|
@ -64,6 +82,7 @@
|
|||
var formData = new FormData();
|
||||
formData.append('address', this.address);
|
||||
formData.append('csrf_token', '{{ csrf_token() }}');
|
||||
console.log( this.address)
|
||||
try {
|
||||
const response = await fetch(
|
||||
url,
|
||||
|
|
|
|||
|
|
@ -94,6 +94,18 @@
|
|||
shadow.appendChild(clone);
|
||||
}
|
||||
|
||||
static get observedAttributes() {return ['data-wallet', 'data-address']; }
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (name=="data-address") {
|
||||
this.address = newValue
|
||||
}
|
||||
if (name=="data-wallet") {
|
||||
this.wallet = newValue
|
||||
}
|
||||
//ToDo: Implement more here
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.address = this.getAttribute('data-address');
|
||||
this.wallet = this.getAttribute('data-wallet');
|
||||
|
|
|
|||
|
|
@ -298,7 +298,10 @@
|
|||
if (this.hideColumns != "null") {
|
||||
this.hideColumns.split(" ").forEach((column_name) => {
|
||||
if (column_name) {
|
||||
this.shadowRoot.getElementById("column-"+ column_name).remove()
|
||||
let column = this.shadowRoot.getElementById("column-"+ column_name)
|
||||
if (column) {
|
||||
column.remove()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -329,14 +329,14 @@
|
|||
this.paginationNext.onclick = () => {
|
||||
this.idx++;
|
||||
console.log("paginationNext fetch");
|
||||
this.fetchTxItems();
|
||||
|
||||
}
|
||||
|
||||
// Previous page action
|
||||
this.paginationBack.onclick = () => {
|
||||
this.idx--;
|
||||
console.log("paginationBack fetch");
|
||||
this.fetchTxItems();
|
||||
this.update();
|
||||
}
|
||||
|
||||
// Listen to changes in page number selection input
|
||||
|
|
@ -348,7 +348,7 @@
|
|||
}
|
||||
this.idx = newIdx - 1;
|
||||
console.log("paginationIdxInput fetch");
|
||||
this.fetchTxItems();
|
||||
this.update();
|
||||
}
|
||||
|
||||
// Listen to changes in search bar
|
||||
|
|
@ -356,14 +356,14 @@
|
|||
this.search = this.searchInput.value;
|
||||
this.export.href = this.export.href.split('?')[0] + `?exportPrices=${this.exportPricesSwitch.checked}&search=${this.search}&sortby=${this.sortby}&sortdir=${this.sortdir}`;
|
||||
console.log("searchInput fetch");
|
||||
this.fetchTxItems();
|
||||
this.update();
|
||||
}
|
||||
|
||||
// Listen to changes in page limit
|
||||
this.pageLimitSelect.onchange = () => {
|
||||
this.limit = this.pageLimitSelect.value;
|
||||
console.log("pageLimitSelect fetch");
|
||||
this.fetchTxItems();
|
||||
this.update();
|
||||
}
|
||||
|
||||
// Setup sorting functionality for each column of the table header
|
||||
|
|
@ -397,7 +397,6 @@
|
|||
this.export.href = this.export.href.split('?')[0] + `?exportPrices=${this.exportPricesSwitch.checked}&search=${this.search}&sortby=${this.sortby}&sortdir=${this.sortdir}`;
|
||||
}
|
||||
console.log("header fetch")
|
||||
this.fetchTxItems();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -468,6 +467,7 @@
|
|||
if (this.hideSwitcher && this.hideSwitcher.toLowerCase() == "true") {
|
||||
this.shadowRoot.querySelector(".switcher").classList.add('hidden')
|
||||
}
|
||||
this.update();
|
||||
}
|
||||
|
||||
getSelectedTxs() {
|
||||
|
|
@ -498,8 +498,6 @@
|
|||
}
|
||||
|
||||
/**
|
||||
* TODO: This is being called 7 times (once for each attribute) when the page is loaded,
|
||||
* causing up to 7 redundant fetchTxItems() hits to the server.
|
||||
*
|
||||
* Listens to changes on the following attributes:
|
||||
* - blockhash: Should show blockhash column. Either "true" or "false"
|
||||
|
|
@ -542,7 +540,10 @@
|
|||
if (!this.listType) {
|
||||
return
|
||||
}
|
||||
this.fetchTxItems();
|
||||
if (oldValue) {
|
||||
this.update();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (attrName == "selected-coins" && newValue != undefined && newValue != "undefined") {
|
||||
|
|
@ -579,27 +580,25 @@
|
|||
return false
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this.getAttribute('tx-data')) {
|
||||
// faking a jsonResponse for the render-method
|
||||
let jsonResponse = {
|
||||
pageCount: 1,
|
||||
txlist: this.getAttribute('tx-data')
|
||||
}
|
||||
let showBlockhash = !this.blockhashHeader.classList.contains('hidden');
|
||||
this.render(jsonResponse, showBlockhash)
|
||||
} else {
|
||||
this.fetchTxItems()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches txlist from the Specter API and loads the result into TxRowElements
|
||||
*/
|
||||
async fetchTxItems() {
|
||||
this.callId++;
|
||||
// Clean up from existing transaction list
|
||||
this.summaryRow.classList.add('hidden');
|
||||
this.emptyRow.children[1].innerText = '{{ _("Fetching transactions...") }}';
|
||||
this.emptyRow.classList.remove('hidden');
|
||||
this.tbody.querySelectorAll('tx-row').forEach((row) => {row.remove()});
|
||||
if (!this.listType) {
|
||||
this.emptyRow.children[1].innerText = '{{ _("Failed to fetch transactions...") }}';
|
||||
return
|
||||
}
|
||||
|
||||
if (this.listType == "txlist") {
|
||||
this.txlistViewBtn.classList.add("checked");
|
||||
} else {
|
||||
this.utxoViewBtn.classList.add("checked");
|
||||
}
|
||||
|
||||
// Select the correct URL based on the list type and selected wallet
|
||||
let url;
|
||||
switch (this.listType) {
|
||||
|
|
@ -662,120 +661,7 @@
|
|||
let showBlockhash = !this.blockhashHeader.classList.contains('hidden');
|
||||
|
||||
if ("txlist" in jsonResponse) {
|
||||
// Populate the table with TXRowElement objects using the fetched txlist
|
||||
if (jsonResponse.txlist) {
|
||||
let txlist = JSON.parse(jsonResponse.txlist);
|
||||
let summaryTxCount = 0;
|
||||
let summaryAmount = 0;
|
||||
let summaryPrice = 0;
|
||||
for (let tx of txlist) {
|
||||
let txRow = document.createRange().createContextualFragment(`
|
||||
<tx-row class="txrowitem"
|
||||
data-btc-unit="${this.btcUnit ? this.btcUnit : 'btc'}"
|
||||
data-price="${this.price ? this.price : 0}"
|
||||
data-symbol="${this.symbol ? this.symbol : ''}"
|
||||
data-tx='${JSON.stringify(tx).replace(/[\(]/g, "(").replace(/[\)]/g, ")").replace(/[\/]/g, "/").replace(/[\']/g, "'")}'
|
||||
data-wallet="${(this.wallet ? this.wallet : tx.wallet_alias)}"
|
||||
data-show-blockhash="${showBlockhash}"
|
||||
data-mode="${this.getAttribute('type') + (this.wallet ? "" : "-overview")}"
|
||||
data-hide-sensitive-info="${this.hideSensitiveInfo}"
|
||||
hide-columns="${this.hideColumns}">
|
||||
</tx-row>
|
||||
`)
|
||||
this.tbody.append(txRow);
|
||||
summaryTxCount++;
|
||||
summaryAmount += parseFloat(this.tbody.children[this.tbody.children.length - 1].shadowRoot.querySelector('.amount').innerText.replace(',', ''));
|
||||
summaryPrice += parseFloat(this.tbody.children[this.tbody.children.length - 1].shadowRoot.querySelector('.amount-price').innerText.replace(`(${this.symbol}`, '').replace(')', '').replace(',', ''));
|
||||
}
|
||||
|
||||
if (summaryTxCount > 0 && this.search) {
|
||||
this.summaryTxCount.innerText = `Transactions count: ${summaryTxCount}`;
|
||||
this.summaryAmount.innerHTML = `Total amount: ${numberWithCommas(summaryAmount.toFixed(8))}`;
|
||||
if (summaryPrice) {
|
||||
this.summaryAmount.innerHTML += ` <span class="note">(${this.symbol}${numberWithCommas(summaryPrice.toFixed(2))})</span>`
|
||||
}
|
||||
this.summaryRow.classList.remove('hidden');
|
||||
}
|
||||
// Pagination
|
||||
this.pageCount = jsonResponse.pageCount;
|
||||
// Show empty row if no transaction found
|
||||
if (this.pageCount == 0 || txlist.length == 0) {
|
||||
this.emptyRow.classList.remove('hidden');
|
||||
this.emptyRow.children[1].innerText = '{{ _("No transactions found...") }}';
|
||||
this.pageCount = 1;
|
||||
} else {
|
||||
this.emptyRow.classList.add('hidden');
|
||||
}
|
||||
|
||||
// If index is greater than the pages count fetch again for the last page
|
||||
if (this.idx >= this.pageCount) {
|
||||
this.idx = this.pageCount - 1;
|
||||
console.log("index greater than pages count fetch");
|
||||
this.fetchTxItems()
|
||||
}
|
||||
|
||||
this.paginationIdxInput.value = this.idx + 1;
|
||||
|
||||
// If there are multiple pages show the pagination bar
|
||||
if (this.pageCount > 1) {
|
||||
this.paginationContainer.classList.remove('hidden');
|
||||
this.paginationCounter.innerText = `of ${this.pageCount}`;
|
||||
this.paginationIdxInput.max = this.pageCount;
|
||||
} else {
|
||||
this.paginationContainer.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Disable pagination back button if on the first page
|
||||
if (this.idx == 0) {
|
||||
this.paginationBack.classList.add('pagination-disabled');
|
||||
} else {
|
||||
this.paginationBack.classList.remove('pagination-disabled');
|
||||
}
|
||||
|
||||
// Disable pagination next button if on the last page
|
||||
if (this.idx + 1 == this.pageCount) {
|
||||
this.paginationNext.classList.add('pagination-disabled');
|
||||
} else {
|
||||
this.paginationNext.classList.remove('pagination-disabled');
|
||||
}
|
||||
|
||||
this.selectedRows = [];
|
||||
this.selectedRowsActionBox.classList.add('hidden');
|
||||
|
||||
// if preselectedCoins are there, we need to dispatch an event
|
||||
let dispatchLater = false
|
||||
for (let txRow of this.shadowRoot.querySelectorAll('tx-row')) {
|
||||
// Preselect if necessary
|
||||
if (this.checkForSelectedCoin(txRow.tx.txid)) {
|
||||
txRow.select()
|
||||
dispatchLater = true
|
||||
}
|
||||
txRow.addEventListener('txRowCustomSelected', (e) => {
|
||||
this.fireCustomSelectedEvent()
|
||||
})
|
||||
|
||||
txRow.addEventListener('txRowSelected', e=>{
|
||||
let selectedRow = e.target;
|
||||
if (e.detail.selected) {
|
||||
this.selectedRows.push({
|
||||
txid: e.detail.txid,
|
||||
vout: e.detail.vout,
|
||||
amount: e.detail.amount.toFixed(8)
|
||||
});
|
||||
} else {
|
||||
this.selectedRows = this.selectedRows.filter(row => !(row.txid === e.detail.txid && row.vout === e.detail.vout))
|
||||
}
|
||||
if (this.selectedRows.length == 0) {
|
||||
this.selectedRowsActionBox.classList.add('hidden');
|
||||
} else {
|
||||
this.selectedRowsActionBox.classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
if (dispatchLater) {
|
||||
this.fireCustomSelectedEvent()
|
||||
}
|
||||
}
|
||||
this.render(jsonResponse, showBlockhash)
|
||||
return
|
||||
};
|
||||
showError(`{{ _("Failed to fetch transactions list.") }}`)
|
||||
|
|
@ -784,7 +670,147 @@
|
|||
showError(`{{ _("Failed to fetch transactions list.") }}: ${e}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
||||
render(jsonResponse, showBlockhash) {
|
||||
// jsonresponse is something like:
|
||||
// {
|
||||
// "pageCount": 1,
|
||||
// "txlist": "[{\"txid\": \"1f2b4f9b6762ab37d3b0495cda51d8733c68a5e3a01356f0a67f215a29b2f05e\", \"blockhash\": \"05d7c1f7c4fa2a530e0916dcd15551657c22f4c30df175292b0c0b0f320adc10\", \"blockheight\": 722, \"time\": 1644162928, \"conflicts\": [], \"bip125-replaceable\": \"no\", \"vsize\": null, \"category\": \"receive\", \"address\": \"bcrt1qc3z4j7kksr3wm2heaahzx62yjrj8fn83s7x3ps\", \"amount\": 20.0, \"ismine\": true, \"confirmations\": 132, \"label\": \"Address #7\", \"validated_blockhash\": \"\", \"wallet_alias\": \"myhot_2\"}, {\"txid\": \"aafaecb085944a6fce4556885c1f10c1cd97bac146a2a0e676a2b12bcf428d3b\", \"blockhash\": \"201eea173347cdc8e8effeaf96053b5867860cec163f3eacec7c8918c94bdaaa\", \"blockheight\": 721, \"time\": 1644162928, \"conflicts\": [], \"bip125-replaceable\": \"no\", \"vsize\": null, \"category\": \"receive\", \"address\": \"bcrt1qyncynwdxmeynk245wa3f4urul3nm2v0ulfueek\", \"amount\": 20.0, \"ismine\": true, \"confirmations\": 133, \"label\": \"Address #6\", \"validated_blockhash\": \"\", \"wallet_alias\": \"myhot_2\"}, {\"txid\": \"bf34b090996dfcd2971c88f645edd4a0a0ad180e15096389d8841cf2bac0ecde\", \"blockhash\": \"07952e41edea8a9507722531e922b9d217518d6116ba4fba47c9e62ce12dee9e\", \"blockheight\": 305, \"time\": 1644159720, \"conflicts\": [], \"bip125-replaceable\": \"no\", \"vsize\": null, \"category\": \"receive\", \"address\": \"bcrt1qyncynwdxmeynk245wa3f4urul3nm2v0ulfueek\", \"amount\": 20.0, \"ismine\": true, \"confirmations\": 549, \"label\": \"Address #6\", \"validated_blockhash\": \"\", \"wallet_alias\": \"myhot_2\"}]"
|
||||
// }
|
||||
|
||||
// Clean up from existing transaction list
|
||||
this.summaryRow.classList.add('hidden');
|
||||
this.emptyRow.children[1].innerText = '{{ _("Fetching transactions...") }}';
|
||||
this.emptyRow.classList.remove('hidden');
|
||||
this.tbody.querySelectorAll('tx-row').forEach((row) => {row.remove()});
|
||||
if (!this.listType) {
|
||||
this.emptyRow.children[1].innerText = '{{ _("Failed to fetch transactions...") }}';
|
||||
return
|
||||
}
|
||||
|
||||
if (this.listType == "txlist") {
|
||||
this.txlistViewBtn.classList.add("checked");
|
||||
} else {
|
||||
this.utxoViewBtn.classList.add("checked");
|
||||
}
|
||||
|
||||
// Populate the table with TXRowElement objects using the fetched txlist
|
||||
if (jsonResponse.txlist) {
|
||||
let txlist = JSON.parse(jsonResponse.txlist);
|
||||
let summaryTxCount = 0;
|
||||
let summaryAmount = 0;
|
||||
let summaryPrice = 0;
|
||||
for (let tx of txlist) {
|
||||
let txRow = document.createRange().createContextualFragment(`
|
||||
<tx-row class="txrowitem"
|
||||
data-btc-unit="${this.btcUnit ? this.btcUnit : 'btc'}"
|
||||
data-price="${this.price ? this.price : 0}"
|
||||
data-symbol="${this.symbol ? this.symbol : ''}"
|
||||
data-tx='${JSON.stringify(tx).replace(/[\(]/g, "(").replace(/[\)]/g, ")").replace(/[\/]/g, "/").replace(/[\']/g, "'")}'
|
||||
data-wallet="${(this.wallet ? this.wallet : tx.wallet_alias)}"
|
||||
data-show-blockhash="${showBlockhash}"
|
||||
data-mode="${this.getAttribute('type') + (this.wallet ? "" : "-overview")}"
|
||||
data-hide-sensitive-info="${this.hideSensitiveInfo}"
|
||||
hide-columns="${this.hideColumns}">
|
||||
</tx-row>
|
||||
`)
|
||||
this.tbody.append(txRow);
|
||||
summaryTxCount++;
|
||||
summaryAmount += parseFloat(this.tbody.children[this.tbody.children.length - 1].shadowRoot.querySelector('.amount').innerText.replace(',', ''));
|
||||
summaryPrice += parseFloat(this.tbody.children[this.tbody.children.length - 1].shadowRoot.querySelector('.amount-price').innerText.replace(`(${this.symbol}`, '').replace(')', '').replace(',', ''));
|
||||
}
|
||||
|
||||
if (summaryTxCount > 0 && this.search) {
|
||||
this.summaryTxCount.innerText = `Transactions count: ${summaryTxCount}`;
|
||||
this.summaryAmount.innerHTML = `Total amount: ${numberWithCommas(summaryAmount.toFixed(8))}`;
|
||||
if (summaryPrice) {
|
||||
this.summaryAmount.innerHTML += ` <span class="note">(${this.symbol}${numberWithCommas(summaryPrice.toFixed(2))})</span>`
|
||||
}
|
||||
this.summaryRow.classList.remove('hidden');
|
||||
}
|
||||
// Pagination
|
||||
this.pageCount = jsonResponse.pageCount;
|
||||
// Show empty row if no transaction found
|
||||
if (this.pageCount == 0 || txlist.length == 0) {
|
||||
this.emptyRow.classList.remove('hidden');
|
||||
this.emptyRow.children[1].innerText = '{{ _("No transactions found...") }}';
|
||||
this.pageCount = 1;
|
||||
} else {
|
||||
this.emptyRow.classList.add('hidden');
|
||||
}
|
||||
|
||||
// If index is greater than the pages count fetch again for the last page
|
||||
if (this.idx >= this.pageCount) {
|
||||
this.idx = this.pageCount - 1;
|
||||
console.log("index greater than pages count fetch");
|
||||
this.fetchTxItems()
|
||||
}
|
||||
|
||||
this.paginationIdxInput.value = this.idx + 1;
|
||||
|
||||
// If there are multiple pages show the pagination bar
|
||||
if (this.pageCount > 1) {
|
||||
this.paginationContainer.classList.remove('hidden');
|
||||
this.paginationCounter.innerText = `of ${this.pageCount}`;
|
||||
this.paginationIdxInput.max = this.pageCount;
|
||||
} else {
|
||||
this.paginationContainer.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Disable pagination back button if on the first page
|
||||
if (this.idx == 0) {
|
||||
this.paginationBack.classList.add('pagination-disabled');
|
||||
} else {
|
||||
this.paginationBack.classList.remove('pagination-disabled');
|
||||
}
|
||||
|
||||
// Disable pagination next button if on the last page
|
||||
if (this.idx + 1 == this.pageCount) {
|
||||
this.paginationNext.classList.add('pagination-disabled');
|
||||
} else {
|
||||
this.paginationNext.classList.remove('pagination-disabled');
|
||||
}
|
||||
|
||||
this.selectedRows = [];
|
||||
this.selectedRowsActionBox.classList.add('hidden');
|
||||
|
||||
// if preselectedCoins are there, we need to dispatch an event
|
||||
let dispatchLater = false
|
||||
for (let txRow of this.shadowRoot.querySelectorAll('tx-row')) {
|
||||
// Preselect if necessary
|
||||
if (this.checkForSelectedCoin(txRow.tx.txid)) {
|
||||
txRow.select()
|
||||
dispatchLater = true
|
||||
}
|
||||
txRow.addEventListener('txRowCustomSelected', (e) => {
|
||||
this.fireCustomSelectedEvent()
|
||||
})
|
||||
|
||||
txRow.addEventListener('txRowSelected', e=>{
|
||||
let selectedRow = e.target;
|
||||
if (e.detail.selected) {
|
||||
this.selectedRows.push({
|
||||
txid: e.detail.txid,
|
||||
vout: e.detail.vout,
|
||||
amount: e.detail.amount.toFixed(8)
|
||||
});
|
||||
} else {
|
||||
this.selectedRows = this.selectedRows.filter(row => !(row.txid === e.detail.txid && row.vout === e.detail.vout))
|
||||
}
|
||||
if (this.selectedRows.length == 0) {
|
||||
this.selectedRowsActionBox.classList.add('hidden');
|
||||
} else {
|
||||
this.selectedRowsActionBox.classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
if (dispatchLater) {
|
||||
this.fireCustomSelectedEvent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this event is fired, then some row got ticked/unticked in the custom-selection-column
|
||||
*/
|
||||
fireCustomSelectedEvent() {
|
||||
|
|
|
|||
0
src/cryptoadvance/specterext/devhelp/__init__.py
Normal file
0
src/cryptoadvance/specterext/devhelp/__init__.py
Normal file
70
src/cryptoadvance/specterext/devhelp/controller.py
Normal file
70
src/cryptoadvance/specterext/devhelp/controller.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import logging
|
||||
from flask import Flask, Response, redirect, render_template, request, url_for
|
||||
from flask import current_app as app
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from cryptoadvance.specter.services.controller import user_secret_decrypted_required
|
||||
from .service import DevhelpService
|
||||
from cryptoadvance.specter.wallet import Wallet
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
devhelp_endpoint = DevhelpService.blueprint
|
||||
|
||||
|
||||
@devhelp_endpoint.route("/")
|
||||
@login_required
|
||||
@user_secret_decrypted_required
|
||||
def index():
|
||||
return render_template(
|
||||
"devhelp/index.jinja",
|
||||
)
|
||||
|
||||
|
||||
@devhelp_endpoint.route("/html/<html_component>")
|
||||
@login_required
|
||||
@user_secret_decrypted_required
|
||||
def html_component(html_component):
|
||||
associated_wallet: Wallet = DevhelpService.get_associated_wallet()
|
||||
return render_template(
|
||||
f"devhelp/html/{html_component}",
|
||||
wallet=associated_wallet,
|
||||
services=app.specter.service_manager.services,
|
||||
address=associated_wallet.get_address(3),
|
||||
)
|
||||
|
||||
|
||||
@devhelp_endpoint.route("/settings", methods=["GET"])
|
||||
@login_required
|
||||
@user_secret_decrypted_required
|
||||
def settings_get():
|
||||
associated_wallet: Wallet = DevhelpService.get_associated_wallet()
|
||||
|
||||
# Get the user's Wallet objs, sorted by Wallet.name
|
||||
wallet_names = sorted(current_user.wallet_manager.wallets.keys())
|
||||
wallets = [current_user.wallet_manager.wallets[name] for name in wallet_names]
|
||||
|
||||
return render_template(
|
||||
"devhelp/settings.jinja",
|
||||
associated_wallet=associated_wallet,
|
||||
wallets=wallets,
|
||||
cookies=request.cookies,
|
||||
)
|
||||
|
||||
|
||||
@devhelp_endpoint.route("/settings", methods=["POST"])
|
||||
@login_required
|
||||
@user_secret_decrypted_required
|
||||
def settings_post():
|
||||
show_menu = request.form["show_menu"]
|
||||
user = app.specter.user_manager.get_user()
|
||||
if show_menu == "yes":
|
||||
user.add_service(DevhelpService.id)
|
||||
else:
|
||||
user.remove_service(DevhelpService.id)
|
||||
used_wallet_alias = request.form.get("used_wallet")
|
||||
if used_wallet_alias != None:
|
||||
wallet = current_user.wallet_manager.get_by_alias(used_wallet_alias)
|
||||
DevhelpService.set_associated_wallet(wallet)
|
||||
return redirect(url_for(f"{DevhelpService.get_blueprint_name()}.settings_get"))
|
||||
45
src/cryptoadvance/specterext/devhelp/service.py
Normal file
45
src/cryptoadvance/specterext/devhelp/service.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import logging
|
||||
from flask import current_app as app
|
||||
from cryptoadvance.specter.services.service import Service, devstatus_alpha
|
||||
from cryptoadvance.specter.specter_error import SpecterError
|
||||
from cryptoadvance.specter.wallet import Wallet
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DevhelpService(Service):
|
||||
id = "devhelp"
|
||||
name = "Development Helper"
|
||||
icon = "devhelp/img/orange-wrench.png"
|
||||
logo = "devhelp/img/orange-wrench.png"
|
||||
desc = "Wrenches at work."
|
||||
has_blueprint = True
|
||||
blueprint_module = "cryptoadvance.specterext.devhelp.controller"
|
||||
devstatus = devstatus_alpha
|
||||
|
||||
sort_priority = 2
|
||||
|
||||
# ServiceEncryptedStorage field names for Swan
|
||||
SPECTER_WALLET_ALIAS = "wallet"
|
||||
|
||||
@classmethod
|
||||
def get_associated_wallet(cls) -> Wallet:
|
||||
"""Get the Specter `Wallet` that is currently associated with this service"""
|
||||
service_data = cls.get_current_user_service_data()
|
||||
if not service_data or cls.SPECTER_WALLET_ALIAS not in service_data:
|
||||
# Service is not initialized; nothing to do
|
||||
return
|
||||
try:
|
||||
return app.specter.wallet_manager.get_by_alias(
|
||||
service_data[cls.SPECTER_WALLET_ALIAS]
|
||||
)
|
||||
except SpecterError as e:
|
||||
logger.debug(e)
|
||||
# Referenced an unknown walletfrom cryptoadvance.specter.wallet import Wallet
|
||||
# TODO: keep ignoring or remove the unknown wallet from service_data?
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def set_associated_wallet(cls, wallet: Wallet):
|
||||
"""Set the Specter `Wallet` that is currently associated with Swan auto-withdrawals"""
|
||||
cls.update_current_user_service_data({cls.SPECTER_WALLET_ALIAS: wallet.alias})
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -0,0 +1,16 @@
|
|||
{% from 'components/menu_item.jinja' import menu_item %}
|
||||
|
||||
{#
|
||||
vaultoro_menu - Tabs menu to navigate between the vaultoro screens.
|
||||
Parameters:
|
||||
- active_menuitem: Current active tab. Options: 'general', 'bitcoin_core', 'auth'.
|
||||
#}
|
||||
{% macro devhelp_menu(active_menuitem) -%}
|
||||
<nav class="row collapse-on-mobile">
|
||||
{{ menu_item(service.id, 'index', 'Main', active_menuitem, isLeft=true) }}
|
||||
{{ menu_item(service.id, 'settings_get', 'Settings', active_menuitem, isRight=true) }}
|
||||
<a href="javascript:void(0);" class="mobile-nav-icon" onclick="toggleMobileNav(this, `{{ url_for('static', filename='img/expand-more.svg') }}`, `{{ url_for('static', filename='img/expand-less.svg') }}`)">
|
||||
<img style="width: 36px;" src="{{ url_for('static', filename='img/expand-more.svg') }}"/>
|
||||
</a>
|
||||
</nav>
|
||||
{%- endmacro %}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{% extends "base.jinja" %}
|
||||
{% block main %}
|
||||
<img src="{{ url_for(service.id +'_endpoint' + '.static', filename=service.logo) }}" width="50"/>
|
||||
{% from 'devhelp/components/devhelp_menu.jinja' import devhelp_menu with context %}
|
||||
{{ devhelp_menu(tab) }}
|
||||
<style>
|
||||
/* ToDo: Refactor to style.css */
|
||||
a {
|
||||
/* Copied from .note */
|
||||
font-size: 0.9em;
|
||||
text-decoration: underline;
|
||||
color: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #fff ;
|
||||
}
|
||||
textarea {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
</style>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
{% extends "devhelp/components/devhelp_tab.jinja" %}
|
||||
{% block title %}address-data{% endblock %}
|
||||
{% set tab = 'html' %}
|
||||
{% block content %}
|
||||
|
||||
<h1>address-data</h1>
|
||||
|
||||
{% include "includes/address-data.html" %}
|
||||
|
||||
|
||||
<code class="html"><textarea rows="40" cols="90" style="height: 200px">
|
||||
<address-data
|
||||
id="someid"
|
||||
data-verify-qr="False"
|
||||
data-verify-hwi="False"
|
||||
data-used="false"
|
||||
data-utxo="0"
|
||||
data-amount="0"
|
||||
data-amount-price=""
|
||||
data-address="lq1qqde0h6pykv3m0754qyfjfz7lcd7hxhyah9urn3zhlszaplcjetk6la7xpl2ezwn59runn290qqnhyws3ckhuwj3nllgd0wsgf"
|
||||
data-service-id="undefined"
|
||||
data-label="Address #5"
|
||||
data-address-wallet="myliquidhot">
|
||||
</address-data>
|
||||
</textarea>
|
||||
</code>
|
||||
|
||||
<p>
|
||||
Address-Data needs an existing wallet-alias and a valid address as the component is making an Ajax-call to <code>{{ url_for('wallets_endpoint_api.addressinfo', wallet_alias='WALLET_ALIAS') }}</code>
|
||||
wallet-alias and address need to be defined in order to get a valid response. You can do that here:
|
||||
</p>
|
||||
|
||||
|
||||
{% include "devhelp/includes/set-wallet.html" %}
|
||||
<br>
|
||||
{% include "devhelp/includes/set-address.html" %}
|
||||
|
||||
|
||||
|
||||
<h2>Render example</h2>
|
||||
<br><br>
|
||||
|
||||
<address-data
|
||||
id="someid"
|
||||
data-verify-qr="False"
|
||||
data-verify-hwi="False"
|
||||
data-used="false"
|
||||
data-utxo="0"
|
||||
data-amount="0"
|
||||
data-amount-price=""
|
||||
data-address="lq1qqde0h6pykv3m0754qyfjfz7lcd7hxhyah9urn3zhlszaplcjetk6la7xpl2ezwn59runn290qqnhyws3ckhuwj3nllgd0wsgf"
|
||||
data-service-id="undefined"
|
||||
data-label="Address #5"
|
||||
data-address-wallet="myliquidhot"></address-data>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
{% extends "devhelp/components/devhelp_tab.jinja" %}
|
||||
{% block title %}address-data{% endblock %}
|
||||
{% set tab = 'html' %}
|
||||
{% block content %}
|
||||
|
||||
<h1>address-label</h1>
|
||||
|
||||
{% include "includes/address-label.html" %}
|
||||
|
||||
|
||||
<code class="html"><textarea rows="40" cols="90" style="height: 200px">
|
||||
<address-label
|
||||
data-address="lq1qq259d9ahc6apt9fcgjx4fmfl5lsfr2vkzrvyy2uuukhrreulzs7xxsyv70jzt0h7rv2s7dwsll8ln2ypf4qujur5m94edx6jv"
|
||||
data-label="Address #0"
|
||||
data-wallet="myliquidhot">
|
||||
</address-label>
|
||||
</textarea>
|
||||
</code>
|
||||
|
||||
<p>
|
||||
Address-label changes the label of an address.
|
||||
wallet-alias and address need to be defined in order to get a valid response.
|
||||
Don't set an data-label attribute because otherwise the initial label doesn't get fetched (bug?!).
|
||||
|
||||
It's using Ajax-call to:
|
||||
<ul>
|
||||
<li>
|
||||
<code>{{ url_for('wallets_endpoint_api.get_label', wallet_alias='WALLET_ALIAS') }}</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>{{ url_for('wallets_endpoint_api.set_label', wallet_alias='WALLET_ALIAS') }}</code>
|
||||
</li>
|
||||
</ul>
|
||||
You set the most important params here:
|
||||
</p>
|
||||
|
||||
|
||||
{% include "devhelp/includes/set-wallet.html" %}
|
||||
<br>
|
||||
{% include "devhelp/includes/set-address.html" %}
|
||||
|
||||
|
||||
<address-label
|
||||
id="someid"
|
||||
data-address="{{ wallet.get_address(2) }}"
|
||||
|
||||
data-wallet="{{wallet.alias}}">
|
||||
</address-label>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
{% extends "devhelp/components/devhelp_tab.jinja" %}
|
||||
{% block title %}address-row{% endblock %}
|
||||
{% set tab = 'html' %}
|
||||
{% block content %}
|
||||
|
||||
<h1>address-row</h1>
|
||||
|
||||
{% include "includes/address-row.html" %}
|
||||
|
||||
|
||||
<code class="html"><textarea rows="40" cols="90" style="height: 200px">
|
||||
<table>
|
||||
<address-row
|
||||
data-verify-qr="False"
|
||||
data-verify-hwi="False"
|
||||
data-btc-unit="btc"
|
||||
data-price="0"
|
||||
data-symbol=""
|
||||
data-address='{"index":0,"address":"bcrt1qreur2f3v9q8ylznanxwkzkhy0gl7vym3zqkwaw","label":"Address #0","amount":20,"used":true,"utxo":1,"type":"receive","service_id":null}'
|
||||
data-hide-sensitive-info="false"
|
||||
data-wallet="syfdds_2">
|
||||
</address-row>
|
||||
</table>
|
||||
</textarea>
|
||||
</code>
|
||||
|
||||
<p>
|
||||
Address-rows are used in address-tables. as discussed in this <a href="https://stackoverflow.com/posts/45765748/revisions">answer</a>, custom elements are not allowed in table-tags.
|
||||
It's not clear how the address-table achieves it that it works nevertheless. So in the example below, we'll render the component without a table-tag.
|
||||
|
||||
The data-address takes json. The data-wallet is used to enable clicking on the address which will show a address-data if the code is properly imported.
|
||||
The component is passive otherwise.
|
||||
|
||||
|
||||
<h1>Render with data-address</h1>
|
||||
|
||||
<address-row
|
||||
id="someid"
|
||||
data-verify-qr="False"
|
||||
data-verify-hwi="False"
|
||||
data-btc-unit="btc"
|
||||
data-price="0"
|
||||
data-symbol=""
|
||||
data-address='{"index":0,"address":"bcrt1qreur2f3v9q8ylznanxwkzkhy0gl7vym3zqkwaw","label":"Address #0","amount":20,"used":true,"utxo":1,"type":"receive","service_id":null}'
|
||||
data-hide-sensitive-info="false"
|
||||
data-wallet="syfdds_2">
|
||||
<!-- -->
|
||||
</address-row>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
{% extends "base.jinja" %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<h1>address-label</h1>
|
||||
|
||||
{% include "includes/fee-selection.html" %}
|
||||
|
||||
|
||||
<code class="html"><textarea rows="40" cols="90" style="height: 200px">
|
||||
<fee-selection id="fee-selection-component"/>
|
||||
</textarea>
|
||||
</code>
|
||||
|
||||
<p>
|
||||
fee-selection let you choose to change the fee you'd like to have. It's using Ajax-call to:
|
||||
<ul>
|
||||
<li>
|
||||
<code>{{ url_for('wallets_endpoint_api.fees') }}</code> : Grab the current fees
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
It's designed to be used in a form and will "drop" what the user has chosen in form-fields which are stored outside the shadow-DOM in the "lightdom". This will look like this:
|
||||
<code class="html"><textarea rows="40" cols="90" style="height: 200px">
|
||||
<input type="hidden" value="dynamic" name="fee_option">
|
||||
<input type="hidden" class="fee_rate hidden" name="fee_rate" id="fee_rate" min="1" step="0.1" value="1" autocomplete="off">
|
||||
<input type="hidden" id="fee_rate_dynamic" name="fee_rate_dynamic" value="4" class="hidden">
|
||||
<input type="hidden" class="rbf-checkbox inline" name="rbf" id="rbf" value="true">
|
||||
<input type="hidden" class="inline" name="subtract" id="subtract" value="false">
|
||||
<input id="subtract_from_input" name="subtract_from" type="hidden" min="1" value="1" step="1" style="width: 80px; min-width: 80px;">
|
||||
<span id="subtract_from" style="display: none">
|
||||
<br>Subtract from recipient number:
|
||||
<input id="subtract_from_input" name="subtract_from" type="number" min="1" value="1" step="1" style="width: 80px; min-width: 80px;"><br>
|
||||
</span>
|
||||
|
||||
</textarea>
|
||||
</code>
|
||||
|
||||
<h2>Rendering</h2>
|
||||
|
||||
<fee-selection id="fee-selection-component"/>
|
||||
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
{% extends "devhelp/components/devhelp_tab.jinja" %}
|
||||
{% block title %}tx-table{% endblock %}
|
||||
{% set tab = 'html' %}
|
||||
{% block content %}
|
||||
|
||||
|
||||
<h1>tx-table</h1>
|
||||
|
||||
<!-- The service-data include needs a service -->
|
||||
{% include "includes/services-data.html" %}
|
||||
{% include "includes/address-label.html" %}
|
||||
{% include "includes/tx-row.html" %}
|
||||
{% include "includes/tx-data.html" %}
|
||||
{% include "includes/explorer-link.html" %}
|
||||
{% include "includes/tx-table.html" %}
|
||||
|
||||
|
||||
<p>
|
||||
Tx-table needs either an existing wallet-alias making an Ajax-call to
|
||||
<code> /wallets/wallet/myhot_2/history/</code> or you pass tx-data via
|
||||
its tx-data attribute. You can insert a service-id attribute which will do?!
|
||||
</p>
|
||||
|
||||
<code class="html"><textarea rows="40" cols="200" style="max-height: 500px;height: 400px;resize: none;overflow-y: scroll;white-space:pre;">
|
||||
<div class="table-holder">
|
||||
<!-- These are the necessary includes (replace ? with % )-->
|
||||
<!-- The next include needs services as template-variable-->
|
||||
{? include "includes/services-data.html" ?}
|
||||
{? include "includes/address-label.html" ?}
|
||||
{? include "includes/tx-row.html" ?}
|
||||
{? include "includes/tx-data.html" ?}
|
||||
{? include "includes/explorer-link.html" ?}
|
||||
{? include "includes/tx-table.html" ?}
|
||||
<tx-table
|
||||
price="41559.45"
|
||||
symbol="$"
|
||||
btc-unit="btc"
|
||||
blockhash="false"
|
||||
type="txlist"
|
||||
hide-sensitive-info="false"
|
||||
service-id="someservice"
|
||||
type="txlist"
|
||||
tx-data='[
|
||||
{"txid": "aafaecb085944a6fce4556885c1f10c1cd97bac146a2a0e676a2b12bcf428d3b", "blockhash": "201eea173347cdc8e8effeaf96053b5867860cec163f3eacec7c8918c94bdaaa", "blockheight": 721, "time": 1644162928, "conflicts": [], "bip125-replaceable": "no", "vsize": null, "category": "receive", "address": "bcrt1qyncynwdxmeynk245wa3f4urul3nm2v0ulfueek", "amount": 20.0, "ismine": true, "confirmations": 780, "label": "Address #6", "validated_blockhash": ""},
|
||||
{"txid": "1f2b4f9b6762ab37d3b0495cda51d8733c68a5e3a01356f0a67f215a29b2f05e", "blockhash": "05d7c1f7c4fa2a530e0916dcd15551657c22f4c30df175292b0c0b0f320adc10", "blockheight": 722, "time": 1644162928, "conflicts": [], "bip125-replaceable": "no", "vsize": null, "category": "receive", "address": "bcrt1qc3z4j7kksr3wm2heaahzx62yjrj8fn83s7x3ps", "amount": 20.0, "ismine": true, "confirmations": 779, "label": "Address #7", "validated_blockhash": ""}]"'
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="table-holder">
|
||||
<tx-table
|
||||
price="41559.45"
|
||||
symbol="$"
|
||||
btc-unit="btc"
|
||||
blockhash="false"
|
||||
type="txlist"
|
||||
hide-sensitive-info="false"
|
||||
wallet="myhot_2">
|
||||
</tx-table>
|
||||
</div>
|
||||
|
||||
</textarea>
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
<div class="card" style="float: left;">
|
||||
{% include "devhelp/includes/set-wallet.html" %}
|
||||
<br>
|
||||
Tx-Data (Does not work yet): <input type="text" id="txdata" site=50/ value='[
|
||||
{"txid": "aafaecb085944a6fce4556885c1f10c1cd97bac146a2a0e676a2b12bcf428d3b", "blockhash": "201eea173347cdc8e8effeaf96053b5867860cec163f3eacec7c8918c94bdaaa", "blockheight": 721, "time": 1644162928, "conflicts": [], "bip125-replaceable": "no", "vsize": null, "category": "receive", "address": "bcrt1qyncynwdxmeynk245wa3f4urul3nm2v0ulfueek", "amount": 20.0, "ismine": true, "confirmations": 780, "label": "Address #6", "validated_blockhash": ""},
|
||||
{"txid": "1f2b4f9b6762ab37d3b0495cda51d8733c68a5e3a01356f0a67f215a29b2f05e", "blockhash": "05d7c1f7c4fa2a530e0916dcd15551657c22f4c30df175292b0c0b0f320adc10", "blockheight": 722, "time": 1644162928, "conflicts": [], "bip125-replaceable": "no", "vsize": null, "category": "receive", "address": "bcrt1qc3z4j7kksr3wm2heaahzx62yjrj8fn83s7x3ps", "amount": 20.0, "ismine": true, "confirmations": 779, "label": "Address #7", "validated_blockhash": ""},
|
||||
]'> <button id="settxdata">SetTxdata</button>
|
||||
</div>
|
||||
|
||||
|
||||
<h1>Render example</h1>
|
||||
<h2>with tx-data</h2>
|
||||
<br><br>
|
||||
|
||||
<tx-table
|
||||
id="someid"
|
||||
price="41559.45"
|
||||
symbol="$"
|
||||
btc-unit="btc"
|
||||
blockhash="false"
|
||||
type="txlist"
|
||||
hide-sensitive-info="false"
|
||||
service-id="someservice"
|
||||
type="txlist"
|
||||
tx-data='[
|
||||
{"txid": "aafaecb085944a6fce4556885c1f10c1cd97bac146a2a0e676a2b12bcf428d3b", "blockhash": "201eea173347cdc8e8effeaf96053b5867860cec163f3eacec7c8918c94bdaaa", "blockheight": 721, "time": 1644162928, "conflicts": [], "bip125-replaceable": "no", "vsize": null, "category": "receive", "address": "bcrt1qyncynwdxmeynk245wa3f4urul3nm2v0ulfueek", "amount": 20.0, "ismine": true, "confirmations": 780, "label": "Address #6", "validated_blockhash": ""},
|
||||
{"txid": "1f2b4f9b6762ab37d3b0495cda51d8733c68a5e3a01356f0a67f215a29b2f05e", "blockhash": "05d7c1f7c4fa2a530e0916dcd15551657c22f4c30df175292b0c0b0f320adc10", "blockheight": 722, "time": 1644162928, "conflicts": [], "bip125-replaceable": "no", "vsize": null, "category": "receive", "address": "bcrt1qc3z4j7kksr3wm2heaahzx62yjrj8fn83s7x3ps", "amount": 20.0, "ismine": true, "confirmations": 779, "label": "Address #7", "validated_blockhash": ""}
|
||||
|
||||
]'
|
||||
>
|
||||
</tx-table>
|
||||
|
||||
<h2>with fetchdata/Ajax</h2>
|
||||
<br><br>
|
||||
|
||||
<tx-table
|
||||
id="someotherid"
|
||||
price="41559.45"
|
||||
symbol="$"
|
||||
btc-unit="btc"
|
||||
blockhash="false"
|
||||
type="txlist"
|
||||
hide-sensitive-info="false"
|
||||
type="txlist"
|
||||
wallet="{{ wallet.alias }}">
|
||||
</tx-table>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script type="text/javascript">
|
||||
|
||||
var setTxdataButton = document.getElementById('settxdata')
|
||||
|
||||
setTxdataButton.addEventListener("click", (event) => {
|
||||
var txdata = document.getElementById('txdata').value
|
||||
document.getElementById('someid').setAttribute("tx-data", address)
|
||||
console.log(walletAlias)
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
potential addresses:
|
||||
<ul>
|
||||
<li>
|
||||
{{ wallet.get_address(1)}}
|
||||
</li>
|
||||
<li>
|
||||
{{ wallet.get_address(2)}}
|
||||
</li>
|
||||
</ul>
|
||||
Address: <input type="text" id="address" site=50 value="{{address}}"/> <button id="setaddress">SetAddress</button>
|
||||
|
||||
<script type="text/javascript">
|
||||
var setAddressButton = document.getElementById('setaddress')
|
||||
|
||||
setAddressButton.addEventListener("click", (event) => {
|
||||
var address = document.getElementById('address').value
|
||||
document.getElementById('someid').setAttribute("data-address", address)
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
WalletAlias: <input type="text" id="walletalias" site=50 value="{{ wallet.alias}}"/> <button id="setwalletalias">SetWalletAlias</button>
|
||||
|
||||
<script type="text/javascript">
|
||||
var setWalletAliasButton = document.getElementById('setwalletalias')
|
||||
|
||||
setWalletAliasButton.addEventListener("click", (event) => {
|
||||
var walletAlias = document.getElementById('walletalias').value
|
||||
document.getElementById('someid').setAttribute("data-address-wallet", walletAlias)
|
||||
console.log(walletAlias)
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{% extends "devhelp/components/devhelp_tab.jinja" %}
|
||||
{% block title %}address-data{% endblock %}
|
||||
{% set tab = 'index' %}
|
||||
{% block content %}
|
||||
|
||||
<p>
|
||||
This Extension is only useful for Developing and testing purposes.
|
||||
</p>
|
||||
<p>
|
||||
You can checkout and test the different HTML-Components. Nothing else to see here.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="html/address-data.jinja">address-data</a></li>
|
||||
<li><a href="html/address-label.jinja">address-label</a></li>
|
||||
<li><a href="html/fee-selection.jinja">fee-selection</a></li>
|
||||
<li><a href="html/tx-table.jinja">tx-table</a></li>
|
||||
<li><a href="html/address-row.jinja">address-row</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
{% extends "devhelp/components/devhelp_tab.jinja" %}
|
||||
{% block title %}Settings{% endblock %}
|
||||
{% set tab = 'settings_get' %}
|
||||
{% block content %}
|
||||
<br/>
|
||||
<style>
|
||||
.big_option {
|
||||
display: inline-block;
|
||||
width: 14em;
|
||||
padding: 1em;
|
||||
vertical-align: middle;
|
||||
height: 3.5em;
|
||||
border: 4px solid var(--cmap-border);
|
||||
border-radius: 0.5em;
|
||||
background-color: var(--cmap-bg-lighter);
|
||||
font-size: 1.5em;
|
||||
margin: 1em;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
.big_option:hover {
|
||||
background-color: var(--cmap-bg-lightest);
|
||||
border: 4px solid var(--main-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.big_option_text {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.instructions {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
{# TODO: End Remove #}
|
||||
|
||||
.css-1rbd7t8 {
|
||||
box-sizing: border-box;
|
||||
margin: 60px 0px 24px;
|
||||
min-width: 0px;
|
||||
}
|
||||
.css-yn7azs {
|
||||
box-sizing: border-box;
|
||||
margin: 0px;
|
||||
min-width: 0px;
|
||||
-moz-box-pack: justify;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
font-family: "Source Serif Pro", serif;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
letter-spacing: -0.02em;
|
||||
font-variant: common-ligatures;
|
||||
text-rendering: optimizelegibility;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="card">
|
||||
<h1>{{ _("Configure your extension") }}</h1>
|
||||
<br>
|
||||
|
||||
<div class="note">
|
||||
{{ _("- Maybe you want to put some notes for the user") }}<br/>
|
||||
{{ _("- One important setting is whether your extension gets a menu-point on the left") }}<br/>
|
||||
{{ _("- Let us assume you want the user to use a specific wallet. That should go here. ") }}<br/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<form action="{{ url_for(service.get_blueprint_name() + '.settings_post') }}" method="POST" role="form">
|
||||
<input type="hidden" class="csrf-token" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
|
||||
<div>Show Menu Item:</div>
|
||||
<select name="show_menu">
|
||||
<option value="yes" {% if show_menu == 'yes' %}selected{% endif %}>Yes</option>
|
||||
<option value="no" {% if show_menu == 'no' %}selected{% endif %}>No</option>
|
||||
</select>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
{{ _("Choose which wallet should be used:") }}:<br>
|
||||
<select name="used_wallet">
|
||||
{% for wallet in wallets %}
|
||||
<option value="{{ wallet.alias }}" {% if associated_wallet == wallet %}selected{% endif %}>{{ wallet.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<div class="row">
|
||||
<button type="submit" class="btn">{{ _("Save") }}</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue