From d1375b2dcf018e0b405505b5d6f2de374f3d51ca Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Sun, 8 Feb 2026 14:50:23 +0100 Subject: [PATCH] feat: implement seller-join flow for E2E chat with pending conversation discovery --- js/components/chat-widget.js | 61 +++++++++------- js/components/pages/page-listing.js | 48 +++++++++++-- js/components/pages/page-messages.js | 101 +++++++++++++++++++++------ js/services/conversations.js | 91 ++++++++++++++++++++++++ js/services/directus/listings.js | 28 ++------ locales/de.json | 12 +++- locales/en.json | 12 +++- locales/es.json | 12 +++- locales/fr.json | 12 +++- locales/it.json | 12 +++- locales/pt.json | 12 +++- locales/ru.json | 12 +++- 12 files changed, 317 insertions(+), 96 deletions(-) diff --git a/js/components/chat-widget.js b/js/components/chat-widget.js index 24f9e09..1bf8045 100644 --- a/js/components/chat-widget.js +++ b/js/components/chat-widget.js @@ -10,7 +10,7 @@ import { escapeHTML } from '../utils/helpers.js' class ChatWidget extends HTMLElement { static get observedAttributes() { - return ['listing-id', 'seller-public-key', 'recipient-name'] + return ['listing-id', 'recipient-name'] } constructor() { @@ -20,27 +20,13 @@ class ChatWidget extends HTMLElement { this.unsubscribe = null this.loading = true this.error = null + this._initialized = false } - async connectedCallback() { - await cryptoService.ready - + connectedCallback() { this.listingId = this.getAttribute('listing-id') - this.sellerPublicKey = this.getAttribute('seller-public-key') this.recipientName = this.getAttribute('recipient-name') || 'Seller' - this.render() - - if (this.listingId && this.sellerPublicKey) { - await this.initConversation() - } else { - this.loading = false - this.error = 'missing-data' - this.render() - } - - this.unsubscribe = conversationsService.subscribe(() => this.refreshMessages()) - this.i18nUnsubscribe = i18n.subscribe(() => this.render()) } disconnectedCallback() { @@ -48,12 +34,34 @@ class ChatWidget extends HTMLElement { if (this.i18nUnsubscribe) this.i18nUnsubscribe() } + async activate() { + if (this._initialized) return + this._initialized = true + + await cryptoService.ready + + if (!cryptoService.getPublicKey()) { + this.loading = false + this.error = 'no-keypair' + this.render() + return + } + + if (this.listingId) { + await this.initConversation() + } else { + this.loading = false + this.error = 'missing-data' + this.render() + } + + this.unsubscribe = conversationsService.subscribe(() => this.refreshMessages()) + this.i18nUnsubscribe = i18n.subscribe(() => this.render()) + } + async initConversation() { try { - this.conversation = await conversationsService.startOrGetConversation( - this.listingId, - this.sellerPublicKey - ) + this.conversation = await conversationsService.startOrFindByListing(this.listingId) await this.loadMessages() } catch (e) { console.error('Failed to init conversation:', e) @@ -101,6 +109,8 @@ class ChatWidget extends HTMLElement { return } + const pending = this.conversation && !this.conversation.otherPublicKey + this.innerHTML = /* html */`
@@ -109,17 +119,20 @@ class ChatWidget extends HTMLElement {
- ${this.renderMessagesHtml()} + ${pending + ? /* html */`

${t('chat.pending')}

` + : this.renderMessagesHtml()}
- + ` : ''}