347 lines
10 KiB
JavaScript
347 lines
10 KiB
JavaScript
import { t, i18n } from '../../i18n.js'
|
|
import { auth } from '../../services/auth.js'
|
|
import { directus } from '../../services/directus.js'
|
|
import { conversationsService } from '../../services/conversations.js'
|
|
import { escapeHTML } from '../../utils/helpers.js'
|
|
|
|
class PageMessages extends HTMLElement {
|
|
constructor() {
|
|
super()
|
|
this.conversations = []
|
|
this.loading = true
|
|
this.error = null
|
|
this.isLoggedIn = false
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.isLoggedIn = auth.isLoggedIn()
|
|
|
|
if (!this.isLoggedIn) {
|
|
window.location.hash = '#/'
|
|
return
|
|
}
|
|
|
|
this.render()
|
|
this.loadConversations()
|
|
|
|
this._unsubs = []
|
|
this._unsubs.push(i18n.subscribe(() => this.render()))
|
|
this._unsubs.push(auth.subscribe(() => {
|
|
this.isLoggedIn = auth.isLoggedIn()
|
|
|
|
if (!this.isLoggedIn) {
|
|
window.location.hash = '#/'
|
|
}
|
|
}))
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
this._unsubs.forEach(fn => fn())
|
|
this._unsubs = []
|
|
}
|
|
|
|
async loadConversations() {
|
|
try {
|
|
const user = await auth.getUser()
|
|
if (!user) {
|
|
this.loading = false
|
|
this.updateContent()
|
|
return
|
|
}
|
|
|
|
const conversations = await conversationsService.getMyConversations()
|
|
this.conversations = conversations || []
|
|
|
|
const missing = this.conversations.filter(c => typeof c.listing_id !== 'object')
|
|
if (missing.length > 0) {
|
|
await Promise.all(missing.map(async conv => {
|
|
try {
|
|
const listing = await directus.getListing(conv.listing_id)
|
|
if (listing) conv.listing_id = listing
|
|
} catch { /* listing may not be accessible */ }
|
|
}))
|
|
}
|
|
|
|
this.loading = false
|
|
this.updateContent()
|
|
} catch (err) {
|
|
console.error('Failed to load conversations:', err)
|
|
this.error = err.message
|
|
this.loading = false
|
|
this.updateContent()
|
|
}
|
|
}
|
|
|
|
showAuthModal() {
|
|
document.querySelector('auth-modal')?.show()
|
|
}
|
|
|
|
render() {
|
|
this.innerHTML = /* html */`
|
|
<div class="messages-page">
|
|
<header class="page-header">
|
|
<h1>${t('messages.title')}</h1>
|
|
<p class="page-subtitle">${t('messages.subtitle')}</p>
|
|
</header>
|
|
|
|
<div id="messages-content" class="conversations-list">
|
|
${this.renderContent()}
|
|
</div>
|
|
</div>
|
|
`
|
|
|
|
this.querySelector('#login-btn')?.addEventListener('click', () => this.showAuthModal())
|
|
}
|
|
|
|
updateContent() {
|
|
const container = this.querySelector('#messages-content')
|
|
if (container) {
|
|
container.innerHTML = this.renderContent()
|
|
this.querySelector('#login-btn')?.addEventListener('click', () => this.showAuthModal())
|
|
}
|
|
}
|
|
|
|
renderContent() {
|
|
if (!this.isLoggedIn) {
|
|
return /* html */`
|
|
<div class="empty-state">
|
|
<div class="empty-icon">🔐</div>
|
|
<h3>${t('messages.loginRequired')}</h3>
|
|
<p>${t('messages.loginHint')}</p>
|
|
<button id="login-btn" class="btn btn-primary">${t('messages.login')}</button>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
if (this.loading) {
|
|
return /* html */`
|
|
<div class="loading-state">
|
|
<div class="spinner"></div>
|
|
<p>${t('common.loading')}</p>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
if (this.error) {
|
|
return /* html */`
|
|
<div class="empty-state">
|
|
<div class="empty-icon">⚠️</div>
|
|
<p>${t('common.error')}</p>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
if (this.conversations.length === 0) {
|
|
return /* html */`
|
|
<div class="empty-state">
|
|
<div class="empty-icon">💬</div>
|
|
<h3>${t('messages.empty')}</h3>
|
|
<p>${t('messages.emptyHint')}</p>
|
|
<a href="#/" class="btn btn-primary">${t('messages.browse')}</a>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
return this.conversations.map(conv => this.renderConversationItem(conv)).join('')
|
|
}
|
|
|
|
renderConversationItem(conv) {
|
|
const listing = typeof conv.listing_id === 'object' ? conv.listing_id : null
|
|
const listingId = listing?.id || conv.listing_id
|
|
const imageId = listing?.images?.[0]?.directus_files_id?.id
|
|
const imageUrl = imageId ? directus.getThumbnailUrl(imageId, 80) : ''
|
|
const title = listing?.status === 'deleted'
|
|
? t('messages.listingRemoved')
|
|
: (listing?.title || t('messages.listing'))
|
|
const dateStr = this.formatDate(conv.date_updated || conv.date_created)
|
|
|
|
return /* html */`
|
|
<a href="#/listing/${listingId}?chat=${conv.id}" class="conversation-item" data-conv-id="${conv.id}">
|
|
<div class="conversation-image">
|
|
${imageUrl
|
|
? `<img src="${imageUrl}" alt="" loading="lazy">`
|
|
: `<div class="image-placeholder">📦</div>`}
|
|
</div>
|
|
<div class="conversation-info">
|
|
<h3 class="conversation-title">${escapeHTML(title)}</h3>
|
|
<p class="conversation-date">${dateStr}</p>
|
|
</div>
|
|
<div class="conversation-arrow">→</div>
|
|
</a>
|
|
`
|
|
}
|
|
|
|
formatDate(dateStr) {
|
|
if (!dateStr) return ''
|
|
const date = new Date(dateStr)
|
|
const now = new Date()
|
|
const diffMs = now - date
|
|
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24))
|
|
|
|
if (diffDays === 0) return `${t('messages.today')}, ${date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`
|
|
if (diffDays === 1) return t('messages.yesterday')
|
|
if (diffDays < 7) return t('messages.daysAgo', { days: diffDays })
|
|
|
|
return date.toLocaleDateString()
|
|
}
|
|
|
|
}
|
|
|
|
customElements.define('page-messages', PageMessages)
|
|
|
|
const style = document.createElement('style')
|
|
style.textContent = /* css */`
|
|
page-messages .messages-page {
|
|
padding: var(--space-lg) 0;
|
|
}
|
|
|
|
page-messages .page-header {
|
|
margin-bottom: var(--space-xl);
|
|
}
|
|
|
|
page-messages .page-header h1 {
|
|
margin: 0 0 var(--space-xs);
|
|
}
|
|
|
|
page-messages .page-subtitle {
|
|
color: var(--color-text-muted);
|
|
margin: 0;
|
|
}
|
|
|
|
page-messages .conversations-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
page-messages .conversation-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-md);
|
|
padding: var(--space-md);
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-md);
|
|
text-decoration: none;
|
|
color: inherit;
|
|
transition: all var(--transition-fast);
|
|
}
|
|
|
|
page-messages .conversation-item:hover {
|
|
border-color: var(--color-primary);
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
page-messages .conversation-image {
|
|
width: 60px;
|
|
height: 60px;
|
|
border-radius: var(--radius-sm);
|
|
overflow: hidden;
|
|
background: var(--color-bg-tertiary);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
page-messages .conversation-image img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
page-messages .image-placeholder {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.5rem;
|
|
filter: grayscale(1);
|
|
}
|
|
|
|
page-messages .conversation-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
page-messages .conversation-title {
|
|
margin: 0 0 var(--space-xs);
|
|
font-size: var(--font-size-base);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
page-messages .conversation-date {
|
|
margin: 0;
|
|
font-size: var(--font-size-sm);
|
|
color: var(--color-text-muted);
|
|
}
|
|
|
|
page-messages .conversation-arrow {
|
|
color: var(--color-text-muted);
|
|
font-size: var(--font-size-lg);
|
|
}
|
|
|
|
page-messages .loading-state,
|
|
page-messages .empty-state {
|
|
text-align: center;
|
|
padding: var(--space-3xl);
|
|
}
|
|
|
|
page-messages .spinner {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 3px solid var(--color-border);
|
|
border-top-color: var(--color-primary);
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
margin: 0 auto var(--space-md);
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
page-messages .empty-icon {
|
|
font-size: 4rem;
|
|
margin-bottom: var(--space-md);
|
|
filter: grayscale(1);
|
|
}
|
|
|
|
page-messages .empty-state h3 {
|
|
margin: 0 0 var(--space-sm);
|
|
}
|
|
|
|
page-messages .empty-state p {
|
|
color: var(--color-text-muted);
|
|
margin: 0 0 var(--space-lg);
|
|
}
|
|
|
|
page-messages .section-title {
|
|
font-size: var(--font-size-sm);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
color: var(--color-text-muted);
|
|
margin: var(--space-lg) 0 var(--space-sm);
|
|
}
|
|
|
|
page-messages .section-title:first-child {
|
|
margin-top: 0;
|
|
}
|
|
|
|
page-messages .conversation-pending {
|
|
border-color: var(--color-primary);
|
|
background: var(--color-bg);
|
|
}
|
|
|
|
page-messages .conversation-badge {
|
|
font-size: var(--font-size-xs);
|
|
font-weight: var(--font-weight-medium);
|
|
padding: var(--space-xs) var(--space-sm);
|
|
border-radius: var(--radius-full);
|
|
background: var(--color-primary);
|
|
color: var(--color-bg);
|
|
white-space: nowrap;
|
|
}
|
|
`
|
|
document.head.appendChild(style)
|