From 28776c271025349a9cdad50195abb69b4c8896a7 Mon Sep 17 00:00:00 2001 From: versx Date: Tue, 16 Mar 2021 01:55:08 -0700 Subject: [PATCH 1/3] Add toggle-able pokemon notifications based on map filters --- src/routes/api.js | 34 ++++++++++++++++ src/views/header.mustache | 2 + static/js/index.js | 82 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 113 insertions(+), 5 deletions(-) diff --git a/src/routes/api.js b/src/routes/api.js index ee9d223f..2f29599a 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 555932fd..a29f93e2 100644 --- a/src/views/header.mustache +++ b/src/views/header.mustache @@ -18,6 +18,7 @@ + @@ -28,6 +29,7 @@ + {{#google_analytics_id}} {{/google_analytics_id}} diff --git a/static/js/index.js b/static/js/index.js index 395e6ea4..e6f48ddd 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -122,6 +122,8 @@ let showPopupPvp; let showMinPokePopup; let showWeatherDetails; +let showPokemonNotifications; + let tileLayer; let nestLayer = new L.LayerGroup(); let scanAreaLayer = new L.LayerGroup(); @@ -134,6 +136,8 @@ let scanAreasDb = {}; let cpMultipliers = {}; let defaultRarity = {}; +let pokemonNotificationsSent = {}; + let skipForms = ['Shadow', 'Purified']; const kanto = [1, 151]; @@ -429,6 +433,7 @@ $(function () { portal: portalFilterNew, weather: weatherFilterNew, device: deviceFilterNew, + // TODO: Export all settings }; let json = JSON.stringify(settings); let el = document.createElement('a'); @@ -1169,6 +1174,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 { @@ -1233,6 +1241,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; @@ -1254,6 +1265,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 () { @@ -1576,6 +1588,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 || @@ -1585,7 +1598,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); @@ -1646,6 +1660,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); @@ -1661,6 +1676,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 || @@ -1937,7 +1953,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); @@ -2407,6 +2423,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) { @@ -3079,7 +3098,7 @@ const getPokemonPopupContent = (pokemon) => { const getIV = (pokemon) => { if (pokemon.atk_iv !== null && 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 ` @@ -4750,7 +4769,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; @@ -5282,7 +5302,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; @@ -6155,6 +6176,54 @@ function getCpAtLevel(id, form, level, isMax) { return 0; } +const showPokemonNotification = (pokemon) => { + // 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) { @@ -7360,6 +7429,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'); From 36efe7748901dfe7d597dbd6101189db2c09ce2f Mon Sep 17 00:00:00 2001 From: versx Date: Tue, 16 Mar 2021 01:59:45 -0700 Subject: [PATCH 2/3] Update locales --- static/locales/_en.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/locales/_en.json b/static/locales/_en.json index 64f1cdbb..091a786a 100644 --- a/static/locales/_en.json +++ b/static/locales/_en.json @@ -189,5 +189,6 @@ "enter_pokestop_name": "Enter Pokestop Name...", "filter_option_rarity": "By Rarity", "filter_option_generation": "By Generation", - "filter_option_form": "By Form" + "filter_option_form": "By Form", + "settings_pokemon_notifications": "Pokemon Notifications" } From b6d961c06c4e60c4941a4b9a06b66cdd0aee71c2 Mon Sep 17 00:00:00 2001 From: versx Date: Thu, 8 Apr 2021 20:29:18 -0700 Subject: [PATCH 3/3] Show notifications only after first map render --- static/js/index.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/static/js/index.js b/static/js/index.js index 0c61282e..e7e53eef 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -3521,8 +3521,10 @@ const getPossibleInvasionRewards = pokestop => { const item = gruntTypes[pokestop.grunt_type]; if (!item) return ''; const encounterNum = { first: '#1', second: '#2', third: '#3' }; - const rewardPercent = item.type === 'Giovanni' ? { third: '100%' } - : item.second_reward ? { first: '85%', second: '15%' } + const rewardPercent = item.type === 'Giovanni' + ? { third: '100%' } + : item.second_reward + ? { first: '85%', second: '15%' } : { first: '100%' }; const makeShadowPokemon = pokemonId => { @@ -6194,6 +6196,10 @@ function getCpAtLevel(id, form, level, isMax) { } 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;