diff --git a/crud.py b/crud.py
index 3be6bf6..7b274bb 100644
--- a/crud.py
+++ b/crud.py
@@ -258,7 +258,7 @@ async def create_product(merchant_id: str, data: PartialProduct) -> Product:
await db.execute(
f"""
- INSERT INTO nostrmarket.products (merchant_id, id, stall_id, name, image, price, quantity, category_list, meta)
+ INSERT INTO nostrmarket.products (merchant_id, id, stall_id, name, price, quantity, image_urls, category_list, meta)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
@@ -266,9 +266,9 @@ async def create_product(merchant_id: str, data: PartialProduct) -> Product:
product_id,
data.stall_id,
data.name,
- data.image,
data.price,
data.quantity,
+ json.dumps(data.images),
json.dumps(data.categories),
json.dumps(data.config.dict()),
),
@@ -283,14 +283,14 @@ async def update_product(merchant_id: str, product: Product) -> Product:
await db.execute(
f"""
- UPDATE nostrmarket.products set name = ?, image = ?, price = ?, quantity = ?, category_list = ?, meta = ?
+ UPDATE nostrmarket.products set name = ?, price = ?, quantity = ?, image_urls = ?, category_list = ?, meta = ?
WHERE merchant_id = ? AND id = ?
""",
(
product.name,
- product.image,
product.price,
product.quantity,
+ json.dumps(product.images),
json.dumps(product.categories),
json.dumps(product.config.dict()),
merchant_id,
diff --git a/migrations.py b/migrations.py
index f928098..f3251f5 100644
--- a/migrations.py
+++ b/migrations.py
@@ -43,7 +43,7 @@ async def m001_initial(db):
id TEXT PRIMARY KEY,
stall_id TEXT NOT NULL,
name TEXT NOT NULL,
- image TEXT,
+ image_urls TEXT DEFAULT '[]',
price REAL NOT NULL,
quantity INTEGER NOT NULL,
category_list TEXT DEFAULT '[]',
diff --git a/models.py b/models.py
index 47bc91b..4251fc5 100644
--- a/models.py
+++ b/models.py
@@ -217,30 +217,11 @@ class PartialProduct(BaseModel):
stall_id: str
name: str
categories: List[str] = []
- image: Optional[str]
+ images: List[str] = []
price: float
quantity: int
config: ProductConfig = ProductConfig()
- def validate_product(self):
- if self.image:
- image_is_url = self.image.startswith("https://") or self.image.startswith(
- "http://"
- )
-
- if not image_is_url:
-
- def size(b64string):
- return int((len(b64string) * 3) / 4 - b64string.count("=", -2))
-
- image_size = size(self.image) / 1024
- if image_size > 100:
- raise ValueError(
- f"""
- Image size is too big, {int(image_size)}Kb.
- Max: 100kb, Compress the image at https://tinypng.com, or use an URL."""
- )
-
class Product(PartialProduct, Nostrable):
id: str
@@ -251,7 +232,7 @@ class Product(PartialProduct, Nostrable):
"stall_id": self.stall_id,
"name": self.name,
"description": self.config.description,
- "image": self.image,
+ "images": self.images,
"currency": self.config.currency,
"price": self.price,
"quantity": self.quantity,
@@ -285,6 +266,7 @@ class Product(PartialProduct, Nostrable):
def from_row(cls, row: Row) -> "Product":
product = cls(**dict(row))
product.config = ProductConfig(**json.loads(row["meta"]))
+ product.images = json.loads(row["image_urls"])
product.categories = json.loads(row["category_list"])
return product
diff --git a/nostr/nostr_client.py b/nostr/nostr_client.py
index 965c827..b788072 100644
--- a/nostr/nostr_client.py
+++ b/nostr/nostr_client.py
@@ -68,8 +68,8 @@ class NostrClient:
await self.send_req_queue.put(["EVENT", e.dict()])
async def subscribe_to_direct_messages(self, public_key: str, since: int):
- in_messages_filter = {"kind": 4, "#p": [public_key]}
- out_messages_filter = {"kind": 4, "authors": [public_key]}
+ in_messages_filter = {"kinds": [4], "#p": [public_key]}
+ out_messages_filter = {"kinds": [4], "authors": [public_key]}
if since and since != 0:
in_messages_filter["since"] = since
out_messages_filter["since"] = since
@@ -82,8 +82,8 @@ class NostrClient:
)
async def subscribe_to_merchant_events(self, public_key: str, since: int):
- stall_filter = {"kind": 30017, "authors": [public_key]}
- product_filter = {"kind": 30018, "authors": [public_key]}
+ stall_filter = {"kinds": [30017], "authors": [public_key]}
+ product_filter = {"kinds": [30018], "authors": [public_key]}
await self.send_req_queue.put(
["REQ", f"stall-events:{public_key}", stall_filter]
@@ -93,7 +93,7 @@ class NostrClient:
)
async def subscribe_to_user_profile(self, public_key: str, since: int):
- profile_filter = {"kind": 0, "authors": [public_key]}
+ profile_filter = {"kinds": [0], "authors": [public_key]}
if since and since != 0:
profile_filter["since"] = since + 1
diff --git a/services.py b/services.py
index 827960b..6a878ed 100644
--- a/services.py
+++ b/services.py
@@ -315,6 +315,11 @@ async def _handle_dirrect_message(
incoming=True,
)
await create_direct_message(merchant_id, dm)
+ await websocketUpdater(
+ merchant_id,
+ json.dumps({"type": "new-direct-message", "customerPubkey": from_pubkey}),
+ )
+
if order:
order["public_key"] = from_pubkey
order["merchant_public_key"] = merchant_public_key
@@ -322,11 +327,6 @@ async def _handle_dirrect_message(
order["event_created_at"] = event_created_at
return await _handle_new_order(PartialOrder(**order))
- await websocketUpdater(
- merchant_id,
- json.dumps({"type": "new-direct-message", "customerPubkey": from_pubkey}),
- )
-
return None
except Exception as ex:
logger.warning(ex)
@@ -355,10 +355,6 @@ async def _handle_new_customer(event, merchant):
merchant.id, Customer(merchant_id=merchant.id, public_key=event.pubkey)
)
await nostr_client.subscribe_to_user_profile(event.pubkey, 0)
- await websocketUpdater(
- merchant.id,
- json.dumps({"type": "new-customer"}),
- )
async def _handle_customer_profile_update(event: NostrEvent):
diff --git a/static/components/stall-details/stall-details.html b/static/components/stall-details/stall-details.html
index 9b5741d..29946ab 100644
--- a/static/components/stall-details/stall-details.html
+++ b/static/components/stall-details/stall-details.html
@@ -226,43 +226,28 @@
label="Categories (Hit Enter to add)"
placeholder="crafts,robots,etc"
>
-
+
-
-
-
-
-
-
-
-
+
+
+
+
{
- let fit = imgSizeFit(image)
- let canvas = document.createElement('canvas')
- canvas.setAttribute('width', fit.width)
- canvas.setAttribute('height', fit.height)
- output = await pica.resize(image, canvas)
- this.productDialog.data.image = output.toDataURL('image/jpeg', 0.4)
- this.productDialog = {...this.productDialog}
+ addProductImage: function () {
+ if (!isValidImageUrl(this.productDialog.data.image)) {
+ this.$q.notify({
+ type: 'warning',
+ message: 'Not a valid image URL',
+ timeout: 5000
+ })
+ return
}
- },
- imageCleared() {
+ this.productDialog.data.images.push(this.productDialog.data.image)
this.productDialog.data.image = null
- this.productDialog = {...this.productDialog}
+ },
+ removeProductImage: function (imageUrl) {
+ const index = this.productDialog.data.images.indexOf(imageUrl)
+ if (index !== -1) {
+ this.productDialog.data.images.splice(index, 1)
+ }
},
getProducts: async function () {
try {
@@ -205,7 +207,7 @@ async function stallDetails(path) {
id: this.productDialog.data.id,
name: this.productDialog.data.name,
- image: this.productDialog.data.image,
+ images: this.productDialog.data.images,
price: this.productDialog.data.price,
quantity: this.productDialog.data.quantity,
categories: this.productDialog.data.categories,
@@ -294,6 +296,7 @@ async function stallDetails(path) {
description: '',
categories: [],
image: null,
+ images: [],
price: 0,
quantity: 0,
config: {
diff --git a/static/js/index.js b/static/js/index.js
index 2a8c6be..6593430 100644
--- a/static/js/index.js
+++ b/static/js/index.js
@@ -85,6 +85,7 @@ const merchant = async () => {
type: 'positive',
message: 'Merchant Created!'
})
+ this.waitForNotifications()
} catch (error) {
LNbits.utils.notifyApiError(error)
}
@@ -108,6 +109,7 @@ const merchant = async () => {
this.orderPubkey = customerPubkey
},
waitForNotifications: async function () {
+ if (!this.merchant) return
try {
const scheme = location.protocol === 'http:' ? 'ws' : 'wss'
const port = location.port ? `:${location.port}` : ''
@@ -122,7 +124,6 @@ const merchant = async () => {
message: 'New Order'
})
await this.$refs.orderListRef.addOrder(data)
- } else if (data.type === 'new-customer') {
} else if (data.type === 'new-direct-message') {
await this.$refs.directMessagesRef.handleNewMessage(data)
}
@@ -131,7 +132,7 @@ const merchant = async () => {
this.$q.notify({
timeout: 5000,
type: 'warning',
- message: 'Failed to watch for updated',
+ message: 'Failed to watch for updates',
caption: `${error}`
})
}
diff --git a/static/js/utils.js b/static/js/utils.js
index 49a585a..5158371 100644
--- a/static/js/utils.js
+++ b/static/js/utils.js
@@ -111,3 +111,13 @@ function timeFromNow(time) {
// Return time from now data
return `${tfn.time} ${tfn.unitOfTime}`
}
+
+function isValidImageUrl(string) {
+ let url
+ try {
+ url = new URL(string)
+ } catch (_) {
+ return false
+ }
+ return url.protocol === 'http:' || url.protocol === 'https:'
+}
diff --git a/views_api.py b/views_api.py
index 6629f83..25a5022 100644
--- a/views_api.py
+++ b/views_api.py
@@ -518,7 +518,6 @@ async def api_create_product(
wallet: WalletTypeInfo = Depends(require_admin_key),
) -> Product:
try:
- data.validate_product()
merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found"
@@ -557,7 +556,6 @@ async def api_update_product(
if product_id != product.id:
raise ValueError("Bad product ID")
- product.validate_product()
merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found"