mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2026-02-23 07:04:26 +01:00
Function was mixing locale-specific thousand separators with hardcoded decimal points, causing incorrect display like '7.500.00' in German. Now uses Intl.NumberFormat properly to respect user's locale settings.
335 lines
12 KiB
JavaScript
335 lines
12 KiB
JavaScript
var app = null;
|
|
var eventAggregator = new Vue();
|
|
|
|
document.addEventListener("DOMContentLoaded",function (ev) {
|
|
Vue.use(Toasted);
|
|
|
|
Vue.component('contribute', {
|
|
props: ["targetCurrency", "active", "perks", "inModal", "displayPerksRanking", "perksValue", "loading"],
|
|
template: "#contribute-template"
|
|
});
|
|
|
|
Vue.component('perks', {
|
|
props: ["perks", "targetCurrency", "active", "inModal","displayPerksRanking", "perksValue", "loading"],
|
|
template: "#perks-template"
|
|
});
|
|
|
|
Vue.component('perk', {
|
|
props: ["perk", "targetCurrency", "active", "inModal", "displayPerksRanking", "perksValue", "index", "loading"],
|
|
template: "#perk-template",
|
|
components: {
|
|
qrcode: VueQrcode
|
|
},
|
|
data: function () {
|
|
return {
|
|
amount: null,
|
|
expanded: false
|
|
}
|
|
},
|
|
computed: {
|
|
canExpand: function(){
|
|
return !this.expanded
|
|
&& this.active &&
|
|
(this.perk.inventory==null || this.perk.inventory > 0)
|
|
}
|
|
},
|
|
methods: {
|
|
onContributeFormSubmit: function (e) {
|
|
if (e) {
|
|
e.preventDefault();
|
|
}
|
|
if (!this.active || this.loading){
|
|
return;
|
|
}
|
|
const formUrl = this.$root.srvModel.formUrl;
|
|
if (formUrl) {
|
|
location.href = formUrl + "?amount=" + this.amount + "&choiceKey=" + this.perk.id;
|
|
return;
|
|
} else {
|
|
eventAggregator.$emit("contribute", { amount: parseFloat(this.amount), choiceKey: this.perk.id });
|
|
}
|
|
},
|
|
expand: function(){
|
|
if(this.canExpand){
|
|
this.expanded = true;
|
|
}
|
|
},
|
|
setAmount: function (amount) {
|
|
if(typeof amount === "string"){
|
|
amount = parseFloat(amount);
|
|
}
|
|
this.amount = this.perk.priceType === "Topup"? null : (amount || 0).noExponents();
|
|
this.expanded = false;
|
|
}
|
|
},
|
|
mounted: function () {
|
|
this.setAmount(this.perk.price);
|
|
},
|
|
watch: {
|
|
perk: function (newValue, oldValue) {
|
|
if(newValue.price.type === "Topup"){
|
|
this.setAmount();
|
|
}else if (newValue.price != oldValue.price) {
|
|
this.setAmount(newValue.price);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
app = new Vue({
|
|
el: '#app',
|
|
data: function(){
|
|
return {
|
|
srvModel: window.srvModel,
|
|
connectionStatus: "",
|
|
endDate: "",
|
|
startDate: "",
|
|
started: false,
|
|
ended: false,
|
|
contributeModalOpen: false,
|
|
endDiff: "",
|
|
startDiff: "",
|
|
active: true,
|
|
animation: true,
|
|
sound: true,
|
|
lastUpdated: "",
|
|
loading: false,
|
|
timeoutState: 0
|
|
}
|
|
},
|
|
computed: {
|
|
raisedAmount: function(){
|
|
return this.formatAmount(this.srvModel.info.currentAmount + this.srvModel.info.currentPendingAmount);
|
|
},
|
|
targetAmount: function(){
|
|
return this.formatAmount(this.srvModel.targetAmount);
|
|
},
|
|
percentageRaisedAmount: function(){
|
|
return parseFloat(this.srvModel.info.progressPercentage + this.srvModel.info.pendingProgressPercentage ).toFixed(2);
|
|
},
|
|
targetCurrency: function(){
|
|
return this.srvModel.targetCurrency.toUpperCase();
|
|
},
|
|
paymentStats: function(){
|
|
var keys = Object.keys(this.srvModel.info.paymentStats);
|
|
var result = [];
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var value = this.srvModel.info.paymentStats[keys[i]].percent.toFixed(2) + '%';
|
|
var newItem = { key: keys[i], value: value, label: this.srvModel.info.paymentStats[keys[i]].label};
|
|
newItem.lightning = this.srvModel.info.paymentStats[keys[i]].isLightning;
|
|
result.push(newItem);
|
|
}
|
|
|
|
if(result.length === 1 && result[0].label === srvModel.targetCurrency){
|
|
return [];
|
|
}
|
|
return result;
|
|
},
|
|
perks: function(){
|
|
const result = [];
|
|
for (let i = 0; i < this.srvModel.perks.length; i++) {
|
|
const currentPerk = this.srvModel.perks[i];
|
|
if(this.srvModel.perkCount.hasOwnProperty(currentPerk.id)){
|
|
currentPerk.sold = this.srvModel.perkCount[currentPerk.id];
|
|
}
|
|
if(this.srvModel.perkValue.hasOwnProperty(currentPerk.id)){
|
|
currentPerk.value = this.srvModel.perkValue[currentPerk.id];
|
|
}
|
|
result.push(currentPerk);
|
|
}
|
|
return result;
|
|
},
|
|
hasPerks() {
|
|
return this.srvModel.perks && this.srvModel.perks.length > 0;
|
|
}
|
|
},
|
|
methods: {
|
|
updateComputed: function () {
|
|
if (this.srvModel.endDate) {
|
|
var endDateM = moment(this.srvModel.endDate);
|
|
this.endDate = endDateM.format('MMMM Do YYYY');
|
|
this.ended = endDateM.isBefore(moment());
|
|
|
|
}else{
|
|
this.ended = false;
|
|
this.endDate = null;
|
|
}
|
|
|
|
if (this.srvModel.startDate) {
|
|
var startDateM = moment(this.srvModel.startDate);
|
|
this.startDate = startDateM.format('MMMM Do YYYY');
|
|
this.started = startDateM.isBefore(moment());
|
|
}else{
|
|
this.started = true;
|
|
this.startDate = null;
|
|
}
|
|
if(this.started && !this.ended && this.srvModel.endDate){
|
|
var mDiffD = moment(this.srvModel.endDate).diff(moment(), "days");
|
|
var mDiffH = moment(this.srvModel.endDate).diff(moment(), "hours");
|
|
var mDiffM = moment(this.srvModel.endDate).diff(moment(), "minutes");
|
|
var mDiffS = moment(this.srvModel.endDate).diff(moment(), "seconds");
|
|
this.endDiff = mDiffD > 0? mDiffD + " days" : mDiffH> 0? mDiffH + " hours" : mDiffM> 0? mDiffM+ " minutes" : mDiffS> 0? mDiffS + " seconds": "";
|
|
}else{
|
|
this.endDiff = null;
|
|
}
|
|
if(!this.started && this.srvModel.startDate){
|
|
var mDiffD = moment(this.srvModel.startDate).diff(moment(), "days");
|
|
var mDiffH = moment(this.srvModel.startDate).diff(moment(), "hours");
|
|
var mDiffM = moment(this.srvModel.startDate).diff(moment(), "minutes");
|
|
var mDiffS = moment(this.srvModel.startDate).diff(moment(), "seconds");
|
|
this.startDiff = mDiffD > 0? mDiffD + " days" : mDiffH> 0? mDiffH + " hours" : mDiffM> 0? mDiffM+ " minutes" : mDiffS> 0? mDiffS + " seconds": "";
|
|
}else {
|
|
this.startDiff = null;
|
|
}
|
|
this.lastUpdated = moment(this.srvModel.info.lastUpdated).calendar();
|
|
this.active = this.started && !this.ended;
|
|
setTimeout(this.updateComputed, 1000);
|
|
},
|
|
setLoading: function(val){
|
|
this.loading = val;
|
|
if(this.timeoutState){
|
|
clearTimeout(this.timeoutState);
|
|
}
|
|
},
|
|
formatAmount: function(amount) {
|
|
return formatAmount(amount, this.srvModel.currencyData.divisibility)
|
|
},
|
|
contribute() {
|
|
if (!this.active || this.loading) return;
|
|
|
|
if (this.hasPerks) {
|
|
this.contributeModalOpen = true
|
|
} else {
|
|
if (this.srvModel.formUrl) {
|
|
window.location.href = this.srvModel.formUrl;
|
|
return;
|
|
} else {
|
|
eventAggregator.$emit("contribute", { amount: null, choiceKey: null });
|
|
}
|
|
}
|
|
}
|
|
},
|
|
mounted: function () {
|
|
hubListener.connect();
|
|
var self = this;
|
|
this.sound = this.srvModel.soundsEnabled;
|
|
this.animation = this.srvModel.animationsEnabled;
|
|
eventAggregator.$on("invoice-created", function (invoiceId) {
|
|
btcpay.appendAndShowInvoiceFrame(invoiceId);
|
|
|
|
self.contributeModalOpen = false;
|
|
self.setLoading(false);
|
|
});
|
|
|
|
eventAggregator.$on("contribute", function () {
|
|
self.setLoading(true);
|
|
|
|
self.timeoutState = setTimeout(function(){
|
|
self.setLoading(false);
|
|
},5000);
|
|
});
|
|
eventAggregator.$on("invoice-error", function(error){
|
|
|
|
self.setLoading(false);
|
|
var msg = "";
|
|
if(typeof error === "string"){
|
|
msg = error;
|
|
}else if(!error){
|
|
msg = "Unknown Error";
|
|
}else{
|
|
msg = JSON.stringify(error);
|
|
}
|
|
|
|
Vue.toasted.show("Error creating invoice: " + msg, {
|
|
iconPack: "fontawesome",
|
|
icon: "exclamation-triangle",
|
|
fullWidth: false,
|
|
theme: "bubble",
|
|
type: "error",
|
|
position: "top-center",
|
|
duration: 10000
|
|
});
|
|
});
|
|
eventAggregator.$on("payment-received", function (amount, currency, prettyPMI, pmi) {
|
|
var onChain = pmi.endsWith("-CHAIN");
|
|
if (self.sound) {
|
|
playRandomSound();
|
|
}
|
|
if (self.animation) {
|
|
fireworks();
|
|
}
|
|
amount = parseFloat(amount).noExponents();
|
|
if (onChain) {
|
|
Vue.toasted.show('New payment of ' + amount + " " + currency + " " + prettyPMI, {
|
|
iconPack: "fontawesome",
|
|
icon: "plus",
|
|
duration: 10000
|
|
});
|
|
} else {
|
|
Vue.toasted.show('New payment of ' + amount + " " + cryptoCode + " " + prettyPMI, {
|
|
iconPack: "fontawesome",
|
|
icon: "bolt",
|
|
duration: 10000
|
|
});
|
|
}
|
|
|
|
|
|
});
|
|
if (srvModel.disqusEnabled) {
|
|
window.disqus_config = function () {
|
|
// Replace PAGE_URL with your page's canonical URL variable
|
|
this.page.url = window.location.href;
|
|
|
|
// Replace PAGE_IDENTIFIER with your page's unique identifier variable
|
|
this.page.identifier = self.srvModel.appId;
|
|
};
|
|
|
|
(function () { // REQUIRED CONFIGURATION VARIABLE: EDIT THE SHORTNAME BELOW
|
|
var d = document, s = d.createElement('script');
|
|
|
|
// IMPORTANT: Replace EXAMPLE with your forum shortname!
|
|
s.src = "https://" + self.srvModel.disqusShortname + ".disqus.com/embed.js";
|
|
s.async = true;
|
|
s.setAttribute('data-timestamp', +new Date());
|
|
(d.head || d.body).appendChild(s);
|
|
|
|
var s2 = d.createElement('script');
|
|
s2.src = "//" + self.srvModel.disqusShortname + ".disqus.com/count.js";
|
|
s2.async = true;
|
|
s.setAttribute('data-timestamp', +new Date());
|
|
(d.head || d.body).appendChild(s);
|
|
})();
|
|
}
|
|
eventAggregator.$on("info-updated", function (model) {
|
|
console.warn("UPDATED", self.srvModel, arguments);
|
|
self.srvModel = model;
|
|
});
|
|
eventAggregator.$on("connection-pending", function () {
|
|
self.connectionStatus = "pending";
|
|
});
|
|
eventAggregator.$on("connection-failed", function () {
|
|
self.connectionStatus = "failed";
|
|
});
|
|
eventAggregator.$on("connection-lost", function () {
|
|
self.connectionStatus = "connection lost";
|
|
});
|
|
this.updateComputed();
|
|
}
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Formats input string as a number according to browser locale
|
|
* with correctly displayed fraction amount (e.g. 0.012345 for BTC instead of just 0.0123)
|
|
*
|
|
* @param {number | string} amount Amount to format
|
|
* @param {number} divisibility Currency divisibility (e.g., 8 for BTC)
|
|
* @returns String formatted as a number according to current browser locale and correct fraction amount
|
|
*/
|
|
function formatAmount(amount, divisibility) {
|
|
return new Intl.NumberFormat(undefined, {
|
|
minimumFractionDigits: divisibility,
|
|
maximumFractionDigits: divisibility
|
|
}).format(parseFloat(amount));
|
|
}
|