From ad51675ac6c19551ce26e034d135f2503746de79 Mon Sep 17 00:00:00 2001 From: jbusa22 <41833473+jbusa22@users.noreply.github.com> Date: Mon, 26 Oct 2020 06:22:46 -0400 Subject: [PATCH] Fixes Issue #12421 - Change page title based on the site section being viewed (#12627) * Update title for tabs not including challenges, guild and team * add section titles to challenges, guilds, and groups * Update dynamic title to use vuex action * Remove duplicate key * Actually remove duplicate key * Fix section sub section in group * Add note to implement setTitle when adding a page * Add missing sections to dynamic title * Features string not translated * Use onGroupUpdate to update group titles * Add watcher to challenges for dynamic title updates * Small fixes * Add register and login to title, remove duplicate keys * Add home page dynamic title functionality * Minor name changes * remove wrong i18n strings from front.js * refactor router note Co-authored-by: Matteo Pagliazzi --- .../src/components/auth/registerLoginReset.vue | 14 +++++++++++++- .../src/components/challenges/challengeDetail.vue | 14 ++++++++++++++ .../src/components/challenges/findChallenges.vue | 4 ++++ .../src/components/challenges/myChallenges.vue | 4 ++++ .../src/components/group-plans/taskInformation.vue | 5 ++++- website/client/src/components/groups/discovery.vue | 6 ++++++ website/client/src/components/groups/group.vue | 10 ++++++++-- website/client/src/components/groups/groupPlan.vue | 3 +++ website/client/src/components/groups/myGuilds.vue | 6 ++++++ website/client/src/components/groups/tavern.vue | 4 ++++ .../src/components/inventory/equipment/index.vue | 5 +++++ .../src/components/inventory/items/index.vue | 6 ++++++ .../src/components/inventory/stable/index.vue | 6 ++++++ website/client/src/components/settings/api.vue | 4 ++++ .../client/src/components/settings/dataExport.vue | 11 +++++++++++ .../src/components/settings/notifications.vue | 4 ++++ .../client/src/components/settings/promoCode.vue | 6 ++++++ website/client/src/components/settings/site.vue | 5 +++++ .../src/components/settings/subscription.vue | 6 ++++++ .../client/src/components/shops/market/index.vue | 4 ++++ .../client/src/components/shops/quests/index.vue | 4 ++++ .../client/src/components/shops/seasonal/index.vue | 5 +++++ .../src/components/shops/timeTravelers/index.vue | 4 ++++ website/client/src/components/static/contact.vue | 5 +++++ website/client/src/components/static/faq.vue | 6 ++++++ website/client/src/components/static/features.vue | 5 +++++ .../client/src/components/static/groupPlans.vue | 3 +++ website/client/src/components/static/home.vue | 3 +++ website/client/src/components/static/pressKit.vue | 5 +++++ website/client/src/components/tasks/user.vue | 5 +++++ website/client/src/components/userMenu/profile.vue | 4 ++++ website/client/src/pages/private-messages.vue | 3 +++ website/client/src/router/index.js | 2 ++ website/client/src/store/actions/common.js | 10 ++++++++++ website/common/locales/en/front.json | 1 - website/common/locales/en/groups.json | 2 +- 36 files changed, 188 insertions(+), 6 deletions(-) diff --git a/website/client/src/components/auth/registerLoginReset.vue b/website/client/src/components/auth/registerLoginReset.vue index 820933b101..d15dc690cd 100644 --- a/website/client/src/components/auth/registerLoginReset.vue +++ b/website/client/src/components/auth/registerLoginReset.vue @@ -745,6 +745,7 @@ export default { watch: { $route: { handler () { + this.setTitle(); if (this.resetPasswordSetNewOne) { const { query } = this.$route; const { code } = query; @@ -760,7 +761,6 @@ export default { this.$router.push({ name: 'login' }); return; } - this.resetPasswordSetNewOneData.code = query.code; this.resetPasswordSetNewOneData.hasError = hasError; } @@ -898,6 +898,18 @@ export default { window.location.href = redirectTo; } }, + setTitle () { + if (this.resetPasswordSetNewOne) { + return; + } + let title = 'login'; + if (this.registering) { + title = 'register'; + } + this.$store.dispatch('common:setTitle', { + section: this.$t(title), + }); + }, handleSubmit () { if (this.registering) { this.register(); diff --git a/website/client/src/components/challenges/challengeDetail.vue b/website/client/src/components/challenges/challengeDetail.vue index 3a6addfeea..023db6c5cf 100644 --- a/website/client/src/components/challenges/challengeDetail.vue +++ b/website/client/src/components/challenges/challengeDetail.vue @@ -384,6 +384,16 @@ export default { memberResults: [], }; }, + watch: { + 'challenge.name': { + handler (newVal) { + this.$store.dispatch('common:setTitle', { + section: this.$t('challenge'), + subSection: newVal.name, + }); + }, + }, + }, computed: { ...mapState({ user: 'user.data' }), isMember () { @@ -433,6 +443,10 @@ export default { this.$router.push('/challenges/findChallenges'); return; } + this.$store.dispatch('common:setTitle', { + subSection: this.challenge.name, + section: this.$t('challenges'), + }); const tasks = await this.$store.dispatch('tasks:getChallengeTasks', { challengeId: this.searchId }); this.tasksByType = { habit: [], diff --git a/website/client/src/components/challenges/findChallenges.vue b/website/client/src/components/challenges/findChallenges.vue index 7c4342af78..620d788122 100644 --- a/website/client/src/components/challenges/findChallenges.vue +++ b/website/client/src/components/challenges/findChallenges.vue @@ -175,6 +175,10 @@ export default { }, }, mounted () { + this.$store.dispatch('common:setTitle', { + subSection: this.$t('findChallenges'), + section: this.$t('challenges'), + }); this.loadChallenges(); }, methods: { diff --git a/website/client/src/components/challenges/myChallenges.vue b/website/client/src/components/challenges/myChallenges.vue index 986d491574..34d3e66817 100644 --- a/website/client/src/components/challenges/myChallenges.vue +++ b/website/client/src/components/challenges/myChallenges.vue @@ -218,6 +218,10 @@ export default { }, }, mounted () { + this.$store.dispatch('common:setTitle', { + subSection: this.$t('myChallenges'), + section: this.$t('challenges'), + }); this.loadChallenges(); }, methods: { diff --git a/website/client/src/components/group-plans/taskInformation.vue b/website/client/src/components/group-plans/taskInformation.vue index 85af3d7a2f..98cf8035cd 100644 --- a/website/client/src/components/group-plans/taskInformation.vue +++ b/website/client/src/components/group-plans/taskInformation.vue @@ -224,7 +224,10 @@ export default { this.group = await this.$store.dispatch('guilds:getGroup', { groupId: this.searchId, }); - + this.$store.dispatch('common:setTitle', { + subSection: this.group.name, + section: this.$t('group'), + }); const members = await this.$store.dispatch('members:getGroupMembers', { groupId: this.searchId }); this.group.members = members; diff --git a/website/client/src/components/groups/discovery.vue b/website/client/src/components/groups/discovery.vue index 909453acf9..2a82b9c17a 100644 --- a/website/client/src/components/groups/discovery.vue +++ b/website/client/src/components/groups/discovery.vue @@ -155,6 +155,12 @@ export default { return this.guilds.filter(guild => filterGuild(guild, filters, search, user)); }, }, + mounted () { + this.$store.dispatch('common:setTitle', { + subSection: this.$t('guildsDiscovery'), + section: this.$t('guilds'), + }); + }, methods: { async updateSearch (eventData) { // this.search = eventData.searchTerm; @TODO: Probably don't need this anymore diff --git a/website/client/src/components/groups/group.vue b/website/client/src/components/groups/group.vue index 5f8d46adbb..ae64637e20 100644 --- a/website/client/src/components/groups/group.vue +++ b/website/client/src/components/groups/group.vue @@ -500,7 +500,10 @@ export default { if (this.isParty) this.searchId = 'party'; if (!this.searchId) this.searchId = this.groupId; await this.fetchGuild(); - + this.$store.dispatch('common:setTitle', { + section: this.$t('groupPlans'), + subSection: this.group.name, + }); this.$root.$on('updatedGroup', this.onGroupUpdate); }, beforeDestroy () { @@ -508,7 +511,6 @@ export default { }, beforeRouteUpdate (to, from, next) { this.$set(this, 'searchId', to.params.groupId); - next(); }, methods: { @@ -518,6 +520,10 @@ export default { onGroupUpdate (group) { const updatedGroup = extend(this.group, group); this.$set(this.group, updatedGroup); + this.$store.dispatch('common:setTitle', { + section: this.$t('groupPlans'), + subSection: group.name, + }); }, /** diff --git a/website/client/src/components/groups/groupPlan.vue b/website/client/src/components/groups/groupPlan.vue index ace068724b..e90a61514f 100644 --- a/website/client/src/components/groups/groupPlan.vue +++ b/website/client/src/components/groups/groupPlan.vue @@ -486,6 +486,9 @@ export default { }, mounted () { this.activePage = this.PAGES.BENEFITS; + this.$store.dispatch('common:setTitle', { + section: this.$t('groupPlans'), + }); }, methods: { launchModal () { diff --git a/website/client/src/components/groups/myGuilds.vue b/website/client/src/components/groups/myGuilds.vue index 1934bffaf8..a60cd6ab60 100644 --- a/website/client/src/components/groups/myGuilds.vue +++ b/website/client/src/components/groups/myGuilds.vue @@ -165,6 +165,12 @@ export default { }); }, }, + mounted () { + this.$store.dispatch('common:setTitle', { + subSection: this.$t('myGuilds'), + section: this.$t('guilds'), + }); + }, created () { this.fetchGuilds(); }, diff --git a/website/client/src/components/groups/tavern.vue b/website/client/src/components/groups/tavern.vue index 26a7adaec3..8d00006a54 100644 --- a/website/client/src/components/groups/tavern.vue +++ b/website/client/src/components/groups/tavern.vue @@ -843,6 +843,10 @@ export default { }, }, async mounted () { + this.$store.dispatch('common:setTitle', { + subSection: this.$t('tavern'), + section: this.$t('guilds'), + }); this.group = await this.$store.dispatch('guilds:getGroup', { groupId: TAVERN_ID }); }, methods: { diff --git a/website/client/src/components/inventory/equipment/index.vue b/website/client/src/components/inventory/equipment/index.vue index 0be161765f..a22db13257 100644 --- a/website/client/src/components/inventory/equipment/index.vue +++ b/website/client/src/components/inventory/equipment/index.vue @@ -508,6 +508,11 @@ export default { this.costumeMode = getLocalSetting( CONSTANTS.keyConstants.CURRENT_EQUIPMENT_DRAWER_TAB, ) === CONSTANTS.equipmentDrawerTabValues.COSTUME_TAB; + + this.$store.dispatch('common:setTitle', { + subSection: this.$t('equipment'), + section: this.$t('inventory'), + }); }, methods: { selectDrawerTab (tabName) { diff --git a/website/client/src/components/inventory/items/index.vue b/website/client/src/components/inventory/items/index.vue index 35ff9a1633..5157d0e76b 100644 --- a/website/client/src/components/inventory/items/index.vue +++ b/website/client/src/components/inventory/items/index.vue @@ -491,6 +491,12 @@ export default { return this.groups.some(g => g.selected); }, }, + mounted () { + this.$store.dispatch('common:setTitle', { + subSection: this.$t('items'), + section: this.$t('inventory'), + }); + }, watch: { searchText: throttle(function throttleSearch () { this.searchTextThrottled = this.searchText.toLowerCase(); diff --git a/website/client/src/components/inventory/stable/index.vue b/website/client/src/components/inventory/stable/index.vue index d0415b9f78..f6e3e85a5a 100644 --- a/website/client/src/components/inventory/stable/index.vue +++ b/website/client/src/components/inventory/stable/index.vue @@ -645,6 +645,12 @@ export default { return Object.values(this.viewOptions).some(g => g.selected); }, }, + mounted () { + this.$store.dispatch('common:setTitle', { + subSection: this.$t('stable'), + section: this.$t('inventory'), + }); + }, watch: { searchText: _throttle(function throttleSearch () { const search = this.searchText.toLowerCase(); diff --git a/website/client/src/components/settings/api.vue b/website/client/src/components/settings/api.vue index e47c278ba5..f361096f6b 100644 --- a/website/client/src/components/settings/api.vue +++ b/website/client/src/components/settings/api.vue @@ -154,6 +154,10 @@ export default { }, }, mounted () { + this.$store.dispatch('common:setTitle', { + section: this.$t('settings'), + subSection: this.$t('API'), + }); window.addEventListener('message', this.receiveMessage, false); }, destroy () { diff --git a/website/client/src/components/settings/dataExport.vue b/website/client/src/components/settings/dataExport.vue index e80987b893..b439483cf5 100644 --- a/website/client/src/components/settings/dataExport.vue +++ b/website/client/src/components/settings/dataExport.vue @@ -13,3 +13,14 @@ + + diff --git a/website/client/src/components/settings/notifications.vue b/website/client/src/components/settings/notifications.vue index 1d1caf25e5..057aeb9dd8 100644 --- a/website/client/src/components/settings/notifications.vue +++ b/website/client/src/components/settings/notifications.vue @@ -105,6 +105,10 @@ export default { ...mapState({ user: 'user.data' }), }, async mounted () { + this.$store.dispatch('common:setTitle', { + section: this.$t('settings'), + subSection: this.$t('notifications'), + }); // If ?unsubFrom param is passed with valid email type, // automatically unsubscribe users from that email and // show an alert diff --git a/website/client/src/components/settings/promoCode.vue b/website/client/src/components/settings/promoCode.vue index 71bbea8c75..02fce4732a 100644 --- a/website/client/src/components/settings/promoCode.vue +++ b/website/client/src/components/settings/promoCode.vue @@ -87,6 +87,12 @@ export default { return '/api/v4/coupons'; }, }, + mounted () { + this.$store.dispatch('common:setTitle', { + section: this.$t('settings'), + subSection: this.$t('promoCode'), + }); + }, methods: { generateCodes () { // $http.post(ApiUrl.get() + '/api/v2/coupons/generate/ diff --git a/website/client/src/components/settings/site.vue b/website/client/src/components/settings/site.vue index dcc984dee0..d29cef0fb2 100644 --- a/website/client/src/components/settings/site.vue +++ b/website/client/src/components/settings/site.vue @@ -678,6 +678,11 @@ export default { this.emailUpdates.newEmail = this.user.auth.local.email || null; this.localAuth.username = this.user.auth.local.username || null; this.soundIndex = 0; + + this.$store.dispatch('common:setTitle', { + section: this.$t('settings'), + }); + hello.init({ facebook: process.env.FACEBOOK_KEY, // eslint-disable-line no-process-env google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line no-process-env diff --git a/website/client/src/components/settings/subscription.vue b/website/client/src/components/settings/subscription.vue index 5ba3ffbfb5..334a45b6cf 100644 --- a/website/client/src/components/settings/subscription.vue +++ b/website/client/src/components/settings/subscription.vue @@ -720,6 +720,12 @@ export default { return moment(this.user.purchased.plan.dateTerminated).format('MM/DD/YYYY'); }, }, + mounted () { + this.$store.dispatch('common:setTitle', { + section: this.$t('settings'), + subSection: this.$t('subscription'), + }); + }, methods: { async applyCoupon (coupon) { const response = await axios.post(`/api/v4/coupons/validate/${coupon}`); diff --git a/website/client/src/components/shops/market/index.vue b/website/client/src/components/shops/market/index.vue index 9d9bbed3c6..0dfe675f4b 100644 --- a/website/client/src/components/shops/market/index.vue +++ b/website/client/src/components/shops/market/index.vue @@ -304,6 +304,10 @@ export default { }, 250), }, async mounted () { + this.$store.dispatch('common:setTitle', { + subSection: this.$t('market'), + section: this.$t('shops'), + }); await this.$store.dispatch('worldState:getWorldState'); }, methods: { diff --git a/website/client/src/components/shops/quests/index.vue b/website/client/src/components/shops/quests/index.vue index fad01ef6ae..8f22454c57 100644 --- a/website/client/src/components/shops/quests/index.vue +++ b/website/client/src/components/shops/quests/index.vue @@ -544,6 +544,10 @@ export default { }, 250), }, async mounted () { + this.$store.dispatch('common:setTitle', { + subSection: this.$t('quests'), + section: this.$t('shops'), + }); await this.$store.dispatch('worldState:getWorldState'); }, methods: { diff --git a/website/client/src/components/shops/seasonal/index.vue b/website/client/src/components/shops/seasonal/index.vue index 948420713d..95d3008f92 100644 --- a/website/client/src/components/shops/seasonal/index.vue +++ b/website/client/src/components/shops/seasonal/index.vue @@ -517,6 +517,11 @@ export default { }, 250), }, async mounted () { + this.$store.dispatch('common:setTitle', { + subSection: this.$t('seasonalShop'), + section: this.$t('shops'), + }); + this.$root.$on('buyModal::boughtItem', () => { this.backgroundUpdate = new Date(); }); diff --git a/website/client/src/components/shops/timeTravelers/index.vue b/website/client/src/components/shops/timeTravelers/index.vue index 6bb70047f6..8976f15581 100644 --- a/website/client/src/components/shops/timeTravelers/index.vue +++ b/website/client/src/components/shops/timeTravelers/index.vue @@ -399,6 +399,10 @@ export default { }, 250), }, mounted () { + this.$store.dispatch('common:setTitle', { + subSection: this.$t('timeTravelers'), + section: this.$t('shops'), + }); this.$root.$on('buyModal::boughtItem', () => { this.backgroundUpdate = new Date(); }); diff --git a/website/client/src/components/static/contact.vue b/website/client/src/components/static/contact.vue index d10973d024..188e5e27f1 100644 --- a/website/client/src/components/static/contact.vue +++ b/website/client/src/components/static/contact.vue @@ -67,5 +67,10 @@ export default { goToModForm(this.user); }, }, + mounted () { + this.$store.dispatch('common:setTitle', { + section: this.$t('contactUs'), + }); + }, }; diff --git a/website/client/src/components/static/faq.vue b/website/client/src/components/static/faq.vue index b8af7f25d1..501e917b01 100644 --- a/website/client/src/components/static/faq.vue +++ b/website/client/src/components/static/faq.vue @@ -112,6 +112,12 @@ export default { // "webFaqStillNeedHelp": "If you have a question that isn't on this list or on the [Wiki FAQ](http://habitica.fandom.com/wiki/FAQ), come ask in the <%= linkStart %>Habitica Help guild<%= linkEnd %>! We're happy to help." }; }, + mounted () { + this.$store.dispatch('common:setTitle', { + section: this.$t('help'), + subSection: this.$t('faq'), + }); + }, methods: { isVisible (heading) { return this.visible && this.visible === heading; diff --git a/website/client/src/components/static/features.vue b/website/client/src/components/static/features.vue index 706ae64305..747051fb6b 100644 --- a/website/client/src/components/static/features.vue +++ b/website/client/src/components/static/features.vue @@ -137,6 +137,11 @@ export default { directives: { markdown: markdownDirective, }, + mounted () { + this.$store.dispatch('common:setTitle', { + section: this.$t('features'), + }); + }, methods: { playButtonClick () { this.$router.push('/register'); diff --git a/website/client/src/components/static/groupPlans.vue b/website/client/src/components/static/groupPlans.vue index e2011defe7..08ad15317e 100644 --- a/website/client/src/components/static/groupPlans.vue +++ b/website/client/src/components/static/groupPlans.vue @@ -274,6 +274,9 @@ export default { // Load external scripts after the app has been rendered setupPayments(); }); + this.$store.dispatch('common:setTitle', { + section: this.$t('groupPlans'), + }); }, methods: { goToNewGroupPage () { diff --git a/website/client/src/components/static/home.vue b/website/client/src/components/static/home.vue index 9f62600332..9c11bd6175 100644 --- a/website/client/src/components/static/home.vue +++ b/website/client/src/components/static/home.vue @@ -909,6 +909,9 @@ export default { // windows: WINDOWS_CLIENT_ID, google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line }); + this.$store.dispatch('common:setTitle', { + fullTitle: 'Habitica - Gamify Your Life', + }); }, methods: { // eslint-disable-next-line func-names diff --git a/website/client/src/components/static/pressKit.vue b/website/client/src/components/static/pressKit.vue index 47c5a071da..74c7645b75 100644 --- a/website/client/src/components/static/pressKit.vue +++ b/website/client/src/components/static/pressKit.vue @@ -172,5 +172,10 @@ export default { ], }); }, + mounted () { + this.$store.dispatch('common:setTitle', { + section: this.$t('presskit'), + }); + }, }; diff --git a/website/client/src/components/tasks/user.vue b/website/client/src/components/tasks/user.vue index 97c252b2f6..30669ea80d 100644 --- a/website/client/src/components/tasks/user.vue +++ b/website/client/src/components/tasks/user.vue @@ -491,6 +491,11 @@ export default { return tagsByType; }, }, + mounted () { + this.$store.dispatch('common:setTitle', { + section: this.$t('tasks'), + }); + }, watch: { searchText: throttle(function throttleSearch () { this.searchTextThrottled = this.searchText.toLowerCase(); diff --git a/website/client/src/components/userMenu/profile.vue b/website/client/src/components/userMenu/profile.vue index b1ebb40271..667b2effd8 100644 --- a/website/client/src/components/userMenu/profile.vue +++ b/website/client/src/components/userMenu/profile.vue @@ -893,6 +893,10 @@ export default { selectPage (page) { this.selectedPage = page || 'profile'; window.history.replaceState(null, null, ''); + this.$store.dispatch('common:setTitle', { + section: this.$t('user'), + subSection: this.$t(this.startingPage), + }); }, getProgressDisplay () { // let currentLoginDay = Content.loginIncentives[this.user.loginIncentives]; diff --git a/website/client/src/pages/private-messages.vue b/website/client/src/pages/private-messages.vue index 7194b114ae..f2f87d2a2b 100644 --- a/website/client/src/pages/private-messages.vue +++ b/website/client/src/pages/private-messages.vue @@ -781,6 +781,9 @@ export default { }, }, async mounted () { + this.$store.dispatch('common:setTitle', { + section: this.$t('messages'), + }); // notification click to refresh this.$root.$on(EVENTS.PM_REFRESH, async () => { await this.reload(); diff --git a/website/client/src/router/index.js b/website/client/src/router/index.js index 0f95cd54fd..9db7348ec7 100644 --- a/website/client/src/router/index.js +++ b/website/client/src/router/index.js @@ -107,6 +107,8 @@ const router = new VueRouter({ return { x: 0, y: 0 }; }, // requiresLogin is true by default, isStatic false + // NOTE: when adding a new route entry make sure to implement the `common:setTitle` action + // in the route component to set a specific subtitle for the page. routes: [ { name: 'register', path: '/register', component: RegisterLoginReset, meta: { requiresLogin: false }, diff --git a/website/client/src/store/actions/common.js b/website/client/src/store/actions/common.js index 394f48c51b..a19a0cc488 100644 --- a/website/client/src/store/actions/common.js +++ b/website/client/src/store/actions/common.js @@ -23,6 +23,16 @@ export function hatch (store, params) { // .catch((err) => console.error('equip', err)); } +export function setTitle (store, params) { + if (params.subSection && params.section) { + store.state.title = `${params.subSection} | ${params.section} | Habitica`; + } else if (params.section) { + store.state.title = `${params.section} | Habitica`; + } else if (params.fullTitle) { + store.state.title = params.fullTitle; + } +} + export async function feed (store, params) { const user = store.state.user.data; feedOp(user, { params }); diff --git a/website/common/locales/en/front.json b/website/common/locales/en/front.json index 8c2e0511b1..2d01e32560 100644 --- a/website/common/locales/en/front.json +++ b/website/common/locales/en/front.json @@ -15,7 +15,6 @@ "emailNewPass": "Email a Password Reset Link", "forgotPasswordSteps": "Enter the email address you used to register your Habitica account.", "sendLink": "Send Link", - "featuredIn": "Featured in", "footerDevs": "Developers", "footerCommunity": "Community", "footerCompany": "Company", diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index f84bbda756..84e8c1451b 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -48,6 +48,7 @@ "memberList": "Member List", "invited": "Invited", "name": "Name", + "features": "Features", "description": "Description", "public": "Public", "inviteOnly": "Invite Only", @@ -136,7 +137,6 @@ "gemGiftsAreOptional": "Please note that Habitica will never require you to gift gems to other players. Begging people for gems is a violation of the Community Guidelines, and all such instances should be reported to <%= hrefTechAssistanceEmail %>.", "battleWithFriends": "Battle Monsters With Friends", "startAParty": "Start a Party", - "possessiveParty": "<%= name %>'s Party", "partyUpName": "Party Up", "partyOnName": "Party On", "partyUpText": "Joined a Party with another person! Have fun battling monsters and supporting each other.",