mergeable-ish client UI

This commit is contained in:
Tiago Vasconcelos 2023-03-03 10:24:42 +00:00
parent 82ef0947cd
commit 9c29653ea0
6 changed files with 283 additions and 64 deletions

View file

@ -70,13 +70,21 @@
<q-card-actions>
<span>Stall: {{ item.stallName }}</span>
<div class="q-ml-auto">
<q-btn flat dense>See product</q-btn>
<q-btn
flat
class="text-weight-bold text-capitalize q-ml-auto"
dense
color="primary"
@click="$emit('change-page', 'stall', {stall: item.stall, pubkey: item.pubkey})"
@click="$emit('change-page', 'stall', {stall: item.stall, product: item.id})"
>
View details
</q-btn>
<q-btn
flat
class="text-weight-bold text-capitalize q-ml-auto"
dense
color="primary"
@click="$emit('change-page', 'stall', {stall: item.stall})"
>
Visit Stall
</q-btn>

View file

@ -12,6 +12,7 @@
<q-toolbar-title></q-toolbar-title>
<q-btn dense round flat icon="shopping_cart" class="q-ml-md"></q-btn>
</q-toolbar>
<product-detail v-if="productDetail" :product="mock"></product-detail>
<div class="row q-col-gutter-md">
<div
class="col-xs-12 col-sm-6 col-md-4 col-lg-3"

View file

@ -1,12 +1,29 @@
async function customerStall(path) {
const template = await loadTemplateAsync(path)
const mock = {
stall: '4M8j9KKGzUckHgb4C3pKCv',
name: 'product 1',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Leo integer malesuada nunc vel risus commodo. Sapien faucibus et molestie ac feugiat sed lectus vestibulum mattis. Cras ornare arcu dui vivamus. Risus pretium quam vulputate dignissim suspendisse in est ante in. Faucibus in ornare quam viverra orci sagittis eu volutpat odio.',
amount: 100,
price: '10',
images: ['https://i.imgur.com/cEfpEjq.jpeg'],
id: ['RyMbE9Hdwk9X333JKtkkNS'],
categories: ['crafts', 'robots'],
currency: 'EUR',
stallName: 'stall 1',
formatedPrice: '€10.00',
priceInSats: 0
}
Vue.component('customer-stall', {
name: 'customer-stall',
template,
props: ['stall', 'products', 'exchange-rates'],
props: ['stall', 'products', 'exchange-rates', 'product-detail'],
data: function () {
return {}
return {
mock: mock
}
},
methods: {},
created() {

View file

@ -1 +1,187 @@
<div></div>
<div class="row q-mt-sm">
<div class="col-lg-5 col-md-5 col-sm-12 col-xs-12">
<div class="q-pa-md">
<q-carousel
swipeable
animated
v-model="slide"
thumbnails
infinite
v-for="(img, i) in product.images"
:key="i"
>
<q-carousel-slide
:name="i + 1"
:img-src="img"
style="/*background-size: contain; background-repeat: no-repeat*/"
></q-carousel-slide>
</q-carousel>
</div>
</div>
<div class="col-lg-7 col-md-7 col-sm-12 col-xs-12">
<div class="row">
<div
class="col-lg-7 col-md-7 col-sm-12 col-xs-12"
:class="$q.platform.is.desktop ? '' : 'q-px-md'"
>
<div class="text-subtitle1 q-mt-sm q-pt-xs">{{ product.name }}</div>
<div v-if="product.categories" class="text-subtitle1">
<q-chip v-for="(cat, i) in product.categories" :key="i" dense
>{{cat}}</q-chip
>
</div>
<div class="q-mt-sm text-weight-bold">{{ product.description }}</div>
<div>
<span v-if="product.currency == 'sat'">
<span class="text-h6">{{ product.price }} sats</span
><span class="q-ml-sm text-grey-6"
>BTC {{ (product.price / 1e8).toFixed(8) }}</span
>
</span>
<span v-else>
<span class="text-h6">{{ product.formatedPrice }}</span>
<span class="q-ml-sm text-grey-6"
>({{ product.priceInSats }} sats)</span
>
</span>
<span
class="q-ml-md text-caption text-green-8 text-weight-bolder q-mt-md"
>{{ product.amount > 0 ? 'In stock.' : 'Out of stock.' }}</span
>
<!-- <div class="text-caption text-weight-bolder q-mt-sm">
Special Price
</div>
<span class="text-h6">₹3,149</span
><span
class="q-ml-sm text-grey-6"
style="text-decoration: line-through"
>₹3,699</span
>
<span
class="q-ml-md text-caption text-green-8 text-weight-bolder q-mt-md"
>20% off</span
> -->
</div>
<div class="q-mt-md">
<q-btn
class="q-mt-md"
color="primary"
icon="shopping_cart"
label="Add to cart"
/>
<q-btn
class="q-mt-md q-ml-md"
color="primary"
icon="shopping_cart"
label="Buy now"
/>
</div>
</div>
<!-- RATING TO BE DONE -->
<div
class="col-lg-5 col-md-5 col-sm-12 col-xs-12 q-mt-md q-pt-xs q-pl-lg"
>
<div class="text-subtitle2">Customer rating</div>
<div class="text-h3">4.2</div>
<div>
<q-rating
v-model="rating_point"
max="5"
size="2em"
color="orange"
icon="star_border"
icon-selected="star"
icon-half="star_half"
no-dimming
readonly
/>
</div>
<div class="text-subtitle2 text-grey-8">(357 reviews)</div>
<div class="text-subtitle2 text-grey-10 q-mt-sm">
93% would recommend to a friend
</div>
<q-list dense bordered padding class="no-border q-mt-lg q-pr-xl">
<q-item style="padding-left: 0 !important" v-ripple>
<span class="text-subtitle2 q-mr-xs">5</span>
<q-icon name="star" size="1.5em" color="orange"></q-icon>
<q-linear-progress
class="q-ml-sm q-mr-sm"
style="margin-top: 5px"
size="13px"
:value="0.9"
/>
<span
style="margin-top: 2px"
class="text-caption text-weight-bold text-grey-8"
>273</span
>
</q-item>
<q-item style="padding-left: 0 !important" v-ripple>
<span class="text-subtitle2 q-mr-xs">4</span>
<q-icon name="star" size="1.5em" color="orange"></q-icon>
<q-linear-progress
class="q-ml-sm q-mr-sm"
style="margin-top: 5px"
size="13px"
:value="0.6"
/>
<span
style="margin-top: 2px"
class="text-caption text-weight-bold text-grey-8"
>&nbsp;&nbsp;69</span
>
</q-item>
<q-item style="padding-left: 0 !important" v-ripple>
<span class="text-subtitle2 q-mr-xs">3</span>
<q-icon name="star" size="1.5em" color="orange"></q-icon>
<q-linear-progress
class="q-ml-sm q-mr-sm"
style="margin-top: 5px"
size="13px"
:value="0.1"
/>
<span
style="margin-top: 2px"
class="text-caption text-weight-bold text-grey-8"
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6</span
>
</q-item>
<q-item style="padding-left: 0 !important" v-ripple>
<span class="text-subtitle2 q-mr-xs">2</span>
<q-icon name="star" size="1.5em" color="orange"></q-icon>
<q-linear-progress
class="q-ml-sm q-mr-sm"
style="margin-top: 5px"
size="13px"
:value="0.1"
/>
<span
style="margin-top: 2px"
class="text-caption text-weight-bold text-grey-8"
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3</span
>
</q-item>
<q-item style="padding-left: 0 !important" v-ripple>
<span class="text-subtitle2 q-mr-xs">1</span>
<q-icon name="star" size="1.5em" color="orange"></q-icon>
<q-linear-progress
class="q-ml-sm q-mr-sm"
style="margin-top: 5px"
size="13px"
:value="0.1"
/>
<span
style="margin-top: 2px"
class="text-caption text-weight-bold text-grey-8"
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6</span
>
</q-item>
</q-list>
</div>
</div>
</div>
<div class="col-12 q-mb-lg">
<q-separator></q-separator>
</div>
</div>

View file

@ -6,8 +6,21 @@ async function productDetail(path) {
props: ['product'],
data: function () {
return {}
return {
slide: 1
}
},
methods: {}
computed: {
win_width() {
return this.$q.screen.width - 59
},
win_height() {
return this.$q.screen.height - 0
}
},
methods: {},
created() {
console.log('ping')
}
})
}

View file

@ -137,35 +137,6 @@
</template>
</q-input>
</q-toolbar>
<!-- <q-toolbar>
<q-breadcrumbs class="cursor">
<q-breadcrumbs-el
label="Market"
icon="home"
@click="navigateTo('market')"
></q-breadcrumbs-el>
<q-breadcrumbs-el
v-if="activeStall"
@click="activeProduct && navigateTo('stall', activeStall)"
:label="stallName"
icon="widgets"
></q-breadcrumbs-el>
<q-breadcrumbs-el
v-if="activeProduct"
:label="productName"
icon="navigation"
></q-breadcrumbs-el>
</q-breadcrumbs>
<q-toolbar-title></q-toolbar-title>
<q-btn
v-if="activeStall"
dense
round
flat
icon="shopping_cart"
class="q-ml-md"
></q-btn>
</q-toolbar> -->
</div>
</div>
@ -174,6 +145,7 @@
:stall="stalls.find(stall => stall.id == activeStall)"
:products="filterProducts"
:exchange-rates="exchangeRates"
:product-detail="activeProduct"
@change-page="navigateTo"
></customer-stall>
<customer-market
@ -190,6 +162,7 @@
<script src="{{ url_for('nostrmarket_static', path='js/utils.js') }}"></script>
<script src="{{ url_for('nostrmarket_static', path='components/customer-market/customer-market.js') }}"></script>
<script src="{{ url_for('nostrmarket_static', path='components/customer-stall/customer-stall.js') }}"></script>
<script src="{{ url_for('nostrmarket_static', path='components/product-detail/product-detail.js') }}"></script>
<script>
const nostr = window.NostrTools
const defaultRelays = [
@ -221,7 +194,8 @@
Promise.all([
customerMarket('static/components/customer-market/customer-market.html'),
customerStall('static/components/customer-stall/customer-stall.html')
customerStall('static/components/customer-stall/customer-stall.html'),
productDetail('static/components/product-detail/product-detail.html')
])
new Vue({
@ -270,25 +244,34 @@
}
},
async created() {
// Check for stored merchants and relays on localStorage
try {
let merchants = this.$q.localStorage.getItem(`diagonAlley.merchants`)
let relays = this.$q.localStorage.getItem(`diagonAlley.relays`)
if (merchants && merchants.length) {
this.pubkeys = new Set(merchants)
}
if (relays && relays.length) {
this.relays = new Set([...defaultRelays, ...relays])
}
} catch (e) {
console.error(e)
}
// Hardcode pubkeys for testing
/*
this.pubkeys.add(
'855ea22a88d7df7ccd8497777db81f115575d5362f51df3af02ead383f5eaba2'
'c1415f950a1e3431de2bc5ee35144639e2f514cf158279abff9ed77d50118796'
)
this.pubkeys.add(
'8f69ac99b96f7c4ad58b98cc38fe5d35ce02daefae7d1609c797ce3b4f92f5fd'
)
*/
// stall ids S4hQgtTwiF5kGJZPbqMH9M jkCbdtkXeMjGBY3LBf8yn4
this.$q.loading.show()
this.relays = new Set(defaultRelays)
// Get notes from Nostr
await this.initNostr()
// What component to render on start
let merchant_pubkey = JSON.parse('{{ merchant_pubkey | tojson }}')
let stall_id = JSON.parse('{{ stall_id | tojson }}')
let product_id = JSON.parse('{{ product_id | tojson }}')
if (merchant_pubkey) {
this.pubkeys.add(merchant_pubkey)
await addPubkey(merchant_pubkey)
/*LNbits.utils
.confirmDialog(
`We found a merchant pubkey in your request. Do you want to add it to the merchants list?`
@ -299,14 +282,18 @@
this.pubkeys.add(merchant_pubkey)
})*/
}
this.$q.loading.show()
this.relays = new Set(defaultRelays)
// Get notes from Nostr
await this.initNostr()
// What component to render on start
if (stall_id) {
if (product_id) {
this.activePage = 'product'
this.activeProduct = product_id
} else {
this.activePage = 'stall'
this.activeStall = stall_id
}
this.activePage = 'stall'
this.activeStall = stall_id
}
this.$q.loading.hide()
@ -321,7 +308,7 @@
let sub = await pool
.list(relays, [
{
kinds: [0, 30005], // for production kind is 30017
kinds: [0, 30017, 30018], // for production kind is 30017
authors: Array.from(this.pubkeys)
}
])
@ -332,10 +319,10 @@
if (e.kind == 0) {
this.profiles.set(e.pubkey, e.content)
return
} else if (e.content.stall) {
} else if (e.kind == 30018) {
//it's a product `d` is the prod. id
products.set(e.d, {...e.content, id: e.d, categories: e.t})
} else {
} else if (e.kind == 30017) {
// it's a stall `d` is the stall id
stalls.set(e.d, {...e.content, id: e.d, pubkey: e.pubkey})
return
@ -346,8 +333,7 @@
this.stalls = await Array.from(stalls.values())
this.products = Array.from(products.values()).map(obj => {
obj.currency = 'EUR' // placeholder for testing/dev
let stall = this.stalls.find(s => s.id == obj.stall)
let stall = this.stalls.find(s => s.id == obj.stall_id)
obj.stallName = stall.name
if (obj.currency != 'sat') {
obj.formatedPrice = this.getAmountFormated(obj.price, obj.currency)
@ -383,14 +369,10 @@
if (stall) {
this.activeStall = stall
url.searchParams.set('stall_id', stall)
}
break
case 'product':
if (stall && product) {
this.activeStall = stall
this.activeProduct = product
url.searchParams.set('stall_id', stall)
url.searchParams.set('product_id', product)
if (product) {
this.activeProduct = product
url.searchParams.set('product_id', product)
}
}
break
default:
@ -415,8 +397,10 @@
getAmountFormated(amount, unit = 'USD') {
return LNbits.utils.formatCurrency(amount, unit)
},
async addPubkey() {
let pubkey = String(this.inputPubkey).trim()
async addPubkey(pubkey = null) {
if (!pubkey) {
pubkey = String(this.inputPubkey).trim()
}
let regExp = /^#([0-9a-f]{3}){1,2}$/i
if (pubkey.startsWith('n')) {
try {
@ -435,6 +419,10 @@
pubkey = pubkey
}
this.pubkeys.add(pubkey)
this.$q.localStorage.set(
`diagonAlley.merchants`,
Array.from(this.pubkeys)
)
await this.initNostr()
},
removePubkey(pubkey) {
@ -443,6 +431,10 @@
pubkeys.delete(pubkey)
this.profiles.delete(pubkey)
this.pubkeys = new Set(Array.from(pubkeys))
this.$q.localStorage.set(
`diagonAlley.merchants`,
Array.from(this.pubkeys)
)
},
async addRelay() {
let relay = String(this.inputRelay).trim()
@ -451,6 +443,7 @@
return
}
this.relays.add(relay)
this.$q.localStorage.set(`diagonAlley.relays`, Array.from(this.relays))
this.inputRelay = null
await this.initNostr()
},
@ -459,6 +452,7 @@
let relays = this.relays
relays.delete(relay)
this.relays = new Set(Array.from(relays))
this.$q.localStorage.set(`diagonAlley.relays`, Array.from(this.relays))
}
}
})