Onboarding guide and initial achievements refactoring (#11536)

* add achievements to user

* add placeholder strings

* add to achievements to common script

* add onboarding achievements category

* add notifications

* more notifications

* award achievements

* wip notification panel

* add achievements icons and copy

* do not count onboarding tasks for the created task achievement

* add notes

* sprites, fixes and completion status and reward

* add onboarding panel

* add toggle

* fix toggle size

* fix tests

* fix typo

* add notification

* start adding modal

* fix remove button positionin, timeout, progress bar

* modal + fixes

* disable broken social links from level up modal

* change toggle icon color on hover

* add border bottom to onboarding guide panel

* add collapse animation

* expanded onboarding on first open

* onboarding: flip toggle colors

* onboarding: show progress bar all the time

* onboarding: fix panel closing on click

* onboarding modal: add close icon and fix padding

* wip: add migration for existing users

* fix titles in guide

* fix achievements copy

* do not award completed task achievement when direction is down

* start implementing new achievements

* start migrating client

* remove social links from achievements modals

* prevent skipping tutorial + fix achievement notification

* sync fixes

* start redesign achievement modal

* misc fixes to achievements, polish generic achievement modal and hatched pet modal

* add special badge for onboarding

* fix badge condition

* modals fixes

* hatched pet modal: add close icon

* fix badge typo

* fix justin button

* new scrolling behavior for dropdowns

* fix strings capitalization

* add common tests

* add api unit tests

* add date check

* achievements modal polishing

* typos

* add toggle for achievements categories

* typo

* fix test

* fix edit avatar modal cannot be closed

* finish migration and correct launch date

* fix migration

* migration fixes

* fix tests
This commit is contained in:
Matteo Pagliazzi
2019-12-16 17:20:47 +01:00
committed by GitHub
parent a00a8cced8
commit 8f5a0cfe79
108 changed files with 18515 additions and 17229 deletions

View File

@@ -180,6 +180,35 @@ const basicAchievs = {
};
Object.assign(achievementsData, basicAchievs);
const onboardingAchievs = {
createdTask: {
icon: 'achievement-createdTask',
titleKey: 'achievementCreatedTask',
textKey: 'achievementCreatedTaskText',
},
completedTask: {
icon: 'achievement-completedTask',
titleKey: 'achievementCompletedTask',
textKey: 'achievementCompletedTaskText',
},
hatchedPet: {
icon: 'achievement-hatchedPet',
titleKey: 'achievementHatchedPet',
textKey: 'achievementHatchedPetText',
},
fedPet: {
icon: 'achievement-fedPet',
titleKey: 'achievementFedPet',
textKey: 'achievementFedPetText',
},
purchasedEquipment: {
icon: 'achievement-purchasedEquipment',
titleKey: 'achievementPurchasedEquipment',
textKey: 'achievementPurchasedEquipmentText',
},
};
Object.assign(achievementsData, onboardingAchievs);
const specialAchievs = {
contributor: {
icon: 'achievement-boot',

View File

@@ -1,82 +1,29 @@
// When using a common module from the website or the server NEVER import the module directly
// but access it through `api` (the main common) module,
// otherwise you would require the non transpiled version of the file in production.
import content from './content/index';
import * as errors from './libs/errors';
import i18n from './i18n';
import commonErrors from './errors/commonErrorMessages';
import apiErrors from './errors/apiErrorMessages';
// TODO under api.libs.cron?
import { shouldDo, daysSince, DAY_MAPPING } from './cron';
import {
MAX_HEALTH,
MAX_LEVEL,
MAX_STAT_POINTS,
MAX_INCENTIVES,
TAVERN_ID,
LARGE_GROUP_COUNT_MESSAGE_CUTOFF,
MAX_SUMMARY_SIZE_FOR_GUILDS,
MAX_SUMMARY_SIZE_FOR_CHALLENGES,
MIN_SHORTNAME_SIZE_FOR_CHALLENGES,
SUPPORTED_SOCIAL_NETWORKS,
GUILDS_PER_PAGE,
PARTY_LIMIT_MEMBERS,
CHAT_FLAG_LIMIT_FOR_HIDING,
CHAT_FLAG_FROM_MOD,
CHAT_FLAG_FROM_SHADOW_MUTE,
CHAT_FLAG_LIMIT_FOR_HIDING,
GUILDS_PER_PAGE,
LARGE_GROUP_COUNT_MESSAGE_CUTOFF,
MAX_HEALTH,
MAX_INCENTIVES,
MAX_LEVEL,
MAX_STAT_POINTS,
MAX_SUMMARY_SIZE_FOR_CHALLENGES,
MAX_SUMMARY_SIZE_FOR_GUILDS,
MIN_SHORTNAME_SIZE_FOR_CHALLENGES,
PARTY_LIMIT_MEMBERS,
SUPPORTED_SOCIAL_NETWORKS,
TAVERN_ID,
} from './constants';
// TODO under api.libs.statHelpers?
import * as statHelpers from './statHelpers';
import splitWhitespace from './libs/splitWhitespace';
import refPush from './libs/refPush';
import planGemLimits from './libs/planGemLimits';
import preenTodos from './libs/preenTodos';
import updateStore from './libs/updateStore';
import inAppRewards from './libs/inAppRewards';
import setDebuffPotionItems from './libs/setDebuffPotionItems';
import getDebuffPotionItems from './libs/getDebuffPotionItems';
import uuid from './libs/uuid';
import taskDefaults from './libs/taskDefaults';
import percent from './libs/percent';
import gold from './libs/gold';
import silver from './libs/silver';
import noTags from './libs/noTags';
import appliedTags from './libs/appliedTags';
import pickDeep from './libs/pickDeep';
import content from './content/index';
import * as count from './count';
import statsComputed from './libs/statsComputed';
import shops from './libs/shops';
import achievements from './libs/achievements';
import randomVal from './libs/randomVal';
import hasClass from './libs/hasClass';
// TODO under api.libs.cron?
import { daysSince, DAY_MAPPING, shouldDo } from './cron';
import apiErrors from './errors/apiErrorMessages';
import commonErrors from './errors/commonErrorMessages';
import autoAllocate from './fns/autoAllocate';
import crit from './fns/crit';
import handleTwoHanded from './fns/handleTwoHanded';
@@ -85,33 +32,59 @@ import randomDrop from './fns/randomDrop';
import resetGear from './fns/resetGear';
import ultimateGear from './fns/ultimateGear';
import updateStats from './fns/updateStats';
import scoreTask from './ops/scoreTask';
import sleep from './ops/sleep';
import allocateNow from './ops/stats/allocateNow';
import allocate from './ops/stats/allocate';
import allocateBulk from './ops/stats/allocateBulk';
import i18n from './i18n';
import achievements from './libs/achievements';
import appliedTags from './libs/appliedTags';
import * as errors from './libs/errors';
import getDebuffPotionItems from './libs/getDebuffPotionItems';
import gold from './libs/gold';
import hasClass from './libs/hasClass';
import inAppRewards from './libs/inAppRewards';
import noTags from './libs/noTags';
import * as onboarding from './libs/onboarding';
import percent from './libs/percent';
import pickDeep from './libs/pickDeep';
import planGemLimits from './libs/planGemLimits';
import preenTodos from './libs/preenTodos';
import randomVal from './libs/randomVal';
import refPush from './libs/refPush';
import setDebuffPotionItems from './libs/setDebuffPotionItems';
import shops from './libs/shops';
import silver from './libs/silver';
import splitWhitespace from './libs/splitWhitespace';
import statsComputed from './libs/statsComputed';
import taskDefaults from './libs/taskDefaults';
import updateStore from './libs/updateStore';
import uuid from './libs/uuid';
import blockUser from './ops/blockUser';
import buy from './ops/buy/buy';
import hatch from './ops/hatch';
import feed from './ops/feed';
import equip from './ops/equip';
import changeClass from './ops/changeClass';
import disableClasses from './ops/disableClasses';
import readCard from './ops/readCard';
import equip from './ops/equip';
import feed from './ops/feed';
import hatch from './ops/hatch';
import markPmsRead from './ops/markPMSRead';
import openMysteryItem from './ops/openMysteryItem';
import releasePets from './ops/releasePets';
import * as pinnedGearUtils from './ops/pinnedGearUtils';
import readCard from './ops/readCard';
import rebirth from './ops/rebirth';
import releaseBoth from './ops/releaseBoth';
import releaseMounts from './ops/releaseMounts';
import updateTask from './ops/updateTask';
import sell from './ops/sell';
import unlock from './ops/unlock';
import revive from './ops/revive';
import rebirth from './ops/rebirth';
import blockUser from './ops/blockUser';
import releasePets from './ops/releasePets';
import reroll from './ops/reroll';
import reset from './ops/reset';
import markPmsRead from './ops/markPMSRead';
import * as pinnedGearUtils from './ops/pinnedGearUtils';
import revive from './ops/revive';
import scoreTask from './ops/scoreTask';
import sell from './ops/sell';
import sleep from './ops/sleep';
import allocate from './ops/stats/allocate';
import allocateBulk from './ops/stats/allocateBulk';
import allocateNow from './ops/stats/allocateNow';
import unlock from './ops/unlock';
import updateTask from './ops/updateTask';
// TODO under api.libs.statHelpers?
import * as statHelpers from './statHelpers';
const api = {};
api.content = content;
@@ -162,10 +135,10 @@ api.shops = shops;
api.achievements = achievements;
api.randomVal = randomVal;
api.hasClass = hasClass;
api.onboarding = onboarding;
api.setDebuffPotionItems = setDebuffPotionItems;
api.getDebuffPotionItems = getDebuffPotionItems;
api.fns = {
autoAllocate,
crit,

View File

@@ -241,6 +241,18 @@ function _getBasicAchievements (user, language) {
return result;
}
function _getOnboardingAchievements (user, language) {
const result = {};
_addSimple(result, user, { path: 'createdTask', language });
_addSimple(result, user, { path: 'completedTask', language });
_addSimple(result, user, { path: 'hatchedPet', language });
_addSimple(result, user, { path: 'fedPet', language });
_addSimple(result, user, { path: 'purchasedEquipment', language });
return result;
}
function _getSeasonalAchievements (user, language) {
const result = {};
@@ -321,6 +333,10 @@ achievs.getAchievementsForProfile = function getAchievementsForProfile (user, la
label: 'Basic',
achievements: _getBasicAchievements(user, language),
},
onboarding: {
label: 'Onboarding',
achievements: _getOnboardingAchievements(user, language),
},
seasonal: {
label: 'Seasonal',
achievements: _getSeasonalAchievements(user, language),

View File

@@ -0,0 +1,31 @@
import moment from 'moment';
const BEGIN_DATE = moment('2019-12-18');
// Only users that signed up after the BEGIN DATE should see the onboarding
export function hasActiveOnboarding (user) {
return BEGIN_DATE.isBefore(user.auth.timestamps.created);
}
export function hasCompletedOnboarding (user) {
return (
user.achievements.createdTask === true
&& user.achievements.completedTask === true
&& user.achievements.hatchedPet === true
&& user.achievements.fedPet === true
&& user.achievements.purchasedEquipment === true
);
}
export function onOnboardingComplete (user) {
// Award gold
user.stats.gp += 100;
}
// Add notification and awards (server)
export function checkOnboardingStatus (user) {
if (hasActiveOnboarding(user) && hasCompletedOnboarding(user) && user.addNotification) {
user.addNotification('ONBOARDING_COMPLETE');
onOnboardingComplete(user);
}
}

View File

@@ -2,6 +2,7 @@ import get from 'lodash/get';
import pick from 'lodash/pick';
import content from '../../content/index';
import splitWhitespace from '../../libs/splitWhitespace';
import { checkOnboardingStatus } from '../../libs/onboarding';
import {
BadRequest,
NotAuthorized,
@@ -67,6 +68,11 @@ export class BuyMarketGearOperation extends AbstractGoldItemOperation { // eslin
message = handleTwoHanded(user, item, undefined, req);
}
if (!user.achievements.purchasedEquipment && user.addAchievement) {
user.addAchievement('purchasedEquipment');
checkOnboardingStatus(user);
}
removePinnedGearAddPossibleNewOnes(user, `gear.flat.${item.key}`, item.key);
if (item.last) ultimateGear(user);

View File

@@ -11,6 +11,7 @@ import {
NotFound,
} from '../libs/errors';
import errorMessage from '../libs/errorMessage';
import { checkOnboardingStatus } from '../libs/onboarding';
function evolve (user, pet, req) {
user.items.pets[pet.key] = -1;
@@ -88,6 +89,11 @@ export default function feed (user, req = {}) {
if (userPets[pet.key] >= 50 && !user.items.mounts[pet.key]) {
message = evolve(user, pet, req);
}
if (!user.achievements.fedPet && user.addAchievement) {
user.addAchievement('fedPet');
checkOnboardingStatus(user);
}
}
user.items.food[food.key] -= 1;

View File

@@ -11,6 +11,7 @@ import {
NotFound,
} from '../libs/errors';
import errorMessage from '../libs/errorMessage';
import { checkOnboardingStatus } from '../libs/onboarding';
export default function hatch (user, req = {}) {
const egg = get(req, 'params.egg');
@@ -49,6 +50,11 @@ export default function hatch (user, req = {}) {
user.markModified('items.hatchingPotions');
}
if (!user.achievements.hatchedPet && user.addAchievement) {
user.addAchievement('hatchedPet');
checkOnboardingStatus(user);
}
forEach(content.animalColorAchievements, achievement => {
if (!user.achievements[achievement.petAchievement]) {
const petIndex = findIndex(

View File

@@ -9,6 +9,7 @@ import i18n from '../i18n';
import updateStats from '../fns/updateStats';
import crit from '../fns/crit';
import statsComputed from '../libs/statsComputed';
import { checkOnboardingStatus } from '../libs/onboarding';
const MAX_TASK_VALUE = 21.27;
const MIN_TASK_VALUE = -47.27;
@@ -343,5 +344,11 @@ export default function scoreTask (options = {}, req = {}) {
req.yesterDailyScored = task.yesterDailyScored;
updateStats(user, stats, req);
if (!user.achievements.completedTask && cron === false && direction === 'up' && user.addAchievement) {
user.addAchievement('completedTask');
checkOnboardingStatus(user);
}
return [delta];
}