diff --git a/package.json b/package.json index 94680a0ac..040ac64c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "reactmap", - "version": "1.3.5", + "version": "1.4.0", "description": "React based frontend map.", "main": "ReactMap.mjs", "author": "TurtIeSocks <58572875+TurtIeSocks@users.noreply.github.com>", diff --git a/server/src/configs/custom-environment-variables.json b/server/src/configs/custom-environment-variables.json index b0c2c648a..4499ead2e 100644 --- a/server/src/configs/custom-environment-variables.json +++ b/server/src/configs/custom-environment-variables.json @@ -315,10 +315,6 @@ "__name": "MAP_MISC_ENABLE_MAP_JS_FILTER", "__format": "boolean" }, - "questRewardTypeFilters": { - "__name": "MAP_MISC_QUEST_REWARD_TYPE_FILTERS", - "__format": "boolean" - }, "fetchLatestInvasions": { "__name": "MAP_MISC_FETCH_LATEST_INVASIONS", "__format": "boolean" @@ -820,6 +816,24 @@ "__name": "DEFAULT_FILTERS_POKESTOPS_STARDUST_ENABLED", "__format": "boolean" } + }, + "xp": { + "min": { + "__name": "DEFAULT_FILTERS_POKESTOPS_XP_MIN", + "__format": "number" + }, + "max": { + "__name": "DEFAULT_FILTERS_POKESTOPS_XP_MAX", + "__format": "number" + }, + "interval": { + "__name": "DEFAULT_FILTERS_POKESTOPS_XP_INTERVAL", + "__format": "number" + }, + "enabled": { + "__name": "DEFAULT_FILTERS_POKESTOPS_XP_ENABLED", + "__format": "boolean" + } } }, "pokemon": { diff --git a/server/src/configs/default.json b/server/src/configs/default.json index e7e4cac3f..04baea61a 100644 --- a/server/src/configs/default.json +++ b/server/src/configs/default.json @@ -142,7 +142,6 @@ }, "misc": { "enableMapJsFilter": true, - "questRewardTypeFilters": false, "fetchLatestInvasions": true, "invasionCacheHrs": 1, "masterfileCacheHrs": 6, @@ -388,6 +387,12 @@ "max": 2000, "interval": 100, "enabled": true + }, + "xp": { + "min": 100, + "max": 1000, + "interval": 100, + "enabled": true } }, "pokemon": { diff --git a/server/src/graphql/mapTypes.js b/server/src/graphql/mapTypes.js index 89358869c..e633aec9d 100644 --- a/server/src/graphql/mapTypes.js +++ b/server/src/graphql/mapTypes.js @@ -95,6 +95,7 @@ module.exports = gql` item_amount: Int candy_pokemon_id: Int candy_amount: Int + xp_amount: Int with_ar: Boolean quest_title: String quest_target: Int diff --git a/server/src/graphql/scannerTypes.js b/server/src/graphql/scannerTypes.js index 108d20c10..500fa3764 100644 --- a/server/src/graphql/scannerTypes.js +++ b/server/src/graphql/scannerTypes.js @@ -89,6 +89,7 @@ module.exports = gql` candy_amount: Int xl_candy_pokemon_id: Int xl_candy_amount: Int + xp_amount: Int with_ar: Boolean key: String } diff --git a/server/src/models/Pokestop.js b/server/src/models/Pokestop.js index 2ed518924..ba3a58ab6 100644 --- a/server/src/models/Pokestop.js +++ b/server/src/models/Pokestop.js @@ -159,6 +159,7 @@ module.exports = class Pokestop extends Model { if (!onlyAllPokestops) { // Skips ugly query if all pokestops are selected anyway + const xp = [] const stardust = [] const invasions = [] const lures = [] @@ -185,6 +186,9 @@ module.exports = class Pokestop extends Model { case 'm': energy.push(pokestop.slice(1)) break + case 'p': + xp.push(pokestop.slice(1)) + break case 'q': items.push(pokestop.slice(1)) break @@ -238,15 +242,20 @@ module.exports = class Pokestop extends Model { .orWhereIn('alternative_quest_pokemon_id', pokemon) } if (hasRewardAmount) { - questTypes.orWhereIn( - isMad ? 'quest_stardust' : 'quest_reward_amount', - stardust, - ) + questTypes.orWhere((dust) => { + dust + .where('quest_reward_type', 3) + .whereIn( + isMad ? 'quest_stardust' : 'quest_reward_amount', + stardust, + ) + }) if (hasAltQuests) { - questTypes.orWhereIn( - 'alternative_quest_reward_amount', - stardust, - ) + questTypes.orWhere((dust) => { + dust + .where('alternative_quest_reward_type', 3) + .whereIn('alternative_quest_reward_amount', stardust) + }) } } else { stardust.forEach((amount) => { @@ -272,6 +281,46 @@ module.exports = class Pokestop extends Model { } }) } + if (hasRewardAmount) { + questTypes.orWhere((exp) => { + exp + .where('quest_reward_type', 1) + .whereIn( + isMad ? 'quest_item_amount' : 'quest_reward_amount', + xp, + ) + }) + if (hasAltQuests) { + questTypes.orWhere((exp) => { + exp + .where('alternative_quest_reward_type', 1) + .whereIn('alternative_quest_reward_amount', xp) + }) + } + } else { + xp.forEach((amount) => { + questTypes.orWhere((xpReward) => { + xpReward + .where('quest_reward_type', 1) + .andWhere( + raw( + `json_extract(quest_rewards, "$[0].info.amount") = ${amount}`, + ), + ) + }) + if (hasAltQuests) { + questTypes.orWhere((altXpReward) => { + altXpReward + .where('alternative_quest_reward_type', 1) + .andWhere( + raw( + `json_extract(alternative_quest_rewards, "$[0].info.amount") = ${amount}`, + ), + ) + }) + } + }) + } energy.forEach((megaEnergy) => { const [pokeId, amount] = megaEnergy.split('-') if (hasRewardAmount) { @@ -419,7 +468,7 @@ module.exports = class Pokestop extends Model { } }) } - if (general.length && map.enableQuestRewardTypeFilters) { + if (general.length) { questTypes.orWhere((rewardType) => { rewardType.whereIn('quest_reward_type', general) }) @@ -574,6 +623,10 @@ module.exports = class Pokestop extends Model { 'quest_title', ] switch (quest.quest_reward_type) { + case 1: + newQuest.key = `p${quest.xp_amount}` + fields.push('xp_amount') + break case 2: newQuest.key = `q${quest.quest_item_id}` fields.push('quest_item_id', 'item_amount') @@ -615,8 +668,7 @@ module.exports = class Pokestop extends Model { (filters[newQuest.key].adv ? quest.quest_title === filters[newQuest.key].adv : true)) || - (filters[`u${quest.quest_reward_type}`] && - map.enableQuestRewardTypeFilters)) + filters[`u${quest.quest_reward_type}`]) ) { this.fieldAssigner(newQuest, quest, fields) filtered.quests.push(newQuest) @@ -733,6 +785,7 @@ module.exports = class Pokestop extends Model { finalList.add(key) } + // items queries.items = this.query() .select('quest_item_id', 'quest_title', 'quest_target') .from(isMad ? 'trs_quest' : 'pokestop') @@ -752,6 +805,9 @@ module.exports = class Pokestop extends Model { 'alternative_quest_target', ) } + // items + + // stardust if (isMad) { queries.stardust = this.query() .select('quest_stardust AS amount', 'quest_title', 'quest_target') @@ -808,6 +864,65 @@ module.exports = class Pokestop extends Model { } } } + // stardust + + // xp + if (isMad) { + queries.xp = this.query() + .select('quest_item_amount AS amount', 'quest_title', 'quest_target') + .from('trs_quest') + .where('quest_reward_type', 1) + .groupBy('quest_item_amount', 'quest_title', 'quest_target') + } else { + queries.xp = this.query().where('quest_reward_type', 1) + if (hasRewardAmount) { + queries.xp + .select( + 'quest_reward_amount AS amount', + 'quest_title', + 'quest_target', + ) + .where('quest_reward_amount', '>', 0) + .groupBy('amount', 'quest_title', 'quest_target') + } else { + queries.xp + .select('quest_title', 'quest_target') + .distinct( + raw('json_extract(quest_rewards, "$[0].info.amount")').as('amount'), + ) + } + if (hasAltQuests) { + queries.xpAlt = this.query().where('alternative_quest_reward_type', 1) + if (hasRewardAmount) { + queries.xpAlt + .select( + 'alternative_quest_reward_amount AS amount', + 'alternative_quest_title AS quest_title', + 'alternative_quest_target AS quest_target', + ) + .where('alternative_quest_reward_amount', '>', 0) + .groupBy( + 'amount', + 'alternative_quest_title', + 'alternative_quest_target', + ) + } else { + queries.xpAlt + .select( + 'alternative_quest_title AS quest_title', + 'alternative_quest_target AS quest_target', + ) + .distinct( + raw( + 'json_extract(alternative_quest_rewards, "$[0].info.amount")', + ).as('amount'), + ) + } + } + } + // xp + + // mega queries.mega = this.query() .from(isMad ? 'trs_quest' : 'pokestop') .where('quest_reward_type', 12) @@ -864,6 +979,9 @@ module.exports = class Pokestop extends Model { ) } } + // mega + + // candy queries.candy = this.query() .select('quest_title', 'quest_target') .distinct('quest_pokemon_id') @@ -878,6 +996,9 @@ module.exports = class Pokestop extends Model { .distinct('alternative_quest_pokemon_id AS quest_pokemon_id') .where('alternative_quest_reward_type', 4) } + // candy + + // xl candy queries.xlCandy = this.query() .select('quest_title', 'quest_target') .distinct('quest_pokemon_id') @@ -892,6 +1013,9 @@ module.exports = class Pokestop extends Model { .distinct('alternative_quest_pokemon_id AS quest_pokemon_id') .where('alternative_quest_reward_type', 9) } + // xl candy + + // pokemon if (isMad) { queries.pokemon = this.query() .select( @@ -930,6 +1054,9 @@ module.exports = class Pokestop extends Model { .where('alternative_quest_reward_type', 7) } } + // pokemon + + // invasions if (hasMultiInvasions) { queries.invasions = this.query() .leftJoin('incident', 'pokestop.id', 'incident.pokestop_id') @@ -950,6 +1077,9 @@ module.exports = class Pokestop extends Model { ) .orderBy('grunt_type') } + // invasions + + // lures queries.lures = this.query() .select(isMad ? 'active_fort_modifier AS lure_id' : 'lure_id') .andWhere( @@ -959,14 +1089,43 @@ module.exports = class Pokestop extends Model { ) .groupBy(isMad ? 'active_fort_modifier' : 'lure_id') .orderBy(isMad ? 'active_fort_modifier' : 'lure_id') + // lures const resolved = Object.fromEntries( await Promise.all( Object.entries(queries).map(async ([key, query]) => [key, await query]), ), ) + let questTypes = [ + ...new Set([ + ...(await this.query() + .distinct('quest_reward_type') + .whereNotNull('quest_reward_type') + .then((results) => results.map((x) => x.quest_reward_type))), + ...(hasAltQuests + ? await this.query() + .distinct('alternative_quest_reward_type') + .whereNotNull('alternative_quest_reward_type') + .then((results) => + results.map((x) => x.alternative_quest_reward_type), + ) + : []), + ]), + ] + Object.entries(resolved).forEach(([questType, rewards]) => { switch (questType) { + case 'xp': + case 'xpAlt': + rewards.forEach((reward) => + process( + `p${reward.amount}`, + reward.quest_title, + reward.quest_target, + ), + ) + questTypes = questTypes.filter((x) => x !== 1) + break case 'itemsAlt': case 'items': rewards.forEach((reward) => @@ -976,6 +1135,7 @@ module.exports = class Pokestop extends Model { reward.quest_target, ), ) + questTypes = questTypes.filter((x) => x !== 2) break case 'megaAlt': case 'mega': @@ -986,6 +1146,7 @@ module.exports = class Pokestop extends Model { reward.quest_target, ), ) + questTypes = questTypes.filter((x) => x !== 9) break case 'stardustAlt': case 'stardust': @@ -996,18 +1157,21 @@ module.exports = class Pokestop extends Model { reward.quest_target, ), ) + questTypes = questTypes.filter((x) => x !== 3) break case 'candyAlt': case 'candy': rewards.forEach((reward) => process(`c${reward.id}`, reward.quest_title, reward.quest_target), ) + questTypes = questTypes.filter((x) => x !== 4) break case 'xlCandyAlt': case 'xlCandy': rewards.forEach((reward) => process(`x${reward.id}`, reward.quest_title, reward.quest_target), ) + questTypes = questTypes.filter((x) => x !== 12) break case 'lures': rewards.forEach((reward) => finalList.add(`l${reward.lure_id}`)) @@ -1023,11 +1187,16 @@ module.exports = class Pokestop extends Model { reward.quest_target, ), ) + questTypes = questTypes.filter((x) => x !== 7) break } }) + return { - available: finalList.size ? [...finalList] : await fetchQuests(), + available: + finalList.size || questTypes.length + ? [...finalList, ...questTypes.map((type) => `u${type}`)] + : await fetchQuests(), conditions, } } @@ -1036,6 +1205,9 @@ module.exports = class Pokestop extends Model { if (quest.quest_reward_type) { const { info } = JSON.parse(quest.quest_rewards)[0] switch (quest.quest_reward_type) { + case 1: + Object.keys(info).forEach((x) => (quest[`xp_${x}`] = info[x])) + break case 2: Object.keys(info).forEach((x) => (quest[`item_${x}`] = info[x])) break @@ -1063,10 +1235,13 @@ module.exports = class Pokestop extends Model { static parseMadRewards = (quest) => { if (quest.quest_reward_type) { - const { item, candy, xl_candy, mega_resource } = JSON.parse( + const { item, exp, candy, xl_candy, mega_resource } = JSON.parse( quest.quest_rewards, )[0] switch (quest.quest_reward_type) { + case 1: + quest.xp_amount = exp + break case 2: Object.keys(item).forEach((x) => (quest[`item_${x}`] = item[x])) break diff --git a/server/src/services/defaultFilters/buildPokestops.js b/server/src/services/defaultFilters/buildPokestops.js index d5731bf37..70f58d77f 100644 --- a/server/src/services/defaultFilters/buildPokestops.js +++ b/server/src/services/defaultFilters/buildPokestops.js @@ -24,6 +24,18 @@ module.exports = function buildPokestops(perms, defaults, available) { quests[`u${type}`] = new GenericFilter(defaults.rewardTypes) } }) + for ( + let i = defaults.xp.min; + i <= defaults.xp.max; + i += defaults.xp.interval + ) { + quests[`p${i}`] = new GenericFilter(defaults.xp.enabled) + } + Object.keys(Event.masterfile.questRewardTypes).forEach((type) => { + if (type !== '0') { + quests[`u${type}`] = new GenericFilter(defaults.rewardTypes) + } + }) } if (perms.invasions) { Object.keys(Event.invasions).forEach((type) => { diff --git a/server/src/services/ui/advMenus.js b/server/src/services/ui/advMenus.js index f106afc64..eaf34be0c 100644 --- a/server/src/services/ui/advMenus.js +++ b/server/src/services/ui/advMenus.js @@ -1,5 +1,4 @@ const { Event } = require('../initialization') -const { map } = require('../config') const categories = { gyms: ['teams', 'eggs', 'raids', 'pokemon'], @@ -12,15 +11,13 @@ const categories = { 'quest_reward_4', 'quest_reward_9', 'quest_reward_3', + 'quest_reward_1', + 'general', ], pokemon: ['pokemon'], nests: ['pokemon'], } -if (map.enableQuestRewardTypeFilters) { - categories.pokestops.push('general') -} - const baseRarity = [ 'common', 'uncommon', diff --git a/src/assets/css/main.css b/src/assets/css/main.css index f4deddaf7..54a085bf2 100644 --- a/src/assets/css/main.css +++ b/src/assets/css/main.css @@ -1,8 +1,9 @@ -@import "holiday.css"; -@import "loading.css"; +@import 'holiday.css'; +@import 'loading.css'; -.leaflet-control-container .leaflet-top, .leaflet-control-container .leaflet-bottom { - transform: translate3d(0px, 0px, 0px); +.leaflet-control-container .leaflet-top, +.leaflet-control-container .leaflet-bottom { + transform: translate3d(0px, 0px, 0px); } .leaflet-container { @@ -313,6 +314,7 @@ img { rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) ); + mask-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1)); } .type-img-2 { @@ -323,6 +325,7 @@ img { rgba(0, 0, 0, 1), rgba(0, 0, 0, 0) ); + mask-image: linear-gradient(to right, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); } .no-scroll::-webkit-scrollbar { @@ -367,7 +370,7 @@ img { width: 100%; } -input[type="time"]::-webkit-calendar-picker-indicator { +input[type='time']::-webkit-calendar-picker-indicator { filter: invert(100%); } diff --git a/src/components/ErrorBoundary.jsx b/src/components/ErrorBoundary.jsx index 956032ead..2a5febd3c 100644 --- a/src/components/ErrorBoundary.jsx +++ b/src/components/ErrorBoundary.jsx @@ -10,7 +10,10 @@ import { withTranslation } from 'react-i18next' class ErrorBoundary extends Component { constructor(props) { super(props) - this.state = { hasError: false, message: '' } + this.state = { + hasError: false, + message: '', + } } componentDidCatch(error) { @@ -26,10 +29,20 @@ class ErrorBoundary extends Component { container justifyContent="center" alignItems="center" - style={{ height: '100vh', width: '100vw', textAlign: 'center' }} + style={ + this.props.style ?? { + height: '100vh', + width: '100vw', + textAlign: 'center', + } + } > - + {this.props.t('react_error')} {this.state.message} -
-
- + {!this.props.noRefresh && ( + <> +
+
+ + + )}
) : ( diff --git a/src/components/layout/dialogs/Search.jsx b/src/components/layout/dialogs/Search.jsx index 235e15246..83f2c6c7c 100644 --- a/src/components/layout/dialogs/Search.jsx +++ b/src/components/layout/dialogs/Search.jsx @@ -61,72 +61,8 @@ export default function Search({ safeSearch, toggleDialog, isMobile, Icons }) { } = option if (quest_reward_type) { - const { - quest_pokemon_id, - quest_form_id, - quest_gender_id, - quest_costume_id, - quest_shiny, - quest_item_id, - item_amount, - stardust_amount, - candy_amount, - xl_candy_amount, - mega_pokemon_id, - mega_amount, - candy_pokemon_id, - xl_candy_pokemon_id, - } = option - let main - let amount = 0 - let tt = '' - switch (quest_reward_type) { - case 2: - main = Icons.getRewards(quest_reward_type, quest_item_id, item_amount) - amount = main.includes('_a') || item_amount <= 1 ? 0 : item_amount - tt = `item_${quest_item_id}` - break - case 3: - tt = `stardust` - main = Icons.getRewards(quest_reward_type, stardust_amount) - amount = main.includes('_a') ? 0 : stardust_amount - break - case 4: - tt = `poke_${candy_pokemon_id}` - main = Icons.getRewards(quest_reward_type, candy_pokemon_id) - amount = main.includes('_a') ? 0 : candy_amount - break - case 7: - tt = [ - quest_form_id ? `form_${quest_form_id}` : '', - `poke_${quest_pokemon_id}`, - ] - main = Icons.getPokemon( - quest_pokemon_id, - quest_form_id, - 0, - quest_gender_id, - quest_costume_id, - quest_shiny, - ) - break - case 9: - tt = `poke_${xl_candy_pokemon_id}` - main = Icons.getRewards(quest_reward_type, xl_candy_pokemon_id) - amount = main.includes('_a') ? 0 : xl_candy_amount - break - case 12: - tt = `poke_${mega_pokemon_id}` - main = Icons.getRewards( - quest_reward_type, - mega_pokemon_id, - mega_amount, - ) - amount = main.includes('_a') ? 0 : mega_amount - break - default: - main = Icons.getRewards(quest_reward_type) - } + const { src, amount, tt } = Utility.getRewardInfo(option, Icons) + return (
{main} { + e.target.onerror = null + e.target.src = + 'https://github.com/WatWowMap/wwm-uicons/blob/main/misc/0.png' + }} /> - {Boolean( - main.includes('stardust') - ? !main.endsWith('0.png') - : !main.includes('_a') && amount, - ) &&
x{amount}
} + {!!amount &&
x{amount}
}
) } diff --git a/src/components/markers/pokestop.jsx b/src/components/markers/pokestop.jsx index 5902cd4e8..ff755de43 100644 --- a/src/components/markers/pokestop.jsx +++ b/src/components/markers/pokestop.jsx @@ -57,6 +57,7 @@ export default function stopMarker( const { quest_item_id, item_amount, + xp_amount, stardust_amount, candy_pokemon_id, candy_amount, @@ -73,6 +74,12 @@ export default function stopMarker( key, } = quest switch (quest_reward_type) { + case 1: + questIcons.unshift({ + url: Icons.getRewards(quest_reward_type, xp_amount), + amount: xp_amount, + }) + break case 2: questIcons.unshift({ url: Icons.getRewards( @@ -175,7 +182,8 @@ export default function stopMarker( /> )} {questIcons.map((icon, i) => ( - + // eslint-disable-next-line react/no-array-index-key + {icon.url} {Boolean( - icon.url.includes('stardust') - ? !icon.url.endsWith('0.png') + icon.url.includes('stardust') || icon.url.includes('experience') + ? icon.url.endsWith('/0.png') : !icon.url.includes('_a') && icon.amount, ) && (
+ {device.id} @@ -27,7 +28,7 @@ export default function DevicePopup({ device, isOnline, ts }) { > {t(isOnline ? 'online' : 'offline')} - + ) } diff --git a/src/components/popups/Gym.jsx b/src/components/popups/Gym.jsx index 1fce5616a..ec55593e6 100644 --- a/src/components/popups/Gym.jsx +++ b/src/components/popups/Gym.jsx @@ -15,6 +15,7 @@ import { useStore, useStatic } from '@hooks/useStore' import useStyles from '@hooks/useStyles' import useWebhook from '@hooks/useWebhook' import Utility from '@services/Utility' +import ErrorBoundary from '@components/ErrorBoundary' import Title from './common/Title' import Dropdown from './common/Dropdown' @@ -45,77 +46,83 @@ export default function GymPopup({ }, []) return ( - - - - </Grid> - <MenuActions - gym={gym} - perms={perms} - hasRaid={hasRaid} - t={t} - badge={badge} - setBadge={setBadge} - /> - {perms.gyms && ( - <Grid item xs={12}> - <Collapse in={!popups.raids || !hasRaid} timeout="auto" unmountOnExit> - <Grid - container - alignItems="center" - justifyContent="space-evenly" - spacing={1} - > - <PoiImage Icons={Icons} gym={gym} /> - <Divider orientation="vertical" flexItem /> - <GymInfo gym={gym} t={t} Icons={Icons} /> - </Grid> - </Collapse> + <ErrorBoundary noRefresh style={{}} variant="h5"> + <Grid + container + style={{ width: 200 }} + direction="row" + justifyContent="space-evenly" + alignItems="center" + spacing={1} + > + <Grid item xs={10}> + <Title mainName={gym.name} backup={t('unknown_gym')} /> </Grid> - )} - {perms.raids && ( - <Grid item xs={12}> - <Collapse in={popups.raids && hasRaid} timeout="auto" unmountOnExit> - <Grid - container - alignItems="center" - justifyContent="center" - spacing={1} + <MenuActions + gym={gym} + perms={perms} + hasRaid={hasRaid} + t={t} + badge={badge} + setBadge={setBadge} + /> + {perms.gyms && ( + <Grid item xs={12}> + <Collapse + in={!popups.raids || !hasRaid} + timeout="auto" + unmountOnExit > - <RaidImage gym={gym} ts={ts} Icons={Icons} t={t} /> - <Divider orientation="vertical" flexItem /> - <RaidInfo gym={gym} t={t} Icons={Icons} ts={ts} /> - {Boolean( - gym.raid_pokemon_id && gym.raid_battle_timestamp >= ts, - ) && <Timer gym={gym} start t={t} />} - <Timer gym={gym} ts={ts} t={t} hasHatched={hasHatched} /> - </Grid> + <Grid + container + alignItems="center" + justifyContent="space-evenly" + spacing={1} + > + <PoiImage Icons={Icons} gym={gym} /> + <Divider orientation="vertical" flexItem /> + <GymInfo gym={gym} t={t} Icons={Icons} /> + </Grid> + </Collapse> + </Grid> + )} + {perms.raids && ( + <Grid item xs={12}> + <Collapse in={popups.raids && hasRaid} timeout="auto" unmountOnExit> + <Grid + container + alignItems="center" + justifyContent="center" + spacing={1} + > + <RaidImage gym={gym} ts={ts} Icons={Icons} t={t} /> + <Divider orientation="vertical" flexItem /> + <RaidInfo gym={gym} t={t} Icons={Icons} ts={ts} /> + {Boolean( + gym.raid_pokemon_id && gym.raid_battle_timestamp >= ts, + ) && <Timer gym={gym} start t={t} />} + <Timer gym={gym} ts={ts} t={t} hasHatched={hasHatched} /> + </Grid> + </Collapse> + </Grid> + )} + <PowerUp {...gym} /> + <GymFooter + gym={gym} + popups={popups} + setPopups={setPopups} + hasRaid={hasRaid} + perms={perms} + t={t} + Icons={Icons} + /> + {perms.gyms && ( + <Collapse in={popups.extras} timeout="auto" unmountOnExit> + <ExtraInfo gym={gym} t={t} ts={ts} /> </Collapse> - </Grid> - )} - <PowerUp {...gym} /> - <GymFooter - gym={gym} - popups={popups} - setPopups={setPopups} - hasRaid={hasRaid} - perms={perms} - t={t} - Icons={Icons} - /> - {perms.gyms && ( - <Collapse in={popups.extras} timeout="auto" unmountOnExit> - <ExtraInfo gym={gym} t={t} ts={ts} /> - </Collapse> - )} - </Grid> + )} + </Grid> + </ErrorBoundary> ) } diff --git a/src/components/popups/Nest.jsx b/src/components/popups/Nest.jsx index 11ca97331..91a1a6b11 100644 --- a/src/components/popups/Nest.jsx +++ b/src/components/popups/Nest.jsx @@ -12,6 +12,7 @@ import { useTranslation } from 'react-i18next' import { useStore, useStatic } from '@hooks/useStore' import Utility from '@services/Utility' +import ErrorBoundary from '@components/ErrorBoundary' export default function NestPopup({ nest, iconUrl, pokemon, recent }) { const { t } = useTranslation() @@ -79,90 +80,94 @@ export default function NestPopup({ nest, iconUrl, pokemon, recent }) { }, []) return ( - <Grid - container - justifyContent="center" - alignItems="center" - style={{ width: 200 }} - spacing={1} - > - <Grid item xs={9}> - <Typography - variant={name.length > 20 ? 'subtitle2' : 'h6'} - align="center" - noWrap={parkName} - onClick={() => setParkName(!parkName)} - > - {name} - </Typography> - </Grid> - <Grid item xs={3}> - <IconButton aria-haspopup="true" onClick={handleClick}> - <MoreVert style={{ color: 'white' }} /> - </IconButton> - </Grid> - <Menu - anchorEl={anchorEl} - keepMounted - open={Boolean(anchorEl)} - onClose={handleClose} - PaperProps={{ - style: { - maxHeight: 216, - minWidth: '20ch', - }, - }} + <ErrorBoundary noRefresh style={{}} variant="h5"> + <Grid + container + justifyContent="center" + alignItems="center" + style={{ width: 200 }} + spacing={1} > - {options.map((option) => ( - <MenuItem key={option.key || option.name} onClick={option.action}> - {typeof option.name === 'string' ? t(option.name) : option.name} - </MenuItem> - ))} - </Menu> - <Grid item xs={6} style={{ textAlign: 'center' }}> - <img - src={iconUrl} - alt={iconUrl} - style={{ - maxHeight: 75, - maxWidth: 75, + <Grid item xs={9}> + <Typography + variant={name.length > 20 ? 'subtitle2' : 'h6'} + align="center" + noWrap={parkName} + onClick={() => setParkName(!parkName)} + > + {name} + </Typography> + </Grid> + <Grid item xs={3}> + <IconButton aria-haspopup="true" onClick={handleClick}> + <MoreVert style={{ color: 'white' }} /> + </IconButton> + </Grid> + <Menu + anchorEl={anchorEl} + keepMounted + open={Boolean(anchorEl)} + onClose={handleClose} + PaperProps={{ + style: { + maxHeight: 216, + minWidth: '20ch', + }, }} - /> - <br /> - <Typography variant="caption"> - {t(`poke_${pokemon.pokemon_id}`)} - </Typography> - </Grid> - <Grid item xs={6} style={{ textAlign: 'center' }}> - <Typography variant="subtitle2">{t('last_updated')}</Typography> - <Typography - variant={lastUpdated.str.includes('D') ? 'h6' : 'subtitle2'} - style={{ color: getColor(lastUpdated.diff) }} > - {lastUpdated.str.replace('days', t('days')).replace('day', t('day'))} - </Typography> - <Typography variant="subtitle2"> - ~{pokemon_avg} {t('spawns_per_hour')} - </Typography> - </Grid> - <Grid item xs={12}> - <Divider style={{ color: 'white', margin: 4 }} /> - </Grid> - <Grid item xs={12} style={{ textAlign: 'center' }}> - {recent ? ( + {options.map((option) => ( + <MenuItem key={option.key || option.name} onClick={option.action}> + {typeof option.name === 'string' ? t(option.name) : option.name} + </MenuItem> + ))} + </Menu> + <Grid item xs={6} style={{ textAlign: 'center' }}> + <img + src={iconUrl} + alt={iconUrl} + style={{ + maxHeight: 75, + maxWidth: 75, + }} + /> + <br /> <Typography variant="caption"> - {t('nest_estimated')} - <br /> - {t('verify_nests')} + {t(`poke_${pokemon.pokemon_id}`)} </Typography> - ) : ( - <Typography variant="caption"> - {t('nest_out_of_date')} - <br /> - {t('nest_check_current')} + </Grid> + <Grid item xs={6} style={{ textAlign: 'center' }}> + <Typography variant="subtitle2">{t('last_updated')}</Typography> + <Typography + variant={lastUpdated.str.includes('D') ? 'h6' : 'subtitle2'} + style={{ color: getColor(lastUpdated.diff) }} + > + {lastUpdated.str + .replace('days', t('days')) + .replace('day', t('day'))} + </Typography> + <Typography variant="subtitle2"> + ~{pokemon_avg} {t('spawns_per_hour')} </Typography> - )} + </Grid> + <Grid item xs={12}> + <Divider style={{ color: 'white', margin: 4 }} /> + </Grid> + <Grid item xs={12} style={{ textAlign: 'center' }}> + {recent ? ( + <Typography variant="caption"> + {t('nest_estimated')} + <br /> + {t('verify_nests')} + </Typography> + ) : ( + <Typography variant="caption"> + {t('nest_out_of_date')} + <br /> + {t('nest_check_current')} + </Typography> + )} + </Grid> </Grid> - </Grid> + </ErrorBoundary> ) } diff --git a/src/components/popups/Pokemon.jsx b/src/components/popups/Pokemon.jsx index 539f03962..10ce07912 100644 --- a/src/components/popups/Pokemon.jsx +++ b/src/components/popups/Pokemon.jsx @@ -17,6 +17,7 @@ import { useTranslation } from 'react-i18next' import { useStore, useStatic } from '@hooks/useStore' import useStyles from '@hooks/useStyles' import Utility from '@services/Utility' +import ErrorBoundary from '@components/ErrorBoundary' import GenericTimer from './common/Timer' import NameTT from './common/NameTT' @@ -64,73 +65,75 @@ export default function PokemonPopup({ }, []) return ( - <Grid - container - style={{ minWidth: 200 }} - alignItems="center" - justifyContent="center" - spacing={1} - > - <Header - pokemon={pokemon} - metaData={metaData} - iconUrl={iconUrl} - t={t} - perms={perms} - userSettings={userSettings} - classes={classes} - isTutorial={isTutorial} - /> - {pokemon.seen_type !== 'encounter' && ( - <Grid item xs={12} style={{ textAlign: 'center' }}> - <Typography variant="caption"> - {t(`seen_${pokemon.seen_type}`, '')} - </Typography> - </Grid> - )} - {pokemon.seen_type === 'nearby_cell' && ( - <Typography>{t('pokemon_cell')}</Typography> - )} - {!!pokemon.expire_timestamp && ( - <Timer pokemon={pokemon} hasStats={hasStats} t={t} /> - )} - {hasStats && pokePerms.iv && ( - <> - <Stats pokemon={pokemon} metaData={metaData} t={t} /> - <Divider orientation="vertical" flexItem /> - </> - )} - <Info - pokemon={pokemon} - metaData={metaData} - perms={pokePerms} - Icons={Icons} - isNight={isNight} - /> - <Footer - pokemon={pokemon} - popups={popups} - setPopups={setPopups} - hasPvp={!!hasLeagues.length} - classes={classes} - Icons={Icons} - /> - <Collapse in={popups.pvp && perms.pvp} timeout="auto" unmountOnExit> - {hasLeagues.map((league) => ( - <PvpInfo - key={league} - league={league} - data={cleanPvp[league]} - t={t} - Icons={Icons} - pokemon={pokemon} - /> - ))} - </Collapse> - <Collapse in={popups.extras} timeout="auto" unmountOnExit> - <ExtraInfo pokemon={pokemon} perms={pokePerms} t={t} Icons={Icons} /> - </Collapse> - </Grid> + <ErrorBoundary noRefresh style={{}} variant="h5"> + <Grid + container + style={{ minWidth: 200 }} + alignItems="center" + justifyContent="center" + spacing={1} + > + <Header + pokemon={pokemon} + metaData={metaData} + iconUrl={iconUrl} + t={t} + perms={perms} + userSettings={userSettings} + classes={classes} + isTutorial={isTutorial} + /> + {pokemon.seen_type !== 'encounter' && ( + <Grid item xs={12} style={{ textAlign: 'center' }}> + <Typography variant="caption"> + {t(`seen_${pokemon.seen_type}`, '')} + </Typography> + </Grid> + )} + {pokemon.seen_type === 'nearby_cell' && ( + <Typography>{t('pokemon_cell')}</Typography> + )} + {!!pokemon.expire_timestamp && ( + <Timer pokemon={pokemon} hasStats={hasStats} t={t} /> + )} + {hasStats && pokePerms.iv && ( + <> + <Stats pokemon={pokemon} metaData={metaData} t={t} /> + <Divider orientation="vertical" flexItem /> + </> + )} + <Info + pokemon={pokemon} + metaData={metaData} + perms={pokePerms} + Icons={Icons} + isNight={isNight} + /> + <Footer + pokemon={pokemon} + popups={popups} + setPopups={setPopups} + hasPvp={!!hasLeagues.length} + classes={classes} + Icons={Icons} + /> + <Collapse in={popups.pvp && perms.pvp} timeout="auto" unmountOnExit> + {hasLeagues.map((league) => ( + <PvpInfo + key={league} + league={league} + data={cleanPvp[league]} + t={t} + Icons={Icons} + pokemon={pokemon} + /> + ))} + </Collapse> + <Collapse in={popups.extras} timeout="auto" unmountOnExit> + <ExtraInfo pokemon={pokemon} perms={pokePerms} t={t} Icons={Icons} /> + </Collapse> + </Grid> + </ErrorBoundary> ) } diff --git a/src/components/popups/Pokestop.jsx b/src/components/popups/Pokestop.jsx index 9fe9a1d1a..5b7d9334f 100644 --- a/src/components/popups/Pokestop.jsx +++ b/src/components/popups/Pokestop.jsx @@ -9,10 +9,11 @@ import { import { ExpandMore, MoreVert } from '@material-ui/icons' import { useTranslation, Trans } from 'react-i18next' +import ErrorBoundary from '@components/ErrorBoundary' import { useStore, useStatic } from '@hooks/useStore' import useStyles from '@hooks/useStyles' - import Utility from '@services/Utility' + import Dropdown from './common/Dropdown' import TimeTile from './common/TimeTile' import Navigation from './common/Navigation' @@ -50,141 +51,143 @@ export default function PokestopPopup({ const plainPokestop = !hasLure && !hasQuest && !hasInvasion return ( - <Grid - container - style={{ width: 200 }} - direction="row" - justifyContent="space-evenly" - alignItems="center" - spacing={1} - > - {!plainPokestop && ( - <Grid item xs={3} style={{ textAlign: 'center' }}> - <HeaderImage - Icons={Icons} - alt={pokestop.name} - url={pokestop.url} - arScanEligible={pokestop.ar_scan_eligible} - /> - </Grid> - )} - <Grid item xs={plainPokestop ? 10 : 7}> - <Title mainName={pokestop.name} backup={t('unknown_pokestop')} /> - </Grid> - <MenuActions - pokestop={pokestop} - perms={perms} - hasInvasion={hasInvasion} - hasQuest={hasQuest} - hasLure={hasLure} - t={t} - ts={ts} - /> - <Grid item xs={12} style={{ textAlign: 'center' }}> - {plainPokestop ? ( - <> + <ErrorBoundary noRefresh style={{}} variant="h5"> + <Grid + container + direction="row" + justifyContent="space-evenly" + alignItems="center" + style={{ width: 200 }} + spacing={1} + > + {!plainPokestop && ( + <Grid item xs={3} style={{ textAlign: 'center' }}> <HeaderImage Icons={Icons} alt={pokestop.name} url={pokestop.url} arScanEligible={pokestop.ar_scan_eligible} - large /> - <PowerUp {...pokestop} /> - </> - ) : ( - <Collapse - in={!popups.invasions || !hasInvasion} - timeout="auto" - unmountOnExit - > - <Grid - container - justifyContent="center" - alignItems="center" - spacing={1} - > - <PowerUp - {...pokestop} - divider={hasInvasion || hasQuest || hasLure} + </Grid> + )} + <Grid item xs={plainPokestop ? 10 : 7}> + <Title mainName={pokestop.name} backup={t('unknown_pokestop')} /> + </Grid> + <MenuActions + pokestop={pokestop} + perms={perms} + hasInvasion={hasInvasion} + hasQuest={hasQuest} + hasLure={hasLure} + t={t} + ts={ts} + /> + <Grid item xs={12} style={{ textAlign: 'center' }}> + {plainPokestop ? ( + <> + <HeaderImage + Icons={Icons} + alt={pokestop.name} + url={pokestop.url} + arScanEligible={pokestop.ar_scan_eligible} + large /> - {hasQuest && - pokestop.quests.map((quest, index) => ( - <Fragment key={quest.with_ar}> - {index ? ( - <Divider light flexItem className="popup-divider" /> - ) : null} - <RewardInfo - quest={quest} - Icons={Icons} - config={config} - t={t} - /> - <QuestConditions - quest={quest} - t={t} - userSettings={userSettings} - /> - </Fragment> - ))} - {hasLure && ( - <> - {hasQuest && ( - <Divider light flexItem className="popup-divider" /> - )} - <TimeTile - expireTime={lure_expire_timestamp} - icon={Icons.getPokestops(lure_id)} - until - tt={`lure_${lure_id}`} - /> - </> - )} - {hasInvasion && ( - <> - {(hasQuest || hasLure) && ( - <Divider light flexItem className="popup-divider" /> - )} - {invasions.map((invasion, index) => ( - <Fragment - key={`${invasion.grunt_type}-${invasion.incident_expire_timestamp}`} - > + <PowerUp {...pokestop} /> + </> + ) : ( + <Collapse + in={!popups.invasions || !hasInvasion} + timeout="auto" + unmountOnExit + > + <Grid + container + justifyContent="center" + alignItems="center" + spacing={1} + > + <PowerUp + {...pokestop} + divider={hasInvasion || hasQuest || hasLure} + /> + {hasQuest && + pokestop.quests.map((quest, index) => ( + <Fragment key={quest.with_ar}> {index ? ( <Divider light flexItem className="popup-divider" /> ) : null} - <TimeTile - expireTime={invasion.incident_expire_timestamp} - icon={Icons.getInvasions(invasion.grunt_type)} - until - tt={`grunt_a_${invasion.grunt_type}`} + <RewardInfo + quest={quest} + Icons={Icons} + config={config} + t={t} + /> + <QuestConditions + quest={quest} + t={t} + userSettings={userSettings} /> </Fragment> ))} - </> - )} - </Grid> + {hasLure && ( + <> + {hasQuest && ( + <Divider light flexItem className="popup-divider" /> + )} + <TimeTile + expireTime={lure_expire_timestamp} + icon={Icons.getPokestops(lure_id)} + until + tt={`lure_${lure_id}`} + /> + </> + )} + {hasInvasion && ( + <> + {(hasQuest || hasLure) && ( + <Divider light flexItem className="popup-divider" /> + )} + {invasions.map((invasion, index) => ( + <Fragment + key={`${invasion.grunt_type}-${invasion.incident_expire_timestamp}`} + > + {index ? ( + <Divider light flexItem className="popup-divider" /> + ) : null} + <TimeTile + expireTime={invasion.incident_expire_timestamp} + icon={Icons.getInvasions(invasion.grunt_type)} + until + tt={`grunt_a_${invasion.grunt_type}`} + /> + </Fragment> + ))} + </> + )} + </Grid> + </Collapse> + )} + </Grid> + {perms.invasions && hasInvasion && ( + <Collapse in={popups.invasions} timeout="auto" unmountOnExit> + <Invasion pokestop={pokestop} Icons={Icons} t={t} /> + </Collapse> + )} + <Footer + pokestop={pokestop} + popups={popups} + setPopups={setPopups} + hasInvasion={hasInvasion} + perms={perms} + Icons={Icons} + /> + {perms.allPokestops && ( + <Collapse in={popups.extras} timeout="auto" unmountOnExit> + <ExtraInfo pokestop={pokestop} t={t} ts={ts} /> </Collapse> )} </Grid> - {perms.invasions && hasInvasion && ( - <Collapse in={popups.invasions} timeout="auto" unmountOnExit> - <Invasion pokestop={pokestop} Icons={Icons} t={t} /> - </Collapse> - )} - <Footer - pokestop={pokestop} - popups={popups} - setPopups={setPopups} - hasInvasion={hasInvasion} - perms={perms} - Icons={Icons} - /> - {perms.allPokestops && ( - <Collapse in={popups.extras} timeout="auto" unmountOnExit> - <ExtraInfo pokestop={pokestop} t={t} ts={ts} /> - </Collapse> - )} - </Grid> + </ErrorBoundary> ) } @@ -333,96 +336,34 @@ const MenuActions = ({ } const RewardInfo = ({ quest, Icons, config, t }) => { - const { - quest_item_id, - item_amount, - stardust_amount, - candy_pokemon_id, - candy_amount, - xl_candy_pokemon_id, - xl_candy_amount, - mega_pokemon_id, - mega_amount, - quest_reward_type, - quest_pokemon_id, - quest_form_id, - quest_gender_id, - quest_costume_id, - quest_shiny, - with_ar, - } = quest - - const image = (() => { - switch (quest_reward_type) { - case 2: - return { - tooltip: `item_${quest_item_id}`, - src: Icons.getRewards(quest_reward_type, quest_item_id, item_amount), - } - case 3: - return { - tooltip: `stardust`, - src: Icons.getRewards(quest_reward_type, stardust_amount), - } - case 4: - return { - tooltip: `poke_${candy_pokemon_id}`, - src: Icons.getRewards( - quest_reward_type, - candy_pokemon_id, - candy_amount, - ), - } - case 7: - return { - tooltip: [ - quest_form_id ? `form_${quest_form_id}` : '', - `poke_${quest_pokemon_id}`, - ], - src: Icons.getPokemon( - quest_pokemon_id, - quest_form_id, - 0, - quest_gender_id, - quest_costume_id, - quest_shiny, - ), - } - case 9: - return { - tooltip: `poke_${xl_candy_pokemon_id}`, - src: Icons.getRewards( - quest_reward_type, - xl_candy_pokemon_id, - xl_candy_amount, - ), - } - case 12: - return { - tooltip: `poke_${mega_pokemon_id}`, - src: Icons.getRewards( - quest_reward_type, - mega_pokemon_id, - mega_amount, - ), - } - default: - return { - tooltip: `quest_reward_${quest_reward_type}`, - src: Icons.getRewards(quest_reward_type), - } - } - })() + const { src, amount, tt } = Utility.getRewardInfo(quest, Icons) return ( - <Grid item xs={3} style={{ textAlign: 'center' }}> - <NameTT id={image.tooltip}> - <img src={image.src} className="quest-popup-img" alt="quest reward" /> + <Grid item xs={3} style={{ textAlign: 'center', position: 'relative' }}> + <NameTT id={tt}> + <img + src={src} + style={{ maxWidth: 35, maxHeight: 35 }} + alt={tt} + onError={(e) => { + e.target.onerror = null + e.target.src = + 'https://github.com/WatWowMap/wwm-uicons/blob/main/misc/0.png' + }} + /> </NameTT> + {!!amount && ( + <div + className="search-amount-holder" + style={{ fontSize: 'medium', bottom: 20 }} + > + x{amount} + </div> + )} <Typography variant="caption" className="ar-task" noWrap> {config.questMessage ? config.questMessage - : t(`ar_quest_${Boolean(with_ar)}`)} + : t(`ar_quest_${Boolean(quest.with_ar)}`)} </Typography> </Grid> ) diff --git a/src/components/popups/Portal.jsx b/src/components/popups/Portal.jsx index 7775d9ac2..f9a3ecf15 100644 --- a/src/components/popups/Portal.jsx +++ b/src/components/popups/Portal.jsx @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next' import { useStore, useStatic } from '@hooks/useStore' import Utility from '@services/Utility' +import ErrorBoundary from '@components/ErrorBoundary' export default function PortalPopup({ portal, ts, Icons }) { const { navigation } = useStore((state) => state.settings) @@ -37,63 +38,65 @@ export default function PortalPopup({ portal, ts, Icons }) { }, []) return ( - <Grid - container - style={{ width: 200 }} - direction="row" - justifyContent="space-evenly" - alignItems="center" - spacing={1} - > - <Grid item xs={12}> - <Typography - variant={name.length > 20 ? 'subtitle2' : 'h6'} - align="center" - noWrap={portalName} - onClick={() => setPortalName(!portalName)} - > - {name} - </Typography> - </Grid> - <Grid item xs={12} style={{ textAlign: 'center' }}> - <a - href={imageUrl} - alt={name || 'unknown'} - target="_blank" - rel="noreferrer" - > - <img - src={src} + <ErrorBoundary noRefresh style={{}} variant="h5"> + <Grid + container + style={{ width: 200 }} + direction="row" + justifyContent="space-evenly" + alignItems="center" + spacing={1} + > + <Grid item xs={12}> + <Typography + variant={name.length > 20 ? 'subtitle2' : 'h6'} + align="center" + noWrap={portalName} + onClick={() => setPortalName(!portalName)} + > + {name} + </Typography> + </Grid> + <Grid item xs={12} style={{ textAlign: 'center' }}> + <a + href={imageUrl} alt={name || 'unknown'} - className="circle-image" - style={{ - maxHeight: 150, - maxWidth: 150, - }} - /> - </a> - </Grid> - <Grid item xs={12} style={{ textAlign: 'center' }}> - {extraMetaData.map((meta) => ( - <Fragment key={meta.description}> - <Typography variant="subtitle1" style={{ textAlign: 'center' }}> - {meta.description} - </Typography> - <Typography variant="caption" style={{ textAlign: 'center' }}> - {meta.data} - </Typography> - </Fragment> - ))} - </Grid> - <Grid item xs={4} style={{ textAlign: 'center' }}> - <IconButton - href={url.replace('{x}', lat).replace('{y}', lon)} - target="_blank" - rel="noreferrer" - > - <Map style={{ color: 'white' }} /> - </IconButton> + target="_blank" + rel="noreferrer" + > + <img + src={src} + alt={name || 'unknown'} + className="circle-image" + style={{ + maxHeight: 150, + maxWidth: 150, + }} + /> + </a> + </Grid> + <Grid item xs={12} style={{ textAlign: 'center' }}> + {extraMetaData.map((meta) => ( + <Fragment key={meta.description}> + <Typography variant="subtitle1" style={{ textAlign: 'center' }}> + {meta.description} + </Typography> + <Typography variant="caption" style={{ textAlign: 'center' }}> + {meta.data} + </Typography> + </Fragment> + ))} + </Grid> + <Grid item xs={4} style={{ textAlign: 'center' }}> + <IconButton + href={url.replace('{x}', lat).replace('{y}', lon)} + target="_blank" + rel="noreferrer" + > + <Map style={{ color: 'white' }} /> + </IconButton> + </Grid> </Grid> - </Grid> + </ErrorBoundary> ) } diff --git a/src/components/popups/ScanCell.jsx b/src/components/popups/ScanCell.jsx index b9bc5e0e2..256896ed2 100644 --- a/src/components/popups/ScanCell.jsx +++ b/src/components/popups/ScanCell.jsx @@ -3,6 +3,7 @@ import { Typography } from '@material-ui/core' import { Trans, useTranslation } from 'react-i18next' import Utility from '@services/Utility' +import ErrorBoundary from '@components/ErrorBoundary' export default function ScanCellPopup({ cell, ts }) { const { t } = useTranslation() @@ -18,7 +19,7 @@ export default function ScanCellPopup({ cell, ts }) { }) return ( - <> + <ErrorBoundary noRefresh style={{}} variant="h5"> <Typography variant="h6" align="center"> <Trans i18nKey="s2_cell_level">{{ level: 15 }}</Trans> </Typography> @@ -34,6 +35,6 @@ export default function ScanCellPopup({ cell, ts }) { <Typography variant="subtitle1" align="center"> {t('id')}: {id} </Typography> - </> + </ErrorBoundary> ) } diff --git a/src/components/popups/Spawnpoint.jsx b/src/components/popups/Spawnpoint.jsx index 5d56aecae..47603e226 100644 --- a/src/components/popups/Spawnpoint.jsx +++ b/src/components/popups/Spawnpoint.jsx @@ -3,6 +3,7 @@ import { Typography } from '@material-ui/core' import { useTranslation } from 'react-i18next' import Utility from '@services/Utility' +import ErrorBoundary from '@components/ErrorBoundary' export default function SpawnpointPopup({ spawnpoint, ts }) { const { t } = useTranslation() @@ -12,7 +13,7 @@ export default function SpawnpointPopup({ spawnpoint, ts }) { const minuteFixed = minute < 10 ? `0${minute}` : minute return ( - <> + <ErrorBoundary noRefresh style={{}} variant="h5"> <Typography variant="h5" align="center"> {t('spawnpoint')} </Typography> @@ -33,6 +34,6 @@ export default function SpawnpointPopup({ spawnpoint, ts }) { {lat},<br /> {lon} </Typography> - </> + </ErrorBoundary> ) } diff --git a/src/components/popups/SubmissionCell.jsx b/src/components/popups/SubmissionCell.jsx index cb594a0da..77d10df3e 100644 --- a/src/components/popups/SubmissionCell.jsx +++ b/src/components/popups/SubmissionCell.jsx @@ -3,6 +3,7 @@ import { Typography } from '@material-ui/core' import { Trans, useTranslation } from 'react-i18next' import Utility from '@services/Utility' +import ErrorBoundary from '@components/ErrorBoundary' export default function SubmissionCellPopup({ cell }) { const { t } = useTranslation() @@ -24,7 +25,7 @@ export default function SubmissionCellPopup({ cell }) { }, []) return ( - <> + <ErrorBoundary noRefresh style={{}} variant="h5"> <Typography variant="h6" align="center"> <Trans i18nKey="s2_cell_level">{{ level: cell.level }}</Trans> </Typography> @@ -40,6 +41,6 @@ export default function SubmissionCellPopup({ cell }) { <Typography variant="subtitle2" align="center"> {t('next_gym')}: {untilNextGym} </Typography> - </> + </ErrorBoundary> ) } diff --git a/src/components/popups/Weather.jsx b/src/components/popups/Weather.jsx index 59ad7cb4f..51997359a 100644 --- a/src/components/popups/Weather.jsx +++ b/src/components/popups/Weather.jsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next' import { useStatic } from '@hooks/useStore' import Utility from '@services/Utility' +import ErrorBoundary from '@components/ErrorBoundary' export default function WeatherPopup({ weather, ts, Icons }) { const { t } = useTranslation() @@ -19,43 +20,45 @@ export default function WeatherPopup({ weather, ts, Icons }) { }, []) return ( - <Grid - container - direction="row" - justifyContent="center" - alignItems="center" - style={{ width: 150 }} - > - <Grid item xs={12}> - <Typography variant="h6" align="center"> - {t(`weather_${gameplay_condition}`)} - </Typography> - </Grid> - <Grid item xs={12}> - <Typography variant="subtitle2" align="center"> - {t('last_updated')}: - </Typography> - </Grid> - <Timer updated={updated} ts={ts} t={t} /> - <Grid item xs={12}> - <Typography variant="subtitle2" align="center"> - {t('boosted_types')}: - </Typography> - </Grid> - {weatherTypes[gameplay_condition].types.map((type) => ( - <Grid item xs={4} key={type} style={{ textAlign: 'center' }}> - <Typography variant="caption">{t(`poke_type_${type}`)}</Typography> - <img - src={Icons.getTypes(type)} - alt={type} - style={{ - maxWidth: 30, - maxHeight: 30, - }} - /> + <ErrorBoundary noRefresh style={{}} variant="h5"> + <Grid + container + direction="row" + justifyContent="center" + alignItems="center" + style={{ width: 150 }} + > + <Grid item xs={12}> + <Typography variant="h6" align="center"> + {t(`weather_${gameplay_condition}`)} + </Typography> + </Grid> + <Grid item xs={12}> + <Typography variant="subtitle2" align="center"> + {t('last_updated')}: + </Typography> </Grid> - ))} - </Grid> + <Timer updated={updated} ts={ts} t={t} /> + <Grid item xs={12}> + <Typography variant="subtitle2" align="center"> + {t('boosted_types')}: + </Typography> + </Grid> + {weatherTypes[gameplay_condition].types.map((type) => ( + <Grid item xs={4} key={type} style={{ textAlign: 'center' }}> + <Typography variant="caption">{t(`poke_type_${type}`)}</Typography> + <img + src={Icons.getTypes(type)} + alt={type} + style={{ + maxWidth: 30, + maxHeight: 30, + }} + /> + </Grid> + ))} + </Grid> + </ErrorBoundary> ) } diff --git a/src/components/tiles/Device.jsx b/src/components/tiles/Device.jsx index 084bc2d87..9cc3b18af 100644 --- a/src/components/tiles/Device.jsx +++ b/src/components/tiles/Device.jsx @@ -1,3 +1,4 @@ +import ErrorBoundary from '@components/ErrorBoundary' import React, { memo, useRef, useEffect, useState } from 'react' import { Marker, Popup } from 'react-leaflet' @@ -30,7 +31,9 @@ const DeviceTile = ({ item, ts, Icons, userSettings }) => { <PopupContent device={item} isOnline={isOnline} ts={ts} /> </Popup> {poly && !item.isMad && ( - <DevicePoly device={item} color={userSettings.devicePathColor} /> + <ErrorBoundary> + <DevicePoly device={item} color={userSettings.devicePathColor} /> + </ErrorBoundary> )} </Marker> ) diff --git a/src/services/Icons.js b/src/services/Icons.js index d84fa8c98..dab560b1d 100644 --- a/src/services/Icons.js +++ b/src/services/Icons.js @@ -143,6 +143,8 @@ export default class UIcons { return this.getPokestops(id.slice(1)) case 'm': return this.getPokemon(id.slice(1).split('-')[0], 0, 1) + case 'p': + return this.getRewards(1, id.slice(1)) case 'q': return this.getRewards(2, ...id.slice(1).split('-')) case 'r': diff --git a/src/services/Utility.js b/src/services/Utility.js index d81881a2b..9c053e564 100644 --- a/src/services/Utility.js +++ b/src/services/Utility.js @@ -8,6 +8,7 @@ import checkAdvFilter from './functions/checkAdvFilter' import dayCheck from './functions/dayCheck' import parseQuestConditions from './functions/parseConditions' import formatter from './functions/formatter' +import getRewardInfo from './functions/getRewardInfo' export default class Utility { static getProperName(word) { @@ -46,6 +47,10 @@ export default class Utility { return str.charAt(0).toUpperCase() + str.slice(1) } + static getRewardInfo(...args) { + return getRewardInfo(...args) + } + static getTileBackground(columnIndex, rowIndex) { return columnIndex % 2 ? rowIndex % 2 === 0 diff --git a/src/services/filtering/genPokestops.js b/src/services/filtering/genPokestops.js index 3dd82697c..92946cd33 100644 --- a/src/services/filtering/genPokestops.js +++ b/src/services/filtering/genPokestops.js @@ -56,6 +56,17 @@ export default function genPokestops(t, pokemon, pokestops, categories) { } } break + case 'p': + if (tempObj.quest_reward_1) { + tempObj.quest_reward_1[id] = { + name: `x${id.slice(1)}`, + perms: ['quests'], + } + tempObj.quest_reward_1[id].searchMeta = `${t( + 'quest_reward_1', + ).toLowerCase()} ${tempObj.quest_reward_1[id].name.toLowerCase()}` + } + break case 'q': if (tempObj.items) { tempObj.items[id] = { diff --git a/src/services/functions/getRewardInfo.js b/src/services/functions/getRewardInfo.js new file mode 100644 index 000000000..ca4f0325a --- /dev/null +++ b/src/services/functions/getRewardInfo.js @@ -0,0 +1,74 @@ +export default function getRewardInfo(quest, Icons) { + const { + quest_pokemon_id, + quest_form_id, + quest_gender_id, + quest_costume_id, + quest_shiny, + quest_item_id, + item_amount, + stardust_amount, + candy_amount, + xl_candy_amount, + xp_amount, + mega_pokemon_id, + mega_amount, + candy_pokemon_id, + xl_candy_pokemon_id, + quest_reward_type, + } = quest + let src = '' + let amount = 0 + let tt = '' + + switch (quest_reward_type) { + case 1: + tt = `quest_reward_${quest_reward_type}` + src = Icons.getRewards(quest_reward_type, xp_amount) + amount = src.endsWith('/0.png') ? xp_amount : 0 + break + case 2: + tt = `item_${quest_item_id}` + src = Icons.getRewards(quest_reward_type, quest_item_id, item_amount) + amount = src.includes('_a') || item_amount <= 1 ? 0 : item_amount + break + case 3: + tt = `quest_reward_3` + src = Icons.getRewards(quest_reward_type, stardust_amount) + amount = src.endsWith('/0.png') ? stardust_amount : 0 + break + case 4: + tt = `poke_${candy_pokemon_id}` + src = Icons.getRewards(quest_reward_type, candy_pokemon_id) + amount = src.includes('_a') ? 0 : candy_amount + break + case 7: + tt = [ + quest_form_id ? `form_${quest_form_id}` : '', + `poke_${quest_pokemon_id}`, + ] + src = Icons.getPokemon( + quest_pokemon_id, + quest_form_id, + 0, + quest_gender_id, + quest_costume_id, + quest_shiny, + ) + break + case 9: + tt = `poke_${xl_candy_pokemon_id}` + src = Icons.getRewards(quest_reward_type, xl_candy_pokemon_id) + amount = src.includes('_a') ? 0 : xl_candy_amount + break + case 12: + tt = `poke_${mega_pokemon_id}` + src = Icons.getRewards(quest_reward_type, mega_pokemon_id, mega_amount) + amount = src.includes('_a') ? 0 : mega_amount + break + default: + tt = `quest_reward_${quest_reward_type}` + src = Icons.getRewards(quest_reward_type) + } + return { src, tt, amount } +} diff --git a/src/services/functions/parseConditions.js b/src/services/functions/parseConditions.js index 1e80b7987..35b4e010f 100644 --- a/src/services/functions/parseConditions.js +++ b/src/services/functions/parseConditions.js @@ -9,32 +9,35 @@ export default function parseQuestConditions(conditions) { switch (specifics.type) { case 1: normalized.info.pokemon_type_ids = - specifics.with_pokemon_type.pokemon_type + specifics?.with_pokemon_type?.pokemon_type || [] break case 2: normalized.info.pokemon_ids = - specifics.with_pokemon_category.pokemon_ids + specifics?.with_pokemon_category?.pokemon_ids || [] break case 7: - normalized.info.raid_levels = specifics.with_raid_level.raid_level + normalized.info.raid_levels = + specifics?.with_raid_level?.raid_level || [] break case 11: - normalized.info.item_id = specifics.with_item.item + normalized.info.item_id = specifics?.with_item?.item || 0 break case 8: case 14: - normalized.info.throw_type_id = specifics.with_throw_type.throw_type + normalized.info.throw_type_id = + specifics?.with_throw_type?.throw_type || 0 break case 26: normalized.info.alignment_ids = - specifics.with_pokemon_alignment.alignment + specifics?.with_pokemon_alignment?.alignment || [] break case 27: normalized.info.character_category_ids = - specifics.with_invasion_character.category + specifics?.with_invasion_character?.category || [] break case 44: - normalized.info.time = specifics.with_elapsed_time.elapsed_time / 1000 + normalized.info.time = + (specifics?.with_elapsed_time?.elapsed_time || 0) / 1000 break default: break diff --git a/src/services/queries/pokestop.js b/src/services/queries/pokestop.js index 25bb23e42..41483c174 100644 --- a/src/services/queries/pokestop.js +++ b/src/services/queries/pokestop.js @@ -46,6 +46,7 @@ const quest = gql` candy_amount xl_candy_pokemon_id xl_candy_amount + xp_amount with_ar key } diff --git a/src/services/queries/search.js b/src/services/queries/search.js index b0c7bf6b4..49f3a5342 100644 --- a/src/services/queries/search.js +++ b/src/services/queries/search.js @@ -126,6 +126,7 @@ export const quests = gql` item_amount candy_pokemon_id candy_amount + xp_amount with_ar quest_title quest_target