diff --git a/lnbits/extensions/diagonalley/crud.py b/lnbits/extensions/diagonalley/crud.py
index f093ba69..8dadb739 100644
--- a/lnbits/extensions/diagonalley/crud.py
+++ b/lnbits/extensions/diagonalley/crud.py
@@ -408,8 +408,10 @@ async def create_diagonalley_market_stalls(
async def update_diagonalley_market(market_id):
pass
+
### CHAT / MESSAGES
+
async def create_chat_message(data: CreateChatMessage):
print("DATA", data)
await db.execute(
@@ -441,3 +443,14 @@ async def get_diagonalley_chat_messages(room_name: str):
)
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]
diff --git a/lnbits/extensions/diagonalley/notifier.py b/lnbits/extensions/diagonalley/notifier.py
index e21be500..08badfc7 100644
--- a/lnbits/extensions/diagonalley/notifier.py
+++ b/lnbits/extensions/diagonalley/notifier.py
@@ -78,9 +78,9 @@ class Notifier:
async def _notify(self, message: str, room_name: str):
"""Notifier"""
-
d = json.loads(message)
d["room_name"] = room_name
+ print("hey", d)
db_msg = CreateChatMessage.parse_obj(d)
print("NOT:", db_msg)
await create_chat_message(data=db_msg)
diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/index.html b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
index 5ad6f6a5..f898fd40 100644
--- a/lnbits/extensions/diagonalley/templates/diagonalley/index.html
+++ b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
@@ -345,20 +345,6 @@
- + Product List a product
- + Product List a product
+ Shipping Zone Create a shipping zone
@@ -382,7 +368,26 @@
Create a market stall to list products on
- + Product List a product
+ + Product List a product
+ Create Market
Makes a simple frontend shop for your stalls (not NOSTR)
+
{{ col.label }}
@@ -434,6 +440,23 @@
:icon="props.expand ? 'remove' : 'add'"
/>
+
+
+
+
+
{{ col.value }}
@@ -824,22 +847,114 @@
{% include "diagonalley/_api_docs.html" %}
+
Messages
-
-
-
+
{% endblock %} {% block scripts %} {{ window_vars(user) }}
@@ -873,6 +987,15 @@
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 => {
obj._data = _.clone(obj)
return obj
@@ -891,6 +1014,7 @@
new Date(obj.time * 1000),
'YYYY-MM-DD HH:mm'
)
+ // obj.unread = false
return obj
}
const mapKeys = obj => {
@@ -933,8 +1057,12 @@
customerKeys: [],
customerKey: '',
customerMessages: {},
+ messages: {},
+ newMessage: '',
+ orderMessages: {},
shippedModel: false,
shippingZoneOptions: [
+ 'Free (digital)',
'Worldwide',
'Europe',
'Australia',
@@ -999,17 +1127,17 @@
ordersTable: {
columns: [
/*{
- name: 'product',
- align: 'left',
- label: 'Product',
- field: 'product'
- },
- {
- name: 'quantity',
- align: 'left',
- label: 'Quantity',
- field: 'quantity'
- },*/
+ name: 'product',
+ align: 'left',
+ label: 'Product',
+ field: 'product'
+ },
+ {
+ name: 'quantity',
+ align: 'left',
+ label: 'Quantity',
+ field: 'quantity'
+ },*/
{
name: 'id',
align: 'left',
@@ -1443,9 +1571,10 @@
let image = new Image()
image.src = blobURL
image.onload = async () => {
+ let fit = imgSizeFit(image)
let canvas = document.createElement('canvas')
- canvas.setAttribute('width', 760)
- canvas.setAttribute('height', 490)
+ canvas.setAttribute('width', fit.width)
+ canvas.setAttribute('height', fit.height)
await pica.resize(image, canvas, {
quality: 0,
alpha: true,
@@ -1657,7 +1786,7 @@
.then(response => {
if (response.data) {
this.markets = response.data.map(mapMarkets)
- console.log(this.markets)
+ // console.log(this.markets)
}
})
.catch(error => {
@@ -1756,10 +1885,10 @@
////////////////////////////////////////
////////////////ORDERS//////////////////
////////////////////////////////////////
- getOrders: function () {
+ getOrders: async function () {
var self = this
- LNbits.api
+ await LNbits.api
.request(
'GET',
'/diagonalley/api/v1/orders?all_wallets=true',
@@ -1768,7 +1897,6 @@
.then(function (response) {
if (response.data) {
self.orders = response.data.map(mapOrders)
- console.log(self.orders)
}
})
.catch(function (error) {
@@ -1839,21 +1967,190 @@
},
exportOrdersCSV: function () {
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) {
this.getStalls()
this.getProducts()
this.getZones()
- this.getOrders()
+ await this.getOrders()
this.getMarkets()
- this.customerKeys = [
- 'cb4c0164fe03fcdadcbfb4f76611c71620790944c24f21a1cd119395cdedfe1b',
- 'a9c17358a6dc4ceb3bb4d883eb87967a66b3453a0f3199f0b1c8eef8070c6a07'
- ]
+ await this.getAllMessages()
+ let keys = this.$q.localStorage.getItem(
+ `lnbits.diagonalley.${this.g.user.id}`
+ )
+ if (keys) {
+ this.keys = keys
+ }
+ setInterval(() => {
+ this.getAllMessages()
+ }, 300000)
}
}
})
+
{% endblock %}
diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/order.html b/lnbits/extensions/diagonalley/templates/diagonalley/order.html
index aa9d0de4..81cdfc36 100644
--- a/lnbits/extensions/diagonalley/templates/diagonalley/order.html
+++ b/lnbits/extensions/diagonalley/templates/diagonalley/order.html
@@ -68,7 +68,7 @@
dense
emit-value
v-model="selectedOrder"
- :options="user.orders"
+ :options="Object.keys(user.orders)"
label="Order"
hint="Select an order from this merchant"
@input="val => { changeOrder() }"
@@ -187,7 +187,7 @@
msg: this.newMessage,
pubkey: this.user.keys.publickey
}
- ws.send(JSON.stringify(message))
+ this.ws.send(JSON.stringify(message))
this.clearMessage()
},
@@ -236,10 +236,16 @@
LNbits.utils.notifyApiError(error)
})
},
- changeOrder() {
- console.log(this.selectedOrder)
+ async changeOrder() {
+ this.products = this.user.orders[this.selectedOrder]
+ this.messages = {}
+ await this.getMessages(this.selectedOrder)
+ this.startChat(this.selectedOrder)
},
startChat(room_name) {
+ if (this.ws) {
+ this.ws.close()
+ }
if (location.protocol == 'https:') {
ws_scheme = 'wss://'
} else {
@@ -268,51 +274,51 @@
}
},
async created() {
- this.stall = JSON.parse('{{ stall | tojson }}')
let order_details = JSON.parse('{{ order | tojson }}')
let products = JSON.parse('{{ products | tojson }}')
-
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`)
try {
if (data) {
this.user = data
//add chat key (merchant pubkey) if not set
- if (!this.user.chats[`${order_id}`]) {
- this.$set(this.user.chats, order_id, [])
+ if (!this.user.orders[`${order_id}`]) {
+ this.$set(this.user.orders, order_id, this.products)
}
//this.$q.localStorage.set(`lnbits.diagonalley.data`, this.user)
} else {
// generate keys
await this.generateKeys()
// populate user data
- this.user.chats = {
- [`${order_id}`]: []
+ this.user.orders = {
+ [`${order_id}`]: this.products
}
- this.user.orders = []
+ //this.user.orders = []
}
- 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.order_details = order_details
- this.user.orders = [...new Set([...this.user.orders, order_id])]
+ //this.user.orders = [...new Set([...this.user.orders, order_id])]
this.selectedOrder = order_id
await this.getMessages(order_id)
this.$q.localStorage.set(`lnbits.diagonalley.data`, this.user)
this.startChat(order_id)
- console.log(this.products)
+ console.log(this.messages)
} catch (e) {
console.error(e)
}
diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/stall.html b/lnbits/extensions/diagonalley/templates/diagonalley/stall.html
index a3a04b1e..05163573 100644
--- a/lnbits/extensions/diagonalley/templates/diagonalley/stall.html
+++ b/lnbits/extensions/diagonalley/templates/diagonalley/stall.html
@@ -125,7 +125,7 @@
>
- {{cat}}
@@ -409,8 +409,18 @@
if (res.data.paid) {
this.$q.notify({
type: 'positive',
- message: 'Sats received, thanks!',
- icon: 'thumb_up'
+ multiLine: true,
+ 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)
this.resetCart()
diff --git a/lnbits/extensions/diagonalley/views.py b/lnbits/extensions/diagonalley/views.py
index 27875287..2bf98211 100644
--- a/lnbits/extensions/diagonalley/views.py
+++ b/lnbits/extensions/diagonalley/views.py
@@ -94,7 +94,9 @@ async def display(request: Request, market_id):
@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)
order = await get_diagonalley_order_invoiceid(invoice_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,
"wallet": stall.wallet,
},
- "order_id": order.id,
+ "order_id": order.invoiceid,
"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()
+# 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}")
async def websocket_endpoint(
websocket: WebSocket, room_name: str, background_tasks: BackgroundTasks
@@ -143,7 +180,7 @@ async def websocket_endpoint(
if websocket not in room_members:
print("Sender not in room member: Reconnecting...")
await notifier.connect(websocket, room_name)
-
+ print("ENDPOINT", data)
await notifier._notify(data, room_name)
except WebSocketDisconnect:
diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py
index f84ea8e9..2f375357 100644
--- a/lnbits/extensions/diagonalley/views_api.py
+++ b/lnbits/extensions/diagonalley/views_api.py
@@ -1,10 +1,10 @@
from base64 import urlsafe_b64encode
from http import HTTPStatus
-from typing import List
+from typing import List, Union
from uuid import uuid4
from fastapi import Request
-from fastapi.param_functions import Query
+from fastapi.param_functions import Body, Query
from fastapi.params import Depends
from loguru import logger
from secp256k1 import PrivateKey, PublicKey
@@ -34,6 +34,7 @@ from .crud import (
delete_diagonalley_product,
delete_diagonalley_stall,
delete_diagonalley_zone,
+ get_diagonalley_chat_by_merchant,
get_diagonalley_chat_messages,
get_diagonalley_latest_chat_messages,
get_diagonalley_market,
@@ -255,6 +256,14 @@ async def api_diagonalley_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")
async def api_diagonalley_order_create(data: createOrder):
ref = urlsafe_short_hash()
@@ -488,6 +497,16 @@ async def api_diagonalley_generate_keys():
## 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}")
async def api_get_latest_chat_msg(room_name: str, all_messages: bool = Query(False)):
if all_messages: