diff --git a/lib/flavor.js b/lib/flavor.js index e4a77ad1..c0dbef4c 100644 --- a/lib/flavor.js +++ b/lib/flavor.js @@ -3,8 +3,11 @@ export const sample = (arr, seed = '') => { return arr[Math.floor(random * arr.length)] } +export const bound = (num, min, max) => { + return Math.min(Math.max(num, min), max) +} + export const loadingSpinners = ['compass.svg', 'skull.svg'] -const loadingMsg = [''] const pseudoRandom = (seed) => { return Math.sin(seed * 10000) / 2 + 0.5 @@ -17,6 +20,12 @@ export const zeroMessage = [ "Can't buy nothin' with nothin'!", ] +// export const shopBlessed = [ +// "Blimey! You got a blessing, didja? Well that's gotta do good for business seeing such upstanding pirate folk shoping here.", +// "Wow! You got the boon of a pirate's blessing!", +// `Blimey, a pirate's blessing! *She ${transcript('superstitions')}, according to pirate tradition.* Please, shop to your heart's content!`, +// ] + export const purchaseWords = [ // Don't uniquify this! Some are rarer than others 'Acquire', @@ -32,6 +41,7 @@ export const purchaseWords = [ 'Plunder', 'Plunder', 'BitTorrent', + 'Loot', ] export const cantAffordWords = [ @@ -45,16 +55,30 @@ export const cantAffordWords = [ // "when you're richer...", ] -export const shopBanner = [ - 'buy something or get out!', - 'spend your doubloons here!', - 'get that booty!', - 'NO REFUNDS', - 'feeling overburdened by money?', - 'plundered from the best dead adventurers in the land', - 'we accept doubloons and PiratePay™', - 'YARR', - '🏴‍☠️', - "you wouldn't download a ship...", - "These prices are walkin' the plank!", +export const shopIcons = { + freaking: 'https://cloud-iz667ljca-hack-club-bot.vercel.app/0freaking.png', + ded: 'https://cloud-iz667ljca-hack-club-bot.vercel.app/1ded.png', + info: 'https://cloud-iz667ljca-hack-club-bot.vercel.app/2info.png', + peefest: 'https://cloud-iz667ljca-hack-club-bot.vercel.app/3pf.png', + threat: 'https://cloud-iz667ljca-hack-club-bot.vercel.app/4threatened.png', + reading: 'https://cloud-iz667ljca-hack-club-bot.vercel.app/5reading_2.png', + reading2: 'https://cloud-iz667ljca-hack-club-bot.vercel.app/6reading.png', + scallywag: 'https://cloud-iz667ljca-hack-club-bot.vercel.app/7scallywag.png', + search: 'https://cloud-iz667ljca-hack-club-bot.vercel.app/8searching_2.png', + search2: 'https://cloud-iz667ljca-hack-club-bot.vercel.app/9searching.png', + question: 'https://cloud-nfmmdwony-hack-club-bot.vercel.app/0000.png', + cute: 'https://cloud-nfmmdwony-hack-club-bot.vercel.app/3003.png', + tinfoil: 'https://cloud-nfmmdwony-hack-club-bot.vercel.app/4004.png', + holdingEars: 'https://cloud-nfmmdwony-hack-club-bot.vercel.app/4004.png', // todo: draw this + woah: 'https://cloud-e9zuzn0u0-hack-club-bot.vercel.app/3003.png', + thumbs: 'https://cloud-r04a8za6c-hack-club-bot.vercel.app/1001.png', + fluster: + 'https://cloud-3wb98dblo-hack-club-bot.vercel.app/0untitled_artwork.gif', + bapanada: + 'https://cloud-o4qa8261z-hack-club-bot.vercel.app/0270a4575-6fa8-4946-b5c8-3fb5b8d9b722-1660621656235.webp', +} + +export const outOfStock = [ + "icon:question|where'd all the stock go?", + 'icon:question|come back when more washes up on shore', ] diff --git a/lib/transcript.js b/lib/transcript.js new file mode 100644 index 00000000..c47de66c --- /dev/null +++ b/lib/transcript.js @@ -0,0 +1,48 @@ +import transcriptData from './transcript.yml' + +function transcript(path, context = {}, skipArrays = true, depth = 0) { + console.log({ path, depth }) + if (depth > 10) { + console.log('hit recursion depth 10!') + return + } + try { + const leaf = getDescendantProp(transcriptData, path, skipArrays) + if (typeof leaf === 'string') { + return evalInContext('`' + leaf + '`', { ...context, t: transcript }) + } + if (leaf === undefined) { + return `transcript.${path}` + } else { + return leaf + } + } catch (e) { + console.error(e) + return path + } +} + +export { transcript } + +function evalInContext(js, context) { + if (process.env.NODE_ENV === 'development') { + console.log({ js, context }) + } + return function () { + return eval(js) + }.call(context) +} + +function getDescendantProp(obj, desc, skipArrays) { + const arr = desc.split('.') + if (process.env.NODE_ENV === 'development') { + console.log({ arr, obj }) + } + while (arr.length) { + obj = obj[arr.shift()] + if (Array.isArray(obj) && skipArrays) { + obj = obj[Math.floor(Math.random() * obj.length)] + } + } + return obj +} diff --git a/lib/transcript.yml b/lib/transcript.yml new file mode 100644 index 00000000..da55256f --- /dev/null +++ b/lib/transcript.yml @@ -0,0 +1,224 @@ +legalThings: + - ® + - ™ + - ℠ + - ℗ + - © + - ${this.t('legalThings')}${this.t('legalThings')} +banner: + - buy something or get out! + - spend your doubloons here! + - get that booty! + - NO REFUNDS + - feeling overburdened by money? + - plundered from the best dead adventurers in the land + - we accept doubloons and PiratePay${this.t('legalThings')} + - YARR + - 🏴‍☠️ + - you wouldn't download a ship... + - These prices are walkin' the plank! + - you break it, you buy it! + - pirate-owned and operated! + - caveat emptor + - all sales are final +selfClick: + - icon:question|w-what? but I'm not for sale + - icon:info|The only thing for sale here is the merchandise + - icon:holdingEars|hey buddy, hands to yourself + - icon:holdingEars|hands off ${this.t('pirateyThings')}! + - icon:threat|I'll have you know I'm not for sale! + - icon:threat|You ain't gonna plunder this booty! + - icon:freaking|N-no! You can't buy me! |icon:sticker|I'm not gonna be some collectible sticker sheet! + - icon:threat|hey!! i'm allergic to cursors! + - icon:thumbs|guess who's got 2 thumbs and is not for sale! + - click me one more time, i dare you... ya ${this.t('pirateyThings')}! + - icon:freaking|w-wait. |icon:blushing|you want to buy me? really? + - icon:fluster|stop clicking! t-that tickles! + - icon:fluster|w-wait. you want to buy me? really? + - d-did you just click me?! + - icon:threat|*angry raccoon chittering noises* +superstitionItems: + - black cat + - rabbit's foot + - horseshoe + - horse's paw + - rabbit's shoe + - monkey's paw + - whole rabbit + - broken mirror + - ladder + - whole salt shaker + - four-leaf clover + - three-leaf clover with an extra leaf taped on + - whole leprechaun (not just the bones) + - whole leprechaun (not just the charms) + - whole leprechaun (not just the gold) + - lucky coin + - pinch of salt +superstitions: + - spits over shoulder + - spins around three times + - knocks on wood + - throws salt over shoulder + - crosses fingers + - signs the cross with her tail + - spits in her hat + - grabs a rabbit's foot + - pins a ${this.t('superstitionItems')} to her hat + - ties up a ${this.t('superstitionItems')} above the door frame + - throws a ${this.t('superstitionItems')} over her shoulder # be careful. this one... is pretty out there +blessed: + - Blimey! You got a blessing, didja? Well that's gotta do good for business seeing such upstanding pirate folk shoping here. + - Wow! You got the boon of a pirate's blessing! + - Blow me down! You got a pirate's blessing! That's gotta be good luck for me shop! + - Blimey, a pirate's blessing! *She ${this.t('superstitions')}, according to pirate tradition.* Please, shop to your heart's content! +cursed: + - Blimey! You've been cursed? *${this.t('superstitions')}* and then *${this.t('superstitions')}* + - Ye've been cursed? *${this.t('superstitions')}* and then *${this.t('superstitions')}* + - By the seven seas! You've been cursed? *spits over shoulder* + - You've been cursed?! Get off me island you ${this.t('pirateyThings')}! + - Hang on, you're cursed? Go take a long walk off a short plank! + - Hold on... are you cursed? Read the sign– no blessing, no business! + - wait... are you C-C-CURSED?! I don't want any trouble! +getout: + - Getouttahere! + - out out out! + - Get out! + - Come back once you got gold! + - whaddya take me for? wonathose nonprofit thingymajigs? + - you tryin ta steal from a thief! what's the world coming to! + - you can't take this stuff, i stole it fare and square! + - you trying to pull my tail?! +help: + - how can i help ye? + - wadya wanna buy? + - the mutinied capt' trash-beard at your service! +greetings: + - icon:bapanada|speed:2|*sigh* . . . bapanada |speed:default|icon:freaking|errrr... i mean... |icon:scallywag|come in! + - well, if it isnt my favorite customer! + - Ahh, my favorite customer! + - raccoon has wares if you have coin! + - have a pretty penny you want to spend? + - your pockets look shiny today! + - that coinpurse looks heavy, let me help you with that. + - I have a feeling you're going to like what you see today. + - Hey there! You look like you could use some help. + - Oh, hello! How can I help you have a GARBAGE day? +tooManyBells: + - icon:freaking|Alright! |icon:threat|Enough with the |speed:4.1|BLOODY BELLS!~ + - icon:holdingears|S-stop it! I got sensitive hear-holes over here! + - icon:freaking|stop ringing that annoysing bell! + - icon:notamused|argh, I'm regretting plundering that bell in the first place + - icon:threat|I swear on me mum & all my ${21 + Math.floor(Math.random() * 8)} siblings, stop with the bell! + - icon:threat|I didn't get |speed:4|MAROONED|speed:default| on this dog-forsaken island to listen to that! + - S-stop taunting me with those infernal bells! + - Go annoy some tanuki on another island with those bells! +pirateyThings: + - bilge rat + - salty dog + - scurvy dog + - scallywag + - landlubber + - scurvy swashbuckler + - scurvy pirate + - scurvy sea dog + - scurvy sea rat + - scurvy sea scoundrel + - scurvy rat + - scurvy scallywag +music: + - icon:question|where's that music coming from? + - icon:question|who's playing that music? + - icon:question|where are those instruments? + - icon:holdingEars|that music is a little loud +doot: # someone clicks a trumpet while music is playing + - icon:freaking|stop with the trumpet! +fav: + removed: + - icon:question|ah, not interested in the [${this.name.toUpperCase()}]? + - icon:info|awwww, not interested in the [${this.name.toUpperCase()}]? + - icon:thinking|hmmmm... is the [${this.name.toUpperCase()}] not to your liking? + - icon:freaking|you're not interested in the [${this.name.toUpperCase()}]?|icon:threat| you try finding stuff to sell while being marooned on an island! + - icon:question|you don't want the [${this.name.toUpperCase()}]? |icon:info|just try finding it anywhere else for ${this.price} doubloons! + added: + - icon:question|ah, interested in the [${this.name.toUpperCase()}]? + - icon:info|for ${this.price} doubloons, some would consider this a steal!|icon:holdingEars|. . . please don't report me to the authorities... + - icon:info|for ${this.price} doubloons, some would consider this a steal! + - icon:info|ooooh, that's a good choice! + - icon:info|you've got good taste! + - icon:info|just try finding [${this.name.toUpperCase()}] anywhere else for ${this.price} doubloons! + addedCanAffort: + - "|icon:thinking| . . . |icon:question| hold on... can't you already affort that?" + - '|icon:thinking| . . . |icon:question| but... you can already affort that?' + - "|icon:thinking| . . . |icon:question| why'd you fave that if you can already buy it?" + - '|icon:thinking| . . . |icon:question| you know you already have enough to buy that, right?' +floatingThings: + - skull + - coconut + - empty bottle + - ship in a bottle + - drift wood + - turtle shell + - sea shell + - seaweed + - washed up beach ball # wilson!!! + - washed up boot +bigNumbers: + - '[ArithmeticOverflowException]' + - '[LargeIntegerOverflow]' + - a barrel-full + - a nautical butt-load +item: + base: icon:question|you want to buy a |speed:2.5|[${this.name.toUpperCase()}]|speed:default|? + generic: + - \|icon:question|see something you like? + - \|icon:info|those have been pretty popular lately. . . |icon:peefest|speed:4.5| *internally crying as she thinks about the ${this.t('floatingThings')} and ${this.t('floatingThings')} that have been her only customers for the past 3 years*|speed:default + - \|icon:question|uuuuh, how many you want? + - \|icon:cute|yes yes, you buy now! + - '|icon:cute|youuuuu... pay this much? [${this.price} doubloons]' + - '|icon:info|why does this label say DO NOT EAT?' + item_pinecil_20: + - ${this.t('item.generic')} + - '|icon:info|*Warning: May contain lead' + - \|icon:info|I hafta be careful with these– me first mate has a lead allergy! + - \|icon:info|Be careful if you have a lead allergy! + - \|icon:freaking|hot! |speed:3.5|Hot! |speed:4|HOT!|speed:default + - \|icon:info|yes, let me fetch one from the back...|icon:freaking|speed:3.5|ow!|speed:4| Ow!|speed:4.5| OWOWOWOWOWOW! + - \|icon:cute|It’s like welding but tiny! + item_github_notebook_37: + - "|icon:info| if only i could read... |icon:holdingEars| *sigh* |icon:info| i'd be able to tell you all about it" + - "|icon:info|I can't read, but I can tell you it's a notebook!" + - '|icon:cute|a place to draw your treasure maps!' + - '|icon:info|write down where you buried your treasure!' + - '|icon:info|write down where you buried your treasure!|icon:holdingEars| *sigh* . . . i wish i could read' + item_factorio: + - "|icon:fluster| you have NO IDEA how much time i've sunk into this game" + - "|icon:info| interesting fact: this is a GREAT way to kill the time when you're marooned on a deserted island" + item_bambu_lab_a1_mini_49: + - If a picture is worth 1000 words, a 3d print should be worth ${this.t('bigNumbers')} + - Oh, do they print in chocolate?! + - It's like a hot glue gun, but with more steps + item_100mhz_oscilloscope_53: + - if i were a smarter racoon i'd say something about "waves" + item_hot_glue_gun: + - cheaper than a 3d printer! + item_pile_of_stickers_10: + - it's like graffiti, but with less commitment + - the perfect way to vandalize your own property + - ooooh, are you going to put that on your laptop? + - Puts the "ad" in "adhesive"! + item_signed_photo_of_malted_19: + - '|icon:info|Due to inflation the average photo is only worth 700 words' + - "|icon:info|I'm not sure who this is, but they look important!" + - '|icon:question|i dont know why i have these, they just washed up one day' + item_digital_calipers_50: + - '|icon:scallywag|measure twice, stab once!' + - used to make sure your plank is just the right walkin' length + - as sharp as a cutlass! + - as sharp as me first mate's hook! + item_flipper_32: + - \|icon:info|I've tried these, but watch out. . .|icon:wet| they aren't water proof + - \|icon:tinfoil|*dons her tinfoil captain's hat*|speed:0.3|. . .|speed:default| don't point that at me head + item_hack_club_socks__34: + - they aren't hacker socks, but any ${this.t('pirateyThings')} worth their salt can make do... + - as a raccoon, i've already got natural socks, but these are good for me crew! diff --git a/lib/useEventEmitter.ts b/lib/useEventEmitter.ts new file mode 100644 index 00000000..a4dde167 --- /dev/null +++ b/lib/useEventEmitter.ts @@ -0,0 +1,28 @@ +import { useRef, useEffect } from 'react' + +const globalEmitter = new EventTarget() + +export function useEventEmitter() { + const emitterRef = useRef(globalEmitter) + + const emit = (eventName: string, detail?: any) => { + emitterRef.current.dispatchEvent(new CustomEvent(eventName, { detail })) + } + + // handy helper for shopkeeper interactions + const emitYap = (interaction: string) => { + emitterRef.current.dispatchEvent( + new CustomEvent('shopkeeper', { detail: { interaction } }), + ) + } + + const on = (eventName: string, callback: (event: CustomEvent) => void) => { + emitterRef.current.addEventListener(eventName, callback as EventListener) + } + + const off = (eventName: string, callback: (event: CustomEvent) => void) => { + emitterRef.current.removeEventListener(eventName, callback as EventListener) + } + + return { emit, on, off, emitYap } +} diff --git a/lib/yap.js b/lib/yap.js new file mode 100644 index 00000000..11bc0371 --- /dev/null +++ b/lib/yap.js @@ -0,0 +1,108 @@ +import { Howl } from 'howler' +let yapping = false +const yap_sounds = { + // these sounds and most of the yapping code are adapted from https://github.com/equalo-official/animalese-generator + a: new Howl({ src: 'audio/yapping/a.wav' }), + b: new Howl({ src: 'audio/yapping/b.wav' }), + c: new Howl({ src: 'audio/yapping/c.wav' }), + d: new Howl({ src: 'audio/yapping/d.wav' }), + e: new Howl({ src: 'audio/yapping/e.wav' }), + f: new Howl({ src: 'audio/yapping/f.wav' }), + g: new Howl({ src: 'audio/yapping/g.wav' }), + h: new Howl({ src: 'audio/yapping/h.wav' }), + i: new Howl({ src: 'audio/yapping/i.wav' }), + j: new Howl({ src: 'audio/yapping/j.wav' }), + k: new Howl({ src: 'audio/yapping/k.wav' }), + l: new Howl({ src: 'audio/yapping/l.wav' }), + m: new Howl({ src: 'audio/yapping/m.wav' }), + n: new Howl({ src: 'audio/yapping/n.wav' }), + o: new Howl({ src: 'audio/yapping/o.wav' }), + p: new Howl({ src: 'audio/yapping/p.wav' }), + q: new Howl({ src: 'audio/yapping/q.wav' }), + r: new Howl({ src: 'audio/yapping/r.wav' }), + s: new Howl({ src: 'audio/yapping/s.wav' }), + t: new Howl({ src: 'audio/yapping/t.wav' }), + u: new Howl({ src: 'audio/yapping/u.wav' }), + v: new Howl({ src: 'audio/yapping/v.wav' }), + w: new Howl({ src: 'audio/yapping/w.wav' }), + x: new Howl({ src: 'audio/yapping/x.wav' }), + y: new Howl({ src: 'audio/yapping/y.wav' }), + z: new Howl({ src: 'audio/yapping/z.wav' }), + th: new Howl({ src: 'audio/yapping/th.wav' }), + sh: new Howl({ src: 'audio/yapping/sh.wav' }), + _: new Howl({ src: 'audio/yapping/_.wav' }), +} + +export async function yap( + text, + { + letterCallback = () => {}, + endCallback = () => {}, + baseRate = 3.2, + rateVariance = 1, + } = {}, +) { + yapping = true + + return new Promise((resolve, reject) => { + const yap_queue = [] + for (let i = 0; i < text.length; i++) { + const char = text[i] + const lowerChar = char?.toLowerCase() + const prevChar = text[i - 1] + const prevLowerChar = prevChar?.toLowerCase() + const nextChar = text[i + 1] + const nextLowerChar = nextChar?.toLowerCase() + + if (lowerChar === 's' && nextLowerChar === 'h') { + // test for 'sh' sound + yap_queue.push({ letter: char, sound: yap_sounds['sh'] }) + continue + } else if (lowerChar === 't' && nextLowerChar === 'h') { + // test for 'th' sound + yap_queue.push({ letter: char, sound: yap_sounds['th'] }) + continue + } else if ( + lowerChar === 'h' && + (prevLowerChar === 's' || prevLowerChar === 't') + ) { + // test if previous letter was 's' or 't' and current letter is 'h' + yap_queue.push({ letter: char, sound: yap_sounds['_'] }) + continue + } else if (',?. '.includes(char)) { + yap_queue.push({ letter: char, sound: yap_sounds['_'] }) + continue + } else if (lowerChar === prevLowerChar) { + // skip repeat letters + yap_queue.push({ letter: char, sound: yap_sounds['_'] }) + continue + } + + if (lowerChar.match(/[a-z.]/)) { + yap_queue.push({ letter: char, sound: yap_sounds[lowerChar] }) + continue // skip characters that are not letters or periods + } + + yap_queue.push({ letter: char, sound: yap_sounds['_'] }) + } + + function next_yap() { + if (yap_queue.length === 0) { + console.log('yap done') + endCallback() + yapping = false + return resolve() + } + let { sound, letter } = yap_queue.shift() + sound.rate(Math.random() * rateVariance + baseRate) + sound.volume(1) + sound.once('end', next_yap) + sound.play() + sound.once('play', () => { + letterCallback({ sound, letter, length: yap_queue.length }) + }) + } + + next_yap() + }) +} diff --git a/next.config.mjs b/next.config.mjs index bcf9de1f..370c588c 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,6 +1,7 @@ /** @type {import('next').NextConfig} */ import { withPlausibleProxy } from 'next-plausible' +import withYaml from 'next-plugin-yaml' import { execSync } from 'child_process' const commitHash = execSync('git log --pretty=format:"%h" -n1') .toString() @@ -15,6 +16,7 @@ const nextConfig = { // !! WARN !! ignoreBuildErrors: true, }, + reactStrictMode: false, env: { COMMIT_HASH: commitHash, }, @@ -42,4 +44,6 @@ const sentryContext = { const plausibleConfig = withPlausibleProxy()(nextConfig) -export default withSentryConfig(plausibleConfig, sentryContext) +const yamlConfig = withYaml(plausibleConfig) + +export default withSentryConfig(yamlConfig, sentryContext) diff --git a/package.json b/package.json index f8d30901..3280985d 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "lucide-react": "^0.446.0", "next": "14.2.13", "next-plausible": "^3.12.2", + "next-plugin-yaml": "^1.0.1", "openai": "^4.68.4", "react": "^18", "react-dom": "^18", diff --git a/src/app/[tab]/page.tsx b/src/app/[tab]/page.tsx index ed3d691e..6fb3b02f 100644 --- a/src/app/[tab]/page.tsx +++ b/src/app/[tab]/page.tsx @@ -5,7 +5,7 @@ import Harbor from '../harbor/tabs/tabs' import { createMagicSession, getSession } from '../utils/auth' import { Card } from '@/components/ui/card' import { SoundButton } from '../../components/sound-button.js' -import { useEffect } from 'react' +import { useEffect, useState } from 'react' import useLocalStorageState from '../../../lib/useLocalStorageState' export default function Page({ @@ -56,7 +56,7 @@ export default function Page({ /> {session?.slackId ? ( diff --git a/src/app/harbor/shop/shop-item-component.js b/src/app/harbor/shop/shop-item-component.js index 4dc9e78c..4711e48f 100644 --- a/src/app/harbor/shop/shop-item-component.js +++ b/src/app/harbor/shop/shop-item-component.js @@ -1,5 +1,7 @@ import { motion } from 'framer-motion' +import { useEventEmitter } from '../../../../lib/useEventEmitter' + import { Card, CardContent, @@ -10,9 +12,8 @@ import { import { Button } from '@/components/ui/button' import { useMemo } from 'react' import { cantAffordWords, purchaseWords, sample } from '../../../../lib/flavor' -import useLocalStorageState from '../../../../lib/useLocalStorageState.js' -import { useState } from 'react' import Icon from '@hackclub/icons' +import { transcript } from '../../../../lib/transcript' const ActionArea = ({ item, filterIndex, affordable }) => { const buyWord = useMemo(() => sample(purchaseWords), [item.id]) const getYourRacksUp = useMemo(() => sample(cantAffordWords), [item.id]) @@ -51,6 +52,42 @@ export const ShopItemComponent = ({ scale: 1.05, }, } + const localPrice = filterIndex == 1 ? item.priceUs : item.priceGlobal + const affordable = localPrice <= parseInt(personTicketBalance) + + const { emitYap } = useEventEmitter() + + const handleFavouriteToggle = () => { + setFavouriteItems((prevFav) => { + if (prevFav.includes(item.id)) { + emitYap( + transcript('fav.removed', { + name: item.name, + price: item.priceGlobal, + }), + ) + return prevFav.filter((favItem) => String(favItem) !== item.id) + } else { + if (Math.random() > 0.5) { + let interaction = transcript('fav.added', { + name: item.name, + price: localPrice, + affordable, + }) + if (affordable && Math.random() > 0.9) { + interaction += + ' ' + + transcript('fav.addedCanAffort', { + name: item.name, + price: localPrice, + }) + } + emitYap(interaction) + } + return [...prevFav, item.id] + } + }) + } return ( @@ -79,7 +116,7 @@ export const ShopItemComponent = ({ height={20} className="mr-1" /> - {filterIndex == 1 ? item.priceUs : item.priceGlobal} + {localPrice} {item.minimumHoursEstimated && item.maximumHoursEstimated ? ( @@ -96,6 +133,25 @@ export const ShopItemComponent = ({ {item.name} { + let interaction = transcript('item.base', { + name: item.name, + price: localPrice, + }) + const itemSpecificInteraction = transcript('item.' + item.id) + if (itemSpecificInteraction.startsWith('transcript.')) { + console.log('transcript not found', itemSpecificInteraction) + interaction += + ' ' + + transcript('item.generic', { + name: item.name, + price: localPrice, + }) + } else { + interaction += ' ' + itemSpecificInteraction + } + emitYap(interaction) + }} className="w-full h-full object-cover transition-transform duration-300 hover:scale-105" /> @@ -106,26 +162,9 @@ export const ShopItemComponent = ({ -