From f1e8566eaba2922245eef1170a6287230f7c3cde Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 2 Apr 2026 20:51:40 -0400 Subject: [PATCH 01/31] fix: sends a trimmed Pokemon payload to Poracle instead --- src/features/webhooks/Manage.jsx | 7 +- src/features/webhooks/services/Poracle.js | 95 +++++++++++++++++++++ src/features/webhooks/tiles/TrackedTile.jsx | 6 +- 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/features/webhooks/Manage.jsx b/src/features/webhooks/Manage.jsx index 811320896..b780aea36 100644 --- a/src/features/webhooks/Manage.jsx +++ b/src/features/webhooks/Manage.jsx @@ -97,12 +97,17 @@ export function Manage() { Object.values(tempFilters || {}).filter((x) => x && x.enabled), useWebhookStore.getState().context.ui[category].defaults, ) + const payload = Poracle.toApiPayload( + category, + Object.values(tempFilters || {}).filter((x) => x && x.enabled), + useWebhookStore.getState().context.ui[category].defaults, + ) apolloClient.mutate({ // @ts-ignore mutation: Query.webhook(category.toUpperCase()), variables: { category, - data: values, + data: payload, status: 'POST', }, refetchQueries: [ALL_PROFILES], diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index bb963909e..3bc1d2196 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -294,12 +294,107 @@ export class Poracle { (newPokemon[field] = pkmn[field] === undefined ? defaults[field] : pkmn[field]), ) + newPokemon.allForms = !!pkmn.allForms + newPokemon.byDistance = !!pkmn.byDistance + newPokemon.noIv = !!pkmn.noIv + newPokemon.pvpEntry = !!pkmn.pvpEntry + newPokemon.xs = !!pkmn.xs + newPokemon.xl = !!pkmn.xl return newPokemon }) .filter((pokemon) => pokemon) } } + static toApiPayload(category, entries, defaults) { + const processed = Poracle.processor(category, entries, defaults) + if (category !== 'pokemon') { + return processed + } + + const requiredFields = ['pokemon_id', 'form', 'profile_no', 'template'] + const scalarFields = ['clean', 'distance', 'gender', 'min_time'] + const rangeGroups = [ + ['min_cp', 'max_cp'], + ['min_level', 'max_level'], + ['atk', 'max_atk'], + ['def', 'max_def'], + ['sta', 'max_sta'], + ['rarity', 'max_rarity'], + ['size', 'max_size'], + ['min_weight', 'max_weight'], + ] + const pvpFields = [ + 'pvp_ranking_league', + 'pvp_ranking_best', + 'pvp_ranking_worst', + 'pvp_ranking_min_cp', + 'pvp_ranking_cap', + ] + + return processed.map((pokemon) => { + const payload = {} + + requiredFields.forEach((field) => { + if (pokemon[field] !== undefined) { + payload[field] = pokemon[field] + } + }) + + if (pokemon.noIv) { + ;['clean', 'distance', 'gender'].forEach((field) => { + if ( + pokemon[field] !== undefined && + pokemon[field] !== defaults[field] + ) { + payload[field] = pokemon[field] + } + }) + payload.min_iv = pokemon.min_iv ?? defaults.min_iv + payload.max_iv = pokemon.max_iv ?? defaults.max_iv + return payload + } + + scalarFields.forEach((field) => { + if ( + pokemon[field] !== undefined && + pokemon[field] !== defaults[field] + ) { + payload[field] = pokemon[field] + } + }) + + if ( + pokemon.min_iv !== defaults.min_iv || + pokemon.max_iv !== defaults.max_iv + ) { + payload.min_iv = pokemon.min_iv + payload.max_iv = pokemon.max_iv + } + + rangeGroups.forEach(([low, high]) => { + if ( + pokemon[low] !== undefined && + pokemon[high] !== undefined && + (pokemon[low] !== defaults[low] || pokemon[high] !== defaults[high]) + ) { + payload[low] = pokemon[low] + payload[high] = pokemon[high] + } + }) + + if (pokemon.pvpEntry && pokemon.pvp_ranking_league) { + pvpFields.forEach((field) => { + if (pokemon[field] !== undefined) { + payload[field] = pokemon[field] + } + }) + } + + return payload + }) + } + /** * * @param {object} item diff --git a/src/features/webhooks/tiles/TrackedTile.jsx b/src/features/webhooks/tiles/TrackedTile.jsx index 6ade3b2b4..6c0c4265b 100644 --- a/src/features/webhooks/tiles/TrackedTile.jsx +++ b/src/features/webhooks/tiles/TrackedTile.jsx @@ -32,11 +32,11 @@ export function TrackedTile({ index }) { useWebhookStore.setState((prev) => ({ tempFilters: { ...prev.tempFilters, - [id]: { ...item, byDistance: !!item.distance }, + [id]: { ...defaults, ...item, byDistance: !!item.distance }, }, })) } - }, [advOpen, id, item]) + }, [advOpen, defaults, id, item]) const onClose = React.useCallback( (newFilter, save) => { @@ -44,7 +44,7 @@ export function TrackedTile({ index }) { apolloClient.mutate({ mutation: webhookNodes[category.toUpperCase()], variables: { - data: Poracle.processor(category, [newFilter], defaults), + data: Poracle.toApiPayload(category, [newFilter], defaults), status: 'POST', category, }, From 21d7229efc228fafea80a9e8c92f159154f3a550 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 2 Apr 2026 21:00:27 -0400 Subject: [PATCH 02/31] fix: review findings --- src/features/webhooks/services/Poracle.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 3bc1d2196..82d201159 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -258,6 +258,7 @@ export class Poracle { return entries .map((pkmn) => { const fields = [ + 'uid', 'pokemon_id', 'form', 'clean', @@ -312,7 +313,13 @@ export class Poracle { return processed } - const requiredFields = ['pokemon_id', 'form', 'profile_no', 'template'] + const requiredFields = [ + 'uid', + 'pokemon_id', + 'form', + 'profile_no', + 'template', + ] const scalarFields = ['clean', 'distance', 'gender', 'min_time'] const rangeGroups = [ ['min_cp', 'max_cp'], @@ -342,7 +349,7 @@ export class Poracle { }) if (pokemon.noIv) { - ;['clean', 'distance', 'gender'].forEach((field) => { + scalarFields.forEach((field) => { if ( pokemon[field] !== undefined && pokemon[field] !== defaults[field] From 14f57552cc081af0b7a7791e83791560d2774851 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 2 Apr 2026 21:09:33 -0400 Subject: [PATCH 03/31] =?UTF-8?q?fix:=20avoid=20treating=20add-new=20Pok?= =?UTF-8?q?=C3=A9mon=20filters=20as=20updates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/webhooks/services/Poracle.js | 14 ++++++-------- src/features/webhooks/tiles/TrackedTile.jsx | 4 +++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 82d201159..1b99bce3b 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -307,19 +307,13 @@ export class Poracle { } } - static toApiPayload(category, entries, defaults) { + static toApiPayload(category, entries, defaults, options = {}) { const processed = Poracle.processor(category, entries, defaults) if (category !== 'pokemon') { return processed } - const requiredFields = [ - 'uid', - 'pokemon_id', - 'form', - 'profile_no', - 'template', - ] + const requiredFields = ['pokemon_id', 'form', 'profile_no', 'template'] const scalarFields = ['clean', 'distance', 'gender', 'min_time'] const rangeGroups = [ ['min_cp', 'max_cp'], @@ -342,6 +336,10 @@ export class Poracle { return processed.map((pokemon) => { const payload = {} + if (options.includeUid && pokemon.uid !== undefined) { + payload.uid = pokemon.uid + } + requiredFields.forEach((field) => { if (pokemon[field] !== undefined) { payload[field] = pokemon[field] diff --git a/src/features/webhooks/tiles/TrackedTile.jsx b/src/features/webhooks/tiles/TrackedTile.jsx index 6c0c4265b..2968f56c7 100644 --- a/src/features/webhooks/tiles/TrackedTile.jsx +++ b/src/features/webhooks/tiles/TrackedTile.jsx @@ -44,7 +44,9 @@ export function TrackedTile({ index }) { apolloClient.mutate({ mutation: webhookNodes[category.toUpperCase()], variables: { - data: Poracle.toApiPayload(category, [newFilter], defaults), + data: Poracle.toApiPayload(category, [newFilter], defaults, { + includeUid: true, + }), status: 'POST', category, }, From 28ef291c6c61d4e9f8a5de34d37089bf2460f4c5 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 2 Apr 2026 21:28:20 -0400 Subject: [PATCH 04/31] fix: preserve no-iv Pokemon filter ranges --- src/features/webhooks/services/Poracle.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 1b99bce3b..9abb1d486 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -355,6 +355,16 @@ export class Poracle { payload[field] = pokemon[field] } }) + rangeGroups.forEach(([low, high]) => { + if ( + pokemon[low] !== undefined && + pokemon[high] !== undefined && + (pokemon[low] !== defaults[low] || pokemon[high] !== defaults[high]) + ) { + payload[low] = pokemon[low] + payload[high] = pokemon[high] + } + }) payload.min_iv = pokemon.min_iv ?? defaults.min_iv payload.max_iv = pokemon.max_iv ?? defaults.max_iv return payload From a2cd348c18db65a8f8630056ca2d6a0500280fe0 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 2 Apr 2026 21:45:25 -0400 Subject: [PATCH 05/31] fix: preserve Pokemon webhook edit state --- src/features/webhooks/services/Poracle.js | 7 ++----- src/features/webhooks/tiles/TrackedTile.jsx | 4 +--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 9abb1d486..c43d9970b 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -266,6 +266,7 @@ export class Poracle { 'min_time', 'template', 'profile_no', + 'ping', 'gender', 'rarity', 'max_rarity', @@ -307,7 +308,7 @@ export class Poracle { } } - static toApiPayload(category, entries, defaults, options = {}) { + static toApiPayload(category, entries, defaults) { const processed = Poracle.processor(category, entries, defaults) if (category !== 'pokemon') { return processed @@ -336,10 +337,6 @@ export class Poracle { return processed.map((pokemon) => { const payload = {} - if (options.includeUid && pokemon.uid !== undefined) { - payload.uid = pokemon.uid - } - requiredFields.forEach((field) => { if (pokemon[field] !== undefined) { payload[field] = pokemon[field] diff --git a/src/features/webhooks/tiles/TrackedTile.jsx b/src/features/webhooks/tiles/TrackedTile.jsx index 2968f56c7..587db61a2 100644 --- a/src/features/webhooks/tiles/TrackedTile.jsx +++ b/src/features/webhooks/tiles/TrackedTile.jsx @@ -44,9 +44,7 @@ export function TrackedTile({ index }) { apolloClient.mutate({ mutation: webhookNodes[category.toUpperCase()], variables: { - data: Poracle.toApiPayload(category, [newFilter], defaults, { - includeUid: true, - }), + data: Poracle.processor(category, [newFilter], defaults), status: 'POST', category, }, From 0526ea60603cf20e12eb502c60ebbb456f3035b6 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 2 Apr 2026 22:06:21 -0400 Subject: [PATCH 06/31] fix: keep Pokemon webhook UI flags local --- src/features/webhooks/Manage.jsx | 2 +- src/features/webhooks/services/Poracle.js | 32 ++++++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/features/webhooks/Manage.jsx b/src/features/webhooks/Manage.jsx index b780aea36..66d835296 100644 --- a/src/features/webhooks/Manage.jsx +++ b/src/features/webhooks/Manage.jsx @@ -92,7 +92,7 @@ export function Manage() { React.useEffect(() => { if (!addNew.open && addNew.save && category !== 'human') { const { tempFilters } = useWebhookStore.getState() - const values = Poracle.processor( + const values = Poracle.toLocalState( category, Object.values(tempFilters || {}).filter((x) => x && x.enabled), useWebhookStore.getState().context.ui[category].defaults, diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index c43d9970b..40e32e0db 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -296,18 +296,38 @@ export class Poracle { (newPokemon[field] = pkmn[field] === undefined ? defaults[field] : pkmn[field]), ) - newPokemon.allForms = !!pkmn.allForms - newPokemon.byDistance = !!pkmn.byDistance - newPokemon.noIv = !!pkmn.noIv - newPokemon.pvpEntry = !!pkmn.pvpEntry - newPokemon.xs = !!pkmn.xs - newPokemon.xl = !!pkmn.xl return newPokemon }) .filter((pokemon) => pokemon) } } + static toLocalState(category, entries, defaults) { + const processed = Poracle.processor(category, entries, defaults) + if (category !== 'pokemon') { + return processed + } + + const uiState = new Map( + entries.map((pokemon) => [ + `${pokemon.pokemon_id}-${pokemon.form}`, + { + allForms: !!pokemon.allForms, + byDistance: !!pokemon.byDistance, + noIv: !!pokemon.noIv, + pvpEntry: !!pokemon.pvpEntry, + xs: !!pokemon.xs, + xl: !!pokemon.xl, + }, + ]), + ) + + return processed.map((pokemon) => ({ + ...pokemon, + ...uiState.get(`${pokemon.pokemon_id}-${pokemon.form}`), + })) + } + static toApiPayload(category, entries, defaults) { const processed = Poracle.processor(category, entries, defaults) if (category !== 'pokemon') { From 2211aab17e76d66ad3a9744a1573f68e87359cd5 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 2 Apr 2026 22:18:02 -0400 Subject: [PATCH 07/31] fix: preserve Pokemon create payload modes --- src/features/webhooks/services/Poracle.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 40e32e0db..2b7d976a9 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -329,7 +329,7 @@ export class Poracle { } static toApiPayload(category, entries, defaults) { - const processed = Poracle.processor(category, entries, defaults) + const processed = Poracle.toLocalState(category, entries, defaults) if (category !== 'pokemon') { return processed } @@ -363,6 +363,10 @@ export class Poracle { } }) + if (pokemon.ping) { + payload.ping = pokemon.ping + } + if (pokemon.noIv) { scalarFields.forEach((field) => { if ( From 5df4ff978939a9c910a63f1bf079e2cb5981aabf Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 2 Apr 2026 22:31:02 -0400 Subject: [PATCH 08/31] fix: preserve all-forms Pokemon mode state --- src/features/webhooks/services/Poracle.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 2b7d976a9..4d901ac93 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -308,19 +308,20 @@ export class Poracle { return processed } - const uiState = new Map( - entries.map((pokemon) => [ - `${pokemon.pokemon_id}-${pokemon.form}`, - { + const uiState = new Map() + entries.forEach((pokemon) => { + const key = `${pokemon.pokemon_id}-${pokemon.form}` + if (!uiState.has(key)) { + uiState.set(key, { allForms: !!pokemon.allForms, byDistance: !!pokemon.byDistance, noIv: !!pokemon.noIv, pvpEntry: !!pokemon.pvpEntry, xs: !!pokemon.xs, xl: !!pokemon.xl, - }, - ]), - ) + }) + } + }) return processed.map((pokemon) => ({ ...pokemon, From 701a88c13d81de895207683553ce60b92e295c24 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 2 Apr 2026 22:44:05 -0400 Subject: [PATCH 09/31] fix: separate Pokemon update serialization --- src/features/webhooks/services/Poracle.js | 148 +++++++++++--------- src/features/webhooks/tiles/TrackedTile.jsx | 2 +- 2 files changed, 85 insertions(+), 65 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 4d901ac93..21927ab9c 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -177,7 +177,7 @@ export class Poracle { return reactMapFriendly } - static processor(category, entries, defaults) { + static processPokemon(entries, defaults, includeUiState = false) { const pvpFields = [ 'pvp_ranking_league', 'pvp_ranking_best', @@ -194,6 +194,70 @@ export class Poracle { 'pvpEntry', ] const dupes = {} + + return entries + .map((pokemon) => { + const normalized = pokemon.allForms + ? { ...pokemon, form: defaults.form } + : pokemon + const fields = [ + 'uid', + 'pokemon_id', + 'form', + 'clean', + 'distance', + 'min_time', + 'template', + 'profile_no', + 'ping', + 'gender', + 'rarity', + 'max_rarity', + 'size', + 'max_size', + ] + const newPokemon = {} + + if (pokemon.allForms) { + if (dupes[pokemon.pokemon_id]) { + return null + } + dupes[pokemon.pokemon_id] = true + } + + if (pokemon.pvpEntry) { + fields.push(...pvpFields) + } else { + fields.push( + ...Object.keys(normalized).filter( + (key) => !pvpFields.includes(key) && !ignoredFields.includes(key), + ), + ) + } + + new Set(fields).forEach((field) => { + newPokemon[field] = + normalized[field] === undefined + ? defaults[field] + : normalized[field] + }) + + if (includeUiState) { + newPokemon.allForms = !!pokemon.allForms + newPokemon.byDistance = !!pokemon.byDistance + newPokemon.noIv = !!pokemon.noIv + newPokemon.pvpEntry = !!pokemon.pvpEntry + newPokemon.xs = !!pokemon.xs + newPokemon.xl = !!pokemon.xl + } + + return newPokemon + }) + .filter((pokemon) => pokemon) + } + + static processor(category, entries, defaults) { + const dupes = {} switch (category) { case 'egg': return entries.map((egg) => ({ @@ -255,78 +319,34 @@ export class Poracle { }) .filter((quest) => quest) default: - return entries - .map((pkmn) => { - const fields = [ - 'uid', - 'pokemon_id', - 'form', - 'clean', - 'distance', - 'min_time', - 'template', - 'profile_no', - 'ping', - 'gender', - 'rarity', - 'max_rarity', - 'size', - 'max_size', - ] - const newPokemon = {} - if (pkmn.allForms) { - pkmn.form = 0 - if (dupes[pkmn.pokemon_id]) { - return null - } - dupes[pkmn.pokemon_id] = true - } - if (pkmn.pvpEntry) { - fields.push(...pvpFields) - } else { - fields.push( - ...Object.keys(pkmn).filter( - (key) => - !pvpFields.includes(key) && !ignoredFields.includes(key), - ), - ) - } - new Set(fields).forEach( - (field) => - (newPokemon[field] = - pkmn[field] === undefined ? defaults[field] : pkmn[field]), - ) - return newPokemon - }) - .filter((pokemon) => pokemon) + return Poracle.processPokemon(entries, defaults) } } static toLocalState(category, entries, defaults) { - const processed = Poracle.processor(category, entries, defaults) + if (category !== 'pokemon') { + return Poracle.processor(category, entries, defaults) + } + + return Poracle.processPokemon(entries, defaults, true) + } + + static toUpdatePayload(category, entries, defaults) { + const processed = Poracle.toLocalState(category, entries, defaults) if (category !== 'pokemon') { return processed } - const uiState = new Map() - entries.forEach((pokemon) => { - const key = `${pokemon.pokemon_id}-${pokemon.form}` - if (!uiState.has(key)) { - uiState.set(key, { - allForms: !!pokemon.allForms, - byDistance: !!pokemon.byDistance, - noIv: !!pokemon.noIv, - pvpEntry: !!pokemon.pvpEntry, - xs: !!pokemon.xs, - xl: !!pokemon.xl, - }) - } + return processed.map((pokemon) => { + const payload = { ...pokemon } + delete payload.allForms + delete payload.byDistance + delete payload.noIv + delete payload.pvpEntry + delete payload.xs + delete payload.xl + return payload }) - - return processed.map((pokemon) => ({ - ...pokemon, - ...uiState.get(`${pokemon.pokemon_id}-${pokemon.form}`), - })) } static toApiPayload(category, entries, defaults) { diff --git a/src/features/webhooks/tiles/TrackedTile.jsx b/src/features/webhooks/tiles/TrackedTile.jsx index 587db61a2..f709fd193 100644 --- a/src/features/webhooks/tiles/TrackedTile.jsx +++ b/src/features/webhooks/tiles/TrackedTile.jsx @@ -44,7 +44,7 @@ export function TrackedTile({ index }) { apolloClient.mutate({ mutation: webhookNodes[category.toUpperCase()], variables: { - data: Poracle.processor(category, [newFilter], defaults), + data: Poracle.toUpdatePayload(category, [newFilter], defaults), status: 'POST', category, }, From 677892af865142be0015c38d953bf62e6ca9e25c Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 2 Apr 2026 23:02:35 -0400 Subject: [PATCH 10/31] fix: normalize Pokemon update payloads --- src/features/webhooks/services/Poracle.js | 70 ++++++++++++++++++++--- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 21927ab9c..6b53c30cf 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -337,14 +337,70 @@ export class Poracle { return processed } + const requiredFields = [ + 'uid', + 'pokemon_id', + 'form', + 'profile_no', + 'template', + ] + const scalarFields = ['clean', 'distance', 'gender', 'min_time'] + const rangeGroups = [ + ['min_cp', 'max_cp'], + ['min_level', 'max_level'], + ['atk', 'max_atk'], + ['def', 'max_def'], + ['sta', 'max_sta'], + ['rarity', 'max_rarity'], + ['size', 'max_size'], + ['min_weight', 'max_weight'], + ] + const pvpFields = [ + 'pvp_ranking_league', + 'pvp_ranking_best', + 'pvp_ranking_worst', + 'pvp_ranking_min_cp', + 'pvp_ranking_cap', + ] + return processed.map((pokemon) => { - const payload = { ...pokemon } - delete payload.allForms - delete payload.byDistance - delete payload.noIv - delete payload.pvpEntry - delete payload.xs - delete payload.xl + const payload = {} + + requiredFields.forEach((field) => { + if (pokemon[field] !== undefined) { + payload[field] = pokemon[field] + } + }) + + if (pokemon.ping !== undefined) { + payload.ping = pokemon.ping + } + + scalarFields.forEach((field) => { + payload[field] = + pokemon[field] === undefined ? defaults[field] : pokemon[field] + }) + + payload.min_iv = + pokemon.min_iv === undefined ? defaults.min_iv : pokemon.min_iv + payload.max_iv = + pokemon.max_iv === undefined ? defaults.max_iv : pokemon.max_iv + + rangeGroups.forEach(([low, high]) => { + payload[low] = pokemon[low] === undefined ? defaults[low] : pokemon[low] + payload[high] = + pokemon[high] === undefined ? defaults[high] : pokemon[high] + }) + + pvpFields.forEach((field) => { + payload[field] = + pokemon.pvpEntry && pokemon.pvp_ranking_league + ? pokemon[field] === undefined + ? defaults[field] + : pokemon[field] + : defaults[field] + }) + return payload }) } From 75826c84a2ad28e0b1062518b13556e399cd8c7e Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 2 Apr 2026 23:29:00 -0400 Subject: [PATCH 11/31] fix: preserve Pokemon create payload defaults --- src/features/webhooks/services/Poracle.js | 96 +---------------------- 1 file changed, 3 insertions(+), 93 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 6b53c30cf..17cbeacfc 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -406,104 +406,14 @@ export class Poracle { } static toApiPayload(category, entries, defaults) { - const processed = Poracle.toLocalState(category, entries, defaults) + const processed = Poracle.toUpdatePayload(category, entries, defaults) if (category !== 'pokemon') { return processed } - const requiredFields = ['pokemon_id', 'form', 'profile_no', 'template'] - const scalarFields = ['clean', 'distance', 'gender', 'min_time'] - const rangeGroups = [ - ['min_cp', 'max_cp'], - ['min_level', 'max_level'], - ['atk', 'max_atk'], - ['def', 'max_def'], - ['sta', 'max_sta'], - ['rarity', 'max_rarity'], - ['size', 'max_size'], - ['min_weight', 'max_weight'], - ] - const pvpFields = [ - 'pvp_ranking_league', - 'pvp_ranking_best', - 'pvp_ranking_worst', - 'pvp_ranking_min_cp', - 'pvp_ranking_cap', - ] - return processed.map((pokemon) => { - const payload = {} - - requiredFields.forEach((field) => { - if (pokemon[field] !== undefined) { - payload[field] = pokemon[field] - } - }) - - if (pokemon.ping) { - payload.ping = pokemon.ping - } - - if (pokemon.noIv) { - scalarFields.forEach((field) => { - if ( - pokemon[field] !== undefined && - pokemon[field] !== defaults[field] - ) { - payload[field] = pokemon[field] - } - }) - rangeGroups.forEach(([low, high]) => { - if ( - pokemon[low] !== undefined && - pokemon[high] !== undefined && - (pokemon[low] !== defaults[low] || pokemon[high] !== defaults[high]) - ) { - payload[low] = pokemon[low] - payload[high] = pokemon[high] - } - }) - payload.min_iv = pokemon.min_iv ?? defaults.min_iv - payload.max_iv = pokemon.max_iv ?? defaults.max_iv - return payload - } - - scalarFields.forEach((field) => { - if ( - pokemon[field] !== undefined && - pokemon[field] !== defaults[field] - ) { - payload[field] = pokemon[field] - } - }) - - if ( - pokemon.min_iv !== defaults.min_iv || - pokemon.max_iv !== defaults.max_iv - ) { - payload.min_iv = pokemon.min_iv - payload.max_iv = pokemon.max_iv - } - - rangeGroups.forEach(([low, high]) => { - if ( - pokemon[low] !== undefined && - pokemon[high] !== undefined && - (pokemon[low] !== defaults[low] || pokemon[high] !== defaults[high]) - ) { - payload[low] = pokemon[low] - payload[high] = pokemon[high] - } - }) - - if (pokemon.pvpEntry && pokemon.pvp_ranking_league) { - pvpFields.forEach((field) => { - if (pokemon[field] !== undefined) { - payload[field] = pokemon[field] - } - }) - } - + const payload = { ...pokemon } + delete payload.uid return payload }) } From 80208546c18751f5988e4a707a0789a53cdaca30 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 00:06:27 -0400 Subject: [PATCH 12/31] fix: copilot comments --- src/features/webhooks/Manage.jsx | 19 +++++++------- src/features/webhooks/services/Poracle.js | 31 ++++++++++------------- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/features/webhooks/Manage.jsx b/src/features/webhooks/Manage.jsx index 66d835296..59dd03b47 100644 --- a/src/features/webhooks/Manage.jsx +++ b/src/features/webhooks/Manage.jsx @@ -91,17 +91,16 @@ export function Manage() { React.useEffect(() => { if (!addNew.open && addNew.save && category !== 'human') { - const { tempFilters } = useWebhookStore.getState() - const values = Poracle.toLocalState( - category, - Object.values(tempFilters || {}).filter((x) => x && x.enabled), - useWebhookStore.getState().context.ui[category].defaults, - ) - const payload = Poracle.toApiPayload( - category, - Object.values(tempFilters || {}).filter((x) => x && x.enabled), - useWebhookStore.getState().context.ui[category].defaults, + const { + tempFilters, + context: { ui }, + } = useWebhookStore.getState() + const { defaults } = ui[category] + const enabledFilters = Object.values(tempFilters || {}).filter( + (x) => x && x.enabled, ) + const values = Poracle.toLocalState(category, enabledFilters, defaults) + const payload = Poracle.toApiPayload(category, enabledFilters, defaults) apolloClient.mutate({ // @ts-ignore mutation: Query.webhook(category.toUpperCase()), diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 17cbeacfc..520c7659d 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -3,6 +3,14 @@ import { t } from 'i18next' import { useWebhookStore } from '@store/useWebhookStore' +const POKEMON_PVP_FIELDS = [ + 'pvp_ranking_league', + 'pvp_ranking_best', + 'pvp_ranking_worst', + 'pvp_ranking_min_cp', + 'pvp_ranking_cap', +] + export class Poracle { static getMapCategory(poracleCategory) { switch (poracleCategory) { @@ -178,13 +186,6 @@ export class Poracle { } static processPokemon(entries, defaults, includeUiState = false) { - const pvpFields = [ - 'pvp_ranking_league', - 'pvp_ranking_best', - 'pvp_ranking_worst', - 'pvp_ranking_min_cp', - 'pvp_ranking_cap', - ] const ignoredFields = [ 'noIv', 'byDistance', @@ -226,11 +227,13 @@ export class Poracle { } if (pokemon.pvpEntry) { - fields.push(...pvpFields) + fields.push(...POKEMON_PVP_FIELDS) } else { fields.push( ...Object.keys(normalized).filter( - (key) => !pvpFields.includes(key) && !ignoredFields.includes(key), + (key) => + !POKEMON_PVP_FIELDS.includes(key) && + !ignoredFields.includes(key), ), ) } @@ -355,14 +358,6 @@ export class Poracle { ['size', 'max_size'], ['min_weight', 'max_weight'], ] - const pvpFields = [ - 'pvp_ranking_league', - 'pvp_ranking_best', - 'pvp_ranking_worst', - 'pvp_ranking_min_cp', - 'pvp_ranking_cap', - ] - return processed.map((pokemon) => { const payload = {} @@ -392,7 +387,7 @@ export class Poracle { pokemon[high] === undefined ? defaults[high] : pokemon[high] }) - pvpFields.forEach((field) => { + POKEMON_PVP_FIELDS.forEach((field) => { payload[field] = pokemon.pvpEntry && pokemon.pvp_ranking_league ? pokemon[field] === undefined From 0edb7293a0bfb77657e29909b18a0cb2d7c28a2a Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 15:21:19 -0400 Subject: [PATCH 13/31] fix: fix Incl. No IV --- src/features/webhooks/services/Poracle.js | 111 +++++++++++++++++----- 1 file changed, 87 insertions(+), 24 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 520c7659d..1232c4b2b 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -11,6 +11,31 @@ const POKEMON_PVP_FIELDS = [ 'pvp_ranking_cap', ] +const POKEMON_CREATE_REQUIRED_FIELDS = [ + 'pokemon_id', + 'form', + 'profile_no', + 'template', +] + +const POKEMON_UPDATE_REQUIRED_FIELDS = [ + 'uid', + ...POKEMON_CREATE_REQUIRED_FIELDS, +] + +const POKEMON_SCALAR_FIELDS = ['clean', 'distance', 'gender', 'min_time'] + +const POKEMON_RANGE_GROUPS = [ + ['min_cp', 'max_cp'], + ['min_level', 'max_level'], + ['atk', 'max_atk'], + ['def', 'max_def'], + ['sta', 'max_sta'], + ['rarity', 'max_rarity'], + ['size', 'max_size'], + ['min_weight', 'max_weight'], +] + export class Poracle { static getMapCategory(poracleCategory) { switch (poracleCategory) { @@ -340,28 +365,10 @@ export class Poracle { return processed } - const requiredFields = [ - 'uid', - 'pokemon_id', - 'form', - 'profile_no', - 'template', - ] - const scalarFields = ['clean', 'distance', 'gender', 'min_time'] - const rangeGroups = [ - ['min_cp', 'max_cp'], - ['min_level', 'max_level'], - ['atk', 'max_atk'], - ['def', 'max_def'], - ['sta', 'max_sta'], - ['rarity', 'max_rarity'], - ['size', 'max_size'], - ['min_weight', 'max_weight'], - ] return processed.map((pokemon) => { const payload = {} - requiredFields.forEach((field) => { + POKEMON_UPDATE_REQUIRED_FIELDS.forEach((field) => { if (pokemon[field] !== undefined) { payload[field] = pokemon[field] } @@ -371,7 +378,7 @@ export class Poracle { payload.ping = pokemon.ping } - scalarFields.forEach((field) => { + POKEMON_SCALAR_FIELDS.forEach((field) => { payload[field] = pokemon[field] === undefined ? defaults[field] : pokemon[field] }) @@ -381,7 +388,7 @@ export class Poracle { payload.max_iv = pokemon.max_iv === undefined ? defaults.max_iv : pokemon.max_iv - rangeGroups.forEach(([low, high]) => { + POKEMON_RANGE_GROUPS.forEach(([low, high]) => { payload[low] = pokemon[low] === undefined ? defaults[low] : pokemon[low] payload[high] = pokemon[high] === undefined ? defaults[high] : pokemon[high] @@ -401,14 +408,70 @@ export class Poracle { } static toApiPayload(category, entries, defaults) { - const processed = Poracle.toUpdatePayload(category, entries, defaults) + const processed = Poracle.toLocalState(category, entries, defaults) if (category !== 'pokemon') { return processed } return processed.map((pokemon) => { - const payload = { ...pokemon } - delete payload.uid + const payload = {} + + POKEMON_CREATE_REQUIRED_FIELDS.forEach((field) => { + if (pokemon[field] !== undefined) { + payload[field] = pokemon[field] + } + }) + + if (pokemon.ping !== undefined) { + payload.ping = pokemon.ping + } + + POKEMON_SCALAR_FIELDS.forEach((field) => { + if ( + pokemon[field] !== undefined && + pokemon[field] !== defaults[field] + ) { + payload[field] = pokemon[field] + } + }) + + if (pokemon.noIv) { + payload.min_iv = + pokemon.min_iv === undefined ? defaults.min_iv : pokemon.min_iv + payload.max_iv = + pokemon.max_iv === undefined ? defaults.max_iv : pokemon.max_iv + return payload + } + + if ( + pokemon.min_iv !== undefined && + pokemon.max_iv !== undefined && + (pokemon.min_iv !== defaults.min_iv || + pokemon.max_iv !== defaults.max_iv) + ) { + payload.min_iv = pokemon.min_iv + payload.max_iv = pokemon.max_iv + } + + POKEMON_RANGE_GROUPS.forEach(([low, high]) => { + if ( + pokemon[low] !== undefined && + pokemon[high] !== undefined && + (pokemon[low] !== defaults[low] || pokemon[high] !== defaults[high]) + ) { + payload[low] = pokemon[low] + payload[high] = pokemon[high] + } + }) + + if (pokemon.pvpEntry && pokemon.pvp_ranking_league) { + POKEMON_PVP_FIELDS.forEach((field) => { + if (pokemon[field] !== undefined) { + payload[field] = pokemon[field] + } + }) + } + return payload }) } From dc9ff5996f26b58fdfb01a8c2ae666b1890bc328 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 15:37:35 -0400 Subject: [PATCH 14/31] fix(webhooks): preserve no-iv pokemon ranges --- src/features/webhooks/services/Poracle.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 1232c4b2b..645e204be 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -440,10 +440,7 @@ export class Poracle { pokemon.min_iv === undefined ? defaults.min_iv : pokemon.min_iv payload.max_iv = pokemon.max_iv === undefined ? defaults.max_iv : pokemon.max_iv - return payload - } - - if ( + } else if ( pokemon.min_iv !== undefined && pokemon.max_iv !== undefined && (pokemon.min_iv !== defaults.min_iv || @@ -464,6 +461,10 @@ export class Poracle { } }) + if (pokemon.noIv) { + return payload + } + if (pokemon.pvpEntry && pokemon.pvp_ranking_league) { POKEMON_PVP_FIELDS.forEach((field) => { if (pokemon[field] !== undefined) { From e7cab4c7b668c8547aa3edee2c61b97b5254165a Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 16:08:07 -0400 Subject: [PATCH 15/31] fix(webhooks): preserve pokemon weight defaults --- src/features/webhooks/services/Poracle.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 645e204be..fb3dee2e5 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -461,6 +461,13 @@ export class Poracle { } }) + if (payload.min_weight === undefined) { + payload.min_weight = defaults.min_weight + } + if (payload.max_weight === undefined) { + payload.max_weight = defaults.max_weight + } + if (pokemon.noIv) { return payload } From 8f859770ff51853b6269bef608ffa6dafd633879 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 16:29:53 -0400 Subject: [PATCH 16/31] fix(webhooks): preserve omitted pokemon iv state --- src/features/webhooks/WebhookAdv.jsx | 1 + src/features/webhooks/services/Poracle.js | 18 ++++++++++++++---- src/features/webhooks/tiles/TrackedTile.jsx | 13 ++++++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/features/webhooks/WebhookAdv.jsx b/src/features/webhooks/WebhookAdv.jsx index 381d65ed4..963cacaa7 100644 --- a/src/features/webhooks/WebhookAdv.jsx +++ b/src/features/webhooks/WebhookAdv.jsx @@ -40,6 +40,7 @@ const skipFields = new Set([ 'allForms', 'pvpEntry', 'noIv', + 'omitIvBounds', 'byDistance', 'distance', 'xs', diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index fb3dee2e5..f4cf0b57c 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -214,6 +214,7 @@ export class Poracle { const ignoredFields = [ 'noIv', 'byDistance', + 'omitIvBounds', 'xs', 'xl', 'allForms', @@ -274,6 +275,7 @@ export class Poracle { newPokemon.allForms = !!pokemon.allForms newPokemon.byDistance = !!pokemon.byDistance newPokemon.noIv = !!pokemon.noIv + newPokemon.omitIvBounds = !!pokemon.omitIvBounds newPokemon.pvpEntry = !!pokemon.pvpEntry newPokemon.xs = !!pokemon.xs newPokemon.xl = !!pokemon.xl @@ -367,6 +369,12 @@ export class Poracle { return processed.map((pokemon) => { const payload = {} + const omitIvBounds = + pokemon.omitIvBounds && + !pokemon.noIv && + !pokemon.pvpEntry && + pokemon.min_iv === defaults.min_iv && + pokemon.max_iv === defaults.max_iv POKEMON_UPDATE_REQUIRED_FIELDS.forEach((field) => { if (pokemon[field] !== undefined) { @@ -383,10 +391,12 @@ export class Poracle { pokemon[field] === undefined ? defaults[field] : pokemon[field] }) - payload.min_iv = - pokemon.min_iv === undefined ? defaults.min_iv : pokemon.min_iv - payload.max_iv = - pokemon.max_iv === undefined ? defaults.max_iv : pokemon.max_iv + if (!omitIvBounds) { + payload.min_iv = + pokemon.min_iv === undefined ? defaults.min_iv : pokemon.min_iv + payload.max_iv = + pokemon.max_iv === undefined ? defaults.max_iv : pokemon.max_iv + } POKEMON_RANGE_GROUPS.forEach(([low, high]) => { payload[low] = pokemon[low] === undefined ? defaults[low] : pokemon[low] diff --git a/src/features/webhooks/tiles/TrackedTile.jsx b/src/features/webhooks/tiles/TrackedTile.jsx index f709fd193..9dfda3267 100644 --- a/src/features/webhooks/tiles/TrackedTile.jsx +++ b/src/features/webhooks/tiles/TrackedTile.jsx @@ -29,10 +29,21 @@ export function TrackedTile({ index }) { React.useEffect(() => { if (advOpen.open && advOpen.id === id && advOpen.uid === item.uid) { + const omitIvBounds = + item.omitIvBounds || + (category === 'pokemon' && + item.min_iv === undefined && + item.max_iv === undefined && + !item.pvpEntry) useWebhookStore.setState((prev) => ({ tempFilters: { ...prev.tempFilters, - [id]: { ...defaults, ...item, byDistance: !!item.distance }, + [id]: { + ...defaults, + ...item, + byDistance: !!item.distance, + omitIvBounds, + }, }, })) } From 4c5abf932135acd4899fdaa97685282b18af8e67 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 16:46:15 -0400 Subject: [PATCH 17/31] fix(webhooks): handle nullable omitted iv bounds --- src/features/webhooks/services/Poracle.js | 4 +--- src/features/webhooks/tiles/TrackedTile.jsx | 16 +++++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index f4cf0b57c..ff3ecf36e 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -266,9 +266,7 @@ export class Poracle { new Set(fields).forEach((field) => { newPokemon[field] = - normalized[field] === undefined - ? defaults[field] - : normalized[field] + normalized[field] == null ? defaults[field] : normalized[field] }) if (includeUiState) { diff --git a/src/features/webhooks/tiles/TrackedTile.jsx b/src/features/webhooks/tiles/TrackedTile.jsx index 9dfda3267..885009949 100644 --- a/src/features/webhooks/tiles/TrackedTile.jsx +++ b/src/features/webhooks/tiles/TrackedTile.jsx @@ -32,17 +32,23 @@ export function TrackedTile({ index }) { const omitIvBounds = item.omitIvBounds || (category === 'pokemon' && - item.min_iv === undefined && - item.max_iv === undefined && + item.min_iv == null && + item.max_iv == null && !item.pvpEntry) + const localItem = + category === 'pokemon' + ? Poracle.toLocalState( + category, + [{ ...item, omitIvBounds }], + defaults, + )[0] + : { ...defaults, ...item } useWebhookStore.setState((prev) => ({ tempFilters: { ...prev.tempFilters, [id]: { - ...defaults, - ...item, + ...localItem, byDistance: !!item.distance, - omitIvBounds, }, }, })) From a4b8e6bd303231b69af9160ee02fb8036c78e700 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 17:14:14 -0400 Subject: [PATCH 18/31] fix(webhooks): preserve omitted pokemon defaults --- src/features/webhooks/Manage.jsx | 7 +- src/features/webhooks/WebhookAdv.jsx | 2 +- src/features/webhooks/services/Poracle.js | 154 ++++++++++++++++---- src/features/webhooks/tiles/TrackedTile.jsx | 13 +- 4 files changed, 139 insertions(+), 37 deletions(-) diff --git a/src/features/webhooks/Manage.jsx b/src/features/webhooks/Manage.jsx index 59dd03b47..5e3a7ba66 100644 --- a/src/features/webhooks/Manage.jsx +++ b/src/features/webhooks/Manage.jsx @@ -99,8 +99,13 @@ export function Manage() { const enabledFilters = Object.values(tempFilters || {}).filter( (x) => x && x.enabled, ) - const values = Poracle.toLocalState(category, enabledFilters, defaults) const payload = Poracle.toApiPayload(category, enabledFilters, defaults) + const values = Poracle.toTrackedState( + category, + enabledFilters, + defaults, + payload, + ) apolloClient.mutate({ // @ts-ignore mutation: Query.webhook(category.toUpperCase()), diff --git a/src/features/webhooks/WebhookAdv.jsx b/src/features/webhooks/WebhookAdv.jsx index 963cacaa7..f716b3ced 100644 --- a/src/features/webhooks/WebhookAdv.jsx +++ b/src/features/webhooks/WebhookAdv.jsx @@ -40,7 +40,7 @@ const skipFields = new Set([ 'allForms', 'pvpEntry', 'noIv', - 'omitIvBounds', + 'omittedFields', 'byDistance', 'distance', 'xs', diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index ff3ecf36e..ca14cddf8 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -36,6 +36,16 @@ const POKEMON_RANGE_GROUPS = [ ['min_weight', 'max_weight'], ] +const POKEMON_OMITTABLE_FIELDS = [ + ...new Set([ + ...POKEMON_SCALAR_FIELDS, + 'min_iv', + 'max_iv', + ...POKEMON_RANGE_GROUPS.flat(), + ...POKEMON_PVP_FIELDS, + ]), +] + export class Poracle { static getMapCategory(poracleCategory) { switch (poracleCategory) { @@ -210,11 +220,51 @@ export class Poracle { return reactMapFriendly } + static getPokemonOmittedFields(pokemon) { + if (!pokemon) return {} + + const omittedFields = { ...(pokemon?.omittedFields || {}) } + + POKEMON_OMITTABLE_FIELDS.forEach((field) => { + if (pokemon?.[field] == null) { + omittedFields[field] = true + } + }) + + return omittedFields + } + + static getPokemonFieldValue(pokemon, defaults, field) { + return pokemon[field] == null ? defaults[field] : pokemon[field] + } + + static shouldOmitPokemonField(pokemon, defaults, omittedFields, field) { + return ( + omittedFields[field] && + Poracle.getPokemonFieldValue(pokemon, defaults, field) === defaults[field] + ) + } + + static toTrackedState(category, entries, defaults, payload = []) { + if (category !== 'pokemon') { + return Poracle.toLocalState(category, entries, defaults) + } + + return Poracle.toLocalState( + category, + entries.map((pokemon, index) => ({ + ...pokemon, + omittedFields: Poracle.getPokemonOmittedFields(payload[index]), + })), + defaults, + ) + } + static processPokemon(entries, defaults, includeUiState = false) { const ignoredFields = [ 'noIv', 'byDistance', - 'omitIvBounds', + 'omittedFields', 'xs', 'xl', 'allForms', @@ -227,6 +277,7 @@ export class Poracle { const normalized = pokemon.allForms ? { ...pokemon, form: defaults.form } : pokemon + const omittedFields = Poracle.getPokemonOmittedFields(normalized) const fields = [ 'uid', 'pokemon_id', @@ -265,15 +316,18 @@ export class Poracle { } new Set(fields).forEach((field) => { - newPokemon[field] = - normalized[field] == null ? defaults[field] : normalized[field] + newPokemon[field] = Poracle.getPokemonFieldValue( + normalized, + defaults, + field, + ) }) if (includeUiState) { newPokemon.allForms = !!pokemon.allForms newPokemon.byDistance = !!pokemon.byDistance newPokemon.noIv = !!pokemon.noIv - newPokemon.omitIvBounds = !!pokemon.omitIvBounds + newPokemon.omittedFields = omittedFields newPokemon.pvpEntry = !!pokemon.pvpEntry newPokemon.xs = !!pokemon.xs newPokemon.xl = !!pokemon.xl @@ -367,12 +421,7 @@ export class Poracle { return processed.map((pokemon) => { const payload = {} - const omitIvBounds = - pokemon.omitIvBounds && - !pokemon.noIv && - !pokemon.pvpEntry && - pokemon.min_iv === defaults.min_iv && - pokemon.max_iv === defaults.max_iv + const omittedFields = Poracle.getPokemonOmittedFields(pokemon) POKEMON_UPDATE_REQUIRED_FIELDS.forEach((field) => { if (pokemon[field] !== undefined) { @@ -385,31 +434,80 @@ export class Poracle { } POKEMON_SCALAR_FIELDS.forEach((field) => { - payload[field] = - pokemon[field] === undefined ? defaults[field] : pokemon[field] + const value = Poracle.getPokemonFieldValue(pokemon, defaults, field) + if ( + !Poracle.shouldOmitPokemonField( + pokemon, + defaults, + omittedFields, + field, + ) + ) { + payload[field] = value + } }) - if (!omitIvBounds) { - payload.min_iv = - pokemon.min_iv === undefined ? defaults.min_iv : pokemon.min_iv - payload.max_iv = - pokemon.max_iv === undefined ? defaults.max_iv : pokemon.max_iv + if ( + pokemon.noIv || + !['min_iv', 'max_iv'].every((field) => + Poracle.shouldOmitPokemonField( + pokemon, + defaults, + omittedFields, + field, + ), + ) + ) { + payload.min_iv = Poracle.getPokemonFieldValue( + pokemon, + defaults, + 'min_iv', + ) + payload.max_iv = Poracle.getPokemonFieldValue( + pokemon, + defaults, + 'max_iv', + ) } POKEMON_RANGE_GROUPS.forEach(([low, high]) => { - payload[low] = pokemon[low] === undefined ? defaults[low] : pokemon[low] - payload[high] = - pokemon[high] === undefined ? defaults[high] : pokemon[high] + if ( + ![low, high].every((field) => + Poracle.shouldOmitPokemonField( + pokemon, + defaults, + omittedFields, + field, + ), + ) + ) { + payload[low] = Poracle.getPokemonFieldValue(pokemon, defaults, low) + payload[high] = Poracle.getPokemonFieldValue(pokemon, defaults, high) + } }) - POKEMON_PVP_FIELDS.forEach((field) => { - payload[field] = - pokemon.pvpEntry && pokemon.pvp_ranking_league - ? pokemon[field] === undefined - ? defaults[field] - : pokemon[field] - : defaults[field] - }) + if (pokemon.pvpEntry && pokemon.pvp_ranking_league) { + POKEMON_PVP_FIELDS.forEach((field) => { + payload[field] = Poracle.getPokemonFieldValue( + pokemon, + defaults, + field, + ) + }) + } else if ( + !POKEMON_PVP_FIELDS.every((field) => + Poracle.shouldOmitPokemonField( + pokemon, + defaults, + omittedFields, + field, + ), + ) + ) { + POKEMON_PVP_FIELDS.forEach((field) => { + payload[field] = defaults[field] + }) + } return payload }) diff --git a/src/features/webhooks/tiles/TrackedTile.jsx b/src/features/webhooks/tiles/TrackedTile.jsx index 885009949..6e49a3ecf 100644 --- a/src/features/webhooks/tiles/TrackedTile.jsx +++ b/src/features/webhooks/tiles/TrackedTile.jsx @@ -29,17 +29,16 @@ export function TrackedTile({ index }) { React.useEffect(() => { if (advOpen.open && advOpen.id === id && advOpen.uid === item.uid) { - const omitIvBounds = - item.omitIvBounds || - (category === 'pokemon' && - item.min_iv == null && - item.max_iv == null && - !item.pvpEntry) const localItem = category === 'pokemon' ? Poracle.toLocalState( category, - [{ ...item, omitIvBounds }], + [ + { + ...item, + omittedFields: Poracle.getPokemonOmittedFields(item), + }, + ], defaults, )[0] : { ...defaults, ...item } From 6d93ac9ba2042b2c011695746b5c3df4f74048d5 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 20:51:56 -0400 Subject: [PATCH 19/31] fix(webhooks): preserve pvp edit fields --- src/features/webhooks/services/Poracle.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index ca14cddf8..c7ea2d142 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -305,6 +305,15 @@ export class Poracle { if (pokemon.pvpEntry) { fields.push(...POKEMON_PVP_FIELDS) + if (includeUiState) { + fields.push( + ...Object.keys(defaults).filter( + (key) => + !POKEMON_PVP_FIELDS.includes(key) && + !ignoredFields.includes(key), + ), + ) + } } else { fields.push( ...Object.keys(normalized).filter( From 194696f6714dd8d189a6d7c18ac185f6e5100a75 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 21:16:04 -0400 Subject: [PATCH 20/31] fix(webhooks): clear disabled pokemon pvp filters --- src/features/webhooks/services/Poracle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index c7ea2d142..70908d1be 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -430,7 +430,7 @@ export class Poracle { return processed.map((pokemon) => { const payload = {} - const omittedFields = Poracle.getPokemonOmittedFields(pokemon) + const omittedFields = { ...(pokemon.omittedFields || {}) } POKEMON_UPDATE_REQUIRED_FIELDS.forEach((field) => { if (pokemon[field] !== undefined) { From 33a1f444ce744ee92a853670ee81353d23ff6f1c Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 21:30:29 -0400 Subject: [PATCH 21/31] fix(webhooks): preserve omitted pokemon bounds --- src/features/webhooks/services/Poracle.js | 82 +++++++---------------- 1 file changed, 23 insertions(+), 59 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 70908d1be..b6ed49c49 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -431,6 +431,22 @@ export class Poracle { return processed.map((pokemon) => { const payload = {} const omittedFields = { ...(pokemon.omittedFields || {}) } + const setPokemonField = (field) => { + if ( + !Poracle.shouldOmitPokemonField( + pokemon, + defaults, + omittedFields, + field, + ) + ) { + payload[field] = Poracle.getPokemonFieldValue( + pokemon, + defaults, + field, + ) + } + } POKEMON_UPDATE_REQUIRED_FIELDS.forEach((field) => { if (pokemon[field] !== undefined) { @@ -442,31 +458,9 @@ export class Poracle { payload.ping = pokemon.ping } - POKEMON_SCALAR_FIELDS.forEach((field) => { - const value = Poracle.getPokemonFieldValue(pokemon, defaults, field) - if ( - !Poracle.shouldOmitPokemonField( - pokemon, - defaults, - omittedFields, - field, - ) - ) { - payload[field] = value - } - }) + POKEMON_SCALAR_FIELDS.forEach(setPokemonField) - if ( - pokemon.noIv || - !['min_iv', 'max_iv'].every((field) => - Poracle.shouldOmitPokemonField( - pokemon, - defaults, - omittedFields, - field, - ), - ) - ) { + if (pokemon.noIv) { payload.min_iv = Poracle.getPokemonFieldValue( pokemon, defaults, @@ -477,46 +471,16 @@ export class Poracle { defaults, 'max_iv', ) + } else { + ;['min_iv', 'max_iv'].forEach(setPokemonField) } POKEMON_RANGE_GROUPS.forEach(([low, high]) => { - if ( - ![low, high].every((field) => - Poracle.shouldOmitPokemonField( - pokemon, - defaults, - omittedFields, - field, - ), - ) - ) { - payload[low] = Poracle.getPokemonFieldValue(pokemon, defaults, low) - payload[high] = Poracle.getPokemonFieldValue(pokemon, defaults, high) - } + setPokemonField(low) + setPokemonField(high) }) - if (pokemon.pvpEntry && pokemon.pvp_ranking_league) { - POKEMON_PVP_FIELDS.forEach((field) => { - payload[field] = Poracle.getPokemonFieldValue( - pokemon, - defaults, - field, - ) - }) - } else if ( - !POKEMON_PVP_FIELDS.every((field) => - Poracle.shouldOmitPokemonField( - pokemon, - defaults, - omittedFields, - field, - ), - ) - ) { - POKEMON_PVP_FIELDS.forEach((field) => { - payload[field] = defaults[field] - }) - } + POKEMON_PVP_FIELDS.forEach(setPokemonField) return payload }) From 14dbebd54c6b66e371c2f0591a1234ced45cf6c1 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 21:41:30 -0400 Subject: [PATCH 22/31] fix(webhooks): align pokemon webhook state --- src/features/webhooks/WebhookAdv.jsx | 5 ++++- src/features/webhooks/services/Poracle.js | 20 +++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/features/webhooks/WebhookAdv.jsx b/src/features/webhooks/WebhookAdv.jsx index f716b3ced..3c2ae6c6d 100644 --- a/src/features/webhooks/WebhookAdv.jsx +++ b/src/features/webhooks/WebhookAdv.jsx @@ -170,12 +170,14 @@ export function WebhookAdvanced() { const handleSlider = React.useCallback( (low, high) => (name, values) => { + const isPvp = name.startsWith('pvp') setFilterValues((prev) => ({ ...prev, [name]: values })) setPoracleValues((prev) => ({ ...prev, [low]: values[0], [high]: values[1], - pvpEntry: name.startsWith('pvp'), + pvpEntry: isPvp, + noIv: isPvp ? false : prev.noIv, })) }, [], @@ -247,6 +249,7 @@ export function WebhookAdvanced() { } if (name.startsWith('pvp')) { newObj.pvpEntry = true + newObj.noIv = false } if (name === 'move' && value !== 9000) { newObj.allMoves = false diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index b6ed49c49..6f0986437 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -250,13 +250,11 @@ export class Poracle { return Poracle.toLocalState(category, entries, defaults) } - return Poracle.toLocalState( - category, - entries.map((pokemon, index) => ({ + return Poracle.toLocalState(category, entries, defaults).map( + (pokemon, index) => ({ ...pokemon, omittedFields: Poracle.getPokemonOmittedFields(payload[index]), - })), - defaults, + }), ) } @@ -431,6 +429,8 @@ export class Poracle { return processed.map((pokemon) => { const payload = {} const omittedFields = { ...(pokemon.omittedFields || {}) } + const hasPvpEntry = pokemon.pvpEntry && pokemon.pvp_ranking_league + const includeNoIv = pokemon.noIv && !hasPvpEntry const setPokemonField = (field) => { if ( !Poracle.shouldOmitPokemonField( @@ -460,7 +460,7 @@ export class Poracle { POKEMON_SCALAR_FIELDS.forEach(setPokemonField) - if (pokemon.noIv) { + if (includeNoIv) { payload.min_iv = Poracle.getPokemonFieldValue( pokemon, defaults, @@ -494,6 +494,8 @@ export class Poracle { return processed.map((pokemon) => { const payload = {} + const hasPvpEntry = pokemon.pvpEntry && pokemon.pvp_ranking_league + const includeNoIv = pokemon.noIv && !hasPvpEntry POKEMON_CREATE_REQUIRED_FIELDS.forEach((field) => { if (pokemon[field] !== undefined) { @@ -514,7 +516,7 @@ export class Poracle { } }) - if (pokemon.noIv) { + if (includeNoIv) { payload.min_iv = pokemon.min_iv === undefined ? defaults.min_iv : pokemon.min_iv payload.max_iv = @@ -547,11 +549,11 @@ export class Poracle { payload.max_weight = defaults.max_weight } - if (pokemon.noIv) { + if (includeNoIv) { return payload } - if (pokemon.pvpEntry && pokemon.pvp_ranking_league) { + if (hasPvpEntry) { POKEMON_PVP_FIELDS.forEach((field) => { if (pokemon[field] !== undefined) { payload[field] = pokemon[field] From 835983cd52e575e6fcbf1a172c27f8b2c82c80c3 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 21:49:44 -0400 Subject: [PATCH 23/31] fix(webhooks): preserve no-iv mode until pvp --- src/features/webhooks/WebhookAdv.jsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/features/webhooks/WebhookAdv.jsx b/src/features/webhooks/WebhookAdv.jsx index 3c2ae6c6d..7271ca01d 100644 --- a/src/features/webhooks/WebhookAdv.jsx +++ b/src/features/webhooks/WebhookAdv.jsx @@ -176,8 +176,8 @@ export function WebhookAdvanced() { ...prev, [low]: values[0], [high]: values[1], - pvpEntry: isPvp, - noIv: isPvp ? false : prev.noIv, + pvpEntry: isPvp ? !!prev.pvp_ranking_league : prev.pvpEntry, + noIv: isPvp && prev.pvp_ranking_league ? false : prev.noIv, })) }, [], @@ -244,12 +244,18 @@ export function WebhookAdvanced() { const handleSelect = (event) => { const { name, value } = event.target const newObj = { [name]: value } + const hasPvpLeague = + name === 'pvp_ranking_league' + ? !!value + : !!poracleValues.pvp_ranking_league if (name === 'pvp_ranking_league') { newObj.pvp_ranking_min_cp = pvp === 'ohbem' ? 0 : value - 50 } if (name.startsWith('pvp')) { - newObj.pvpEntry = true - newObj.noIv = false + newObj.pvpEntry = hasPvpLeague + if (hasPvpLeague) { + newObj.noIv = false + } } if (name === 'move' && value !== 9000) { newObj.allMoves = false From 3596ece92e59486129c869809ce26065f1c2318e Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 21:57:31 -0400 Subject: [PATCH 24/31] fix(webhooks): exit pvp mode on range edits --- src/features/webhooks/WebhookAdv.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/webhooks/WebhookAdv.jsx b/src/features/webhooks/WebhookAdv.jsx index 7271ca01d..0d523e544 100644 --- a/src/features/webhooks/WebhookAdv.jsx +++ b/src/features/webhooks/WebhookAdv.jsx @@ -176,7 +176,7 @@ export function WebhookAdvanced() { ...prev, [low]: values[0], [high]: values[1], - pvpEntry: isPvp ? !!prev.pvp_ranking_league : prev.pvpEntry, + pvpEntry: isPvp ? !!prev.pvp_ranking_league : false, noIv: isPvp && prev.pvp_ranking_league ? false : prev.noIv, })) }, From 12aca359031702b06b04ed2b6986732973f92af5 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 22:03:00 -0400 Subject: [PATCH 25/31] fix(webhooks): keep pvp size filters active --- src/features/webhooks/WebhookAdv.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/webhooks/WebhookAdv.jsx b/src/features/webhooks/WebhookAdv.jsx index 0d523e544..6eb706708 100644 --- a/src/features/webhooks/WebhookAdv.jsx +++ b/src/features/webhooks/WebhookAdv.jsx @@ -171,12 +171,13 @@ export function WebhookAdvanced() { const handleSlider = React.useCallback( (low, high) => (name, values) => { const isPvp = name.startsWith('pvp') + const keepsPvp = isPvp || name === 'size' setFilterValues((prev) => ({ ...prev, [name]: values })) setPoracleValues((prev) => ({ ...prev, [low]: values[0], [high]: values[1], - pvpEntry: isPvp ? !!prev.pvp_ranking_league : false, + pvpEntry: keepsPvp ? !!prev.pvp_ranking_league : false, noIv: isPvp && prev.pvp_ranking_league ? false : prev.noIv, })) }, From 6ff4252b6b8f7f572a4f0861b725422ec0577a12 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 22:07:27 -0400 Subject: [PATCH 26/31] fix(webhooks): keep pvp size mode explicit --- src/features/webhooks/WebhookAdv.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/features/webhooks/WebhookAdv.jsx b/src/features/webhooks/WebhookAdv.jsx index 6eb706708..d647685be 100644 --- a/src/features/webhooks/WebhookAdv.jsx +++ b/src/features/webhooks/WebhookAdv.jsx @@ -171,13 +171,16 @@ export function WebhookAdvanced() { const handleSlider = React.useCallback( (low, high) => (name, values) => { const isPvp = name.startsWith('pvp') - const keepsPvp = isPvp || name === 'size' setFilterValues((prev) => ({ ...prev, [name]: values })) setPoracleValues((prev) => ({ ...prev, [low]: values[0], [high]: values[1], - pvpEntry: keepsPvp ? !!prev.pvp_ranking_league : false, + pvpEntry: isPvp + ? !!prev.pvp_ranking_league + : name === 'size' + ? prev.pvpEntry && !!prev.pvp_ranking_league + : false, noIv: isPvp && prev.pvp_ranking_league ? false : prev.noIv, })) }, From fb882d0c07bf45fbaded4fa0bf2b9a10fab42618 Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 22:15:04 -0400 Subject: [PATCH 27/31] fix(webhooks): isolate pvp pokemon payloads --- src/features/webhooks/services/Poracle.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 6f0986437..d8b4b0a1f 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -36,6 +36,11 @@ const POKEMON_RANGE_GROUPS = [ ['min_weight', 'max_weight'], ] +const POKEMON_PVP_RANGE_GROUPS = [ + ['rarity', 'max_rarity'], + ['size', 'max_size'], +] + const POKEMON_OMITTABLE_FIELDS = [ ...new Set([ ...POKEMON_SCALAR_FIELDS, @@ -431,6 +436,9 @@ export class Poracle { const omittedFields = { ...(pokemon.omittedFields || {}) } const hasPvpEntry = pokemon.pvpEntry && pokemon.pvp_ranking_league const includeNoIv = pokemon.noIv && !hasPvpEntry + const rangeGroups = hasPvpEntry + ? POKEMON_PVP_RANGE_GROUPS + : POKEMON_RANGE_GROUPS const setPokemonField = (field) => { if ( !Poracle.shouldOmitPokemonField( @@ -460,7 +468,9 @@ export class Poracle { POKEMON_SCALAR_FIELDS.forEach(setPokemonField) - if (includeNoIv) { + if (hasPvpEntry) { + // PvP alerts intentionally ignore regular IV/stat ranges. + } else if (includeNoIv) { payload.min_iv = Poracle.getPokemonFieldValue( pokemon, defaults, @@ -475,7 +485,7 @@ export class Poracle { ;['min_iv', 'max_iv'].forEach(setPokemonField) } - POKEMON_RANGE_GROUPS.forEach(([low, high]) => { + rangeGroups.forEach(([low, high]) => { setPokemonField(low) setPokemonField(high) }) @@ -496,6 +506,9 @@ export class Poracle { const payload = {} const hasPvpEntry = pokemon.pvpEntry && pokemon.pvp_ranking_league const includeNoIv = pokemon.noIv && !hasPvpEntry + const rangeGroups = hasPvpEntry + ? POKEMON_PVP_RANGE_GROUPS + : POKEMON_RANGE_GROUPS POKEMON_CREATE_REQUIRED_FIELDS.forEach((field) => { if (pokemon[field] !== undefined) { @@ -516,7 +529,9 @@ export class Poracle { } }) - if (includeNoIv) { + if (hasPvpEntry) { + // PvP alerts intentionally ignore regular IV/stat ranges. + } else if (includeNoIv) { payload.min_iv = pokemon.min_iv === undefined ? defaults.min_iv : pokemon.min_iv payload.max_iv = @@ -531,7 +546,7 @@ export class Poracle { payload.max_iv = pokemon.max_iv } - POKEMON_RANGE_GROUPS.forEach(([low, high]) => { + rangeGroups.forEach(([low, high]) => { if ( pokemon[low] !== undefined && pokemon[high] !== undefined && From ef7c5662563df4d6e01ed57d4c26c8b8fe2d5aeb Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 22:23:38 -0400 Subject: [PATCH 28/31] fix(webhooks): seed pvp fields in pokemon edits --- src/features/webhooks/services/Poracle.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index d8b4b0a1f..2a9719ecb 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -325,6 +325,9 @@ export class Poracle { !ignoredFields.includes(key), ), ) + if (includeUiState) { + fields.push(...POKEMON_PVP_FIELDS) + } } new Set(fields).forEach((field) => { From 5456fc80e533eb29a294646d49cb9c141b8edaba Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 22:34:11 -0400 Subject: [PATCH 29/31] fix(webhooks): reset pokemon pvp fields on disable --- src/features/webhooks/WebhookAdv.jsx | 15 ++++++++++----- src/features/webhooks/services/Poracle.js | 6 ++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/features/webhooks/WebhookAdv.jsx b/src/features/webhooks/WebhookAdv.jsx index d647685be..228dd196f 100644 --- a/src/features/webhooks/WebhookAdv.jsx +++ b/src/features/webhooks/WebhookAdv.jsx @@ -220,11 +220,16 @@ export function WebhookAdvanced() { }) break case 'pvpEntry': - setPoracleValues({ - ...poracleValues, - [name]: checked, - noIv: false, - }) + { + const nextPoracleValues = { + ...poracleValues, + ...(checked ? {} : Poracle.getPokemonPvpDefaults(info?.defaults)), + [name]: checked, + noIv: false, + } + setPoracleValues(nextPoracleValues) + setFilterValues(Poracle.reactMapFriendly(nextPoracleValues)) + } break case 'allMoves': setPoracleValues({ diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index 2a9719ecb..b6c18aa62 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -52,6 +52,12 @@ const POKEMON_OMITTABLE_FIELDS = [ ] export class Poracle { + static getPokemonPvpDefaults(defaults = {}) { + return Object.fromEntries( + POKEMON_PVP_FIELDS.map((field) => [field, defaults[field]]), + ) + } + static getMapCategory(poracleCategory) { switch (poracleCategory) { case 'gym': From be5fe391a85a4f52ae9d6a20052af96bf4c5a6ec Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 22:41:56 -0400 Subject: [PATCH 30/31] fix(webhooks): clear pokemon pvp exit paths --- src/features/webhooks/WebhookAdv.jsx | 61 ++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/src/features/webhooks/WebhookAdv.jsx b/src/features/webhooks/WebhookAdv.jsx index 228dd196f..58f60f6a7 100644 --- a/src/features/webhooks/WebhookAdv.jsx +++ b/src/features/webhooks/WebhookAdv.jsx @@ -168,23 +168,38 @@ export function WebhookAdvanced() { })) }, [selectedIds]) + const normalizePokemonPvpValues = React.useCallback( + (nextPoracleValues) => + nextPoracleValues.pvpEntry + ? nextPoracleValues + : { + ...nextPoracleValues, + ...Poracle.getPokemonPvpDefaults(info?.defaults), + }, + [info?.defaults], + ) + const handleSlider = React.useCallback( (low, high) => (name, values) => { const isPvp = name.startsWith('pvp') - setFilterValues((prev) => ({ ...prev, [name]: values })) - setPoracleValues((prev) => ({ - ...prev, + const nextPoracleValues = normalizePokemonPvpValues({ + ...poracleValues, [low]: values[0], [high]: values[1], pvpEntry: isPvp - ? !!prev.pvp_ranking_league + ? !!poracleValues.pvp_ranking_league : name === 'size' - ? prev.pvpEntry && !!prev.pvp_ranking_league + ? poracleValues.pvpEntry && !!poracleValues.pvp_ranking_league : false, - noIv: isPvp && prev.pvp_ranking_league ? false : prev.noIv, - })) + noIv: + isPvp && poracleValues.pvp_ranking_league + ? false + : poracleValues.noIv, + }) + setFilterValues(Poracle.reactMapFriendly(nextPoracleValues)) + setPoracleValues(nextPoracleValues) }, - [], + [normalizePokemonPvpValues, poracleValues], ) const handleSwitch = (event) => { @@ -213,20 +228,23 @@ export function WebhookAdvanced() { }) break case 'noIv': - setPoracleValues({ - ...poracleValues, - [name]: checked, - pvpEntry: false, - }) + { + const nextPoracleValues = normalizePokemonPvpValues({ + ...poracleValues, + [name]: checked, + pvpEntry: false, + }) + setPoracleValues(nextPoracleValues) + setFilterValues(Poracle.reactMapFriendly(nextPoracleValues)) + } break case 'pvpEntry': { - const nextPoracleValues = { + const nextPoracleValues = normalizePokemonPvpValues({ ...poracleValues, - ...(checked ? {} : Poracle.getPokemonPvpDefaults(info?.defaults)), [name]: checked, noIv: false, - } + }) setPoracleValues(nextPoracleValues) setFilterValues(Poracle.reactMapFriendly(nextPoracleValues)) } @@ -272,7 +290,16 @@ export function WebhookAdvanced() { if (name === 'template') { newObj[name] = value?.toString() || '' } - setPoracleValues({ ...poracleValues, ...newObj }) + { + const nextPoracleValues = normalizePokemonPvpValues({ + ...poracleValues, + ...newObj, + }) + setPoracleValues(nextPoracleValues) + if (name.startsWith('pvp')) { + setFilterValues(Poracle.reactMapFriendly(nextPoracleValues)) + } + } } const handleChange = (panel) => (_, isExpanded) => { From 8b34f48551e5f6df63aeb869a78ebce8c806c41a Mon Sep 17 00:00:00 2001 From: Mygod Date: Fri, 3 Apr 2026 22:48:55 -0400 Subject: [PATCH 31/31] fix(webhooks): clear inactive pokemon pvp payloads --- src/features/webhooks/WebhookAdv.jsx | 70 ++++++----------------- src/features/webhooks/services/Poracle.js | 23 +++++--- 2 files changed, 35 insertions(+), 58 deletions(-) diff --git a/src/features/webhooks/WebhookAdv.jsx b/src/features/webhooks/WebhookAdv.jsx index 58f60f6a7..d647685be 100644 --- a/src/features/webhooks/WebhookAdv.jsx +++ b/src/features/webhooks/WebhookAdv.jsx @@ -168,38 +168,23 @@ export function WebhookAdvanced() { })) }, [selectedIds]) - const normalizePokemonPvpValues = React.useCallback( - (nextPoracleValues) => - nextPoracleValues.pvpEntry - ? nextPoracleValues - : { - ...nextPoracleValues, - ...Poracle.getPokemonPvpDefaults(info?.defaults), - }, - [info?.defaults], - ) - const handleSlider = React.useCallback( (low, high) => (name, values) => { const isPvp = name.startsWith('pvp') - const nextPoracleValues = normalizePokemonPvpValues({ - ...poracleValues, + setFilterValues((prev) => ({ ...prev, [name]: values })) + setPoracleValues((prev) => ({ + ...prev, [low]: values[0], [high]: values[1], pvpEntry: isPvp - ? !!poracleValues.pvp_ranking_league + ? !!prev.pvp_ranking_league : name === 'size' - ? poracleValues.pvpEntry && !!poracleValues.pvp_ranking_league + ? prev.pvpEntry && !!prev.pvp_ranking_league : false, - noIv: - isPvp && poracleValues.pvp_ranking_league - ? false - : poracleValues.noIv, - }) - setFilterValues(Poracle.reactMapFriendly(nextPoracleValues)) - setPoracleValues(nextPoracleValues) + noIv: isPvp && prev.pvp_ranking_league ? false : prev.noIv, + })) }, - [normalizePokemonPvpValues, poracleValues], + [], ) const handleSwitch = (event) => { @@ -228,26 +213,18 @@ export function WebhookAdvanced() { }) break case 'noIv': - { - const nextPoracleValues = normalizePokemonPvpValues({ - ...poracleValues, - [name]: checked, - pvpEntry: false, - }) - setPoracleValues(nextPoracleValues) - setFilterValues(Poracle.reactMapFriendly(nextPoracleValues)) - } + setPoracleValues({ + ...poracleValues, + [name]: checked, + pvpEntry: false, + }) break case 'pvpEntry': - { - const nextPoracleValues = normalizePokemonPvpValues({ - ...poracleValues, - [name]: checked, - noIv: false, - }) - setPoracleValues(nextPoracleValues) - setFilterValues(Poracle.reactMapFriendly(nextPoracleValues)) - } + setPoracleValues({ + ...poracleValues, + [name]: checked, + noIv: false, + }) break case 'allMoves': setPoracleValues({ @@ -290,16 +267,7 @@ export function WebhookAdvanced() { if (name === 'template') { newObj[name] = value?.toString() || '' } - { - const nextPoracleValues = normalizePokemonPvpValues({ - ...poracleValues, - ...newObj, - }) - setPoracleValues(nextPoracleValues) - if (name.startsWith('pvp')) { - setFilterValues(Poracle.reactMapFriendly(nextPoracleValues)) - } - } + setPoracleValues({ ...poracleValues, ...newObj }) } const handleChange = (panel) => (_, isExpanded) => { diff --git a/src/features/webhooks/services/Poracle.js b/src/features/webhooks/services/Poracle.js index b6c18aa62..76048111b 100644 --- a/src/features/webhooks/services/Poracle.js +++ b/src/features/webhooks/services/Poracle.js @@ -52,12 +52,6 @@ const POKEMON_OMITTABLE_FIELDS = [ ] export class Poracle { - static getPokemonPvpDefaults(defaults = {}) { - return Object.fromEntries( - POKEMON_PVP_FIELDS.map((field) => [field, defaults[field]]), - ) - } - static getMapCategory(poracleCategory) { switch (poracleCategory) { case 'gym': @@ -499,7 +493,22 @@ export class Poracle { setPokemonField(high) }) - POKEMON_PVP_FIELDS.forEach(setPokemonField) + if (hasPvpEntry) { + POKEMON_PVP_FIELDS.forEach(setPokemonField) + } else if ( + !POKEMON_PVP_FIELDS.every((field) => + Poracle.shouldOmitPokemonField( + pokemon, + defaults, + omittedFields, + field, + ), + ) + ) { + POKEMON_PVP_FIELDS.forEach((field) => { + payload[field] = defaults[field] + }) + } return payload })