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.noRefresh && (
+ <>
+
+
+
+ >
+ )}