diff --git a/src/routes/api.js b/src/routes/api.js
index 6ec9247e..b7cbf10e 100644
--- a/src/routes/api.js
+++ b/src/routes/api.js
@@ -6,6 +6,7 @@ const router = express.Router();
const map = require('../data/map.js');
const utils = require('../services/utils.js');
const masterfile = require('../../static/data/masterfile.json');
+const { config } = require('../services/discord.js');
const skipForms = ['shadow', 'purified'];
router.get('/get_data', async (req, res) => {
@@ -57,6 +58,31 @@ const getSettings = (perms) => {
const level50String = i18n.__('filter_level50_stats');
const level51String = i18n.__('filter_level51_stats');
const weatherDetailsString = i18n.__('settings_weather_details');
+ const pokemonNotificationsString = i18n.__('settings_pokemon_notifications');
+
+ // Check if perms is not set and discord config not set, then allow all permissions
+ if (!config.discord.enabled && !perms) {
+ perms = {
+ map: true,
+ pokemon: true,
+ raids: true,
+ gyms: true,
+ pokestops: true,
+ quests: true,
+ lures: true,
+ invasions: true,
+ spawnpoints: true,
+ iv: true,
+ pvp: true,
+ s2cells: true,
+ submissionCells: true,
+ nests: true,
+ portals: true,
+ scanAreas: true,
+ weather: true,
+ devices: true,
+ };
+ }
if (perms.pokemon) {
/*
@@ -98,6 +124,14 @@ const getSettings = (perms) => {
'filter': generateShowHideButtons('pokemon-timers', 'pokemon-timers'),
'type': pokemonSettingsString,
});
+ settingsData.push({
+ 'id': {
+ 'sort': utils.zeroPad(3, 3),
+ },
+ 'name': pokemonNotificationsString,
+ 'filter': generateShowHideButtons('pokemon-notifications', 'pokemon-notifications'),
+ 'type': pokemonSettingsString,
+ });
}
if (perms.gyms || perms.raids) {
settingsData.push({
diff --git a/src/views/header.mustache b/src/views/header.mustache
index e7c0aa45..bce58452 100644
--- a/src/views/header.mustache
+++ b/src/views/header.mustache
@@ -18,6 +18,7 @@
+
@@ -27,6 +28,7 @@
+
{{#google_analytics_id}}
{{/google_analytics_id}}
diff --git a/static/js/index.js b/static/js/index.js
index da94dfaf..03d83c77 100644
--- a/static/js/index.js
+++ b/static/js/index.js
@@ -123,6 +123,8 @@ let showPopupPvp;
let showMinPokePopup;
let showWeatherDetails;
+let showPokemonNotifications;
+
let tileLayer;
let nestLayer = new L.LayerGroup();
let scanAreaLayer = new L.LayerGroup();
@@ -135,6 +137,8 @@ let scanAreasDb = {};
let cpMultipliers = {};
let defaultRarity = {};
+let pokemonNotificationsSent = {};
+
let skipForms = ['Shadow', 'Purified'];
const kanto = [1, 151];
@@ -430,6 +434,7 @@ $(function () {
portal: portalFilterNew,
weather: weatherFilterNew,
device: deviceFilterNew,
+ // TODO: Export all settings
};
let json = JSON.stringify(settings);
let el = document.createElement('a');
@@ -1170,6 +1175,9 @@ function loadStorage () {
if (defaultSettings['level51-stats'] === undefined) {
defaultSettings['level51-stats'] = { show: configPvp.l51stats };
}
+ if (defaultSettings['pokemon-notifications'] == undefined) {
+ defaultSettings['pokemon-notifications'] = { show: false }; // TODO: Configurable as default value
+ }
store('settings', JSON.stringify(defaultSettings));
settings = defaultSettings;
} else {
@@ -1234,6 +1242,9 @@ function loadStorage () {
if (settings['level51-stats'] === undefined) {
settings['level51-stats'] = { show: configPvp.l51stats };
}
+ if (settings['pokemon-notifications'] === undefined) {
+ settings['pokemon-notifications'] = { show: false }; // TODO: Configurable as default value
+ }
}
clusterPokemon = settings['pokemon-cluster'].show;
clusterGyms = settings['gym-cluster'].show;
@@ -1255,6 +1266,7 @@ function loadStorage () {
showLevel41Stats = settings['level41-stats'].show;
showLevel50Stats = settings['level50-stats'].show;
showLevel51Stats = settings['level51-stats'].show;
+ showPokemonNotifications = settings['pokemon-notifications'].show;
}
function initMap () {
@@ -1577,6 +1589,7 @@ function initMap () {
const newShowLevel41Stats = settingsNew['level41-stats'].show;
const newShowLevel50Stats = settingsNew['level50-stats'].show;
const newShowLevel51Stats = settingsNew['level51-stats'].show;
+ const newShowPokemonNotifications = settingsNew['pokemon-notifications'].show;
if (clusterPokemon !== newClusterPokemon ||
showPokemonGlow !== newShowPokemonGlow ||
showPokemonTimers !== newShowPokemonTimers ||
@@ -1586,7 +1599,8 @@ function initMap () {
showLevel40Stats !== newShowLevel40Stats ||
showLevel41Stats !== newShowLevel41Stats ||
showLevel50Stats !== newShowLevel50Stats ||
- showLevel51Stats !== newShowLevel51Stats) {
+ showLevel51Stats !== newShowLevel51Stats ||
+ showPokemonNotifications !== newShowPokemonNotifications) {
$.each(pokemonMarkers, function (index, pokemon) {
if (clusterPokemon) {
clusters.removeLayer(pokemon.marker);
@@ -1647,6 +1661,7 @@ function initMap () {
showLevel41Stats = newShowLevel41Stats;
showLevel50Stats = newShowLevel50Stats;
showLevel51Stats = newShowLevel51Stats;
+ showPokemonNotifications = newShowPokemonNotifications;
store('show_pokemon_timers', newShowPokemonTimers);
store('show_raid_timers', newShowRaidTimers);
store('show_invasion_timers', newShowInvasionTimers);
@@ -1662,6 +1677,7 @@ function initMap () {
store('level41_stats', newShowLevel41Stats);
store('level50_stats', newShowLevel50Stats);
store('level51_stats', newShowLevel51Stats);
+ store('show_pokemon_notifications', newShowPokemonNotifications);
if (pokemonMarkers.length === 0 ||
gymMarkers.length === 0 ||
@@ -1938,7 +1954,7 @@ function loadSearchData (id, value) {
}
function centerOnMap(lat, lon) {
- $('#searchModal').modal('toggle');
+ $('#searchModal').modal('hide');
let latlng = new L.LatLng(lat, lon);
let zoom = maxZoom;
map.setView(latlng, zoom);
@@ -2411,6 +2427,9 @@ function loadData () {
} else {
pokemon.marker.addTo(map);
}
+ // Show Pokemon notification
+ // TODO: Check if setting is set for notifications
+ showPokemonNotification(pokemon);
} else {
if (oldPokemon.expire_timestamp !== pokemon.expire_timestamp) {
@@ -3081,7 +3100,7 @@ const getPokemonPopupContent = (pokemon) => {
const getIV = (pokemon) => {
if (pokemon.atk_iv !== null && pokemon.atk_iv !== undefined && popupDetails.pokemon.iv) {
const ivColors = { 0: 'red', 66: 'orange', 82: 'yellow', 100: 'green' }
- const ivPercent = ((pokemon.atk_iv + pokemon.def_iv + pokemon.sta_iv) / .45).toFixed(2);
+ const ivPercent = calculateIV(pokemon);
let selectedColor
Object.keys(ivColors).forEach(range => ivPercent >= parseInt(range) ? selectedColor = ivColors[range] : '');
return `
@@ -4732,7 +4751,8 @@ function manageSelectButton(e, isNew) {
type === 'pvp-level40-stats' ||
type === 'pvp-level41-stats' ||
type === 'pvp-level50-stats' ||
- type === 'pvp-level51-stats') {
+ type === 'pvp-level51-stats' ||
+ type === 'pokemon-notifications') {
switch (info) {
case 'hide':
shouldShow = settingsNew[id].show === false;
@@ -5264,7 +5284,8 @@ function manageSelectButton(e, isNew) {
type === 'pvp-level40-stats' ||
type === 'pvp-level41-stats' ||
type === 'pvp-level50-stats' ||
- type === 'pvp-level51-stats') {
+ type === 'pvp-level51-stats' ||
+ type === 'pokemon-notifications') {
switch (info) {
case 'hide':
settingsNew[id].show = false;
@@ -6135,6 +6156,58 @@ function getCpAtLevel(id, form, level, isMax) {
return 0;
}
+const showPokemonNotification = (pokemon) => {
+ // Check if we have at least rendered the map once before showing notifications
+ if (lastUpdate === 0)
+ return;
+
+ // Check if pokemon notifications are enabled
+ if (!showPokemonNotifications) {
+ return;
+ }
+ if (pokemonNotificationsSent[pokemon.id]) {
+ // Prevent notifications of the same pokemon twice
+ return;
+ }
+ pokemonNotificationsSent[pokemon.id] = true;
+ const iv = pokemon.atk_iv !== null ? calculateIV(pokemon) : '?';
+ const name = getPokemonName(pokemon.pokemon_id);
+ const formName = getFormName(pokemon.form);
+ // TODO: Add pokemon costume to notification
+ const genderIcon = getGenderIcon(pokemon.gender);
+ const title = `Found Pokemon: ${name}${pokemon.form > 0 ? ' (Form: ' + formName + ')' : ''}`;
+ const message = `IV: ${iv}% Lvl: ${pokemon.level || '?'} Gender: ${genderIcon}`;
+ const icon = getPokemonIcon(pokemon.pokemon_id, pokemon.form, 0, pokemon.gender, pokemon.costume);
+ const iconUrl = `${availableIconStyles[selectedIconStyle].path}/${icon}.png`;
+ let notification = toastr.info(message, title, {
+ closeButton: true,
+ positionClass: 'toast-top-right',
+ preventDuplicates: true,
+ onclick: () => {
+ centerOnMap(pokemon.lat, pokemon.lon);
+ },
+ showDuration: '2000',
+ hideDuration: '1000',
+ timeOut: '6000',
+ extendedTimeOut: '1500',
+ showEasing: 'swing',
+ hideEasing: 'linear',
+ showMethod: 'fadeIn',
+ hideMethod: 'fadeOut',
+ });
+ notification.removeClass('toast-info');
+ notification.css({
+ 'padding-left': '74px',
+ 'background-image': `url('${iconUrl}')`,
+ 'background-size': '48px',
+ 'background-color': '#15151D', // TODO: Check dark mode?
+ });
+};
+
+const calculateIV = (pokemon) => {
+ return ((pokemon.atk_iv + pokemon.def_iv + pokemon.sta_iv) / .45).toFixed(2);
+};
+
// MARK: - Init Filter
function populateImage (row, type, set, meta) {
@@ -7340,6 +7413,9 @@ function loadFilterSettings (e) {
showLevel51Stats = obj.level51_stats;
store('level51_stats', showLevel51Stats);
+ showPokemonNotifications = obj.show_pokemon_notifications;
+ store('show_pokemon_notifications', showPokemonNotifications);
+
if (showGyms) {
$('#show-gyms').addClass('active');
$('#hide-gyms').removeClass('active');