Compare commits

..

10 commits

9 changed files with 665 additions and 651 deletions

View file

@ -221,10 +221,8 @@
font-size: 0.9em; font-size: 0.9em;
} }
#create-offer-controls { .create-offer-controls {
text-align: center; text-align: center;
overflow-y: auto;
max-height: 800px;
padding: 20px; padding: 20px;
} }
@ -243,7 +241,7 @@
margin-top: 0; margin-top: 0;
} }
#close-offer-controls-area { .close-offer-controls-area {
display: flex; display: flex;
justify-content: end; justify-content: end;
} }
@ -386,7 +384,7 @@
width: 33%; width: 33%;
} }
#close-offer { .close-offer {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }

View file

@ -141,6 +141,8 @@ h1 {
padding: 10px; padding: 10px;
width: fit-content; width: fit-content;
max-width: 95%; max-width: 95%;
max-height: 90vh;
overflow: auto;
margin: 20px auto; margin: 20px auto;
} }

View file

@ -7,7 +7,6 @@ class BigNotesCheckbox {
render() { render() {
const container = document.createElement('div'); const container = document.createElement('div');
container.id = 'large-bills-area';
container.className = 'checkbox-row'; container.className = 'checkbox-row';
const checkbox = document.createElement('input'); const checkbox = document.createElement('input');

View file

@ -0,0 +1,21 @@
class CloseModalButton {
constructor({ parentElement, id, onClickCallback }) {
this.element = null;
this.parentElement = parentElement;
this.id = id;
this.onClickCallback = onClickCallback;
}
render() {
const closeButton = document.createElement('button');
closeButton.className = 'close-offer button-secondary button-medium';
closeButton.textContent = 'Volver';
closeButton.addEventListener('click', this.onClickCallback);
this.element = closeButton;
this.parentElement.appendChild(this.element);
}
}
module.exports = CloseModalButton;

View file

@ -0,0 +1,239 @@
const PublishOfferButton = require('./PublishOfferButton');
const BuyOrSellButtonGroup = require('./BuyOrSellButtonGroup');
const PremiumSelector = require('./PremiumSelector');
const PriceDisplay = require('./PriceDisplay');
const AmountInput = require('./AmountInput');
const PlaceInput = require('./PlaceInput');
const TimeInput = require('./TimeInput');
const BitcoinMethodCheckboxes = require('./BitcoinMethodCheckboxes');
const TrustCheckboxes = require('./TrustCheckboxes');
const BigNotesCheckbox = require('./BigNotesCheckbox');
const CloseModalButton = require('./CloseModalButton');
class CreateOfferModal {
constructor({ parentElement, onCreationCallback, offerService }) {
this.element = null;
this.parentElement = parentElement;
this.onCreationCallback = onCreationCallback;
this.offerService = offerService;
this.publishOfferButton = null;
this.buyOrSellButtonGroup = null;
this.premiumSelector = null;
this.amountInput = null;
this.placeInput = null;
this.timeInput = null;
this.btcMethodCheckboxes = null;
this.trustCheckboxes = null;
this.bigNotesCheckbox = null;
}
render() {
const modalRoot = document.createElement('div');
this.element = modalRoot;
modalRoot.className = 'full-screen-modal-background';
const modal = document.createElement('div');
modal.className = 'full-screen-modal';
const controls = document.createElement('div');
controls.className = 'create-offer-controls';
const title = document.createElement('h2');
title.textContent = 'Añade los detalles de tu oferta';
controls.appendChild(title);
const buyOrSellDiv = document.createElement('div');
buyOrSellDiv.className = 'create-offer-step';
this.buyOrSellButtonGroup = new BuyOrSellButtonGroup({
parentElement: buyOrSellDiv,
});
this.buyOrSellButtonGroup.render();
controls.appendChild(buyOrSellDiv);
const premiumDiv = document.createElement('div');
premiumDiv.classList = 'premium-area';
premiumDiv.className = 'create-offer-step';
const premiumHeading = document.createElement('h3');
premiumHeading.textContent = 'Premium';
premiumDiv.appendChild(premiumHeading);
const premiumContentDiv = document.createElement('div');
premiumContentDiv.classList = 'premium-content-area';
premiumDiv.appendChild(premiumContentDiv);
controls.appendChild(premiumDiv);
const createOfferEventBus = new EventTarget();
const mockPriceProvidingCallback = () => {
return Math.floor(Math.random() * (95000 - 70000 + 1) + 70000);
};
this.premiumSelector = new PremiumSelector({
parentElement: premiumContentDiv,
eventSink: createOfferEventBus,
});
this.premiumSelector.render();
const priceDisplay = new PriceDisplay({
parentElement: premiumContentDiv,
id: 'premium-price-display-area',
premiumProvidingCallback: () => {
return this.premiumSelector.getPremium();
},
priceProvidingCallback: mockPriceProvidingCallback,
});
priceDisplay.render();
createOfferEventBus.addEventListener('premium-changed', () => {
priceDisplay.updatePrices();
});
const amountDiv = document.createElement('div');
amountDiv.className = 'create-offer-step';
const amountHeading = document.createElement('h3');
amountHeading.textContent = '¿Cuánto?';
amountDiv.appendChild(amountHeading);
controls.appendChild(amountDiv);
this.amountInput = new AmountInput({
parentElement: amountDiv,
});
this.amountInput.render();
const placeTimeDiv = document.createElement('div');
placeTimeDiv.className = 'create-offer-step';
const placeTimeHeading = document.createElement('h3');
placeTimeHeading.textContent = '¿Dónde y cuándo?';
placeTimeDiv.appendChild(placeTimeHeading);
const placeTimeContentDiv = document.createElement('div');
placeTimeDiv.appendChild(placeTimeContentDiv);
controls.appendChild(placeTimeDiv);
this.placeInput = new PlaceInput({
parentElement: placeTimeContentDiv,
});
this.placeInput.render();
this.timeInput = new TimeInput({
parentElement: placeTimeContentDiv,
});
this.timeInput.render();
const bitcoinMethodsDiv = document.createElement('div');
bitcoinMethodsDiv.className = 'create-offer-step';
const bitcoinMethodsHeading = document.createElement('h3');
bitcoinMethodsHeading.textContent = '¿Cómo se mueve el Bitcoin?';
bitcoinMethodsDiv.appendChild(bitcoinMethodsHeading);
const bitcoinMethodsContentDiv = document.createElement('div');
bitcoinMethodsDiv.appendChild(bitcoinMethodsContentDiv);
controls.appendChild(bitcoinMethodsDiv);
this.btcMethodCheckboxes = new BitcoinMethodCheckboxes({
parentElement: bitcoinMethodsContentDiv,
});
this.btcMethodCheckboxes.render();
const trustDiv = document.createElement('div');
trustDiv.className = 'create-offer-step';
const trustHeading = document.createElement('h3');
trustHeading.textContent = '¿Quién puede ver la oferta?';
trustDiv.appendChild(trustHeading);
const trustContentDiv = document.createElement('div');
trustDiv.appendChild(trustContentDiv);
controls.appendChild(trustDiv);
this.trustCheckboxes = new TrustCheckboxes({
parentElement: trustContentDiv,
});
this.trustCheckboxes.render();
const otherDiv = document.createElement('div');
otherDiv.className = 'create-offer-step';
const otherHeading = document.createElement('h3');
otherHeading.textContent = 'Extras';
otherDiv.appendChild(otherHeading);
controls.appendChild(otherDiv);
this.bigNotesCheckbox = new BigNotesCheckbox({
parentElement: otherDiv,
});
this.bigNotesCheckbox.render();
//Continue moving components up here
const submitButtonArea = document.createElement('div');
submitButtonArea.classList.add('submit-button-area');
this.publishOfferButton = new PublishOfferButton({
parentElement: submitButtonArea,
id: 'button-submit-offer',
onClickCallback: async () => {
await this.createOffer();
await this.onCreationCallback();
this.toggle();
},
});
this.publishOfferButton.render();
const closeButtonArea = document.createElement('div');
closeButtonArea.className = 'close-offer-controls-area';
const closeButton = new CloseModalButton({
parentElement: closeButtonArea,
onClickCallback: () => {
this.toggle();
},
});
closeButton.render();
controls.appendChild(submitButtonArea);
controls.appendChild(closeButtonArea);
modal.appendChild(controls);
modalRoot.appendChild(modal);
this.parentElement.appendChild(this.element);
}
toggle() {
this.element.classList.toggle('shown');
}
async createOffer() {
const wants = this.buyOrSellButtonGroup.wants();
const premium = this.premiumSelector.getPremium();
const trade_amount_eur = this.amountInput.intEurAmount;
const location_details = this.placeInput.inputText;
const time_availability_details = this.timeInput.inputText;
const is_onchain_accepted = this.btcMethodCheckboxes.isOnchainAccepted;
const is_lightning_accepted = this.btcMethodCheckboxes.isLightningAccepted;
const show_offer_to_trusted = this.trustCheckboxes.showOfferToTrusted;
const show_offer_to_trusted_trusted =
this.trustCheckboxes.showOfferToTrustedTrusted;
const show_offer_to_all_members =
this.trustCheckboxes.showOfferToAllMembers;
const are_big_notes_accepted = this.bigNotesCheckbox.areBigNotesAccepted;
const offerDetails = {
wants,
premium,
trade_amount_eur,
location_details,
time_availability_details,
is_onchain_accepted,
is_lightning_accepted,
show_offer_to_trusted,
show_offer_to_trusted_trusted,
show_offer_to_all_members,
are_big_notes_accepted,
};
await this.offerService.createOffer(offerDetails);
}
}
module.exports = CreateOfferModal;

View file

@ -0,0 +1,374 @@
class OfferCard {
constructor({ offerData, deleteButtonCallback }) {
this.uuid = offerData.uuid;
this.public_key = offerData.public_key;
this.wants = offerData.wants;
this.premium = offerData.premium;
this.trade_amount_eur = offerData.trade_amount_eur;
this.location_details = offerData.location_details;
this.time_availability_details = offerData.time_availability_details;
this.show_offer_to_trusted = offerData.show_offer_to_trusted;
this.show_offer_to_trusted_trusted =
offerData.show_offer_to_trusted_trusted;
this.show_offer_to_all_members = offerData.show_offer_to_all_members;
this.is_onchain_accepted = offerData.is_onchain_accepted;
this.is_lightning_accepted = offerData.is_lightning_accepted;
this.are_big_notes_accepted = offerData.are_big_notes_accepted;
this.created_at = offerData.created_at;
this.last_updated_at = offerData.last_updated_at;
this.deleteButtonCallback = deleteButtonCallback;
}
buildHTML() {
const offerCard = document.createElement('div');
offerCard.classList.add('myoffer-card');
offerCard.classList.add('shadowed-round-area');
const tradeDescDiv = document.createElement('div');
tradeDescDiv.classList.add('trade-desc');
const youBuyText = document.createElement('p');
youBuyText.classList.add('offer-card-content-title');
youBuyText.innerText = 'Compras';
tradeDescDiv.append(youBuyText);
const youBuyData = document.createElement('p');
youBuyData.classList.add('offer-card-content-data');
if (this.wants === 'BTC') {
youBuyData.innerText = `${this.trade_amount_eur * 1021} sats`;
}
if (this.wants === 'EUR') {
youBuyData.innerText = `${this.trade_amount_eur}`;
}
tradeDescDiv.append(youBuyData);
const youSellText = document.createElement('p');
youSellText.classList.add('offer-card-content-title');
youSellText.innerText = 'Vendes';
tradeDescDiv.append(youSellText);
const youSellData = document.createElement('p');
youSellData.classList.add('offer-card-content-data');
if (this.wants === 'BTC') {
youSellData.innerText = `${this.trade_amount_eur}`;
}
if (this.wants === 'EUR') {
youSellData.innerText = `${this.trade_amount_eur * 1021} sats`;
}
tradeDescDiv.append(youSellData);
const premiumDescDiv = document.createElement('div');
premiumDescDiv.classList.add('premium-desc');
const premiumTitle = document.createElement('p');
premiumTitle.classList.add('offer-card-content-title');
premiumTitle.innerText = 'Premium';
premiumDescDiv.append(premiumTitle);
const premiumData = document.createElement('p');
premiumData.classList.add('offer-card-content-data');
premiumData.innerText = `${this.premium * 100} %`;
premiumDescDiv.append(premiumData);
const offerPriceTitle = document.createElement('p');
offerPriceTitle.classList.add('offer-card-content-title');
offerPriceTitle.innerText = 'Precio oferta';
premiumDescDiv.append(offerPriceTitle);
const offerPriceData = document.createElement('p');
offerPriceData.classList.add('offer-card-content-data');
offerPriceData.innerText = `90000 €/BTC`;
premiumDescDiv.append(offerPriceData);
const marketPriceTitle = document.createElement('p');
marketPriceTitle.classList.add('offer-card-content-title');
marketPriceTitle.innerText = 'Precio mercado';
premiumDescDiv.append(marketPriceTitle);
const marketPriceData = document.createElement('p');
marketPriceData.innerText = `88000 €/BTC`;
premiumDescDiv.append(marketPriceData);
const whereDescDiv = document.createElement('div');
whereDescDiv.classList.add('where-desc');
const whereDescTitle = document.createElement('p');
whereDescTitle.classList.add('offer-card-content-title');
whereDescTitle.innerText = 'Dónde';
whereDescDiv.append(whereDescTitle);
const whereDescData = document.createElement('p');
whereDescData.classList.add('offer-long-text');
whereDescData.innerText = `${this.location_details}`;
whereDescDiv.append(whereDescData);
const whenDescDiv = document.createElement('div');
whenDescDiv.classList.add('when-desc');
const whenDescTitle = document.createElement('p');
whenDescTitle.classList.add('offer-card-content-title');
whenDescTitle.innerText = 'Cúando';
whenDescDiv.append(whenDescTitle);
const whenDescData = document.createElement('p');
whenDescData.classList.add('offer-long-text');
whenDescData.innerText = `${this.time_availability_details}`;
whenDescDiv.append(whenDescData);
const bitcoinMethodsDiv = document.createElement('div');
bitcoinMethodsDiv.classList.add('bitcoin-methods-desc');
const bitcoinMethodsTitle = document.createElement('p');
bitcoinMethodsTitle.classList.add('offer-card-content-title');
bitcoinMethodsTitle.innerText = 'Protocolos Bitcoin aceptados';
bitcoinMethodsDiv.append(bitcoinMethodsTitle);
const onchainAcceptedContainer = document.createElement('div');
onchainAcceptedContainer.classList.add('left-icon-checkboxed-field');
if (this.is_onchain_accepted) {
const onchainIcon = document.createElement('img');
onchainIcon.src = '/img/chains-lasecagold.svg';
const onchainText = document.createElement('p');
onchainText.innerText = 'Onchain';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
onchainAcceptedContainer.append(onchainIcon, onchainText, checkIcon);
} else {
const onchainIcon = document.createElement('img');
onchainIcon.src = '/img/chains-gray.svg';
const onchainText = document.createElement('p');
onchainText.innerText = 'Onchain';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
onchainAcceptedContainer.append(onchainIcon, onchainText, checkIcon);
}
const lightningAcceptedContainer = document.createElement('div');
lightningAcceptedContainer.classList.add('left-icon-checkboxed-field');
if (this.is_lightning_accepted) {
const lightningIcon = document.createElement('img');
lightningIcon.src = '/img/bolt-lightning-lasecagold.svg';
const lightningText = document.createElement('p');
lightningText.innerText = 'Lightning';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
lightningAcceptedContainer.append(
lightningIcon,
lightningText,
checkIcon
);
} else {
const lightningIcon = document.createElement('img');
lightningIcon.src = '/img/bolt-lightning-gray.svg';
const lightningText = document.createElement('p');
lightningText.innerText = 'Lightning';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
lightningAcceptedContainer.append(
lightningIcon,
lightningText,
checkIcon
);
}
bitcoinMethodsDiv.append(
onchainAcceptedContainer,
lightningAcceptedContainer
);
const visibilityDiv = document.createElement('div');
visibilityDiv.classList.add('visibility-desc');
const visibilityTitle = document.createElement('p');
visibilityTitle.classList.add('offer-card-content-title');
visibilityTitle.innerText = 'Visibilidad';
visibilityDiv.append(visibilityTitle);
const showOfferToTrustedContainer = document.createElement('div');
showOfferToTrustedContainer.classList.add('right-icon-checkboxed-field');
if (this.show_offer_to_trusted) {
const showOfferToTrustedIcon = document.createElement('img');
showOfferToTrustedIcon.src = '/img/user-lasecagold.svg';
const showOfferToTrustedText = document.createElement('p');
showOfferToTrustedText.innerText = 'Confiados';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
showOfferToTrustedContainer.append(
showOfferToTrustedIcon,
showOfferToTrustedText,
checkIcon
);
} else {
const showOfferToTrustedIcon = document.createElement('img');
showOfferToTrustedIcon.src = '/img/user-gray.svg';
const showOfferToTrustedText = document.createElement('p');
showOfferToTrustedText.innerText = 'Confiados';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
showOfferToTrustedContainer.append(
showOfferToTrustedIcon,
showOfferToTrustedText,
checkIcon
);
}
const showOfferToTrustedTrustedContainer = document.createElement('div');
showOfferToTrustedTrustedContainer.classList.add(
'right-icon-checkboxed-field'
);
if (this.show_offer_to_trusted_trusted) {
const showOfferToTrustedTrustedIcon = document.createElement('img');
showOfferToTrustedTrustedIcon.src = '/img/user-group-lasecagold.svg';
const showOfferToTrustedTrustedText = document.createElement('p');
showOfferToTrustedTrustedText.innerText = 'Sus confiados';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
showOfferToTrustedTrustedContainer.append(
showOfferToTrustedTrustedIcon,
showOfferToTrustedTrustedText,
checkIcon
);
} else {
const showOfferToTrustedTrustedIcon = document.createElement('img');
showOfferToTrustedTrustedIcon.src = '/img/user-group-gray.svg';
const showOfferToTrustedTrustedText = document.createElement('p');
showOfferToTrustedTrustedText.innerText = 'Sus confiados';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
showOfferToTrustedTrustedContainer.append(
showOfferToTrustedTrustedIcon,
showOfferToTrustedTrustedText,
checkIcon
);
}
const showOfferToAllMembersContainer = document.createElement('div');
showOfferToAllMembersContainer.classList.add('right-icon-checkboxed-field');
if (this.show_offer_to_all_members) {
const showOfferToAllMembersIcon = document.createElement('img');
showOfferToAllMembersIcon.src = '/img/many-users-lasecagold.svg';
const showOfferToAllMembersText = document.createElement('p');
showOfferToAllMembersText.innerText = 'Todos';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
showOfferToAllMembersContainer.append(
showOfferToAllMembersIcon,
showOfferToAllMembersText,
checkIcon
);
} else {
const showOfferToAllMembersIcon = document.createElement('img');
showOfferToAllMembersIcon.src = '/img/many-users-gray.svg';
const showOfferToAllMembersText = document.createElement('p');
showOfferToAllMembersText.innerText = 'Todos';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
showOfferToAllMembersContainer.append(
showOfferToAllMembersIcon,
showOfferToAllMembersText,
checkIcon
);
}
visibilityDiv.append(
showOfferToTrustedContainer,
showOfferToTrustedTrustedContainer,
showOfferToAllMembersContainer
);
const otherOfferFeaturesDiv = document.createElement('div');
otherOfferFeaturesDiv.classList.add('other-desc');
const otherOfferFeaturesTitle = document.createElement('p');
otherOfferFeaturesTitle.classList.add('offer-card-content-title');
otherOfferFeaturesTitle.innerText = 'Otros';
otherOfferFeaturesDiv.append(otherOfferFeaturesTitle);
const areBigNotesAcceptedContainer = document.createElement('div');
areBigNotesAcceptedContainer.classList.add('left-icon-checkboxed-field');
if (this.are_big_notes_accepted) {
const areBigNotesAcceptedIcon = document.createElement('img');
areBigNotesAcceptedIcon.src = '/img/eur-bill-lasecagold.svg';
const areBigNotesAcceptedText = document.createElement('p');
areBigNotesAcceptedText.innerText = 'Billetes grandes';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
areBigNotesAcceptedContainer.append(
areBigNotesAcceptedIcon,
areBigNotesAcceptedText,
checkIcon
);
} else {
const areBigNotesAcceptedIcon = document.createElement('img');
areBigNotesAcceptedIcon.src = '/img/eur-bill-gray.svg';
const areBigNotesAcceptedText = document.createElement('p');
areBigNotesAcceptedText.innerText = 'Billetes grandes';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
areBigNotesAcceptedContainer.append(
areBigNotesAcceptedIcon,
areBigNotesAcceptedText,
checkIcon
);
}
otherOfferFeaturesDiv.append(areBigNotesAcceptedContainer);
const actionButtonsArea = document.createElement('p');
actionButtonsArea.classList.add('offer-action-buttons-area');
const editActionArea = document.createElement('div');
editActionArea.classList.add('offer-action-area');
editActionArea.classList.add('subtle-box');
const editActionIcon = document.createElement('img');
editActionIcon.src = '/img/edit.svg';
const editActionText = document.createElement('p');
editActionText.innerText = 'Editar';
editActionArea.append(editActionIcon, editActionText);
const deleteActionArea = document.createElement('div');
deleteActionArea.classList.add('offer-action-area');
deleteActionArea.classList.add('subtle-box');
const deleteActionIcon = document.createElement('img');
deleteActionIcon.src = '/img/trash-can-darkred.svg';
const deleteActionText = document.createElement('p');
deleteActionText.innerText = 'Eliminar';
deleteActionArea.append(deleteActionIcon, deleteActionText);
deleteActionArea.addEventListener('click', async () => {
await this.deleteButtonCallback();
});
actionButtonsArea.append(editActionArea, deleteActionArea);
offerCard.append(
tradeDescDiv,
premiumDescDiv,
whereDescDiv,
whenDescDiv,
bitcoinMethodsDiv,
visibilityDiv,
otherOfferFeaturesDiv,
actionButtonsArea
);
return offerCard;
}
}
module.exports = OfferCard;

View file

@ -1,246 +1,9 @@
const PublishOfferButton = require('../components/PublishOfferButton');
const BuyOrSellButtonGroup = require('../components/BuyOrSellButtonGroup');
const PremiumSelector = require('../components/PremiumSelector');
const PriceDisplay = require('../components/PriceDisplay');
const AmountInput = require('../components/AmountInput');
const PlaceInput = require('../components/PlaceInput');
const TimeInput = require('../components/TimeInput');
const BitcoinMethodCheckboxes = require('../components/BitcoinMethodCheckboxes');
const TrustCheckboxes = require('../components/TrustCheckboxes');
const BigNotesCheckbox = require('../components/BigNotesCheckbox');
const PopupNotification = require('../components/PopupNotification'); const PopupNotification = require('../components/PopupNotification');
const CreateOfferModal = require('../components/CreateOfferModal');
const OfferCard = require('../components/OfferCard');
const offerService = require('../services/offerService'); const offerService = require('../services/offerService');
class CreateOfferModal {
// Stop relying on IDs
constructor({ parentElement, onCreationCallback, offerService }) {
this.element = null;
this.parentElement = parentElement;
this.onCreationCallback = onCreationCallback;
this.offerService = offerService;
this.publishOfferButton = null;
this.buyOrSellButtonGroup = null;
this.premiumSelector = null;
this.amountInput = null;
this.placeInput = null;
this.timeInput = null;
this.btcMethodCheckboxes = null;
this.trustCheckboxes = null;
this.bigNotesCheckbox = null;
}
render() {
const modalRoot = document.createElement('div');
this.element = modalRoot;
modalRoot.id = 'create-offer-modal-root';
modalRoot.className = 'full-screen-modal-background';
const modal = document.createElement('div');
modal.className = 'full-screen-modal';
modal.id = 'create-offer-root';
const controls = document.createElement('div');
controls.id = 'create-offer-controls';
const title = document.createElement('h2');
title.textContent = 'Añade los detalles de tu oferta';
controls.appendChild(title);
const buyOrSellDiv = document.createElement('div');
buyOrSellDiv.className = 'create-offer-step';
this.buyOrSellButtonGroup = new BuyOrSellButtonGroup({
parentElement: buyOrSellDiv,
});
this.buyOrSellButtonGroup.render();
controls.appendChild(buyOrSellDiv);
const premiumDiv = document.createElement('div');
premiumDiv.classList = 'premium-area';
premiumDiv.className = 'create-offer-step';
const premiumHeading = document.createElement('h3');
premiumHeading.textContent = 'Premium';
premiumDiv.appendChild(premiumHeading);
const premiumContentDiv = document.createElement('div');
premiumContentDiv.classList = 'premium-content-area';
premiumDiv.appendChild(premiumContentDiv);
controls.appendChild(premiumDiv);
const createOfferEventBus = new EventTarget();
const mockPriceProvidingCallback = () => {
return Math.floor(Math.random() * (95000 - 70000 + 1) + 70000);
};
this.premiumSelector = new PremiumSelector({
parentElement: premiumContentDiv,
eventSink: createOfferEventBus,
});
this.premiumSelector.render();
const priceDisplay = new PriceDisplay({
parentElement: premiumContentDiv,
id: 'premium-price-display-area',
premiumProvidingCallback: () => {
return this.premiumSelector.getPremium();
},
priceProvidingCallback: mockPriceProvidingCallback,
});
priceDisplay.render();
createOfferEventBus.addEventListener('premium-changed', () => {
priceDisplay.updatePrices();
});
const amountDiv = document.createElement('div');
amountDiv.id = 'amount-area';
amountDiv.className = 'create-offer-step';
const amountHeading = document.createElement('h3');
amountHeading.textContent = '¿Cuánto?';
amountDiv.appendChild(amountHeading);
controls.appendChild(amountDiv);
this.amountInput = new AmountInput({
parentElement: amountDiv,
});
this.amountInput.render();
const placeTimeDiv = document.createElement('div');
placeTimeDiv.className = 'create-offer-step';
const placeTimeHeading = document.createElement('h3');
placeTimeHeading.textContent = '¿Dónde y cuándo?';
placeTimeDiv.appendChild(placeTimeHeading);
const placeTimeContentDiv = document.createElement('div');
placeTimeDiv.appendChild(placeTimeContentDiv);
controls.appendChild(placeTimeDiv);
this.placeInput = new PlaceInput({
parentElement: placeTimeContentDiv,
});
this.placeInput.render();
this.timeInput = new TimeInput({
parentElement: placeTimeContentDiv,
});
this.timeInput.render();
const bitcoinMethodsDiv = document.createElement('div');
bitcoinMethodsDiv.className = 'create-offer-step';
const bitcoinMethodsHeading = document.createElement('h3');
bitcoinMethodsHeading.textContent = '¿Cómo se mueve el Bitcoin?';
bitcoinMethodsDiv.appendChild(bitcoinMethodsHeading);
const bitcoinMethodsContentDiv = document.createElement('div');
bitcoinMethodsDiv.appendChild(bitcoinMethodsContentDiv);
controls.appendChild(bitcoinMethodsDiv);
this.btcMethodCheckboxes = new BitcoinMethodCheckboxes({
parentElement: bitcoinMethodsContentDiv,
});
this.btcMethodCheckboxes.render();
const trustDiv = document.createElement('div');
trustDiv.className = 'create-offer-step';
const trustHeading = document.createElement('h3');
trustHeading.textContent = '¿Quién puede ver la oferta?';
trustDiv.appendChild(trustHeading);
const trustContentDiv = document.createElement('div');
trustDiv.appendChild(trustContentDiv);
controls.appendChild(trustDiv);
this.trustCheckboxes = new TrustCheckboxes({
parentElement: trustContentDiv,
});
this.trustCheckboxes.render();
const otherDiv = document.createElement('div');
otherDiv.id = 'other-area';
otherDiv.className = 'create-offer-step';
const otherHeading = document.createElement('h3');
otherHeading.textContent = 'Extras';
otherDiv.appendChild(otherHeading);
controls.appendChild(otherDiv);
this.bigNotesCheckbox = new BigNotesCheckbox({
parentElement: otherDiv,
});
this.bigNotesCheckbox.render();
//Continue moving components up here
const submitButtonArea = document.createElement('div');
submitButtonArea.classList.add('submit-button-area');
this.publishOfferButton = new PublishOfferButton({
parentElement: submitButtonArea,
id: 'button-submit-offer',
onClickCallback: async () => {
await this.createOffer();
await this.onCreationCallback();
},
});
this.publishOfferButton.render();
const closeButtonArea = document.createElement('div');
closeButtonArea.id = 'close-offer-controls-area';
const closeButton = document.createElement('button');
closeButton.id = 'close-offer';
closeButton.className = 'button-secondary button-medium';
closeButton.textContent = 'Volver';
closeButtonArea.appendChild(closeButton);
controls.appendChild(submitButtonArea);
controls.appendChild(closeButtonArea);
modal.appendChild(controls);
modalRoot.appendChild(modal);
this.parentElement.appendChild(this.element);
}
toggle() {
this.element.classList.toggle('shown');
}
async createOffer() {
const wants = this.buyOrSellButtonGroup.wants();
const premium = this.premiumSelector.getPremium();
const trade_amount_eur = this.amountInput.intEurAmount;
const location_details = this.placeInput.inputText;
const time_availability_details = this.timeInput.inputText;
const is_onchain_accepted = this.btcMethodCheckboxes.isOnchainAccepted;
const is_lightning_accepted = this.btcMethodCheckboxes.isLightningAccepted;
const show_offer_to_trusted = this.trustCheckboxes.showOfferToTrusted;
const show_offer_to_trusted_trusted =
this.trustCheckboxes.showOfferToTrustedTrusted;
const show_offer_to_all_members =
this.trustCheckboxes.showOfferToAllMembers;
const are_big_notes_accepted = this.bigNotesCheckbox.areBigNotesAccepted;
const offerDetails = {
wants,
premium,
trade_amount_eur,
location_details,
time_availability_details,
is_onchain_accepted,
is_lightning_accepted,
show_offer_to_trusted,
show_offer_to_trusted_trusted,
show_offer_to_all_members,
are_big_notes_accepted,
};
await this.offerService.createOffer(offerDetails);
this.toggle();
}
}
function offersPage() { function offersPage() {
const offerCreatedPopup = new PopupNotification({ const offerCreatedPopup = new PopupNotification({
parentElement: document.body, parentElement: document.body,
@ -280,398 +43,14 @@ function offersPage() {
'button-start-create-offer' 'button-start-create-offer'
); );
const buttonViewMyOffers = document.getElementById('button-view-my-offers'); const buttonViewMyOffers = document.getElementById('button-view-my-offers');
const closeOffer = document.getElementById('close-offer');
const createOfferModalRoot = document.getElementById(
'create-offer-modal-root'
);
const viewMyOffersRoot = document.getElementById('view-my-offers-root'); const viewMyOffersRoot = document.getElementById('view-my-offers-root');
const ownOffersContainer = document.getElementById('own-offers-container'); const ownOffersContainer = document.getElementById('own-offers-container');
function toggleCreateOfferModal() {
createOfferModalRoot.classList.toggle('shown');
}
function toggleViewMyOffersPanel() { function toggleViewMyOffersPanel() {
viewMyOffersRoot.style.display = viewMyOffersRoot.style.display =
viewMyOffersRoot.style.display === 'block' ? 'none' : 'block'; viewMyOffersRoot.style.display === 'block' ? 'none' : 'block';
} }
class Offer {
constructor(offerData) {
this.uuid = offerData.uuid;
this.public_key = offerData.public_key;
this.wants = offerData.wants;
this.premium = offerData.premium;
this.trade_amount_eur = offerData.trade_amount_eur;
this.location_details = offerData.location_details;
this.time_availability_details = offerData.time_availability_details;
this.show_offer_to_trusted = offerData.show_offer_to_trusted;
this.show_offer_to_trusted_trusted =
offerData.show_offer_to_trusted_trusted;
this.show_offer_to_all_members = offerData.show_offer_to_all_members;
this.is_onchain_accepted = offerData.is_onchain_accepted;
this.is_lightning_accepted = offerData.is_lightning_accepted;
this.are_big_notes_accepted = offerData.are_big_notes_accepted;
this.created_at = offerData.created_at;
this.last_updated_at = offerData.last_updated_at;
}
buildHTML() {
const offerCard = document.createElement('div');
offerCard.classList.add('myoffer-card');
offerCard.classList.add('shadowed-round-area');
const tradeDescDiv = document.createElement('div');
tradeDescDiv.classList.add('trade-desc');
const youBuyText = document.createElement('p');
youBuyText.classList.add('offer-card-content-title');
youBuyText.innerText = 'Compras';
tradeDescDiv.append(youBuyText);
const youBuyData = document.createElement('p');
youBuyData.classList.add('offer-card-content-data');
if (this.wants === 'BTC') {
youBuyData.innerText = `${this.trade_amount_eur * 1021} sats`;
}
if (this.wants === 'EUR') {
youBuyData.innerText = `${this.trade_amount_eur}`;
}
tradeDescDiv.append(youBuyData);
const youSellText = document.createElement('p');
youSellText.id = 'you-sell-title';
youSellText.classList.add('offer-card-content-title');
youSellText.innerText = 'Vendes';
tradeDescDiv.append(youSellText);
const youSellData = document.createElement('p');
youSellData.classList.add('offer-card-content-data');
if (this.wants === 'BTC') {
youSellData.innerText = `${this.trade_amount_eur}`;
}
if (this.wants === 'EUR') {
youSellData.innerText = `${this.trade_amount_eur * 1021} sats`;
}
tradeDescDiv.append(youSellData);
const premiumDescDiv = document.createElement('div');
premiumDescDiv.classList.add('premium-desc');
const premiumTitle = document.createElement('p');
premiumTitle.classList.add('offer-card-content-title');
premiumTitle.innerText = 'Premium';
premiumDescDiv.append(premiumTitle);
const premiumData = document.createElement('p');
premiumData.classList.add('offer-card-content-data');
premiumData.innerText = `${this.premium * 100} %`;
premiumDescDiv.append(premiumData);
const offerPriceTitle = document.createElement('p');
offerPriceTitle.classList.add('offer-card-content-title');
offerPriceTitle.innerText = 'Precio oferta';
premiumDescDiv.append(offerPriceTitle);
const offerPriceData = document.createElement('p');
offerPriceData.classList.add('offer-card-content-data');
offerPriceData.innerText = `90000 €/BTC`;
premiumDescDiv.append(offerPriceData);
const marketPriceTitle = document.createElement('p');
marketPriceTitle.classList.add('offer-card-content-title');
marketPriceTitle.innerText = 'Precio mercado';
premiumDescDiv.append(marketPriceTitle);
const marketPriceData = document.createElement('p');
marketPriceData.innerText = `88000 €/BTC`;
premiumDescDiv.append(marketPriceData);
const whereDescDiv = document.createElement('div');
whereDescDiv.classList.add('where-desc');
const whereDescTitle = document.createElement('p');
whereDescTitle.classList.add('offer-card-content-title');
whereDescTitle.innerText = 'Dónde';
whereDescDiv.append(whereDescTitle);
const whereDescData = document.createElement('p');
whereDescData.classList.add('offer-long-text');
whereDescData.innerText = `${this.location_details}`;
whereDescDiv.append(whereDescData);
const whenDescDiv = document.createElement('div');
whenDescDiv.classList.add('when-desc');
const whenDescTitle = document.createElement('p');
whenDescTitle.classList.add('offer-card-content-title');
whenDescTitle.innerText = 'Cúando';
whenDescDiv.append(whenDescTitle);
const whenDescData = document.createElement('p');
whenDescData.classList.add('offer-long-text');
whenDescData.innerText = `${this.time_availability_details}`;
whenDescDiv.append(whenDescData);
const bitcoinMethodsDiv = document.createElement('div');
bitcoinMethodsDiv.classList.add('bitcoin-methods-desc');
const bitcoinMethodsTitle = document.createElement('p');
bitcoinMethodsTitle.classList.add('offer-card-content-title');
bitcoinMethodsTitle.innerText = 'Protocolos Bitcoin aceptados';
bitcoinMethodsDiv.append(bitcoinMethodsTitle);
const onchainAcceptedContainer = document.createElement('div');
onchainAcceptedContainer.classList.add('left-icon-checkboxed-field');
if (this.is_onchain_accepted) {
const onchainIcon = document.createElement('img');
onchainIcon.src = '/img/chains-lasecagold.svg';
const onchainText = document.createElement('p');
onchainText.innerText = 'Onchain';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
onchainAcceptedContainer.append(onchainIcon, onchainText, checkIcon);
} else {
const onchainIcon = document.createElement('img');
onchainIcon.src = '/img/chains-gray.svg';
const onchainText = document.createElement('p');
onchainText.innerText = 'Onchain';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
onchainAcceptedContainer.append(onchainIcon, onchainText, checkIcon);
}
const lightningAcceptedContainer = document.createElement('div');
lightningAcceptedContainer.classList.add('left-icon-checkboxed-field');
if (this.is_lightning_accepted) {
const lightningIcon = document.createElement('img');
lightningIcon.src = '/img/bolt-lightning-lasecagold.svg';
const lightningText = document.createElement('p');
lightningText.innerText = 'Lightning';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
lightningAcceptedContainer.append(
lightningIcon,
lightningText,
checkIcon
);
} else {
const lightningIcon = document.createElement('img');
lightningIcon.src = '/img/bolt-lightning-gray.svg';
const lightningText = document.createElement('p');
lightningText.innerText = 'Lightning';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
lightningAcceptedContainer.append(
lightningIcon,
lightningText,
checkIcon
);
}
bitcoinMethodsDiv.append(
onchainAcceptedContainer,
lightningAcceptedContainer
);
const visibilityDiv = document.createElement('div');
visibilityDiv.classList.add('visibility-desc');
const visibilityTitle = document.createElement('p');
visibilityTitle.classList.add('offer-card-content-title');
visibilityTitle.innerText = 'Visibilidad';
visibilityDiv.append(visibilityTitle);
const showOfferToTrustedContainer = document.createElement('div');
showOfferToTrustedContainer.classList.add('right-icon-checkboxed-field');
if (this.show_offer_to_trusted) {
const showOfferToTrustedIcon = document.createElement('img');
showOfferToTrustedIcon.src = '/img/user-lasecagold.svg';
const showOfferToTrustedText = document.createElement('p');
showOfferToTrustedText.innerText = 'Confiados';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
showOfferToTrustedContainer.append(
showOfferToTrustedIcon,
showOfferToTrustedText,
checkIcon
);
} else {
const showOfferToTrustedIcon = document.createElement('img');
showOfferToTrustedIcon.src = '/img/user-gray.svg';
const showOfferToTrustedText = document.createElement('p');
showOfferToTrustedText.innerText = 'Confiados';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
showOfferToTrustedContainer.append(
showOfferToTrustedIcon,
showOfferToTrustedText,
checkIcon
);
}
const showOfferToTrustedTrustedContainer = document.createElement('div');
showOfferToTrustedTrustedContainer.classList.add(
'right-icon-checkboxed-field'
);
if (this.show_offer_to_trusted_trusted) {
const showOfferToTrustedTrustedIcon = document.createElement('img');
showOfferToTrustedTrustedIcon.src = '/img/user-group-lasecagold.svg';
const showOfferToTrustedTrustedText = document.createElement('p');
showOfferToTrustedTrustedText.innerText = 'Sus confiados';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
showOfferToTrustedTrustedContainer.append(
showOfferToTrustedTrustedIcon,
showOfferToTrustedTrustedText,
checkIcon
);
} else {
const showOfferToTrustedTrustedIcon = document.createElement('img');
showOfferToTrustedTrustedIcon.src = '/img/user-group-gray.svg';
const showOfferToTrustedTrustedText = document.createElement('p');
showOfferToTrustedTrustedText.innerText = 'Sus confiados';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
showOfferToTrustedTrustedContainer.append(
showOfferToTrustedTrustedIcon,
showOfferToTrustedTrustedText,
checkIcon
);
}
const showOfferToAllMembersContainer = document.createElement('div');
showOfferToAllMembersContainer.classList.add(
'right-icon-checkboxed-field'
);
if (this.show_offer_to_all_members) {
const showOfferToAllMembersIcon = document.createElement('img');
showOfferToAllMembersIcon.src = '/img/many-users-lasecagold.svg';
const showOfferToAllMembersText = document.createElement('p');
showOfferToAllMembersText.innerText = 'Todos';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
showOfferToAllMembersContainer.append(
showOfferToAllMembersIcon,
showOfferToAllMembersText,
checkIcon
);
} else {
const showOfferToAllMembersIcon = document.createElement('img');
showOfferToAllMembersIcon.src = '/img/many-users-gray.svg';
const showOfferToAllMembersText = document.createElement('p');
showOfferToAllMembersText.innerText = 'Todos';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
showOfferToAllMembersContainer.append(
showOfferToAllMembersIcon,
showOfferToAllMembersText,
checkIcon
);
}
visibilityDiv.append(
showOfferToTrustedContainer,
showOfferToTrustedTrustedContainer,
showOfferToAllMembersContainer
);
const otherOfferFeaturesDiv = document.createElement('div');
otherOfferFeaturesDiv.classList.add('other-desc');
const otherOfferFeaturesTitle = document.createElement('p');
otherOfferFeaturesTitle.classList.add('offer-card-content-title');
otherOfferFeaturesTitle.innerText = 'Otros';
otherOfferFeaturesDiv.append(otherOfferFeaturesTitle);
const areBigNotesAcceptedContainer = document.createElement('div');
areBigNotesAcceptedContainer.classList.add('left-icon-checkboxed-field');
if (this.are_big_notes_accepted) {
const areBigNotesAcceptedIcon = document.createElement('img');
areBigNotesAcceptedIcon.src = '/img/eur-bill-lasecagold.svg';
const areBigNotesAcceptedText = document.createElement('p');
areBigNotesAcceptedText.innerText = 'Billetes grandes';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-check-green.svg';
areBigNotesAcceptedContainer.append(
areBigNotesAcceptedIcon,
areBigNotesAcceptedText,
checkIcon
);
} else {
const areBigNotesAcceptedIcon = document.createElement('img');
areBigNotesAcceptedIcon.src = '/img/eur-bill-gray.svg';
const areBigNotesAcceptedText = document.createElement('p');
areBigNotesAcceptedText.innerText = 'Billetes grandes';
const checkIcon = document.createElement('img');
checkIcon.src = '/img/circle-xmark-gray.svg';
areBigNotesAcceptedContainer.append(
areBigNotesAcceptedIcon,
areBigNotesAcceptedText,
checkIcon
);
}
otherOfferFeaturesDiv.append(areBigNotesAcceptedContainer);
const actionButtonsArea = document.createElement('p');
actionButtonsArea.classList.add('offer-action-buttons-area');
const editActionArea = document.createElement('div');
editActionArea.classList.add('offer-action-area');
editActionArea.classList.add('subtle-box');
const editActionIcon = document.createElement('img');
editActionIcon.src = '/img/edit.svg';
const editActionText = document.createElement('p');
editActionText.innerText = 'Editar';
editActionArea.append(editActionIcon, editActionText);
const deleteActionArea = document.createElement('div');
deleteActionArea.classList.add('offer-action-area');
deleteActionArea.classList.add('subtle-box');
const deleteActionIcon = document.createElement('img');
deleteActionIcon.src = '/img/trash-can-darkred.svg';
const deleteActionText = document.createElement('p');
deleteActionText.innerText = 'Eliminar';
deleteActionArea.append(deleteActionIcon, deleteActionText);
deleteActionArea.addEventListener('click', async () => {
await deleteOfferByUuid(this.uuid);
await myOffers.getOffersFromApi();
await myOffers.render();
offerDeletedPopup.displayTemporarily(3000);
});
actionButtonsArea.append(editActionArea, deleteActionArea);
offerCard.append(
tradeDescDiv,
premiumDescDiv,
whereDescDiv,
whenDescDiv,
bitcoinMethodsDiv,
visibilityDiv,
otherOfferFeaturesDiv,
actionButtonsArea
);
return offerCard;
}
}
class MyOffers { class MyOffers {
constructor(ownOffersContainerElement) { constructor(ownOffersContainerElement) {
@ -687,7 +66,17 @@ function offersPage() {
const offersData = (await offersResponse.json()).data; const offersData = (await offersResponse.json()).data;
if (offersResponse.ok) { if (offersResponse.ok) {
for (const record of offersData) { for (const record of offersData) {
this.offers.push(new Offer(record)); this.offers.push(
new OfferCard({
offerData: record,
deleteButtonCallback: async () => {
await offerService.deleteOffer(record.uuid);
await this.getOffersFromApi();
await this.render();
offerDeletedPopup.displayTemporarily(3000);
},
})
);
} }
} }
} }
@ -706,17 +95,8 @@ function offersPage() {
} }
} }
async function deleteOfferByUuid(offerUuid) {
await fetch(`/api/offer/${offerUuid}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
}
buttonStartCreateOffer.addEventListener('click', () => { buttonStartCreateOffer.addEventListener('click', () => {
toggleCreateOfferModal(); createOfferModal.toggle();
}); });
buttonViewMyOffers.addEventListener('click', async () => { buttonViewMyOffers.addEventListener('click', async () => {
@ -725,10 +105,6 @@ function offersPage() {
toggleViewMyOffersPanel(); toggleViewMyOffersPanel();
}); });
closeOffer.addEventListener('click', () => {
toggleCreateOfferModal();
});
const myOffers = new MyOffers(ownOffersContainer); const myOffers = new MyOffers(ownOffersContainer);
} }

View file

@ -4,14 +4,9 @@ const requestAndRespondLoginChallenge = async ({
onRejectedPubKeyCallback, onRejectedPubKeyCallback,
onRejectedSignatureCallback, onRejectedSignatureCallback,
}) => { }) => {
onRejectedPubKeyCallback = () => {
document.querySelector('#rejected-nostr-nudges').style.display = 'block';
};
onRejectedSignatureCallback = onRejectedPubKeyCallback;
let challengeResponse; let challengeResponse;
try { try {
challengeResponse = await fetch('/api/login/nostr-challenge', { challengeResponse = await fetch(constants.API_PATHS.loginNostrChallenge, {
method: 'GET', method: 'GET',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -49,7 +44,7 @@ const requestAndRespondLoginChallenge = async ({
let verifyResponse; let verifyResponse;
try { try {
verifyResponse = await fetch('/api/login/nostr-verify', { verifyResponse = await fetch(constants.API_PATHS.loginNostrVerify, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(signedEvent), body: JSON.stringify(signedEvent),

View file

@ -10,6 +10,16 @@ const createOffer = async (offerDetails) => {
}); });
}; };
const deleteOffer = async (offerUuid) => {
await fetch(`${constants.API_PATHS.offer}/${offerUuid}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
};
module.exports = { module.exports = {
createOffer, createOffer,
deleteOffer,
}; };