{"version":3,"file":"yotpo-review-detail-actions-element-section.min.js","mappings":"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","sources":["webpack://lange/../node_modules/@integrabeauty/assert/src/index.ts","webpack://lange/./lib/decorate-error.ts","webpack://lange/./sections/yotpo-review-detail-actions/yotpo-review-detail-actions/index.ts","webpack://lange/./sections/yotpo-review-detail-actions/yotpo-review-detail-actions/yotpo-vote.ts"],"sourcesContent":["/**\n * Throws an assertion error if the given condition is falsy.\n *\n * @param expression any expression, although one that evaluates to true or false is preferred by\n * convention\n * @param message optional error text\n */\nexport function assert(expression: unknown, message?: string) {\n  if (!expression) {\n    throw new AssertionError(message);\n  }\n}\n\nexport class AssertionError extends Error {\n  constructor(message = 'Assertion error') {\n    super(message);\n    this.name = this.constructor.name;\n  }\n}\n","/**\n * Copies a single piece of supplementary context onto an error before it is thrown or reported.\n *\n * Use this when decorating a value the calling code did not construct, such as a caught value or an\n * error handed in by another module. An error the caller just built with new is always writable, so\n * plain assignment is fine there. A value from elsewhere may be a foreign object this code does not\n * own. Such a value can carry a non-writable property, such as a DOMException whose code is a read-\n * only accessor, or be frozen outright, and assigning to it then throws. Attaching context must\n * never itself fail, so a rejected assignment is swallowed and the remaining context is still\n * attempted. Guarding each property in isolation means a single read-only slot, the common case,\n * costs only that one property rather than all of them.\n *\n * In practice the value that triggers this is almost always a DOMException that reached the caller\n * from a fetch the browser cut short, such as an abort when an in-flight request is interrupted.\n * The platform defines a DOMException's code, name, and message as read-only attributes, so\n * assigning a code onto one throws under the strict mode that all bundled module code runs in. It\n * surfaces from Safari far more than elsewhere for two reasons. The reported wording, attempted to\n * assign to readonly property, is JavaScriptCore's phrasing for this failure, so the same bug in\n * another engine is worded differently and groups as a separate report, which makes this one look\n * Safari specific even though the read-only rule is not. And iOS Safari abandons in-flight fetches\n * aggressively when a tab is backgrounded or a connection drops, which is when such a DOMException\n * tends to be produced and handed to code that assumed an ordinary writable error.\n *\n * A swallowed failure is logged at a level low enough that it is recorded as a breadcrumb for\n * monitoring rather than escalated into its own captured event. That keeps the lost context visible\n * alongside the eventual report without adding fresh noise of its own. The breadcrumb names the\n * property that could not be attached, the kind of value that rejected it, and why, which is enough\n * to recognize a read-only or frozen caught value after the fact.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Read-only\n * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMException\n * @see https://webidl.spec.whatwg.org/#dfn-read-only\n */\nexport function decorateError(error: any, key: string, value: unknown) {\n  try {\n    error[key] = value;\n  } catch (assignmentError) {\n    let reason: string;\n    if (assignmentError instanceof Error) {\n      reason = assignmentError.message;\n    } else {\n      reason = `${assignmentError}`;\n    }\n\n    console.log('Failed to assign error property', {\n      key,\n      name: error?.name,\n      tag: Object.prototype.toString.call(error),\n      reason\n    });\n  }\n}\n","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\nimport { vote } from './yotpo-vote.js';\n\n/**\n * Renders thumbs up icon for the yotpo review. It handles yotpo api calls on upvote\n */\nclass YotpoReviewDetailActions extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return [\n      'data-votes-up'\n    ];\n  }\n\n  declare readonly dataset: {\n    /**\n     * Yotpo review id to which the review in this element pertain\n     */\n    reviewId: string;\n\n    /**\n     * Yotpo votes up number for the review in this element pertain\n     */\n    votesUp: string;\n  };\n\n  private declare votesUp: number;\n  private voted = false;\n  private onButtonClickedBound = this.onButtonClicked.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n    this.votesUp = parseInt(this.dataset.votesUp, 10);\n\n    const button = this.shadowRoot.querySelector('button');\n    button?.addEventListener('click', this.onButtonClickedBound);\n\n    this.render();\n  }\n\n  public disconnectedCallback() {\n    const button = this.shadowRoot.querySelector('button');\n    button?.removeEventListener('click', this.onButtonClickedBound);\n  }\n\n  public attributeChangedCallback(_name: string, oldValue: string, newValue: string) {\n    this.initializeShadowRoot();\n\n    if (this.isConnected && oldValue !== newValue) {\n      this.votesUp = parseInt(this.dataset.votesUp, 10);\n      this.render();\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private onButtonClicked(_event: MouseEvent) {\n    if (this.voted) {\n      return;\n    }\n\n    const content = this.shadowRoot.querySelector('.content');\n    content.classList.add('loading');\n\n    vote('reviews', this.dataset.reviewId, 'up').then(() => {\n      content.classList.remove('loading');\n      this.voted = true;\n      this.votesUp += 1;\n      this.render();\n    }).catch((error) => {\n      console.warn(error);\n      content.classList.remove('loading');\n    });\n  }\n\n  private render() {\n    const counter = this.shadowRoot.querySelector<HTMLElement>('.counter');\n    if (isNaN(this.votesUp)) {\n      counter.textContent = '0';\n    } else {\n      counter.textContent = this.votesUp.toString();\n    }\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'yotpo-review-detail-actions': YotpoReviewDetailActions;\n  }\n}\n\nif (!customElements.get('yotpo-review-detail-actions')) {\n  customElements.define('yotpo-review-detail-actions', YotpoReviewDetailActions);\n}\n","import { assert } from '@integrabeauty/assert';\nimport { decorateError } from '../../../lib/decorate-error.js';\n\n/**\n * Submits an up or down vote on an answer or a review.\n *\n * @see https://apidocs.yotpo.com/reference/vote-on-reviews\n */\nexport async function vote(type: 'answers' | 'reviews', id: number | string,\n  direction: 'down' | 'up') {\n  assert(type === 'answers' || type === 'reviews', 'Invalid entity type');\n  assert(typeof id === 'string' || typeof id === 'number', 'Invalid id');\n\n  const path = `/apps/yotpo-ugc/${type}/${id}/vote/${direction}`;\n\n  let response;\n  try {\n    response = await fetch(path);\n  } catch (error) {\n    decorateError(error, 'url', path);\n    decorateError(error, 'input', { id, type, direction });\n    throw error;\n  }\n\n  if (!response.ok) {\n    const error: any = new Error(`Received status ${response.status} submitting vote`);\n    error.status = response.status;\n    error.url = response.url;\n    error.input = { id, type, direction };\n    throw error;\n  }\n\n  let responseText;\n  try {\n    responseText = await response.text();\n  } catch (error) {\n    decorateError(error, 'status', response.status);\n    decorateError(error, 'url', response.url);\n    decorateError(error, 'input', { id, type, direction });\n    throw error;\n  }\n\n  let body: ResponseBody;\n  try {\n    body = JSON.parse(responseText);\n  } catch (error) {\n    decorateError(error, 'body', responseText);\n    decorateError(error, 'status', response.status);\n    decorateError(error, 'url', response.url);\n    decorateError(error, 'input', { id, type, direction });\n    throw error;\n  }\n\n  if (!isResponseBody(body)) {\n    const error: any = new Error('Unexpected response body');\n    error.body = body;\n    error.status = response.status;\n    error.url = response.url;\n    error.input = { id, type, direction };\n    throw error;\n  }\n\n  if (body.status.code > 399) {\n    const error: any = new Error('Nested response status code is not ok');\n    error.body = body;\n    error.status = response.status;\n    error.url = response.url;\n    error.input = { id, type, direction };\n    throw error;\n  }\n\n  return body.response;\n}\n\nexport interface ResponseBody {\n  response: {\n    vote: { id: number; };\n  };\n  status: {\n    code: number;\n    message: string;\n  };\n}\n\nexport function isResponseBody(value: any): value is ResponseBody {\n  return typeof value === 'object' && value !== null && typeof value.status === 'object' &&\n    value.status !== null && 'code' in value.status;\n}\n"],"names":["assert","expression","message","AssertionError","Error","constructor","super","this","name","decorateError","error","key","value","assignmentError","reason","console","log","tag","Object","prototype","toString","call","YotpoReviewDetailActions","HTMLElement","observedAttributes","voted","onButtonClickedBound","onButtonClicked","bind","connectedCallback","initializeShadowRoot","votesUp","parseInt","dataset","button","shadowRoot","querySelector","addEventListener","render","disconnectedCallback","removeEventListener","attributeChangedCallback","_name","oldValue","newValue","isConnected","attachShadow","mode","innerHTML","_event","content","classList","add","async","type","id","direction","path","response","responseText","body","fetch","ok","status","url","input","text","JSON","parse","code","vote","reviewId","then","remove","catch","warn","counter","isNaN","textContent","customElements","get","define"],"sourceRoot":""}