merchant chat functional almost out of WIP
This commit is contained in:
parent
ebe8d8b0b9
commit
532e12ec83
7 changed files with 461 additions and 79 deletions
|
|
@ -408,8 +408,10 @@ async def create_diagonalley_market_stalls(
|
||||||
async def update_diagonalley_market(market_id):
|
async def update_diagonalley_market(market_id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
### CHAT / MESSAGES
|
### CHAT / MESSAGES
|
||||||
|
|
||||||
|
|
||||||
async def create_chat_message(data: CreateChatMessage):
|
async def create_chat_message(data: CreateChatMessage):
|
||||||
print("DATA", data)
|
print("DATA", data)
|
||||||
await db.execute(
|
await db.execute(
|
||||||
|
|
@ -441,3 +443,14 @@ async def get_diagonalley_chat_messages(room_name: str):
|
||||||
)
|
)
|
||||||
|
|
||||||
return [ChatMessage(**row) for row in rows]
|
return [ChatMessage(**row) for row in rows]
|
||||||
|
|
||||||
|
|
||||||
|
async def get_diagonalley_chat_by_merchant(ids: List[str]) -> List[ChatMessage]:
|
||||||
|
|
||||||
|
q = ",".join(["?"] * len(ids))
|
||||||
|
rows = await db.fetchall(
|
||||||
|
f"SELECT * FROM diagonalley.messages WHERE id_conversation IN ({q})",
|
||||||
|
(*ids,),
|
||||||
|
)
|
||||||
|
print(ids, q, rows)
|
||||||
|
return [ChatMessage(**row) for row in rows]
|
||||||
|
|
|
||||||
|
|
@ -78,9 +78,9 @@ class Notifier:
|
||||||
|
|
||||||
async def _notify(self, message: str, room_name: str):
|
async def _notify(self, message: str, room_name: str):
|
||||||
"""Notifier"""
|
"""Notifier"""
|
||||||
|
|
||||||
d = json.loads(message)
|
d = json.loads(message)
|
||||||
d["room_name"] = room_name
|
d["room_name"] = room_name
|
||||||
|
print("hey", d)
|
||||||
db_msg = CreateChatMessage.parse_obj(d)
|
db_msg = CreateChatMessage.parse_obj(d)
|
||||||
print("NOT:", db_msg)
|
print("NOT:", db_msg)
|
||||||
await create_chat_message(data=db_msg)
|
await create_chat_message(data=db_msg)
|
||||||
|
|
|
||||||
|
|
@ -345,20 +345,6 @@
|
||||||
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
v-if="stalls.length > 0"
|
|
||||||
color="primary"
|
|
||||||
@click="productDialog.show = true"
|
|
||||||
>+ Product <q-tooltip> List a product </q-tooltip></q-btn
|
|
||||||
>
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
v-else
|
|
||||||
color="primary"
|
|
||||||
@click="errorMessage('First set shipping zone(s), then create a stall.')"
|
|
||||||
>+ Product <q-tooltip> List a product </q-tooltip></q-btn
|
|
||||||
>
|
|
||||||
<q-btn unelevated color="primary" @click="zoneDialog.show = true"
|
<q-btn unelevated color="primary" @click="zoneDialog.show = true"
|
||||||
>+ Shipping Zone<q-tooltip> Create a shipping zone </q-tooltip></q-btn
|
>+ Shipping Zone<q-tooltip> Create a shipping zone </q-tooltip></q-btn
|
||||||
>
|
>
|
||||||
|
|
@ -382,7 +368,26 @@
|
||||||
Create a market stall to list products on
|
Create a market stall to list products on
|
||||||
</q-tooltip></q-btn
|
</q-tooltip></q-btn
|
||||||
>
|
>
|
||||||
<q-btn unelevated color="primary" @click="marketDialog.show = true"
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
v-if="stalls.length > 0"
|
||||||
|
color="primary"
|
||||||
|
@click="productDialog.show = true"
|
||||||
|
>+ Product <q-tooltip> List a product </q-tooltip></q-btn
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
v-else
|
||||||
|
color="primary"
|
||||||
|
@click="errorMessage('First set shipping zone(s), then create a stall.')"
|
||||||
|
>+ Product <q-tooltip> List a product </q-tooltip></q-btn
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
class="float-right"
|
||||||
|
unelevated
|
||||||
|
flat
|
||||||
|
color="primary"
|
||||||
|
@click="marketDialog.show = true"
|
||||||
>Create Market
|
>Create Market
|
||||||
<q-tooltip>
|
<q-tooltip>
|
||||||
Makes a simple frontend shop for your stalls (not NOSTR)</q-tooltip
|
Makes a simple frontend shop for your stalls (not NOSTR)</q-tooltip
|
||||||
|
|
@ -415,6 +420,7 @@
|
||||||
{% raw %}
|
{% raw %}
|
||||||
<template v-slot:header="props">
|
<template v-slot:header="props">
|
||||||
<q-tr :props="props">
|
<q-tr :props="props">
|
||||||
|
<q-th auto-width></q-th>
|
||||||
<q-th auto-width></q-th>
|
<q-th auto-width></q-th>
|
||||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
||||||
{{ col.label }}
|
{{ col.label }}
|
||||||
|
|
@ -434,6 +440,23 @@
|
||||||
:icon="props.expand ? 'remove' : 'add'"
|
:icon="props.expand ? 'remove' : 'add'"
|
||||||
/>
|
/>
|
||||||
</q-td>
|
</q-td>
|
||||||
|
<q-td auto-width>
|
||||||
|
<q-btn
|
||||||
|
size="sm"
|
||||||
|
color="green"
|
||||||
|
dense
|
||||||
|
icon="chat"
|
||||||
|
@click="chatRoom(props.row.invoiceid)"
|
||||||
|
>
|
||||||
|
<q-badge
|
||||||
|
v-if="props.row.unread"
|
||||||
|
color="red"
|
||||||
|
rounded
|
||||||
|
floating
|
||||||
|
style="padding: 6px; border-radius: 6px"
|
||||||
|
/>
|
||||||
|
</q-btn>
|
||||||
|
</q-td>
|
||||||
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
||||||
{{ col.value }}
|
{{ col.value }}
|
||||||
</q-td>
|
</q-td>
|
||||||
|
|
@ -824,22 +847,114 @@
|
||||||
<q-list> {% include "diagonalley/_api_docs.html" %} </q-list>
|
<q-list> {% include "diagonalley/_api_docs.html" %} </q-list>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
<!-- CHAT BOX -->
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<h6 class="text-subtitle1 q-my-none">Messages</h6>
|
<h6 class="text-subtitle1 q-my-none">Messages</h6>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="q-pa-none">
|
<q-card-section class="q-pa-none">
|
||||||
<q-separator></q-separator>
|
<q-separator></q-separator>
|
||||||
|
</q-card-section>
|
||||||
<div class="column q-ma-md q-pb-lg" style="height: 350px">
|
<q-card-section>
|
||||||
<div class="col q-pb-md">
|
<q-select
|
||||||
|
v-model="customerKey"
|
||||||
|
:options="Object.keys(messages)"
|
||||||
|
label="Customers"
|
||||||
|
@input="chatRoom(customerKey)"
|
||||||
|
></q-select>
|
||||||
|
</q-card-section>
|
||||||
|
<div class="chat-container q-pa-md" ref="chatCard">
|
||||||
|
<div class="chat-box">
|
||||||
|
<!-- <p v-if="Object.keys(messages).length === 0">No messages yet</p> -->
|
||||||
|
<div class="chat-messages">
|
||||||
|
<q-chat-message
|
||||||
|
:key="index"
|
||||||
|
v-for="(message, index) in orderMessages"
|
||||||
|
:name="message.pubkey == keys.pubkey ? 'me' : 'customer'"
|
||||||
|
:text="[message.msg]"
|
||||||
|
:sent="message.pubkey == keys.pubkey ? true : false"
|
||||||
|
:bg-color="message.pubkey == keys.pubkey ? 'white' : 'light-green-2'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-form @submit="sendMessage" class="full-width chat-input">
|
||||||
|
<q-input
|
||||||
|
ref="newMessage"
|
||||||
|
v-model="newMessage"
|
||||||
|
placeholder="Message"
|
||||||
|
class="full-width"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
>
|
||||||
|
<template>
|
||||||
|
<q-btn
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
type="submit"
|
||||||
|
icon="send"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
<!-- <q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<h6 class="text-subtitle1 q-my-none">Messages</h6>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<q-separator></q-separator>
|
||||||
|
<div
|
||||||
|
ref="chatCard"
|
||||||
|
class="q-ma-md q-pb-lg"
|
||||||
|
style="height: 350px"
|
||||||
|
>
|
||||||
|
<div class="q-pb-md">
|
||||||
<q-select
|
<q-select
|
||||||
v-model="customerKey"
|
v-model="customerKey"
|
||||||
style="width: 80%"
|
style="width: 80%"
|
||||||
:options="customerKeys"
|
:options="Object.keys(messages)"
|
||||||
label="Customers"
|
label="Customers"
|
||||||
@input="getMessages(customerKey)"
|
@input="chatRoom(customerKey)"
|
||||||
></q-select>
|
></q-select>
|
||||||
|
<div class="chat-container q-pa-md">
|
||||||
|
<div class="chat-box">
|
||||||
|
<p v-if="Object.keys(messages).length === 0">No messages yet</p>
|
||||||
|
<div class="chat-messages">
|
||||||
|
<q-chat-message
|
||||||
|
:key="index"
|
||||||
|
v-for="(message, index) in orderMessages"
|
||||||
|
:name="message.pubkey == keys.pubkey ? 'me' : 'customer'"
|
||||||
|
:text="[message.msg]"
|
||||||
|
:sent="message.pubkey == keys.pubkey ? true : false"
|
||||||
|
:bg-color="message.pubkey == keys.pubkey ? 'white' : 'light-green-2'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-form @submit="sendMessage" class="full-width chat-input">
|
||||||
|
<q-input
|
||||||
|
ref="newMessage"
|
||||||
|
v-model="newMessage"
|
||||||
|
placeholder="Message"
|
||||||
|
class="full-width"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
>
|
||||||
|
<template>
|
||||||
|
<q-btn
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
type="submit"
|
||||||
|
icon="send"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 q-px-md">
|
<div class="col-8 q-px-md">
|
||||||
<div v-for="message in customerMessages">
|
<div v-for="message in customerMessages">
|
||||||
|
|
@ -860,11 +975,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- </div>
|
|
||||||
</div> -->
|
|
||||||
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/pica@6.1.1/dist/pica.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/pica@6.1.1/dist/pica.min.js"></script>
|
||||||
|
|
||||||
|
|
@ -873,6 +987,15 @@
|
||||||
|
|
||||||
const pica = window.pica()
|
const pica = window.pica()
|
||||||
|
|
||||||
|
function imgSizeFit(img, maxWidth = 1024, maxHeight = 768) {
|
||||||
|
let ratio = Math.min(
|
||||||
|
1,
|
||||||
|
maxWidth / img.naturalWidth,
|
||||||
|
maxHeight / img.naturalHeight
|
||||||
|
)
|
||||||
|
return {width: img.naturalWidth * ratio, height: img.naturalHeight * ratio}
|
||||||
|
}
|
||||||
|
|
||||||
const mapStalls = obj => {
|
const mapStalls = obj => {
|
||||||
obj._data = _.clone(obj)
|
obj._data = _.clone(obj)
|
||||||
return obj
|
return obj
|
||||||
|
|
@ -891,6 +1014,7 @@
|
||||||
new Date(obj.time * 1000),
|
new Date(obj.time * 1000),
|
||||||
'YYYY-MM-DD HH:mm'
|
'YYYY-MM-DD HH:mm'
|
||||||
)
|
)
|
||||||
|
// obj.unread = false
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
const mapKeys = obj => {
|
const mapKeys = obj => {
|
||||||
|
|
@ -933,8 +1057,12 @@
|
||||||
customerKeys: [],
|
customerKeys: [],
|
||||||
customerKey: '',
|
customerKey: '',
|
||||||
customerMessages: {},
|
customerMessages: {},
|
||||||
|
messages: {},
|
||||||
|
newMessage: '',
|
||||||
|
orderMessages: {},
|
||||||
shippedModel: false,
|
shippedModel: false,
|
||||||
shippingZoneOptions: [
|
shippingZoneOptions: [
|
||||||
|
'Free (digital)',
|
||||||
'Worldwide',
|
'Worldwide',
|
||||||
'Europe',
|
'Europe',
|
||||||
'Australia',
|
'Australia',
|
||||||
|
|
@ -999,17 +1127,17 @@
|
||||||
ordersTable: {
|
ordersTable: {
|
||||||
columns: [
|
columns: [
|
||||||
/*{
|
/*{
|
||||||
name: 'product',
|
name: 'product',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
label: 'Product',
|
label: 'Product',
|
||||||
field: 'product'
|
field: 'product'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'quantity',
|
name: 'quantity',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
label: 'Quantity',
|
label: 'Quantity',
|
||||||
field: 'quantity'
|
field: 'quantity'
|
||||||
},*/
|
},*/
|
||||||
{
|
{
|
||||||
name: 'id',
|
name: 'id',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
|
@ -1443,9 +1571,10 @@
|
||||||
let image = new Image()
|
let image = new Image()
|
||||||
image.src = blobURL
|
image.src = blobURL
|
||||||
image.onload = async () => {
|
image.onload = async () => {
|
||||||
|
let fit = imgSizeFit(image)
|
||||||
let canvas = document.createElement('canvas')
|
let canvas = document.createElement('canvas')
|
||||||
canvas.setAttribute('width', 760)
|
canvas.setAttribute('width', fit.width)
|
||||||
canvas.setAttribute('height', 490)
|
canvas.setAttribute('height', fit.height)
|
||||||
await pica.resize(image, canvas, {
|
await pica.resize(image, canvas, {
|
||||||
quality: 0,
|
quality: 0,
|
||||||
alpha: true,
|
alpha: true,
|
||||||
|
|
@ -1657,7 +1786,7 @@
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.markets = response.data.map(mapMarkets)
|
this.markets = response.data.map(mapMarkets)
|
||||||
console.log(this.markets)
|
// console.log(this.markets)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
|
@ -1756,10 +1885,10 @@
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
////////////////ORDERS//////////////////
|
////////////////ORDERS//////////////////
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
getOrders: function () {
|
getOrders: async function () {
|
||||||
var self = this
|
var self = this
|
||||||
|
|
||||||
LNbits.api
|
await LNbits.api
|
||||||
.request(
|
.request(
|
||||||
'GET',
|
'GET',
|
||||||
'/diagonalley/api/v1/orders?all_wallets=true',
|
'/diagonalley/api/v1/orders?all_wallets=true',
|
||||||
|
|
@ -1768,7 +1897,6 @@
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
self.orders = response.data.map(mapOrders)
|
self.orders = response.data.map(mapOrders)
|
||||||
console.log(self.orders)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
|
|
@ -1839,21 +1967,190 @@
|
||||||
},
|
},
|
||||||
exportOrdersCSV: function () {
|
exportOrdersCSV: function () {
|
||||||
LNbits.utils.exportCSV(this.ordersTable.columns, this.orders)
|
LNbits.utils.exportCSV(this.ordersTable.columns, this.orders)
|
||||||
|
},
|
||||||
|
/// CHAT
|
||||||
|
async getAllMessages() {
|
||||||
|
await LNbits.api
|
||||||
|
.request(
|
||||||
|
'GET',
|
||||||
|
`/diagonalley/api/v1/chat/messages/merchant?orders=${this.orders
|
||||||
|
.map(o => o.invoiceid)
|
||||||
|
.toString()}`,
|
||||||
|
this.g.user.wallets[0].adminkey
|
||||||
|
)
|
||||||
|
.then(res => {
|
||||||
|
this.messages = _.groupBy(res.data, 'id_conversation')
|
||||||
|
this.checkUnreadMessages()
|
||||||
|
console.log('Get new messages!')
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateLastSeenMsg(id) {
|
||||||
|
let data = this.$q.localStorage.getItem(
|
||||||
|
`lnbits.diagonalley.${this.g.user.id}`
|
||||||
|
)
|
||||||
|
let chat = {
|
||||||
|
...data.chat,
|
||||||
|
[`${id}`]: {
|
||||||
|
timestamp: Object.keys(this.orderMessages)[
|
||||||
|
Object.keys(this.orderMessages).length - 1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log({chat})
|
||||||
|
this.$q.localStorage.set(`lnbits.diagonalley.${this.g.user.id}`, {
|
||||||
|
...data,
|
||||||
|
chat
|
||||||
|
})
|
||||||
|
this.checkUnreadMessages()
|
||||||
|
},
|
||||||
|
checkUnreadMessages() {
|
||||||
|
let lastMsgs = this.$q.localStorage.getItem(
|
||||||
|
`lnbits.diagonalley.${this.g.user.id}`
|
||||||
|
).chat
|
||||||
|
for (let key in this.messages) {
|
||||||
|
let idx = this.orders.findIndex(f => f.invoiceid == key)
|
||||||
|
if (!lastMsgs[key]) {
|
||||||
|
this.updateLastSeenMsg(key)
|
||||||
|
//this.orders[idx].unread = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
'Key',
|
||||||
|
key,
|
||||||
|
'saved:',
|
||||||
|
lastMsgs[key].timestamp,
|
||||||
|
'messages: ',
|
||||||
|
Math.max(...this.messages[key].map(c => c.timestamp)),
|
||||||
|
lastMsgs[key].timestamp <
|
||||||
|
Math.max(...this.messages[key].map(c => c.timestamp))
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
lastMsgs[key].timestamp <
|
||||||
|
Math.max(...this.messages[key].map(c => c.timestamp))
|
||||||
|
) {
|
||||||
|
this.$set(this.orders[idx], 'unread', true)
|
||||||
|
// this.orders[idx].unread = true
|
||||||
|
} else {
|
||||||
|
this.$set(this.orders[idx], 'unread', false)
|
||||||
|
// this.orders[idx].unread = false
|
||||||
|
}
|
||||||
|
console.log('Order:', this.orders[idx])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearMessage() {
|
||||||
|
this.newMessage = ''
|
||||||
|
this.$refs.newMessage.focus()
|
||||||
|
},
|
||||||
|
sendMessage() {
|
||||||
|
let message = {
|
||||||
|
msg: this.newMessage,
|
||||||
|
pubkey: this.keys.pubkey
|
||||||
|
}
|
||||||
|
this.ws.send(JSON.stringify(message))
|
||||||
|
|
||||||
|
this.clearMessage()
|
||||||
|
},
|
||||||
|
chatRoom(id) {
|
||||||
|
this.startChat(id)
|
||||||
|
this.orderMessages = {}
|
||||||
|
this.messages[id].map(m => {
|
||||||
|
this.$set(this.orderMessages, m.timestamp, {
|
||||||
|
msg: m.msg,
|
||||||
|
pubkey: m.pubkey
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.$refs.chatCard.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
inline: 'nearest'
|
||||||
|
})
|
||||||
|
this.updateLastSeenMsg(id)
|
||||||
|
//"ea2fbf6c91aa228603681e2cc34bb06e34e6d1375fa4d6c35756182b2fa3307f"
|
||||||
|
//"c7435a04875c26e28db91a377bd6e991dbfefeefea8258415f3ae0c716ed2335"
|
||||||
|
},
|
||||||
|
startChat(room_name) {
|
||||||
|
if (this.ws) {
|
||||||
|
this.ws.close()
|
||||||
|
}
|
||||||
|
if (location.protocol == 'https:') {
|
||||||
|
ws_scheme = 'wss://'
|
||||||
|
} else {
|
||||||
|
ws_scheme = 'ws://'
|
||||||
|
}
|
||||||
|
ws = new WebSocket(
|
||||||
|
ws_scheme + location.host + '/diagonalley/ws/' + room_name
|
||||||
|
)
|
||||||
|
|
||||||
|
function checkWebSocket(event) {
|
||||||
|
if (ws.readyState === WebSocket.CLOSED) {
|
||||||
|
console.log('WebSocket CLOSED: Reopening')
|
||||||
|
ws = new WebSocket(
|
||||||
|
ws_scheme + location.host + '/diagonalley/ws/' + room_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onmessage = event => {
|
||||||
|
let event_data = JSON.parse(event.data)
|
||||||
|
|
||||||
|
this.$set(this.orderMessages, Date.now(), event_data)
|
||||||
|
this.updateLastSeenMsg(room_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onclose = event => {
|
||||||
|
this.updateLastSeenMsg(room_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ws = ws
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created: function () {
|
async created() {
|
||||||
if (this.g.user.wallets.length) {
|
if (this.g.user.wallets.length) {
|
||||||
this.getStalls()
|
this.getStalls()
|
||||||
this.getProducts()
|
this.getProducts()
|
||||||
this.getZones()
|
this.getZones()
|
||||||
this.getOrders()
|
await this.getOrders()
|
||||||
this.getMarkets()
|
this.getMarkets()
|
||||||
this.customerKeys = [
|
await this.getAllMessages()
|
||||||
'cb4c0164fe03fcdadcbfb4f76611c71620790944c24f21a1cd119395cdedfe1b',
|
let keys = this.$q.localStorage.getItem(
|
||||||
'a9c17358a6dc4ceb3bb4d883eb87967a66b3453a0f3199f0b1c8eef8070c6a07'
|
`lnbits.diagonalley.${this.g.user.id}`
|
||||||
]
|
)
|
||||||
|
if (keys) {
|
||||||
|
this.keys = keys
|
||||||
|
}
|
||||||
|
setInterval(() => {
|
||||||
|
this.getAllMessages()
|
||||||
|
}, 300000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.chat-container {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 1fr auto;
|
||||||
|
height: calc(100vh - 140px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
padding: 1rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-messages {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
dense
|
dense
|
||||||
emit-value
|
emit-value
|
||||||
v-model="selectedOrder"
|
v-model="selectedOrder"
|
||||||
:options="user.orders"
|
:options="Object.keys(user.orders)"
|
||||||
label="Order"
|
label="Order"
|
||||||
hint="Select an order from this merchant"
|
hint="Select an order from this merchant"
|
||||||
@input="val => { changeOrder() }"
|
@input="val => { changeOrder() }"
|
||||||
|
|
@ -187,7 +187,7 @@
|
||||||
msg: this.newMessage,
|
msg: this.newMessage,
|
||||||
pubkey: this.user.keys.publickey
|
pubkey: this.user.keys.publickey
|
||||||
}
|
}
|
||||||
ws.send(JSON.stringify(message))
|
this.ws.send(JSON.stringify(message))
|
||||||
|
|
||||||
this.clearMessage()
|
this.clearMessage()
|
||||||
},
|
},
|
||||||
|
|
@ -236,10 +236,16 @@
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(error)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
changeOrder() {
|
async changeOrder() {
|
||||||
console.log(this.selectedOrder)
|
this.products = this.user.orders[this.selectedOrder]
|
||||||
|
this.messages = {}
|
||||||
|
await this.getMessages(this.selectedOrder)
|
||||||
|
this.startChat(this.selectedOrder)
|
||||||
},
|
},
|
||||||
startChat(room_name) {
|
startChat(room_name) {
|
||||||
|
if (this.ws) {
|
||||||
|
this.ws.close()
|
||||||
|
}
|
||||||
if (location.protocol == 'https:') {
|
if (location.protocol == 'https:') {
|
||||||
ws_scheme = 'wss://'
|
ws_scheme = 'wss://'
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -268,51 +274,51 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
this.stall = JSON.parse('{{ stall | tojson }}')
|
|
||||||
let order_details = JSON.parse('{{ order | tojson }}')
|
let order_details = JSON.parse('{{ order | tojson }}')
|
||||||
let products = JSON.parse('{{ products | tojson }}')
|
let products = JSON.parse('{{ products | tojson }}')
|
||||||
|
|
||||||
let order_id = '{{ order_id }}'
|
let order_id = '{{ order_id }}'
|
||||||
|
|
||||||
|
this.stall = JSON.parse('{{ stall | tojson }}')
|
||||||
|
this.products = order_details.map(o => {
|
||||||
|
let product = products.find(p => p.id == o.product_id)
|
||||||
|
return {
|
||||||
|
quantity: o.quantity,
|
||||||
|
name: product.product,
|
||||||
|
image: product.image,
|
||||||
|
price: product.price
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
let data = this.$q.localStorage.getItem(`lnbits.diagonalley.data`)
|
let data = this.$q.localStorage.getItem(`lnbits.diagonalley.data`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (data) {
|
if (data) {
|
||||||
this.user = data
|
this.user = data
|
||||||
//add chat key (merchant pubkey) if not set
|
//add chat key (merchant pubkey) if not set
|
||||||
if (!this.user.chats[`${order_id}`]) {
|
if (!this.user.orders[`${order_id}`]) {
|
||||||
this.$set(this.user.chats, order_id, [])
|
this.$set(this.user.orders, order_id, this.products)
|
||||||
}
|
}
|
||||||
//this.$q.localStorage.set(`lnbits.diagonalley.data`, this.user)
|
//this.$q.localStorage.set(`lnbits.diagonalley.data`, this.user)
|
||||||
} else {
|
} else {
|
||||||
// generate keys
|
// generate keys
|
||||||
await this.generateKeys()
|
await this.generateKeys()
|
||||||
// populate user data
|
// populate user data
|
||||||
this.user.chats = {
|
this.user.orders = {
|
||||||
[`${order_id}`]: []
|
[`${order_id}`]: this.products
|
||||||
}
|
}
|
||||||
this.user.orders = []
|
//this.user.orders = []
|
||||||
}
|
}
|
||||||
|
|
||||||
this.order_details = order_details
|
//this.order_details = order_details
|
||||||
this.products = order_details.map(o => {
|
|
||||||
let product = products.find(p => p.id == o.product_id)
|
|
||||||
return {
|
|
||||||
quantity: o.quantity,
|
|
||||||
name: product.product,
|
|
||||||
image: product.image,
|
|
||||||
price: product.price
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
this.user.orders = [...new Set([...this.user.orders, order_id])]
|
//this.user.orders = [...new Set([...this.user.orders, order_id])]
|
||||||
this.selectedOrder = order_id
|
this.selectedOrder = order_id
|
||||||
|
|
||||||
await this.getMessages(order_id)
|
await this.getMessages(order_id)
|
||||||
|
|
||||||
this.$q.localStorage.set(`lnbits.diagonalley.data`, this.user)
|
this.$q.localStorage.set(`lnbits.diagonalley.data`, this.user)
|
||||||
this.startChat(order_id)
|
this.startChat(order_id)
|
||||||
console.log(this.products)
|
console.log(this.messages)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="item.categories" class="text-subtitle1">
|
<div v-if="item.categories" class="text-subtitle1">
|
||||||
<q-chip v-for="cat in item.categories.split(',')" dense
|
<q-chip v-for="(cat, i) in item.categories.split(',')" :key="i" dense
|
||||||
>{{cat}}</q-chip
|
>{{cat}}</q-chip
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -409,8 +409,18 @@
|
||||||
if (res.data.paid) {
|
if (res.data.paid) {
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
message: 'Sats received, thanks!',
|
multiLine: true,
|
||||||
icon: 'thumb_up'
|
message:
|
||||||
|
"Sats received, thanks! You'l be redirected to the order page...",
|
||||||
|
icon: 'thumb_up',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: 'See Order',
|
||||||
|
handler: () => {
|
||||||
|
window.location.href = `/diagonalley/order/?merch=${this.stall.id}&invoice_id=${this.qrCodeDialog.data.payment_hash}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
clearInterval(this.qrCodeDialog.paymentChecker)
|
clearInterval(this.qrCodeDialog.paymentChecker)
|
||||||
this.resetCart()
|
this.resetCart()
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,9 @@ async def display(request: Request, market_id):
|
||||||
|
|
||||||
|
|
||||||
@diagonalley_ext.get("/order", response_class=HTMLResponse)
|
@diagonalley_ext.get("/order", response_class=HTMLResponse)
|
||||||
async def chat_page(request: Request, merch: str = Query(...), invoice_id: str = Query(...)):
|
async def chat_page(
|
||||||
|
request: Request, merch: str = Query(...), invoice_id: str = Query(...)
|
||||||
|
):
|
||||||
stall = await get_diagonalley_stall(merch)
|
stall = await get_diagonalley_stall(merch)
|
||||||
order = await get_diagonalley_order_invoiceid(invoice_id)
|
order = await get_diagonalley_order_invoiceid(invoice_id)
|
||||||
_order = await get_diagonalley_order_details(order.id)
|
_order = await get_diagonalley_order_details(order.id)
|
||||||
|
|
@ -110,9 +112,9 @@ async def chat_page(request: Request, merch: str = Query(...), invoice_id: str =
|
||||||
"publickey": stall.publickey,
|
"publickey": stall.publickey,
|
||||||
"wallet": stall.wallet,
|
"wallet": stall.wallet,
|
||||||
},
|
},
|
||||||
"order_id": order.id,
|
"order_id": order.invoiceid,
|
||||||
"order": [details.dict() for details in _order],
|
"order": [details.dict() for details in _order],
|
||||||
"products": [product.dict() for product in products]
|
"products": [product.dict() for product in products],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -123,6 +125,41 @@ async def chat_page(request: Request, merch: str = Query(...), invoice_id: str =
|
||||||
notifier = Notifier()
|
notifier = Notifier()
|
||||||
|
|
||||||
|
|
||||||
|
# class ConnectionManager:
|
||||||
|
# def __init__(self):
|
||||||
|
# self.active_connections: List[WebSocket] = []
|
||||||
|
|
||||||
|
# async def connect(self, websocket: WebSocket, room_name: str):
|
||||||
|
# await websocket.accept()
|
||||||
|
# websocket.id = room_name
|
||||||
|
# self.active_connections.append(websocket)
|
||||||
|
|
||||||
|
# def disconnect(self, websocket: WebSocket):
|
||||||
|
# self.active_connections.remove(websocket)
|
||||||
|
|
||||||
|
# async def send_personal_message(self, message: str, room_name: str):
|
||||||
|
# for connection in self.active_connections:
|
||||||
|
# if connection.id == room_name:
|
||||||
|
# await connection.send_text(message)
|
||||||
|
|
||||||
|
# async def broadcast(self, message: str):
|
||||||
|
# for connection in self.active_connections:
|
||||||
|
# await connection.send_text(message)
|
||||||
|
|
||||||
|
|
||||||
|
# manager = ConnectionManager()
|
||||||
|
|
||||||
|
|
||||||
|
# @diagonalley_ext.websocket("/ws/{room_name}")
|
||||||
|
# async def websocket_endpoint(websocket: WebSocket, room_name: str):
|
||||||
|
# await manager.connect(websocket, room_name)
|
||||||
|
# try:
|
||||||
|
# while True:
|
||||||
|
# data = await websocket.receive_text()
|
||||||
|
# except WebSocketDisconnect:
|
||||||
|
# manager.disconnect(websocket)
|
||||||
|
|
||||||
|
|
||||||
@diagonalley_ext.websocket("/ws/{room_name}")
|
@diagonalley_ext.websocket("/ws/{room_name}")
|
||||||
async def websocket_endpoint(
|
async def websocket_endpoint(
|
||||||
websocket: WebSocket, room_name: str, background_tasks: BackgroundTasks
|
websocket: WebSocket, room_name: str, background_tasks: BackgroundTasks
|
||||||
|
|
@ -143,7 +180,7 @@ async def websocket_endpoint(
|
||||||
if websocket not in room_members:
|
if websocket not in room_members:
|
||||||
print("Sender not in room member: Reconnecting...")
|
print("Sender not in room member: Reconnecting...")
|
||||||
await notifier.connect(websocket, room_name)
|
await notifier.connect(websocket, room_name)
|
||||||
|
print("ENDPOINT", data)
|
||||||
await notifier._notify(data, room_name)
|
await notifier._notify(data, room_name)
|
||||||
|
|
||||||
except WebSocketDisconnect:
|
except WebSocketDisconnect:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
from base64 import urlsafe_b64encode
|
from base64 import urlsafe_b64encode
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import List
|
from typing import List, Union
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
from fastapi.param_functions import Query
|
from fastapi.param_functions import Body, Query
|
||||||
from fastapi.params import Depends
|
from fastapi.params import Depends
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from secp256k1 import PrivateKey, PublicKey
|
from secp256k1 import PrivateKey, PublicKey
|
||||||
|
|
@ -34,6 +34,7 @@ from .crud import (
|
||||||
delete_diagonalley_product,
|
delete_diagonalley_product,
|
||||||
delete_diagonalley_stall,
|
delete_diagonalley_stall,
|
||||||
delete_diagonalley_zone,
|
delete_diagonalley_zone,
|
||||||
|
get_diagonalley_chat_by_merchant,
|
||||||
get_diagonalley_chat_messages,
|
get_diagonalley_chat_messages,
|
||||||
get_diagonalley_latest_chat_messages,
|
get_diagonalley_latest_chat_messages,
|
||||||
get_diagonalley_market,
|
get_diagonalley_market,
|
||||||
|
|
@ -255,6 +256,14 @@ async def api_diagonalley_orders(
|
||||||
return {"message": "We could not retrieve the orders."}
|
return {"message": "We could not retrieve the orders."}
|
||||||
|
|
||||||
|
|
||||||
|
@diagonalley_ext.get("/api/v1/orders/{order_id}")
|
||||||
|
async def api_diagonalley_order_by_id(order_id: str):
|
||||||
|
order = (await get_diagonalley_order(order_id)).dict()
|
||||||
|
order["details"] = await get_diagonalley_order_details(order_id)
|
||||||
|
|
||||||
|
return order
|
||||||
|
|
||||||
|
|
||||||
@diagonalley_ext.post("/api/v1/orders")
|
@diagonalley_ext.post("/api/v1/orders")
|
||||||
async def api_diagonalley_order_create(data: createOrder):
|
async def api_diagonalley_order_create(data: createOrder):
|
||||||
ref = urlsafe_short_hash()
|
ref = urlsafe_short_hash()
|
||||||
|
|
@ -488,6 +497,16 @@ async def api_diagonalley_generate_keys():
|
||||||
## MESSAGES/CHAT
|
## MESSAGES/CHAT
|
||||||
|
|
||||||
|
|
||||||
|
@diagonalley_ext.get("/api/v1/chat/messages/merchant")
|
||||||
|
async def api_get_merchant_messages(
|
||||||
|
orders: str = Query(...), wallet: WalletTypeInfo = Depends(require_admin_key)
|
||||||
|
):
|
||||||
|
|
||||||
|
return [
|
||||||
|
msg.dict() for msg in await get_diagonalley_chat_by_merchant(orders.split(","))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@diagonalley_ext.get("/api/v1/chat/messages/{room_name}")
|
@diagonalley_ext.get("/api/v1/chat/messages/{room_name}")
|
||||||
async def api_get_latest_chat_msg(room_name: str, all_messages: bool = Query(False)):
|
async def api_get_latest_chat_msg(room_name: str, all_messages: bool = Query(False)):
|
||||||
if all_messages:
|
if all_messages:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue