christmas/src/structures/Wishlist.js
Frostyfrog d633e90874
Avoid saving product data if nothing was returned (#101)
* fix: Check if productData is set first

Throughout my testing and debugging, it was possible for productData to
be "undefined". To prevent errors, it's best to check for it's existence
first.

Signed-off-by: Colton Wolkins (Laptop) <colton@indicio.tech>

* fix: avoid saving wishlist when there's no change

Signed-off-by: Colton Wolkins (Laptop) <colton@indicio.tech>

---------

Signed-off-by: Colton Wolkins (Laptop) <colton@indicio.tech>
2023-11-25 21:37:15 -05:00

184 lines
4.6 KiB
JavaScript

const getProductData = require('get-product-name')
const u64 = require('u64')
class Wishlist {
static async new (username) {
const instance = new this({ username })
await instance.fetch()
return instance
}
constructor (opts) {
this.username = opts.username
}
async fetch () {
try {
this.doc = await _CC.usersDb.get(this.username)
} catch {
throw new Error(_CC.lang('WISHLIST_FETCH_FAIL'))
}
this.items = this.doc.wishlist
}
async save () {
try {
const { rev } = await _CC.usersDb.put(this.doc)
this.doc._rev = rev
} catch {
await this.fetch()
throw new Error(_CC.lang('WISHLIST_CONFLICT'))
}
}
async get (id) {
const item = this.items.find(item => item.id === id)
if (!item) throw new Error(_CC.lang('WISHLIST_ITEM_MISSING'))
return item
}
async itemsVisibleToUser (username) {
const addedBySelfAtTop = async (items) => {
return [
...items.filter(item => item.addedBy === this.username),
...items.filter(item => item.addedBy !== this.username)
]
}
if (this.username === username) {
return this.items
.filter(item => item.addedBy === username)
}
return addedBySelfAtTop(this.items)
}
async add ({ itemUrlOrName, suggest, note, addedBy }) {
if (!itemUrlOrName) {
throw new Error(_CC.lang('WISHLIST_URL_REQUIRED'))
}
const item = {}
const nonFatalErrors = []
const potentialUrl = itemUrlOrName.split(' ').pop()
const url = parseURL(potentialUrl)
let productData
try {
if (url) productData = await getProductData(url, _CC.config.proxyServer)
} catch (err) {
nonFatalErrors.push(err.toString())
}
item.id = u64.encode(new Date().getTime().toString())
item.name = (productData ? productData.name : '')
item.price = productData?.price
item.image = productData?.image
item.addedBy = addedBy
item.pledgedBy = (addedBy === this.username || suggest ? undefined : addedBy)
item.note = note
if (url) item.url = url
if (!url) item.name = itemUrlOrName
this.items.push(item)
await this.save()
return { nonFatalErrors }
}
async remove (id) {
const index = this.items.findIndex(item => item.id === id)
if (index === -1) throw new Error(_CC.lang('WISHLIST_ITEM_MISSING'))
this.items.splice(index, 1)
await this.save()
}
async pledge (id, user) {
const item = await this.get(id)
item.pledgedBy = user
await this.save()
}
async unpledge (id) {
const item = await this.get(id)
item.pledgedBy = undefined
await this.save()
}
async move (id, places) {
if (places === 0) throw new Error('places should never be 0')
const index = this.items.findIndex(item => item.id === id)
if (index === -1) throw new Error(_CC.lang('WISHLIST_ITEM_MISSING'))
while (this.items[index + places] && this.items[index + places].addedBy !== this.username) {
if (places < 0) {
places--
} else {
places++
}
}
if (index < 0 || index >= this.items.length || index + places < 0 || index + places >= this.items.length) {
throw new Error(_CC.lang('WISHLIST_MOVE_INVALID'))
}
const item = this.items.splice(index, 1)[0]
this.items.splice(index + places, 0, item)
await this.save()
}
async moveTop (id) {
const index = this.items.findIndex(item => item.id === id)
if (index === -1) throw new Error(_CC.lang('WISHLIST_ITEM_MISSING'))
const item = this.items.splice(index, 1)[0]
this.items.unshift(item)
await this.save()
}
async setItemData (id, data) {
const item = await this.get(id)
for (const key of [
'name', 'note', 'url', 'price', 'image'
]) {
if (!Object.prototype.hasOwnProperty.call(data, key)) {
throw new Error(_CC.lang('NOTE_MISSING_PROP', key))
}
item[key] = data[key]
}
await this.save()
}
async refreshItemData (id) {
const item = await this.get(id)
if (!item.url) {
throw new Error(_CC.lang('WISHLIST_REFRESH_NO_URL'))
}
const productData = await getProductData(item.url)
if (!productData) return; // short-circuit when there's no data
for (const key of ['name', 'price', 'image']) {
if (productData[key]) item[key] = productData[key]
}
await this.save()
}
}
function parseURL (string) {
try {
const url = new URL(string)
if (_CC.config.wishlist.smile) {
if (url.hostname === 'www.amazon.com') url.hostname = 'smile.amazon.com'
}
if (url) return url
} catch {}
}
module.exports = { Wishlist }