- ${t('home.recentListings')}
+ ${this.getListingsTitle()}
${this.renderListings()}
@@ -57,21 +108,21 @@ class PageHome extends HTMLElement {
renderListings() {
if (this.loading) {
- return /* html */`${t('common.loading') || 'Laden...'}
`
+ return /* html */`${t('common.loading')}
`
}
if (this.error) {
- return /* html */`${t('common.error') || 'Fehler beim Laden'}
`
+ return /* html */`${t('common.error')}
`
}
if (this.listings.length === 0) {
- return /* html */`${t('home.noListings') || 'Keine Anzeigen gefunden'}
`
+ return /* html */`${t('home.noListings')}
`
}
return this.listings.map(listing => {
const imageId = listing.images?.[0]?.directus_files_id?.id || listing.images?.[0]?.directus_files_id
const imageUrl = imageId ? directus.getThumbnailUrl(imageId, 300) : ''
- const locationName = listing.location?.name || listing.location || ''
+ const locationName = listing.location?.name || ''
return /* html */`
${this.listing.condition ? `${this.getConditionLabel(this.listing.condition)}` : ''}
${this.listing.shipping ? `📦 ${t('listing.shippingAvailable')}` : ''}
+ 👁 ${this.formatViews(this.listing.views || 0)}
${t('listing.postedOn')} ${createdDate}
@@ -442,6 +446,13 @@ class PageListing extends HTMLElement {
return labels[condition] || condition
}
+ formatViews(count) {
+ if (count === 1) {
+ return `1 ${t('listing.viewSingular')}`
+ }
+ return `${count} ${t('listing.viewPlural')}`
+ }
+
formatDescription(text) {
if (!text) return ''
return text
@@ -604,6 +615,10 @@ style.textContent = /* css */`
page-listing .meta-date {
margin-left: auto;
}
+
+ page-listing .views-icon {
+ filter: grayscale(1);
+ }
page-listing .listing-description,
page-listing .listing-location-section {
diff --git a/js/components/search-box.js b/js/components/search-box.js
index 3dc2f6b..ecb4741 100644
--- a/js/components/search-box.js
+++ b/js/components/search-box.js
@@ -337,8 +337,8 @@ class SearchBox extends HTMLElement {
this.selectedCategory = ''
this.selectedSubcategory = ''
this._expandedCategory = ''
- this.saveFiltersToStorage()
categoryMenu?.classList.remove('open')
+ this.emitFilterChange()
this.render()
this.setupEventListeners()
})
@@ -350,8 +350,8 @@ class SearchBox extends HTMLElement {
this.selectedCategory = item.dataset.category
this.selectedSubcategory = item.dataset.subcategory
this._expandedCategory = ''
- this.saveFiltersToStorage()
categoryMenu?.classList.remove('open')
+ this.emitFilterChange()
this.render()
this.setupEventListeners()
})
@@ -366,7 +366,7 @@ class SearchBox extends HTMLElement {
this.useCurrentLocation = false
this.selectedCountry = e.target.value
}
- this.saveFiltersToStorage()
+ this.emitFilterChange()
this.render()
this.setupEventListeners()
}
@@ -377,7 +377,7 @@ class SearchBox extends HTMLElement {
// Radius select handler (both desktop and mobile)
const handleRadiusChange = (e) => {
this.selectedRadius = parseInt(e.target.value)
- this.saveFiltersToStorage()
+ this.emitFilterChange()
}
radiusSelect?.addEventListener('change', handleRadiusChange)
@@ -410,6 +410,28 @@ class SearchBox extends HTMLElement {
measurer.remove()
}
+ getFilterDetails() {
+ return {
+ query: this.searchQuery,
+ category: this.selectedCategory,
+ subcategory: this.selectedSubcategory,
+ country: this.selectedCountry,
+ useCurrentLocation: this.useCurrentLocation,
+ lat: this.currentLat,
+ lng: this.currentLng,
+ radius: this.selectedRadius
+ }
+ }
+
+ emitFilterChange() {
+ this.saveFiltersToStorage()
+
+ this.dispatchEvent(new CustomEvent('filter-change', {
+ bubbles: true,
+ detail: this.getFilterDetails()
+ }))
+ }
+
handleSearch() {
const params = new URLSearchParams()
@@ -432,14 +454,7 @@ class SearchBox extends HTMLElement {
bubbles: true,
cancelable: true,
detail: {
- query: this.searchQuery,
- category: this.selectedCategory,
- subcategory: this.selectedSubcategory,
- country: this.selectedCountry,
- useCurrentLocation: this.useCurrentLocation,
- lat: this.currentLat,
- lng: this.currentLng,
- radius: this.selectedRadius,
+ ...this.getFilterDetails(),
params: params.toString()
}
})
diff --git a/js/services/directus.js b/js/services/directus.js
index 9d5b44a..9306f10 100644
--- a/js/services/directus.js
+++ b/js/services/directus.js
@@ -331,6 +331,34 @@ class DirectusService {
return response.data
}
+ async incrementListingViews(id) {
+ // Check if user already viewed this listing (session-based)
+ const viewedKey = `dgray_viewed_${id}`
+ if (sessionStorage.getItem(viewedKey)) {
+ return false
+ }
+
+ try {
+ // Get current views
+ const listing = await this.get(`/items/listings/${id}`, {
+ fields: ['views']
+ })
+ const currentViews = listing.data?.views || 0
+
+ // Increment views
+ await this.patch(`/items/listings/${id}`, {
+ views: currentViews + 1
+ })
+
+ // Mark as viewed for this session
+ sessionStorage.setItem(viewedKey, 'true')
+ return true
+ } catch (error) {
+ console.error('Failed to increment views:', error)
+ return false
+ }
+ }
+
async deleteListing(id) {
return this.delete(`/items/listings/${id}`)
}
diff --git a/locales/de.json b/locales/de.json
index 08d6438..9c16552 100644
--- a/locales/de.json
+++ b/locales/de.json
@@ -116,6 +116,8 @@
"contactHint": "Kopiere die Adresse und sende den Betrag über dein Monero-Wallet.",
"priceOnRequest": "Preis auf Anfrage",
"shippingAvailable": "Versand verfügbar",
+ "viewSingular": "Aufruf",
+ "viewPlural": "Aufrufe",
"share": "Teilen",
"report": "Melden",
"moreFromSeller": "Weitere Anzeigen des Anbieters"
diff --git a/locales/en.json b/locales/en.json
index a21ec97..2375b87 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -116,6 +116,8 @@
"contactHint": "Copy the address and send the amount using your Monero wallet.",
"priceOnRequest": "Price on request",
"shippingAvailable": "Shipping available",
+ "viewSingular": "view",
+ "viewPlural": "views",
"share": "Share",
"report": "Report",
"moreFromSeller": "More from this seller"
diff --git a/locales/fr.json b/locales/fr.json
index 71b8565..06e2988 100644
--- a/locales/fr.json
+++ b/locales/fr.json
@@ -116,6 +116,8 @@
"contactHint": "Copiez l'adresse et envoyez le montant via votre portefeuille Monero.",
"priceOnRequest": "Prix sur demande",
"shippingAvailable": "Livraison disponible",
+ "viewSingular": "vue",
+ "viewPlural": "vues",
"share": "Partager",
"report": "Signaler",
"moreFromSeller": "Autres annonces du vendeur"