From 62737e7a795cda85dd8c56cac776114e1f10b9e2 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Mon, 3 Apr 2023 15:11:46 +0300 Subject: [PATCH 1/2] fix: filter and notifications --- nostr/nostr_client.py | 10 +++++----- services.py | 14 +++++--------- static/js/index.js | 5 +++-- 3 files changed, 13 insertions(+), 16 deletions(-) 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/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}` }) } From d032c8b259c4a15e6349525439fa451f2bbce7b6 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Mon, 3 Apr 2023 18:36:14 +0300 Subject: [PATCH 2/2] feat: add more images for product --- crud.py | 8 ++-- migrations.py | 2 +- models.py | 24 ++--------- .../components/product-card/product-card.html | 2 +- .../stall-details/stall-details.html | 43 ++++++------------- .../components/stall-details/stall-details.js | 33 +++++++------- static/js/utils.js | 10 +++++ views_api.py | 2 - 8 files changed, 51 insertions(+), 73 deletions(-) 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/static/components/product-card/product-card.html b/static/components/product-card/product-card.html index 55eefb5..5e5b267 100644 --- a/static/components/product-card/product-card.html +++ b/static/components/product-card/product-card.html @@ -1,6 +1,6 @@ - + - - - - + + + + { - 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/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"