Compare commits

...

65 Commits

Author SHA1 Message Date
Sabe Jones
6c65056e2b 4.11.0 2017-11-15 01:01:18 +00:00
Sabe Jones
2bf0fdf4a2 chore(i18n): update locales 2017-11-15 01:00:56 +00:00
SabreCat
cd5ff04ee4 chore(sprites): compile 2017-11-15 00:53:27 +00:00
SabreCat
dbc5b9f850 feat(content): Yarn Pet Quest 2017-11-15 00:53:10 +00:00
Keith Holliday
c43ca62bc4 Added check for balance with respect to quantity (#9469) 2017-11-14 16:55:08 -07:00
Sabe Jones
848883736d 4.10.1 2017-11-09 21:23:55 +00:00
Sabe Jones
28149202db chore(i18n): update locales 2017-11-09 17:53:06 +00:00
Alys
4b23cd9f23 add missing string for dateEndNovember 2017-11-09 17:46:12 +00:00
Alys
8aaabdc086 add missing removeInvite string 2017-11-09 17:45:59 +00:00
SabreCat
6e6ca05352 fix(sprites): missing Guild image 2017-11-09 17:44:48 +00:00
Sabe Jones
8d1ebff7e9 4.10.0 2017-11-07 23:55:16 +00:00
Sabe Jones
993df72708 chore(i18n): update locales 2017-11-07 23:53:47 +00:00
SabreCat
50d3226a86 feat(content): change to Thunderstorm Hatching Potions 2017-11-07 22:02:46 +00:00
Keith Holliday
f964e3c0a5 Updated avatar menu icons and style (#9409) 2017-11-07 11:57:42 -07:00
Matteo Pagliazzi
f25fe9e263 remove forked vue version 2017-11-06 23:46:18 +00:00
Sabe Jones
6eb06fb054 4.9.1 2017-11-03 22:58:12 +00:00
Keith Holliday
286c8c7530 Ensured sort value is true for checklists (#9386) 2017-11-03 15:50:11 -05:00
Keith Holliday
47ab8f2073 Added exitence checks (#9383) 2017-11-03 15:38:05 -05:00
Sabe Jones
83353f6481 Merge branch 'paglias/fix-sprites' into release 2017-11-03 20:35:20 +00:00
Matteo Pagliazzi
9cbd7ad62d fix customize-options sprites 2017-11-03 19:06:06 +01:00
thehollidayinn
3485a1d0bc Reloaded completed todos if we are unlinking a todo 2017-11-03 10:33:20 -06:00
thehollidayinn
2e5106fda1 Removed old state item 2017-11-03 10:24:46 -06:00
thehollidayinn
2e5f5714e4 Added broken task event 2017-11-03 10:24:00 -06:00
Sabe Jones
3cf7b2c96c 4.9.0 2017-11-03 03:04:52 +00:00
Sabe Jones
286db39478 chore(i18n): update locales 2017-11-03 03:04:15 +00:00
SabreCat
4d4c1cfaf3 chore(sprites): compile 2017-11-03 02:54:26 +00:00
SabreCat
d7ad3efabf fix(news): proper Take This announcement 2017-11-03 02:49:41 +00:00
SabreCat
f8876fe055 feat(content): Backgrounds and Armoire 2017-11
End Habitoween and Fall Festival
2017-11-03 01:56:15 +00:00
Keith Holliday
b973335d69 Added extra months to account for months with larger amount of days (#9379) 2017-11-02 15:44:24 -06:00
Phillip Thelen
3b6fce0708 Add "stats" as a tutorial step (#9377)
This is used mostly for the mobile apps to identify if the tutorial step about stats has be shown yet or not.
2017-11-02 15:57:16 -05:00
Keith Holliday
ff6bd6de71 Ensured user selects plan (#9378) 2017-11-02 14:26:00 -06:00
Keith Holliday
042afe1df3 Add close to tags popup (#9376) 2017-11-02 14:25:37 -06:00
Matteo Pagliazzi
a208ba4aba Tasks v2 Part 2 (#9236)
* start updating colors for tasks controls

* finish updating controls colors

* change hoevr behavior

* change transition duration

* update color with transition

* refactor menu wip

* wip

* upgrade vue deps

* fix warnings

* fix menu

* misc fixes

* more fixes

* fix badge

* fix margins in menu

* wip tooltips

* tooltips

* fix checklist colors

* add badges

* fix quick add input

* add transition to task controls too

* add batch add

* fix task filtering

* finish tasks badges

* fix menu

* upgrade deps

* fix shop items using all the same image

* fix animation

* disable client tests until we remove phantomjs

* revert changes to tasks colors

* fix opacity in task modal inputs

* remove client unit tests from travis

* wip task dropdown

* fix z-index for task footer/header

* fixes and add files

* fixes

* bigger clickable area

* more space to open task dropdown

* droddown position

* fix menu position

* make sure other dropdowns get closed correctly

* fixes

* start to fix z-index

* draggable = false for task dropdown

* fix for dropdown position

* implement move to top / bottom

* fix push to bottom

* typo

* fix drag and drop

* use standard code

* wider click area for dropdown

* unify badge look

* fix padding

* misc fixes

* more fixes

* make dropdown scrollable

* misc fixes

* fix padding for notes

* use existing code instead of new props
2017-11-02 21:07:38 +01:00
Keith Holliday
0e958fd306 Prevented challenge edit during RYA (#9373) 2017-11-02 11:37:29 -06:00
Keith Holliday
d98fe79e9c Fixed member modal search (#9375) 2017-11-02 11:36:08 -06:00
Keith Holliday
0e5a811b98 Prevented challenge prize edit. Fixed edit to create modal (#9374) 2017-11-02 11:30:01 -06:00
Keith Holliday
a28aea65f8 Added reward text (#9332) 2017-11-01 10:03:23 -06:00
Keith Holliday
0f92349902 Added fix for multiple level up (#9330) 2017-11-01 10:01:58 -06:00
Sabe Jones
d4881cb73a Merge branch 'release' into develop 2017-10-31 19:22:58 +00:00
Sabe Jones
b3216fdb85 4.8.0 2017-10-31 19:22:27 +00:00
Keith Holliday
3e37941e0a Bulk stats (#9260)
* Reorganized stats

* Organized allocation common code

* Added bulk allocate to common

* Added allocate bulk route

* Fixed structure and lint

* Fixed import and apidoc
2017-10-31 12:57:44 -06:00
Alys
32088767ac update github templates for new website and wiki page locations 2017-10-31 21:25:01 +10:00
Alys
f4d021ab8c fix an inaccurate comment about guilds needing summaries 2017-10-31 20:54:23 +10:00
SabreCat
8532203717 Merge branch 'release' into develop 2017-10-31 01:03:07 +00:00
SabreCat
365daba6fc 4.7.1 2017-10-31 00:49:58 +00:00
SabreCat
70692752c7 fix(sprites): bad shirt centering 2017-10-31 00:48:37 +00:00
Sabe Jones
95d4016678 Merge branch 'release' into develop 2017-10-30 20:50:32 +00:00
Sabe Jones
061457b268 4.7.0 2017-10-30 20:50:11 +00:00
Sabe Jones
e7ec9a6d65 chore(i18n): update locales 2017-10-30 20:49:09 +00:00
SabreCat
d1396e7bc6 chore(sprites): compile 2017-10-30 20:41:32 +00:00
SabreCat
d5cedaa925 feat(event): Habitoween 2017-10-30 20:40:29 +00:00
Sabe Jones
bea813b318 Merge branch 'release' into develop 2017-10-26 22:47:18 +00:00
Sabe Jones
b31268fbc2 4.6.3 2017-10-26 22:46:51 +00:00
thehollidayinn
35727228f0 Removed autorefresh 2017-10-26 17:20:52 -05:00
SabreCat
feb7ab8345 Merge branch 'release' into develop 2017-10-26 21:16:09 +00:00
SabreCat
f4422b8d6c 4.6.2 2017-10-26 21:14:13 +00:00
SabreCat
2d4dc9e23c chore(news): blog Bailey 2017-10-26 21:13:04 +00:00
Tyler Nychka
bba2e71af3 Task order fix (#8928) 2017-10-26 10:37:51 -05:00
Mel
26123ac6ae API - Get challenges for a group does not allow party or habitrpg (#8882)
* test get party challenges by party ID

* tavern challenge tests; failing tests with ID 'party' or 'habitrpg'

* allow finding challenges by groupid 'party' or 'habitrpg'

* use single quotes in strings
2017-10-25 15:35:23 -05:00
Zobdek
addee73e4d Set _cronSignature to current time instead of uuid (#8565)
* Changes made to satisfy #8163. _cronSignature is set to current time when cron starts so that if cron fails to set _cronSignature to 'NOT_RUNNING' for any reason a new cron can be started after a set amount of time (1 hour for now)

* fix lint errors

* changed cronTimeout to CRON_TIMEOUT

* Changed variable names and comments to be more clear

* Fixed stub for failing test so that it matches new mongo db update call signature

* First pass at unit tests, error messages and some other things need to be determined

* Fixed a tab that snuck in :/

* Fixed lint issues (issues with spaces)

* Fix infix operator spacing

* Created constant. Make sure cron failure test verifies that it is failing for the right reason

* Fixed lint errors

* Removed no longer used uuid import
2017-10-25 15:29:16 -05:00
Keith Holliday
c39505d41c Hotfix/add autoreload (#9269)
* Added auto refresh on server update back

* Fixed lint

* Updated copy
2017-10-25 15:22:26 -05:00
Lachlan Heywood
9736ef0d25 Add ESLint to gulp scripts (#9259)
* Remove gulp/* and gulpfile from ESLint ignores

* Update .eslintrc in local gulp folder

* Start work on refactoring gulp files

* add radix

* Add line-specific eslint exceptions

* removed redundant eslint file for gulp

* add more exceptions

* Add exceptions to main gulpfile.js
2017-10-25 10:45:03 +02:00
Sabe Jones
638259b885 Merge branch 'release' into develop 2017-10-24 22:42:20 +00:00
Sabe Jones
d2f0d7b20b Merge branch 'release' into develop 2017-10-24 20:36:24 +00:00
Lula Villalobos
3c9f7ff9d8 Bug Fix on ScoreTask: Gp added instead of append [fixes #9180] (#9207)
* added parseInt to stats.gp so it can add the new value

* added radix parameter to fix lint issue

* revert changes in scoreTask.js

* convert restoreValues into numbers before setting to users.stats
2017-10-24 16:23:12 +02:00
596 changed files with 23892 additions and 21630 deletions

View File

@@ -16,5 +16,3 @@ migrations/*
scripts/*
website/common/browserify.js
Gruntfile.js
gulpfile.js
gulp

View File

@@ -4,7 +4,7 @@
# Pull Request
[Please see these instructions for adding a pull request](http://habitica.wikia.com/wiki/Using_Habitica_Git#Pull_Request)
[Please see these instructions for adding a pull request](http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API)
# Requesting a feature

View File

@@ -6,7 +6,7 @@
[//]: # (For more guidelines see https://github.com/HabitRPG/habitica/issues/2760)
[//]: # (Fill out relevant information - UUID is found in Settings -> API)
[//]: # (Fill out relevant information - UUID is found from the Habitia website at User Icon > Settings > API)
### General Info
* UUID:
* Browser:

View File

@@ -1,4 +1,4 @@
[//]: # (Note: See http://habitica.wikia.com/wiki/Using_Habitica_Git#Pull_Request for more info)
[//]: # (Note: See http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API for more info)
[//]: # (Put Issue # or URL here, if applicable. This will automatically close the issue if your PR is merged in)
Fixes put_issue_url_here
@@ -8,7 +8,7 @@ Fixes put_issue_url_here
[//]: # (Put User ID in here - found in Settings -> API)
[//]: # (Put User ID in here - found on the Habitica website at User Icon > Settings > API)
----
UUID:

View File

@@ -34,5 +34,4 @@ env:
- TEST="test:sanity"
- TEST="test:content" COVERAGE=true
- TEST="test:common" COVERAGE=true
- TEST="client:unit" COVERAGE=true
- TEST="apidoc"

View File

@@ -1,5 +1,16 @@
FROM node:boron
ENV ADMIN_EMAIL admin@habitica.com
ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e
ENV AMAZON_PAYMENTS_SELLER_ID AMQ3SB4SG5E91
ENV AMPLITUDE_KEY e8d4c24b3d6ef3ee73eeba715023dd43
ENV BASE_URL https://habitica.com
ENV FACEBOOK_KEY 128307497299777
ENV GA_ID UA-33510635-1
ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com
ENV NODE_ENV production
ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA
# Upgrade NPM to v5 (Yarn is needed because of this bug https://github.com/npm/npm/issues/16807)
# The used solution is suggested here https://github.com/npm/npm/issues/16807#issuecomment-313591975
RUN yarn global add npm@5
@@ -9,7 +20,7 @@ RUN npm install -g gulp mocha
# Clone Habitica repo and install dependencies
RUN mkdir -p /usr/src/habitrpg
WORKDIR /usr/src/habitrpg
RUN git clone --branch v4.0.3 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
RUN git clone --branch v4.10.1 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
RUN npm install
RUN gulp build:prod --force

View File

@@ -1,10 +0,0 @@
{
"root": true,
"env": {
"node": true,
},
"extends": [
"habitrpg/server",
"habitrpg/babel"
],
}

View File

@@ -22,5 +22,5 @@ gulp.task('apidoc', ['apidoc:clean'], (done) => {
});
gulp.task('apidoc:watch', ['apidoc'], () => {
return gulp.watch(APIDOC_SRC_PATH + '/**/*.js', ['apidoc']);
return gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, ['apidoc']);
});

View File

@@ -8,10 +8,10 @@ const BOOSTRAP_NEW_CONFIG_PATH = 'website/client/assets/scss/bootstrap_config.sc
const BOOTSTRAP_ORIGINAL_CONFIG_PATH = 'node_modules/bootstrap/scss/_custom.scss';
// https://stackoverflow.com/a/14387791/969528
function copyFile(source, target, cb) {
function copyFile (source, target, cb) {
let cbCalled = false;
function done(err) {
function done (err) {
if (!cbCalled) {
cb(err);
cbCalled = true;
@@ -33,4 +33,4 @@ gulp.task('bootstrap', (done) => {
BOOTSTRAP_ORIGINAL_CONFIG_PATH,
done,
);
});
});

View File

@@ -1,10 +1,9 @@
import gulp from 'gulp';
import runSequence from 'run-sequence';
import babel from 'gulp-babel';
import webpackProductionBuild from '../webpack/build';
gulp.task('build', () => {
if (process.env.NODE_ENV === 'production') {
if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env
gulp.start('build:prod');
}
});
@@ -27,12 +26,12 @@ gulp.task('build:server', ['build:src', 'build:common']);
gulp.task('build:client', ['bootstrap'], (done) => {
webpackProductionBuild((err, output) => {
if (err) return done(err);
console.log(output);
console.log(output); // eslint-disable-line no-console
});
});
gulp.task('build:prod', [
'build:server',
'build:server',
'build:client',
'apidoc',
]);

View File

@@ -7,10 +7,11 @@ import gulp from 'gulp';
// Add additional properties to the repl's context
let improveRepl = (context) => {
// Let "exit" and "quit" terminate the console
['exit', 'quit'].forEach((term) => {
Object.defineProperty(context, term, { get () { process.exit(); }});
Object.defineProperty(context, term, { get () {
process.exit();
}});
});
// "clear" clears the screen
@@ -18,12 +19,12 @@ let improveRepl = (context) => {
process.stdout.write('\u001B[2J\u001B[0;0f');
}});
context.Challenge = require('../website/server/models/challenge').model;
context.Group = require('../website/server/models/group').model;
context.User = require('../website/server/models/user').model;
context.Challenge = require('../website/server/models/challenge').model; // eslint-disable-line global-require
context.Group = require('../website/server/models/group').model; // eslint-disable-line global-require
context.User = require('../website/server/models/user').model; // eslint-disable-line global-require
var isProd = nconf.get('NODE_ENV') === 'production';
var mongooseOptions = !isProd ? {} : {
const isProd = nconf.get('NODE_ENV') === 'production';
const mongooseOptions = !isProd ? {} : {
replset: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
server: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
};
@@ -31,16 +32,15 @@ let improveRepl = (context) => {
mongoose.connect(
nconf.get('NODE_DB_URI'),
mongooseOptions,
function (err) {
(err) => {
if (err) throw err;
logger.info('Connected with Mongoose');
}
)
);
};
gulp.task('console', (cb) => {
gulp.task('console', () => {
improveRepl(repl.start({
prompt: 'Habitica > ',
}).context);

View File

@@ -14,75 +14,35 @@ const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3;
const IMG_DIST_PATH = 'website/static/sprites/';
const CSS_DIST_PATH = 'website/client/assets/css/sprites/';
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
function checkForSpecialTreatment (name) {
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/;
return name.match(regex) || name === 'head_0';
}
gulp.task('sprites:main', () => {
let mainSrc = sync('website/raw_sprites/spritesmith/**/*.png');
return createSpritesStream('main', mainSrc);
});
function calculateImgDimensions (img, addPadding) {
let dims = sizeOf(img);
gulp.task('sprites:largeSprites', () => {
let largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png');
return createSpritesStream('largeSprites', largeSrc);
});
gulp.task('sprites:clean', (done) => {
clean(`${IMG_DIST_PATH}spritesmith*,${CSS_DIST_PATH}spritesmith*}`, done);
});
gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => {
console.log('Verifiying that images do not exceed max dimensions');
let numberOfSheetsThatAreTooBig = 0;
let distSpritesheets = sync(`${IMG_DIST_PATH}*.png`);
each(distSpritesheets, (img, index) => {
let spriteSize = calculateImgDimensions(img);
if (spriteSize > MAX_SPRITESHEET_SIZE) {
numberOfSheetsThatAreTooBig++;
let name = basename(img, '.png');
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`);
}
});
if (numberOfSheetsThatAreTooBig > 0) {
console.error(`${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle them, but there is a margin of error in these calculations so it is probably okay. Mention this to an admin so they can test a staging site on mobile Safari after your PR is merged.`); // https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180
} else {
console.log('All images are within the correct dimensions');
let requiresSpecialTreatment = checkForSpecialTreatment(img);
if (requiresSpecialTreatment) {
let newWidth = dims.width < 90 ? 90 : dims.width;
let newHeight = dims.height < 90 ? 90 : dims.height;
dims = {
width: newWidth,
height: newHeight,
};
}
});
function createSpritesStream (name, src) {
let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
let stream = mergeStream();
let padding = 0;
each(spritesheetSliceIndicies, (start, index) => {
let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
if (addPadding) {
padding = dims.width * 8 + dims.height * 8;
}
let spriteData = gulp.src(slicedSrc)
.pipe(spritesmith({
imgName: `spritesmith-${name}-${index}.png`,
cssName: `spritesmith-${name}-${index}.css`,
algorithm: 'binary-tree',
padding: 1,
cssTemplate: 'website/raw_sprites/css/css.template.handlebars',
cssVarMap: cssVarMap,
}));
if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims); // eslint-disable-line no-console
let imgStream = spriteData.img
.pipe(imagemin())
.pipe(gulp.dest(IMG_DIST_PATH));
let totalPixelSize = dims.width * dims.height + padding;
let cssStream = spriteData.css
.pipe(gulp.dest(CSS_DIST_PATH));
stream.add(imgStream);
stream.add(cssStream);
});
return stream;
return totalPixelSize;
}
function calculateSpritesheetsSrcIndicies (src) {
@@ -102,37 +62,6 @@ function calculateSpritesheetsSrcIndicies (src) {
return slices;
}
function calculateImgDimensions (img, addPadding) {
let dims = sizeOf(img);
let requiresSpecialTreatment = checkForSpecialTreatment(img);
if (requiresSpecialTreatment) {
let newWidth = dims.width < 90 ? 90 : dims.width;
let newHeight = dims.height < 90 ? 90 : dims.height;
dims = {
width: newWidth,
height: newHeight,
};
}
let padding = 0;
if (addPadding) {
padding = (dims.width * 8) + (dims.height * 8);
}
if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims);
let totalPixelSize = (dims.width * dims.height) + padding;
return totalPixelSize;
}
function checkForSpecialTreatment (name) {
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/;
return name.match(regex) || name === 'head_0';
}
function cssVarMap (sprite) {
// For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, which works as a
// 60x60 image pointing at the proper part of the 90x90 sprite.
@@ -141,18 +70,93 @@ function cssVarMap (sprite) {
if (requiresSpecialTreatment) {
sprite.custom = {
px: {
offset_x: `-${ sprite.x + 25 }px`,
offset_y: `-${ sprite.y + 15 }px`,
offsetX: `-${ sprite.x + 25 }px`,
offsetY: `-${ sprite.y + 15 }px`,
width: '60px',
height: '60px',
},
};
}
if (~sprite.name.indexOf('shirt'))
sprite.custom.px.offset_y = `-${ sprite.y + 30 }px`; // even more for shirts
if (~sprite.name.indexOf('hair_base')) {
let styleArray = sprite.name.split('_').slice(2,3);
if (sprite.name.indexOf('shirt') !== -1)
sprite.custom.px.offsetY = `-${ sprite.y + 35 }px`; // even more for shirts
if (sprite.name.indexOf('hair_base') !== -1) {
let styleArray = sprite.name.split('_').slice(2, 3);
if (Number(styleArray[0]) > 14)
sprite.custom.px.offset_y = `-${ sprite.y }px`; // don't crop updos
sprite.custom.px.offsetY = `-${ sprite.y }px`; // don't crop updos
}
}
function createSpritesStream (name, src) {
let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
let stream = mergeStream();
each(spritesheetSliceIndicies, (start, index) => {
let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
let spriteData = gulp.src(slicedSrc)
.pipe(spritesmith({
imgName: `spritesmith-${name}-${index}.png`,
cssName: `spritesmith-${name}-${index}.css`,
algorithm: 'binary-tree',
padding: 1,
cssTemplate: 'website/raw_sprites/css/css.template.handlebars',
cssVarMap,
}));
let imgStream = spriteData.img
.pipe(imagemin())
.pipe(gulp.dest(IMG_DIST_PATH));
let cssStream = spriteData.css
.pipe(gulp.dest(CSS_DIST_PATH));
stream.add(imgStream);
stream.add(cssStream);
});
return stream;
}
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
gulp.task('sprites:main', () => {
let mainSrc = sync('website/raw_sprites/spritesmith/**/*.png');
return createSpritesStream('main', mainSrc);
});
gulp.task('sprites:largeSprites', () => {
let largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png');
return createSpritesStream('largeSprites', largeSrc);
});
gulp.task('sprites:clean', (done) => {
clean(`${IMG_DIST_PATH}spritesmith*,${CSS_DIST_PATH}spritesmith*}`, done);
});
gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => {
console.log('Verifiying that images do not exceed max dimensions'); // eslint-disable-line no-console
let numberOfSheetsThatAreTooBig = 0;
let distSpritesheets = sync(`${IMG_DIST_PATH}*.png`);
each(distSpritesheets, (img) => {
let spriteSize = calculateImgDimensions(img);
if (spriteSize > MAX_SPRITESHEET_SIZE) {
numberOfSheetsThatAreTooBig++;
let name = basename(img, '.png');
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); // eslint-disable-line no-console
}
});
if (numberOfSheetsThatAreTooBig > 0) {
// https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180
console.error( // eslint-disable-line no-console
`${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle
them, but there is a margin of error in these calculations so it is probably okay. Mention
this to an admin so they can test a staging site on mobile Safari after your PR is merged.`);
} else {
console.log('All images are within the correct dimensions'); // eslint-disable-line no-console
}
});

View File

@@ -1,21 +1,12 @@
import {
pipe,
awaitPort,
kill,
runMochaTests,
} from './taskHelper';
import { server as karma } from 'karma';
import mongoose from 'mongoose';
import { exec } from 'child_process';
import psTree from 'ps-tree';
import gulp from 'gulp';
import Bluebird from 'bluebird';
import runSequence from 'run-sequence';
import os from 'os';
import nconf from 'nconf';
import fs from 'fs';
const i18n = require('../website/server/libs/i18n');
// TODO rewrite
@@ -24,7 +15,6 @@ let server;
const TEST_DB_URI = nconf.get('TEST_DB_URI');
const API_V3_TEST_COMMAND = 'npm run test:api-v3';
const SANITY_TEST_COMMAND = 'npm run test:sanity';
const COMMON_TEST_COMMAND = 'npm run test:common';
const CONTENT_TEST_COMMAND = 'npm run test:content';
@@ -34,14 +24,14 @@ const CONTENT_OPTIONS = {maxBuffer: 1024 * 500};
let testResults = [];
let testCount = (stdout, regexp) => {
let match = stdout.match(regexp);
return parseInt(match && match[1] || 0);
return parseInt(match && match[1] || 0, 10);
};
let testBin = (string, additionalEnvVariables = '') => {
if (os.platform() === 'win32') {
if (additionalEnvVariables != '') {
if (additionalEnvVariables !== '') {
additionalEnvVariables = additionalEnvVariables.split(' ').join('&&set ');
additionalEnvVariables = 'set ' + additionalEnvVariables + '&&';
additionalEnvVariables = `set ${additionalEnvVariables}&&`;
}
return `set NODE_ENV=test&&${additionalEnvVariables}${string}`;
} else {
@@ -49,9 +39,9 @@ let testBin = (string, additionalEnvVariables = '') => {
}
};
gulp.task('test:nodemon', (done) => {
process.env.PORT = TEST_SERVER_PORT;
process.env.NODE_DB_URI = TEST_DB_URI;
gulp.task('test:nodemon', () => {
process.env.PORT = TEST_SERVER_PORT; // eslint-disable-line no-process-env
process.env.NODE_DB_URI = TEST_DB_URI; // eslint-disable-line no-process-env
runSequence('nodemon');
});
@@ -68,8 +58,12 @@ gulp.task('test:prepare:mongo', (cb) => {
gulp.task('test:prepare:server', ['test:prepare:mongo'], () => {
if (!server) {
server = exec(testBin('node ./website/server/index.js', `NODE_DB_URI=${TEST_DB_URI} PORT=${TEST_SERVER_PORT}`), (error, stdout, stderr) => {
if (error) { throw `Problem with the server: ${error}`; }
if (stderr) { console.error(stderr); }
if (error) {
throw new Error(`Problem with the server: ${error}`);
}
if (stderr) {
console.error(stderr); // eslint-disable-line no-console
}
});
}
});
@@ -84,7 +78,7 @@ gulp.task('test:prepare', [
gulp.task('test:sanity', (cb) => {
let runner = exec(
testBin(SANITY_TEST_COMMAND),
(err, stdout, stderr) => {
(err) => {
if (err) {
process.exit(1);
}
@@ -97,7 +91,7 @@ gulp.task('test:sanity', (cb) => {
gulp.task('test:common', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(COMMON_TEST_COMMAND),
(err, stdout, stderr) => {
(err) => {
if (err) {
process.exit(1);
}
@@ -118,7 +112,7 @@ gulp.task('test:common:watch', ['test:common:clean'], () => {
gulp.task('test:common:safe', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(COMMON_TEST_COMMAND),
(err, stdout, stderr) => {
(err, stdout) => { // eslint-disable-line handle-callback-err
testResults.push({
suite: 'Common Specs\t',
pass: testCount(stdout, /(\d+) passing/),
@@ -135,7 +129,7 @@ gulp.task('test:content', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(CONTENT_TEST_COMMAND),
CONTENT_OPTIONS,
(err, stdout, stderr) => {
(err) => {
if (err) {
process.exit(1);
}
@@ -157,7 +151,7 @@ gulp.task('test:content:safe', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(CONTENT_TEST_COMMAND),
CONTENT_OPTIONS,
(err, stdout, stderr) => {
(err, stdout) => { // eslint-disable-line handle-callback-err
testResults.push({
suite: 'Content Specs\t',
pass: testCount(stdout, /(\d+) passing/),
@@ -173,7 +167,7 @@ gulp.task('test:content:safe', ['test:prepare:build'], (cb) => {
gulp.task('test:api-v3:unit', (done) => {
let runner = exec(
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-unit --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/unit --recursive --require ./test/helpers/start-server'),
(err, stdout, stderr) => {
(err) => {
if (err) {
process.exit(1);
}
@@ -192,7 +186,7 @@ gulp.task('test:api-v3:integration', (done) => {
let runner = exec(
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'),
{maxBuffer: 500 * 1024},
(err, stdout, stderr) => {
(err) => {
if (err) {
process.exit(1);
}
@@ -212,7 +206,7 @@ gulp.task('test:api-v3:integration:separate-server', (done) => {
let runner = exec(
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
{maxBuffer: 500 * 1024},
(err, stdout, stderr) => done(err)
(err) => done(err)
);
pipe(runner);

View File

@@ -1,6 +1,5 @@
import fs from 'fs';
import _ from 'lodash';
import nconf from 'nconf';
import gulp from 'gulp';
import { postToSlack, conf } from './taskHelper';
@@ -12,8 +11,82 @@ const SLACK_CONFIG = {
const LOCALES = './website/common/locales/';
const ENGLISH_LOCALE = `${LOCALES}en/`;
function getArrayOfLanguages () {
let languages = fs.readdirSync(LOCALES);
languages.shift(); // Remove README.md from array of languages
return languages;
}
const ALL_LANGUAGES = getArrayOfLanguages();
function stripOutNonJsonFiles (collection) {
let onlyJson = _.filter(collection, (file) => {
return file.match(/[a-zA-Z]*\.json/);
});
return onlyJson;
}
function eachTranslationFile (languages, cb) {
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
_.each(languages, (lang) => {
_.each(jsonFiles, (filename) => {
let parsedTranslationFile;
try {
const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
parsedTranslationFile = JSON.parse(translationFile);
} catch (err) {
return cb(err);
}
let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
let parsedEnglishFile = JSON.parse(englishFile);
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
});
});
}
function eachTranslationString (languages, cb) {
eachTranslationFile(languages, (error, language, filename, englishJSON, translationJSON) => {
if (error) return;
_.each(englishJSON, (string, key) => {
const translationString = translationJSON[key];
cb(language, filename, key, string, translationString);
});
});
}
function formatMessageForPosting (msg, items) {
let body = `*Warning:* ${msg}`;
body += '\n\n```\n';
body += items.join('\n');
body += '\n```';
return body;
}
function getStringsWith (json, interpolationRegex) {
let strings = {};
_.each(json, (fileName) => {
const rawFile = fs.readFileSync(ENGLISH_LOCALE + fileName);
const parsedJson = JSON.parse(rawFile);
strings[fileName] = {};
_.each(parsedJson, (value, key) => {
const match = value.match(interpolationRegex);
if (match) strings[fileName][key] = match;
});
});
return strings;
}
const malformedStringExceptions = {
messageDropFood: true,
armoireFood: true,
@@ -23,7 +96,6 @@ const malformedStringExceptions = {
gulp.task('transifex', ['transifex:missingFiles', 'transifex:missingStrings', 'transifex:malformedStrings']);
gulp.task('transifex:missingFiles', () => {
let missingStrings = [];
eachTranslationFile(ALL_LANGUAGES, (error) => {
@@ -40,7 +112,6 @@ gulp.task('transifex:missingFiles', () => {
});
gulp.task('transifex:missingStrings', () => {
let missingStrings = [];
eachTranslationString(ALL_LANGUAGES, (language, filename, key, englishString, translationString) => {
@@ -58,7 +129,6 @@ gulp.task('transifex:missingStrings', () => {
});
gulp.task('transifex:malformedStrings', () => {
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
let interpolationRegex = /<%= [a-zA-Z]* %>/g;
let stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex);
@@ -66,25 +136,23 @@ gulp.task('transifex:malformedStrings', () => {
let stringsWithMalformedInterpolations = [];
let stringsWithIncorrectNumberOfInterpolations = [];
let count = 0;
_.each(ALL_LANGUAGES, function (lang) {
_.each(stringsToLookFor, function (strings, file) {
let translationFile = fs.readFileSync(LOCALES + lang + '/' + file);
_.each(ALL_LANGUAGES, (lang) => {
_.each(stringsToLookFor, (strings, filename) => {
let translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
let parsedTranslationFile = JSON.parse(translationFile);
_.each(strings, function (value, key) {
_.each(strings, (value, key) => { // eslint-disable-line max-nested-callbacks
let translationString = parsedTranslationFile[key];
if (!translationString) return;
let englishOccurences = stringsToLookFor[file][key];
let englishOccurences = stringsToLookFor[filename][key];
let translationOccurences = translationString.match(interpolationRegex);
if (!translationOccurences) {
let malformedString = `${lang} - ${file} - ${key} - ${translationString}`;
let malformedString = `${lang} - ${filename} - ${key} - ${translationString}`;
stringsWithMalformedInterpolations.push(malformedString);
} else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) {
let missingInterpolationString = `${lang} - ${file} - ${key} - ${translationString}`;
let missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`;
stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString);
}
});
@@ -103,74 +171,3 @@ gulp.task('transifex:malformedStrings', () => {
postToSlack(formattedMessage, SLACK_CONFIG);
}
});
function getArrayOfLanguages () {
let languages = fs.readdirSync(LOCALES);
languages.shift(); // Remove README.md from array of languages
return languages;
}
function eachTranslationFile (languages, cb) {
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
_.each(languages, (lang) => {
_.each(jsonFiles, (filename) => {
try {
var translationFile = fs.readFileSync(LOCALES + lang + '/' + filename);
var parsedTranslationFile = JSON.parse(translationFile);
} catch (err) {
return cb(err);
}
let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
let parsedEnglishFile = JSON.parse(englishFile);
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
});
});
}
function eachTranslationString (languages, cb) {
eachTranslationFile(languages, (error, language, filename, englishJSON, translationJSON) => {
if (error) return;
_.each(englishJSON, (string, key) => {
var translationString = translationJSON[key];
cb(language, filename, key, string, translationString);
});
});
}
function formatMessageForPosting (msg, items) {
let body = `*Warning:* ${msg}`;
body += '\n\n```\n';
body += items.join('\n');
body += '\n```';
return body;
}
function getStringsWith (json, interpolationRegex) {
var strings = {};
_.each(json, function (file_name) {
var raw_file = fs.readFileSync(ENGLISH_LOCALE + file_name);
var parsed_json = JSON.parse(raw_file);
strings[file_name] = {};
_.each(parsed_json, function (value, key) {
var match = value.match(interpolationRegex);
if (match) strings[file_name][key] = match;
});
});
return strings;
}
function stripOutNonJsonFiles (collection) {
let onlyJson = _.filter(collection, (file) => {
return file.match(/[a-zA-Z]*\.json/);
});
return onlyJson;
}

View File

@@ -12,7 +12,7 @@ import { resolve } from 'path';
* Get access to configruable values
*/
nconf.argv().env().file({ file: 'config.json' });
export var conf = nconf;
export const conf = nconf;
/*
* Kill a child process and any sub-children that process may have spawned.
@@ -26,11 +26,12 @@ export function kill (proc) {
pids.forEach(kill); return;
}
try {
exec(/^win/.test(process.platform)
? `taskkill /PID ${pid} /T /F`
: `kill -9 ${pid}`);
exec(/^win/.test(process.platform) ?
`taskkill /PID ${pid} /T /F` :
`kill -9 ${pid}`);
} catch (e) {
console.log(e); // eslint-disable-line no-console
}
catch (e) { console.log(e); }
});
};
@@ -44,21 +45,25 @@ export function kill (proc) {
* before failing.
*/
export function awaitPort (port, max = 60) {
return new Bluebird((reject, resolve) => {
let socket, timeout, interval;
return new Bluebird((rej, res) => {
let socket;
let timeout;
let interval;
timeout = setTimeout(() => {
clearInterval(interval);
reject(`Timed out after ${max} seconds`);
rej(`Timed out after ${max} seconds`);
}, max * 1000);
interval = setInterval(() => {
socket = net.connect({port: port}, () => {
socket = net.connect({port}, () => {
clearInterval(interval);
clearTimeout(timeout);
socket.destroy();
resolve();
}).on('error', () => { socket.destroy; });
res();
}).on('error', () => {
socket.destroy();
});
}, 1000);
});
}
@@ -67,8 +72,12 @@ export function awaitPort (port, max = 60) {
* Pipe the child's stdin and stderr to the parent process.
*/
export function pipe (child) {
child.stdout.on('data', (data) => { process.stdout.write(data); });
child.stderr.on('data', (data) => { process.stderr.write(data); });
child.stdout.on('data', (data) => {
process.stdout.write(data);
});
child.stderr.on('data', (data) => {
process.stderr.write(data);
});
}
/*
@@ -78,8 +87,8 @@ export function postToSlack (msg, config = {}) {
let slackUrl = nconf.get('SLACK_URL');
if (!slackUrl) {
console.error('No slack post url specified. Your message was:');
console.log(msg);
console.error('No slack post url specified. Your message was:'); // eslint-disable-line no-console
console.log(msg); // eslint-disable-line no-console
return;
}
@@ -89,15 +98,15 @@ export function postToSlack (msg, config = {}) {
channel: `#${config.channel || '#general'}`,
username: config.username || 'gulp task',
text: msg,
icon_emoji: `:${config.emoji || 'gulp'}:`,
icon_emoji: `:${config.emoji || 'gulp'}:`, // eslint-disable-line camelcase
})
.end((err, res) => {
if (err) console.error('Unable to post to slack', err);
.end((err) => {
if (err) console.error('Unable to post to slack', err); // eslint-disable-line no-console
});
}
export function runMochaTests (files, server, cb) {
require('../test/helpers/globals.helper');
require('../test/helpers/globals.helper'); // eslint-disable-line global-require
let mocha = new Mocha({reporter: 'spec'});
let tests = glob(files);
@@ -108,7 +117,7 @@ export function runMochaTests (files, server, cb) {
});
mocha.run((numberOfFailures) => {
if (!process.env.RUN_INTEGRATION_TEST_FOREVER) {
if (!process.env.RUN_INTEGRATION_TEST_FOREVER) { // eslint-disable-line no-process-env
if (server) kill(server);
process.exit(numberOfFailures);
}

View File

@@ -8,11 +8,11 @@
require('babel-register');
if (process.env.NODE_ENV === 'production') {
require('./gulp/gulp-apidoc');
require('./gulp/gulp-build');
require('./gulp/gulp-bootstrap');
if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env
require('./gulp/gulp-apidoc'); // eslint-disable-line global-require
require('./gulp/gulp-build'); // eslint-disable-line global-require
require('./gulp/gulp-bootstrap'); // eslint-disable-line global-require
} else {
require('glob').sync('./gulp/gulp-*').forEach(require);
require('gulp').task('default', ['test']);
require('glob').sync('./gulp/gulp-*').forEach(require); // eslint-disable-line global-require
require('gulp').task('default', ['test']); // eslint-disable-line global-require
}

View File

@@ -0,0 +1,111 @@
var migrationName = '20171030_jackolanterns.js';
var authorName = 'Sabe'; // in case script author needs to know when their ...
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
/*
* Award the Jack-O'-Lantern ladder:
* Ghost Jack-O-Lantern Mount to owners of Ghost Jack-O-Lantern Pet
* Ghost Jack-O-Lantern Pet to owners of Jack-O-Lantern Mount
* Jack-O-Lantern Mount to owners of Jack-O-Lantern Pet
* Jack-O-Lantern Pet to everyone else
*/
var monk = require('monk');
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
var dbUsers = monk(connectionString).get('users', { castIds: false });
function processUsers(lastId) {
// specify a query to limit the affected users (empty for all users):
var query = {
'migration':{$ne:migrationName},
};
if (lastId) {
query._id = {
$gt: lastId
}
}
dbUsers.find(query, {
sort: {_id: 1},
limit: 250,
fields: [
'items.pets',
'items.mounts',
] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
})
.then(updateUsers)
.catch(function (err) {
console.log(err);
return exiting(1, 'ERROR! ' + err);
});
}
var progressCount = 1000;
var count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return;
}
var userPromises = users.map(updateUser);
var lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(function () {
processUsers(lastUser._id);
});
}
function updateUser (user) {
count++;
var set = {};
var inc = {
'items.food.Candy_Skeleton': 1,
'items.food.Candy_Base': 1,
'items.food.Candy_CottonCandyBlue': 1,
'items.food.Candy_CottonCandyPink': 1,
'items.food.Candy_Shade': 1,
'items.food.Candy_White': 1,
'items.food.Candy_Golden': 1,
'items.food.Candy_Zombie': 1,
'items.food.Candy_Desert': 1,
'items.food.Candy_Red': 1,
};
if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
set = {'migration':migrationName, 'items.mounts.JackOLantern-Ghost': true};
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
set = {'migration':migrationName, 'items.pets.JackOLantern-Ghost': 5};
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
set = {'migration':migrationName, 'items.mounts.JackOLantern-Base': true};
} else {
set = {'migration':migrationName, 'items.pets.JackOLantern-Base': 5};
}
dbUsers.update({_id: user._id}, {$set:set, $inc:inc});
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
if (user._id == authorUuid) console.warn(authorName + ' processed');
}
function displayData() {
console.warn('\n' + count + ' users processed\n');
return exiting(0);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}
module.exports = processUsers;

View File

@@ -65,19 +65,29 @@ function updateUser (user) {
set = {'migration':migrationName};
} else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.back_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', '_id': monk.id()}};
} else {
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', '_id': monk.id()}};
}
dbUsers.update({_id: user._id}, {$set:set});
if (push) {
dbUsers.update({_id: user._id}, {$set: set, $push: push});
} else {
dbUsers.update({_id: user._id}, {$set: set});
}
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
if (user._id == authorUuid) console.warn(authorName + ' processed');

1686
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "4.6.1",
"version": "4.11.0",
"main": "./website/server/index.js",
"dependencies": {
"@slack/client": "^3.8.1",
@@ -116,12 +116,12 @@
"validator": "^4.9.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"vue": "^2.1.0",
"vue-loader": "^11.0.0",
"vue": "^2.5.2",
"vue-loader": "^13.3.0",
"vue-mugen-scroll": "^0.2.1",
"vue-router": "^2.0.0-rc.5",
"vue-router": "^3.0.0",
"vue-style-loader": "^3.0.0",
"vue-template-compiler": "^2.1.10",
"vue-template-compiler": "^2.5.2",
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker#45e607a7bccf4e3e089761b3b7b33e3f2c5dc21f",
"webpack": "^2.2.1",
"webpack-merge": "^4.0.0",
@@ -136,7 +136,7 @@
},
"scripts": {
"lint": "eslint --ext .js,.vue .",
"test": "npm run lint && gulp test && npm run client:unit && gulp apidoc",
"test": "npm run lint && gulp test && gulp apidoc",
"test:build": "gulp test:prepare:build",
"test:api-v3": "gulp test:api-v3",
"test:api-v3:unit": "gulp test:api-v3:unit",

View File

@@ -4,6 +4,7 @@ import {
createAndPopulateGroup,
translate as t,
} from '../../../../helpers/api-v3-integration.helper';
import { TAVERN_ID } from '../../../../../website/common/script/constants';
describe('GET challenges/groups/:groupId', () => {
context('Public Guild', () => {
@@ -181,4 +182,123 @@ describe('GET challenges/groups/:groupId', () => {
expect(foundChallengeIndex).to.eql(1);
});
});
context('Party', () => {
let party, user, nonMember, challenge, challenge2;
before(async () => {
let { group, groupLeader } = await createAndPopulateGroup({
groupDetails: {
name: 'TestParty',
type: 'party',
},
});
party = group;
user = groupLeader;
nonMember = await generateUser();
challenge = await generateChallenge(user, group);
challenge2 = await generateChallenge(user, group);
});
it('should prevent non-member from seeing challenges', async () => {
await expect(nonMember.get(`/challenges/groups/${party._id}`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('should return group challenges for member with populated leader', async () => {
let challenges = await user.get(`/challenges/groups/${party._id}`);
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
expect(foundChallenge1).to.exist;
expect(foundChallenge1.leader).to.eql({
_id: party.leader._id,
id: party.leader._id,
profile: {name: user.profile.name},
});
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
expect(foundChallenge2).to.exist;
expect(foundChallenge2.leader).to.eql({
_id: party.leader._id,
id: party.leader._id,
profile: {name: user.profile.name},
});
});
it('should return group challenges for member using ID "party"', async () => {
let challenges = await user.get('/challenges/groups/party');
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
expect(foundChallenge1).to.exist;
expect(foundChallenge1.leader).to.eql({
_id: party.leader._id,
id: party.leader._id,
profile: {name: user.profile.name},
});
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
expect(foundChallenge2).to.exist;
expect(foundChallenge2.leader).to.eql({
_id: party.leader._id,
id: party.leader._id,
profile: {name: user.profile.name},
});
});
});
context('Tavern', () => {
let tavern, user, challenge, challenge2;
before(async () => {
user = await generateUser();
await user.update({balance: 0.5});
tavern = await user.get(`/groups/${TAVERN_ID}`);
challenge = await generateChallenge(user, tavern, {prize: 1});
challenge2 = await generateChallenge(user, tavern, {prize: 1});
});
it('should return tavern challenges with populated leader', async () => {
let challenges = await user.get(`/challenges/groups/${TAVERN_ID}`);
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
expect(foundChallenge1).to.exist;
expect(foundChallenge1.leader).to.eql({
_id: user._id,
id: user._id,
profile: {name: user.profile.name},
});
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
expect(foundChallenge2).to.exist;
expect(foundChallenge2.leader).to.eql({
_id: user._id,
id: user._id,
profile: {name: user.profile.name},
});
});
it('should return tavern challenges using ID "habitrpg', async () => {
let challenges = await user.get('/challenges/groups/habitrpg');
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
expect(foundChallenge1).to.exist;
expect(foundChallenge1.leader).to.eql({
_id: user._id,
id: user._id,
profile: {name: user.profile.name},
});
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
expect(foundChallenge2).to.exist;
expect(foundChallenge2.leader).to.eql({
_id: user._id,
id: user._id,
profile: {name: user.profile.name},
});
});
});
});

View File

@@ -1,7 +1,7 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
} from '../../../../../helpers/api-integration/v3';
describe('POST /user/allocate', () => {
let user;

View File

@@ -0,0 +1,40 @@
import {
generateUser,
translate as t,
} from '../../../../../helpers/api-integration/v3';
describe('POST /user/allocate-bulk', () => {
let user;
const statsUpdate = {
stats: {
con: 1,
str: 2,
},
};
beforeEach(async () => {
user = await generateUser();
});
// More tests in common code unit tests
it('returns an error if user does not have enough points', async () => {
await expect(user.post('/user/allocate-bulk', statsUpdate))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('notEnoughAttrPoints'),
});
});
it('allocates attribute points', async () => {
await user.update({'stats.points': 3});
await user.post('/user/allocate-bulk', statsUpdate);
await user.sync();
expect(user.stats.con).to.equal(1);
expect(user.stats.str).to.equal(2);
expect(user.stats.points).to.equal(0);
});
});

View File

@@ -1,6 +1,6 @@
import {
generateUser,
} from '../../../../helpers/api-integration/v3';
} from '../../../../../helpers/api-integration/v3';
describe('POST /user/allocate-now', () => {
// More tests in common code unit tests

View File

@@ -365,6 +365,72 @@ describe('cron', () => {
expect(user.history.todos).to.be.lengthOf(1);
});
it('should remove completed todos from users taskOrder list', () => {
tasksByType.todos = [];
user.tasksOrder.todos = [];
let todo = {
text: 'test todo',
type: 'todo',
value: 0,
};
let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
tasksByType.todos.push(task);
task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
tasksByType.todos.push(task);
tasksByType.todos[0].completed = true;
user.tasksOrder.todos = tasksByType.todos.map(taskTodo => {
return taskTodo._id;
});
// Since ideally tasksByType should not contain completed todos, fake ids should be filtered too
user.tasksOrder.todos.push('00000000-0000-0000-0000-000000000000');
expect(tasksByType.todos).to.be.lengthOf(2);
expect(user.tasksOrder.todos).to.be.lengthOf(3);
cron({user, tasksByType, daysMissed, analytics});
// user.tasksOrder.todos should be filtered while tasks by type remains unchanged
expect(tasksByType.todos).to.be.lengthOf(2);
expect(user.tasksOrder.todos).to.be.lengthOf(1);
});
it('should preserve todos order in task list', () => {
tasksByType.todos = [];
user.tasksOrder.todos = [];
let todo = {
text: 'test todo',
type: 'todo',
value: 0,
};
let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
tasksByType.todos.push(task);
task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
tasksByType.todos.push(task);
task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
tasksByType.todos.push(task);
// Set up user.tasksOrder list in a specific order
user.tasksOrder.todos = tasksByType.todos.map(todoTask => {
return todoTask._id;
}).reverse();
let original = user.tasksOrder.todos; // Preserve the original order
cron({user, tasksByType, daysMissed, analytics});
let listsAreEqual = true;
user.tasksOrder.todos.forEach((taskId, index) => {
if (original[index]._id !== taskId) {
listsAreEqual = false;
}
});
expect(listsAreEqual);
expect(user.tasksOrder.todos).to.be.lengthOf(original.length);
});
});
describe('dailys', () => {

View File

@@ -475,7 +475,7 @@ describe('Purchasing a group plan for group', () => {
let updatedUser = await User.findById(recipient._id).exec();
expect(updatedUser.purchased.plan.extraMonths).to.within(3, 4);
expect(updatedUser.purchased.plan.extraMonths).to.within(3, 5);
});
it('adds months to members with existing recurring subscription (Paypal)', async () => {

View File

@@ -13,6 +13,9 @@ import analyticsService from '../../../../../website/server/libs/analyticsServic
import * as cronLib from '../../../../../website/server/libs/cron';
import { v4 as generateUUID } from 'uuid';
const CRON_TIMEOUT_WAIT = new Date(60 * 60 * 1000).getTime();
const CRON_TIMEOUT_UNIT = new Date(60 * 1000).getTime();
describe('cron middleware', () => {
let res, req;
let user;
@@ -235,7 +238,13 @@ describe('cron middleware', () => {
sandbox.spy(cronLib, 'recoverCron');
sandbox.stub(User, 'update')
.withArgs({ _id: user._id, _cronSignature: 'NOT_RUNNING' })
.withArgs({
_id: user._id,
$or: [
{_cronSignature: 'NOT_RUNNING'},
{_cronSignature: {$lt: sinon.match.number}},
],
})
.returns({
exec () {
return Promise.resolve(updatedUser);
@@ -251,4 +260,48 @@ describe('cron middleware', () => {
});
});
});
it('cronSignature less than an hour ago should error', async () => {
user.lastCron = moment(new Date()).subtract({days: 2});
let now = new Date();
await User.update({
_id: user._id,
}, {
$set: {
_cronSignature: now.getTime() - CRON_TIMEOUT_WAIT + CRON_TIMEOUT_UNIT,
},
}).exec();
await user.save();
let expectedErrMessage = `Impossible to recover from cron for user ${user._id}.`;
await new Promise((resolve, reject) => {
cronMiddleware(req, res, (err) => {
if (!err) return reject(new Error('Cron should have failed.'));
expect(err.message).to.be.equal(expectedErrMessage);
resolve();
});
});
});
it('cronSignature longer than an hour ago should allow cron', async () => {
user.lastCron = moment(new Date()).subtract({days: 2});
let now = new Date();
await User.update({
_id: user._id,
}, {
$set: {
_cronSignature: now.getTime() - CRON_TIMEOUT_WAIT - CRON_TIMEOUT_UNIT,
},
}).exec();
await user.save();
await new Promise((resolve, reject) => {
cronMiddleware(req, res, (err) => {
if (err) return reject(err);
expect(moment(now).isSame(user.auth.timestamps.loggedin, 'day'));
expect(user._cronSignature).to.be.equal('NOT_RUNNING');
resolve();
});
});
});
});

View File

@@ -231,13 +231,30 @@ describe('shared.ops.purchase', () => {
context('bulk purchase', () => {
let userGemAmount = 10;
before(() => {
beforeEach(() => {
user.balance = userGemAmount;
user.stats.gp = goldPoints;
user.purchased.plan.gemsBought = 0;
user.purchased.plan.customerId = 'customer-id';
});
it('errors when user does not have enough gems', (done) => {
user.balance = 1;
let type = 'eggs';
let key = 'TigerCub';
try {
purchase(user, {
params: {type, key},
quantity: 2,
});
} catch (err) {
expect(err).to.be.an.instanceof(NotAuthorized);
expect(err.message).to.equal(i18n.t('notEnoughGems'));
done();
}
});
it('makes bulk purchases of gems', () => {
let [, message] = purchase(user, {
params: {type: 'gems', key: 'gem'},

View File

@@ -1,12 +1,12 @@
import allocate from '../../../website/common/script/ops/allocate';
import allocate from '../../../../website/common/script/ops/stats/allocate';
import {
BadRequest,
NotAuthorized,
} from '../../../website/common/script/libs/errors';
import i18n from '../../../website/common/script/i18n';
} from '../../../../website/common/script/libs/errors';
import i18n from '../../../../website/common/script/i18n';
import {
generateUser,
} from '../../helpers/common.helper';
} from '../../../helpers/common.helper';
describe('shared.ops.allocate', () => {
let user;

View File

@@ -0,0 +1,98 @@
import allocateBulk from '../../../../website/common/script/ops/stats/allocateBulk';
import {
BadRequest,
NotAuthorized,
} from '../../../../website/common/script/libs/errors';
import i18n from '../../../../website/common/script/i18n';
import {
generateUser,
} from '../../../helpers/common.helper';
describe('shared.ops.allocateBulk', () => {
let user;
beforeEach(() => {
user = generateUser();
});
it('throws an error if an invalid attribute is supplied', (done) => {
try {
allocateBulk(user, {
body: {
stats: {
invalid: 1,
str: 2,
},
},
});
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('invalidAttribute', {attr: 'invalid'}));
done();
}
});
it('throws an error if the stats are not supplied', (done) => {
try {
allocateBulk(user);
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('statsObjectRequired'));
done();
}
});
it('throws an error if the user doesn\'t have attribute points', (done) => {
try {
allocateBulk(user, {
body: {
stats: {
int: 1,
str: 2,
},
},
});
} catch (err) {
expect(err).to.be.an.instanceof(NotAuthorized);
expect(err.message).to.equal(i18n.t('notEnoughAttrPoints'));
done();
}
});
it('throws an error if the user doesn\'t have enough attribute points', (done) => {
user.stats.points = 1;
try {
allocateBulk(user, {
body: {
stats: {
int: 1,
str: 2,
},
},
});
} catch (err) {
expect(err).to.be.an.instanceof(NotAuthorized);
expect(err.message).to.equal(i18n.t('notEnoughAttrPoints'));
done();
}
});
it('allocates attribute points', () => {
user.stats.points = 3;
expect(user.stats.int).to.equal(0);
expect(user.stats.str).to.equal(0);
allocateBulk(user, {
body: {
stats: {
int: 1,
str: 2,
},
},
});
expect(user.stats.str).to.equal(2);
expect(user.stats.int).to.equal(1);
expect(user.stats.points).to.equal(0);
});
});

View File

@@ -1,7 +1,7 @@
import allocateNow from '../../../website/common/script/ops/allocateNow';
import allocateNow from '../../../../website/common/script/ops/stats/allocateNow';
import {
generateUser,
} from '../../helpers/common.helper';
} from '../../../helpers/common.helper';
describe('shared.ops.allocateNow', () => {
let user;

View File

@@ -71,8 +71,8 @@
import axios from 'axios';
import { loadProgressBar } from 'axios-progress-bar';
import AppMenu from './components/appMenu';
import AppHeader from './components/appHeader';
import AppMenu from './components/header/menu';
import AppHeader from './components/header/index';
import AppFooter from './components/appFooter';
import notificationsDisplay from './components/notifications';
import snackbars from './components/snackbars/notifications';
@@ -216,15 +216,11 @@ export default {
// Verify the client is updated
// const serverAppVersion = response.data.appVersion;
// let serverAppVersionState = this.$store.state.serverAppVersion;
// let deniedUpdate = this.$store.state.deniedUpdate;
// if (isApiCall && !serverAppVersionState) {
// this.$store.state.serverAppVersion = serverAppVersion;
// } else if (isApiCall && serverAppVersionState !== serverAppVersion && !deniedUpdate || isCron) {
// // For reload on cron
// if (isCron || confirm(this.$t('habiticaHasUpdated'))) {
// } else if (isApiCall && serverAppVersionState !== serverAppVersion) {
// if (document.activeElement.tagName !== 'INPUT' || confirm(this.$t('habiticaHasUpdated'))) {
// location.reload(true);
// } else {
// this.$store.state.deniedUpdate = true;
// }
// }

View File

@@ -1,12 +1,24 @@
.promo_mystery_201710 {
.promo_potions_thunderstorm {
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
background-position: 0px -244px;
width: 93px;
height: 90px;
background-position: -499px 0px;
width: 141px;
height: 441px;
}
.scene_positivity {
.promo_take_this {
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
background-position: -303px -250px;
width: 114px;
height: 87px;
}
.scene_guilds {
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
background-position: 0px 0px;
width: 531px;
height: 243px;
width: 498px;
height: 249px;
}
.scene_habit_cycle {
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
background-position: 0px -250px;
width: 302px;
height: 264px;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,222 +1,666 @@
.Pet-Wolf-Ghost {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -82px 0px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Golden {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -328px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Holly {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -246px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Peppermint {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -164px 0px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Red {
.Pet-TRex-Base {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: 0px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-RoyalPurple {
.Pet-TRex-CottonCandyBlue {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -82px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Shade {
.Pet-TRex-CottonCandyPink {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -164px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Shimmer {
.Pet-TRex-Desert {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -246px 0px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Skeleton {
.Pet-TRex-Golden {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: 0px 0px;
background-position: -246px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Spooky {
.Pet-TRex-Red {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: 0px -200px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Thunderstorm {
.Pet-TRex-Shade {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -82px -200px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Veteran {
.Pet-TRex-Skeleton {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -164px -200px;
width: 81px;
height: 99px;
}
.Pet-Wolf-White {
.Pet-TRex-White {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -246px -200px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Zombie {
.Pet-TRex-Zombie {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -328px 0px;
width: 81px;
height: 99px;
}
.Pet_HatchingPotion_Aquatic {
.Pet-Treeling-Skeleton {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -82px 0px;
width: 81px;
height: 99px;
}
.Pet-Treeling-White {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -82px -600px;
width: 81px;
height: 99px;
}
.Pet-Treeling-Zombie {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -164px 0px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Base {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -328px -100px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-CottonCandyBlue {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -328px -200px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-CottonCandyPink {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: 0px -300px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Desert {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -82px -300px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Golden {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -164px -300px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Red {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -246px -300px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Shade {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -328px -300px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Skeleton {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px 0px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-White {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px -100px;
width: 81px;
height: 99px;
}
.Pet-Triceratops-Zombie {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px -200px;
width: 81px;
height: 99px;
}
.Pet-Turkey-Base {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px -300px;
width: 81px;
height: 99px;
}
.Pet-Turkey-Gilded {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -492px 0px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Base {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -492px -100px;
width: 81px;
height: 99px;
}
.Pet-Turtle-CottonCandyBlue {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -492px -200px;
width: 81px;
height: 99px;
}
.Pet-Turtle-CottonCandyPink {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -492px -300px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Desert {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: 0px -400px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Golden {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -82px -400px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Red {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -164px -400px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Shade {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -246px -400px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Skeleton {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -328px -400px;
width: 81px;
height: 99px;
}
.Pet-Turtle-White {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px -400px;
width: 81px;
height: 99px;
}
.Pet-Turtle-Zombie {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -492px -400px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Base {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -574px 0px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-CottonCandyBlue {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -574px -100px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-CottonCandyPink {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -574px -200px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Desert {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -574px -300px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Golden {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -574px -400px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Red {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: 0px -500px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Shade {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -82px -500px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Skeleton {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -164px -500px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-White {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -246px -500px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Zombie {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -328px -500px;
width: 81px;
height: 99px;
}
.Pet-Whale-Base {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px -500px;
width: 81px;
height: 99px;
}
.Pet-Whale-CottonCandyBlue {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -492px -500px;
width: 81px;
height: 99px;
}
.Pet-Whale-CottonCandyPink {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -574px -500px;
width: 81px;
height: 99px;
}
.Pet-Whale-Desert {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -656px 0px;
width: 81px;
height: 99px;
}
.Pet-Whale-Golden {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -656px -100px;
width: 81px;
height: 99px;
}
.Pet-Whale-Red {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -656px -200px;
width: 81px;
height: 99px;
}
.Pet-Whale-Shade {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -656px -300px;
width: 81px;
height: 99px;
}
.Pet-Whale-Skeleton {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -656px -400px;
width: 81px;
height: 99px;
}
.Pet-Whale-White {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -656px -500px;
width: 81px;
height: 99px;
}
.Pet-Whale-Zombie {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: 0px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Aquatic {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: 0px 0px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Base {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -164px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-CottonCandyBlue {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -246px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-CottonCandyPink {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -328px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Cupid {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Desert {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -492px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Ember {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -574px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Fairy {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -656px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Floral {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -738px 0px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Ghost {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -738px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Golden {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -738px -200px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Holly {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -738px -300px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Peppermint {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -738px -400px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Red {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -738px -500px;
width: 81px;
height: 99px;
}
.Pet-Wolf-RoyalPurple {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -738px -600px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Shade {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: 0px -700px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Shimmer {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -82px -700px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Skeleton {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -164px -700px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Spooky {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -246px -700px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Thunderstorm {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -328px -700px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Veteran {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px -700px;
width: 81px;
height: 99px;
}
.Pet-Wolf-White {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -492px -700px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Zombie {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -574px -700px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Base {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -656px -700px;
width: 81px;
height: 99px;
}
.Pet-Yarn-CottonCandyBlue {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -738px -700px;
width: 81px;
height: 99px;
}
.Pet-Yarn-CottonCandyPink {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -820px 0px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Desert {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -820px -100px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Golden {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -820px -200px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Red {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -820px -300px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Shade {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -820px -400px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Skeleton {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -820px -500px;
width: 81px;
height: 99px;
}
.Pet-Yarn-White {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -820px -600px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Zombie {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -820px -700px;
width: 81px;
height: 99px;
}
.Pet_HatchingPotion_Aquatic {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -69px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Base {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: 0px -300px;
background-position: -759px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_CottonCandyBlue {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -69px -300px;
background-position: -138px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_CottonCandyPink {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -138px -300px;
background-position: -207px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Cupid {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -479px -345px;
background-position: -276px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Desert {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -276px -300px;
background-position: -345px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Ember {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px 0px;
background-position: -414px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Fairy {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px -69px;
background-position: -483px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Floral {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px -138px;
background-position: -552px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Ghost {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px -207px;
background-position: -621px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Golden {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -410px -276px;
background-position: -690px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Holly {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: 0px -369px;
background-position: 0px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Peppermint {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -69px -369px;
background-position: -828px -800px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Purple {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -138px -369px;
background-position: -902px 0px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Red {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -207px -369px;
background-position: -902px -69px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_RoyalPurple {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -276px -369px;
background-position: -902px -138px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Shade {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -345px -369px;
background-position: -902px -207px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Shimmer {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -479px 0px;
background-position: -902px -276px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Skeleton {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -479px -69px;
background-position: -902px -345px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Spooky {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -479px -138px;
background-position: -902px -414px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Thunderstorm {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -479px -207px;
background-position: -902px -483px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_White {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -479px -276px;
background-position: -902px -552px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Zombie {
background-image: url(/static/sprites/spritesmith-main-20.png);
background-position: -207px -300px;
background-position: -902px -621px;
width: 68px;
height: 68px;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -20,3 +20,11 @@
position: absolute;
top: -9px;
}
.badge-purple {
position: absolute;
color: $white;
background: $purple-400;
line-height: 1.2;
font-size: 10px;
}

View File

@@ -196,7 +196,7 @@
&-daily-todo-content-disabled {
background: $gray-600;
* {
.task-title, .task-notes {
color: $gray-300 !important;
}
}

View File

@@ -1,9 +1,9 @@
// this variables are used to determine which shop npc/backgrounds should be loaded
// possible values are: normal, fall
// possible values are: normal, fall, habitoween
// more to be added on future seasons
$npc_market_flavor: "fall";
$npc_quests_flavor: "fall";
$npc_seasonal_flavor: "fall";
$npc_timetravelers_flavor: "fall";
$npc_tavern_flavor: "fall";
$npc_market_flavor: 'normal';
$npc_quests_flavor: 'normal';
$npc_seasonal_flavor: 'normal';
$npc_timetravelers_flavor: 'normal';
$npc_tavern_flavor: 'normal';

View File

@@ -1,6 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="23" viewBox="0 0 40 23">
<g fill="none" fill-rule="evenodd" stroke="#A5A1AC" stroke-width="2.4">
<path d="M23.324 10.53h14.621M2.248 10.53h21.946M16.804 15.667s1.501-.742 3.293-.742a7.57 7.57 0 0 1 3.197.742"/>
<path stroke-linecap="round" stroke-linejoin="round" d="M16.838 10.53v3.878c0 3.879-3.22 7.052-7.154 7.052H8.359c-3.497 0-6.36-2.822-6.36-6.269v-4.968l8.289-7.205c2.02-1.756 4.63-1.113 6.482.958M23.26 10.53v3.878c0 3.879 3.219 7.052 7.154 7.052h1.325c3.497 0 6.359-2.822 6.359-6.269v-4.968L29.81 3.018c-2.02-1.756-4.63-1.113-6.482.958"/>
</g>
</svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 38.5 21.85"><defs><style>.cls-1{}</style></defs><title>extras</title><path class="cls-1" d="M38.84,13.59l-8.29-7.2c-2.39-2.08-5.74-1.64-8.16,1.06A1.2,1.2,0,0,0,24.17,9c1.17-1.3,3.12-2.31,4.8-.85l6.22,5.41H4.81L11,8.19c1.68-1.46,3.63-.45,4.8.85a1.2,1.2,0,1,0,1.79-1.6c-2.42-2.7-5.77-3.14-8.16-1.06l-8.29,7.2a1.2,1.2,0,0,0-.41.91v5a7.52,7.52,0,0,0,7.56,7.47H9.63a8.35,8.35,0,0,0,8.05-6.08A7,7,0,0,1,20,20.4a6.41,6.41,0,0,1,2.26.44,8.35,8.35,0,0,0,8.05,6.09h1.32a7.52,7.52,0,0,0,7.56-7.47v-5A1.2,1.2,0,0,0,38.84,13.59ZM9.63,24.53H8.31a5.12,5.12,0,0,1-5.16-5.07V16H15.59v2.68A5.91,5.91,0,0,1,9.63,24.53ZM18,18.25V16h4v2.24A8.39,8.39,0,0,0,20,18,8.83,8.83,0,0,0,18,18.25Zm13.7,6.28H30.37a5.91,5.91,0,0,1-6-5.85V16H36.85v3.46A5.12,5.12,0,0,1,31.69,24.53Z" transform="translate(-0.75 -5.08)"/></svg>

Before

Width:  |  Height:  |  Size: 644 B

After

Width:  |  Height:  |  Size: 879 B

View File

@@ -1,6 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<rect width="29.6" height="29.6" x="1.2" y="1.2" stroke="#A5A1AC" stroke-width="2.4" rx="4"/>
<path fill="#A5A1AC" d="M11 11l2-1-2-1-1-2-1 2-2 1 2 1 1 2zM23 14l2-1-2-1-1-2-1 2-2 1 2 1 1 2zM15.333 22.333L18 21l-2.667-1.333L14 17l-1.333 2.667L10 21l2.667 1.333L14 25z"/>
</g>
</svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><defs><style>.cls-1{fill-rule:evenodd;}</style></defs><title>background</title><path class="cls-1" d="M28,0H4A4,4,0,0,0,0,4V28a4,4,0,0,0,4,4H28a4,4,0,0,0,4-4V4A4,4,0,0,0,28,0Zm1.6,28A1.6,1.6,0,0,1,28,29.6H4A1.6,1.6,0,0,1,2.4,28V4A1.6,1.6,0,0,1,4,2.4H28A1.6,1.6,0,0,1,29.6,4V28ZM10,13L9,11,7,10,9,9l1-2,1,2,2,1-2,1Zm13-1,2,1-2,1-1,2-1-2-2-1,2-1,1-2Zm-7.67,7.67L18,21l-2.67,1.33L14,25l-1.33-2.67L10,21l2.67-1.33L14,17Z"/></svg>

Before

Width:  |  Height:  |  Size: 425 B

After

Width:  |  Height:  |  Size: 519 B

View File

@@ -1,8 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="34" viewBox="0 0 32 34">
<g fill="none" fill-rule="evenodd">
<path class='path' stroke="#6133B4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" d="M2.124 20.456S2.854 2 15.734 2c12.88 0 13.61 18.456 13.61 18.456"/>
<path class='path' stroke="#6133B4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" d="M10.29 32.388s-1.725-7.96-1.725-14.681c0-6.722 2.382-14.682 2.382-14.682M21.064 32.388s1.724-7.96 1.724-14.681c0-6.722-2.382-14.682-2.382-14.682"/>
<path class='path' stroke="#6133B4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" d="M8.957 22.65s3.372-.64 6.933-.64c3.563 0 6.54.64 6.54.64M7.003 32.388h3.29M21.144 32.388h3.289"/>
<path fill="#6133B4" d="M3.784 21.533a1.893 1.893 0 1 1-3.785 0 1.893 1.893 0 0 1 3.785 0M31.237 21.533a1.893 1.893 0 1 1-3.786 0 1.893 1.893 0 0 1 3.786 0"/>
</g>
</svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30.49 32"><defs><style>.cls-1{fill-rule:evenodd;}</style></defs><title>body</title><path class="cls-1" d="M30.55,18.8C30.19,12.11,26.77,0,16.11,0A11.9,11.9,0,0,0,11,1.11l-0.12.05C4.35,4.32,2,13.19,1.69,18.64A1.85,1.85,0,1,0,4,19.05c0.09-1.31.83-9.79,5.5-14.14A53.73,53.73,0,0,0,7.94,16.5,76.9,76.9,0,0,0,9.37,29.66H7.59a1.17,1.17,0,0,0,0,2.34h3.21L11,32l0.1,0a1.13,1.13,0,0,0,.23-0.1l0.17-.11a1.12,1.12,0,0,0,.37-0.57,1.12,1.12,0,0,0,0-.2,0.33,0.33,0,0,0,0-.26,1.09,1.09,0,0,0,0-.11c0-.05-0.82-3.82-1.3-8.26a39.75,39.75,0,0,1,5.62-.45,33.72,33.72,0,0,1,5.21.43c-0.48,4.44-1.29,8.23-1.3,8.28a1.17,1.17,0,0,0,.9,1.39l0.25,0H24.6a1.17,1.17,0,0,0,0-2.34H22.74A76.9,76.9,0,0,0,24.17,16.5a54,54,0,0,0-1.6-11.72C27.16,8.91,28.05,17,28.2,18.84A1.85,1.85,0,1,0,30.55,18.8Zm-8.72-2.3c0,1.13-.05,2.3-0.14,3.46a35.84,35.84,0,0,0-5.42-.43,41.52,41.52,0,0,0-5.84.46c-0.09-1.17-.14-2.35-0.14-3.48A57.14,57.14,0,0,1,12.41,3a10.15,10.15,0,0,1,7.28,0A57.16,57.16,0,0,1,21.83,16.5Z" transform="translate(-0.76)"/></svg>

Before

Width:  |  Height:  |  Size: 960 B

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="11" viewBox="0 0 10 11">
<g fill="none" fill-rule="evenodd" stroke="#686274" stroke-width="2">
<path d="M5,0v8 M1,5l4,4l4-4"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 213 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16">
<path fill="#A5A1AC" fill-rule="evenodd" d="M3 14h8V4H3v10zM14 4h-1v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4H0V2h4V1a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v1h4v2zm-6 8h1V6H8v6zm-3 0h1V6H5v6z"/>
<path fill-rule="evenodd" d="M3 14h8V4H3v10zM14 4h-1v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4H0V2h4V1a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v1h4v2zm-6 8h1V6H8v6zm-3 0h1V6H5v6z"/>
</svg>

Before

Width:  |  Height:  |  Size: 274 B

After

Width:  |  Height:  |  Size: 259 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M2.723 11.859l1.418 1.419-2.219.788.8-2.207zm2.762.686l-2.03-2.03 7.386-7.385 2.03 2.03-7.386 7.385zm8.704-10.731c.56.56.56 1.468 0 2.03l-.285.284-2.03-2.03.286-.284a1.438 1.438 0 0 1 2.027 0h.002zM11.125.782l-.8.8-8.417 8.415a.731.731 0 0 0-.098.122s-.012.024-.02.036a.713.713 0 0 0-.048.1v.012L.044 15.022a.73.73 0 0 0 .934.935l4.755-1.704a.728.728 0 0 0 .102-.05l.034-.018a.731.731 0 0 0 .122-.097l9.227-9.213A2.896 2.896 0 0 0 11.125.782z"/>
</svg>

After

Width:  |  Height:  |  Size: 570 B

View File

@@ -1,6 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="35" height="35" viewBox="0 0 35 35">
<g fill="none" fill-rule="evenodd">
<path fill="#A5A1AC" d="M22.688 0a7.222 7.222 0 0 0-5.12 2.12L2.12 17.569a7.241 7.241 0 0 0 0 10.24l2.538 2.538L30.346 4.66 27.809 2.12A7.222 7.222 0 0 0 22.688 0m0 2.414c1.289 0 2.502.502 3.413 1.414l.832.83L4.659 26.934l-.831-.831a4.793 4.793 0 0 1-1.415-3.414c0-1.29.502-2.501 1.415-3.414L19.275 3.828a4.793 4.793 0 0 1 3.413-1.414"/>
<path stroke="#A5A1AC" stroke-width="2.4" d="M4.385 28.385l5.746 5.747M28.36 4.41l5.746 5.746M16.372 16.398l5.746 5.746M7.382 25.389l5.746 5.746M10.379 22.392l5.746 5.746M13.376 19.395l5.746 5.746M19.37 13.4l5.745 5.747M22.366 10.404l5.746 5.746M25.363 7.407l5.746 5.746"/>
</g>
</svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 31.24 31.25"><defs><style>.cls-1{}</style></defs><title>hair</title><path class="cls-1" d="M26.14,6.55l4,4,1.7-1.7L27,4l0,0-0.85-.85-1-1a5.49,5.49,0,0,0-7.76,0L2.21,17.38a5.49,5.49,0,0,0,0,7.76l1,1L4,27l0,0,4.86,4.86,1.7-1.7-4-4,0,0L8.5,24.19l4,4,1.7-1.7-4-4,1.8-1.8,4,4L17.7,23l-4-4,1.8-1.8,4,4,1.7-1.7-4-4,1.8-1.8,4,4L24.7,16l-4-4,1.8-1.8,4,4,1.7-1.7-4-4,1.92-1.92ZM4.88,24.42l-1-1a3.09,3.09,0,0,1,0-4.37L19.08,3.9a3.09,3.09,0,0,1,4.37,0l1,1Z" transform="translate(-0.6 -0.6)"/></svg>

Before

Width:  |  Height:  |  Size: 768 B

After

Width:  |  Height:  |  Size: 573 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="4" height="16" viewBox="0 0 4 16">
<path fill-rule="evenodd" d="M2 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
</svg>

After

Width:  |  Height:  |  Size: 225 B

View File

@@ -1,6 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="37" height="33" viewBox="0 0 37 33">
<g fill="none" fill-rule="evenodd" stroke="#A5A1AC" stroke-linejoin="round" stroke-width="2.4">
<path stroke-linecap="round" d="M13.782 21.846s1.794 3.682 0 5.454c-1.793 1.773-5.957 0-8.327.938C3.085 29.175 2 31.65 2 31.65M23.744 21.846s-1.794 3.682 0 5.454c1.793 1.773 5.957 0 8.327.938 2.37.937 3.455 3.412 3.455 3.412"/>
<path d="M27.939 12.642c0 6.321-5.755 11.445-9.277 11.445-2.98 0-9.277-5.124-9.277-11.445C9.385 6.32 13.089 2 18.662 2c5.572 0 9.277 4.32 9.277 10.642z"/>
</g>
</svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 31.8 28.4"><defs><style>.cls-1{}</style></defs><title>skin</title><path class="cls-1" d="M31.8,28.52a7.44,7.44,0,0,0-3.69-3.63,8.72,8.72,0,0,0-3.82-.26c-1.21.09-2.57,0.19-3.08-.3-0.72-.71-0.15-2.6.24-3.4a1.13,1.13,0,0,0,0-.18,12,12,0,0,0,3.75-8.41c0-6.2-3.84-10.53-9.33-10.53S6.58,6.13,6.58,12.33a12.19,12.19,0,0,0,3.94,8.46,1.13,1.13,0,0,0,0,.13c0.39,0.8,1,2.7.24,3.41-0.5.5-1.87,0.39-3.08,0.3a8.75,8.75,0,0,0-3.82.26A7.43,7.43,0,0,0,.2,28.52a1.2,1.2,0,1,0,2.2,1,5,5,0,0,1,2.37-2.36A7.3,7.3,0,0,1,7.53,27c1.72,0.13,3.66.28,4.94-1a3.9,3.9,0,0,0,1-3.11,5.72,5.72,0,0,0,2.39.64,6.15,6.15,0,0,0,2.57-.67,3.92,3.92,0,0,0,1,3.14c1.28,1.26,3.22,1.12,4.94,1a7.34,7.34,0,0,1,2.76.1,5,5,0,0,1,2.37,2.36A1.2,1.2,0,1,0,31.8,28.52ZM9,12.33C9,7.39,11.7,4.2,15.91,4.2s6.93,3.19,6.93,8.13-4.62,8.84-6.93,8.84C14,21.17,9,17.25,9,12.33Z" transform="translate(-0.1 -1.8)"/></svg>

Before

Width:  |  Height:  |  Size: 599 B

After

Width:  |  Height:  |  Size: 948 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="11" viewBox="0 0 10 11">
<g fill="none" fill-rule="evenodd" stroke="#686274" stroke-width="2">
<path d="M5 3v8M9 6L5 2 1 6"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 212 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="24" viewBox="0 0 22 24">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M15 13h-.4c1.9-1.2 3.3-3.3 3.4-5.8.1-3.8-3.1-7.2-6.9-7.2C7.1 0 4 3.1 4 7c0 2.6 1.3 4.8 3.4 6H7c-3.9 0-7 3.1-7 7v1c0 1.7 1.3 3 3 3h16c1.7 0 3-1.3 3-3v-1c0-3.9-3.1-7-7-7zM6 7c0-2.8 2.2-5 5-5s5 2.2 5 5-2.2 5-5 5-5-2.2-5-5zm13 15H3c-.6 0-1-.4-1-1v-1c0-2.8 2.2-5 5-5h8c2.8 0 5 2.2 5 5v1c0 .6-.4 1-1 1z"/>
</svg>

Before

Width:  |  Height:  |  Size: 424 B

After

Width:  |  Height:  |  Size: 424 B

View File

@@ -1,17 +1,24 @@
<template lang="pug">
b-modal#login-incentives(:title="data.message", size='md', :hide-footer="true")
.modal-body
.row
h3.col-12.text-center(v-if='data.rewardText') {{ $t('unlockedReward', {reward: data.rewardText}) }}
.row.reward-row
.col-12
avatar.avatar(:member='user', :avatarOnly='true', :withBackground='true')
.text-center.col-12
.reward-wrap
.reward-wrap(v-if="!data.rewardText")
div(v-if="nextReward.rewardKey.length === 1", :class="nextReward.rewardKey[0]")
.reward(v-for="reward in nextReward.rewardKey", v-if="nextReward.rewardKey.length > 1", :class='reward')
.reward-wrap(v-if="data.rewardText")
div(v-if="data.rewardKey.length === 1", :class="data.rewardKey[0]")
.reward(v-for="reward in data.rewardKey", v-if="data.rewardKey.length > 1", :class='reward')
.col-12.text-center(v-if="data.nextRewardAt")
h4 {{ $t('countLeft', {count: data.nextRewardAt - user.loginIncentives}) }}
.row
.col-8.offset-2.text-center
.col-12.text-center(v-if='data.rewardText')
p {{ $t('earnedRewardForDevotion', {reward: data.rewardText}) }}
.col-12.text-center
p {{ $t('incentivesDescription') }}
.col-12.text-center(v-if="data.nextRewardAt")
h3 {{ $t('nextRewardUnlocksIn', {numberOfCheckinsLeft: data.nextRewardAt - user.loginIncentives}) }}

View File

@@ -47,7 +47,7 @@
label
strong(v-once) {{$t('endDate')}}
b-form-input.end-date-input
.form-group
.form-group(v-if='creating')
label
strong(v-once) {{$t('prize')}}
input(type='number', :min='minPrize', :max='maxPrize', v-model="workingChallenge.prize")

View File

@@ -150,6 +150,7 @@ export default {
this.filters = eventData;
},
createChallenge () {
this.$store.state.challengeOptions.workingChallenge = {};
this.$root.$emit('show::modal', 'challenge-modal');
},
async loadchallanges () {

View File

@@ -16,23 +16,23 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
button.btn.btn-secondary(v-once) {{$t('randomize')}}
#options-nav.container.section.text-center.customize-menu
.row
div(:class='{"col-3": !editing, "col-2 offset-1": editing}')
.menu-container(:class='{"col-3": !editing, "col-2 offset-1": editing, active: activeTopPage === "body"}')
.menu-item(@click='changeTopPage("body", "size")')
.svg-icon(v-html='icons.bodyIcon')
strong(v-once) {{$t('bodyBody')}}
div(:class='{"col-3": !editing, "col-2": editing}')
.menu-container(:class='{"col-3": !editing, "col-2": editing, active: activeTopPage === "skin"}')
.menu-item(@click='changeTopPage("skin", "color")')
.svg-icon(v-html='icons.skinIcon')
strong(v-once) {{$t('skin')}}
div(:class='{"col-3": !editing, "col-2": editing}')
.menu-container(:class='{"col-3": !editing, "col-2": editing, active: activeTopPage === "hair"}')
.menu-item(@click='changeTopPage("hair", "color")')
.svg-icon(v-html='icons.hairIcon')
strong(v-once) {{$t('hair')}}
div(:class='{"col-3": !editing, "col-2": editing}')
.menu-container(:class='{"col-3": !editing, "col-2": editing, active: activeTopPage === "extra"}')
.menu-item(@click='changeTopPage("extra", "glasses")')
.svg-icon(v-html='icons.accessoriesIcon')
strong(v-once) {{$t('extra')}}
.col-2(v-if='editing')
.menu-container.col-2(v-if='editing', :class='{active: activeTopPage === "backgrounds"}')
.menu-item(@click='changeTopPage("backgrounds", "2017")')
.svg-icon(v-html='icons.backgroundsIcon')
strong(v-once) {{$t('backgrounds')}}
@@ -505,14 +505,15 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
width: 32px;
height: 32px;
margin: 0 auto;
color: #6133B4;
}
.menu-item:hover {
.menu-container {
color: #a5a1ac;
}
.menu-container:hover, .menu-container.active {
cursor: pointer;
svg path, strong {
stroke: purple !important;
}
color: #6133B4;
}
}

View File

@@ -255,7 +255,7 @@ export default {
if (this.searchTerm) {
sortedMembers = sortedMembers.filter(member => {
return member.profile.name.toLowerCase().indexOf(this.searchTerm.toLowerCase) !== -1;
return member.profile.name.toLowerCase().indexOf(this.searchTerm.toLowerCase()) !== -1;
});
}

View File

@@ -102,9 +102,9 @@ div
<script>
import { mapGetters, mapActions } from 'client/libs/store';
import MemberDetails from './memberDetails';
import createPartyModal from './groups/createPartyModal';
import membersModal from './groups/membersModal';
import MemberDetails from '../memberDetails';
import createPartyModal from '../groups/createPartyModal';
import membersModal from '../groups/membersModal';
import ResizeDirective from 'client/directives/resize.directive';
export default {

View File

@@ -53,42 +53,20 @@ div
a.dropdown-item(href="https://trello.com/c/odmhIqyW/440-read-first-table-of-contents", target='_blank') {{ $t('requestAF') }}
a.dropdown-item(href="http://habitica.wikia.com/wiki/Contributing_to_Habitica", target='_blank') {{ $t('contributing') }}
a.dropdown-item(href="http://habitica.wikia.com/wiki/Habitica_Wiki", target='_blank') {{ $t('wiki') }}
.item-with-icon(v-if="userHourglasses > 0")
.svg-icon(v-html="icons.hourglasses")
span {{ userHourglasses }}
.item-with-icon
.svg-icon.gem(v-html="icons.gem", @click='showBuyGemsModal("gems")')
span {{userGems | roundBigNumber}}
.item-with-icon
.svg-icon(v-html="icons.gold")
span {{Math.floor(user.stats.gp * 100) / 100}}
a.item-with-icon(@click="sync")
.svg-icon(v-html="icons.sync")
notification-menu
a.dropdown.item-with-icon.item-user
span.message-count.top-count(v-if='user.inbox.newMessages > 0') {{user.inbox.newMessages}}
.svg-icon.user(v-html="icons.user")
.dropdown-menu.dropdown-menu-right.user-dropdown
a.dropdown-item.edit-avatar.dropdown-separated(@click='showAvatar()')
h3 {{ user.profile.name }}
span.small-text {{ $t('editAvatar') }}
a.nav-link.dropdown-item.dropdown-separated(@click.prevent='showInbox()')
| {{ $t('messages') }}
span.message-count(v-if='user.inbox.newMessages > 0') {{user.inbox.newMessages}}
a.dropdown-item(@click='showAvatar("backgrounds", "2017")') {{ $t('backgrounds') }}
a.dropdown-item(@click='showProfile("stats")') {{ $t('stats') }}
a.dropdown-item(@click='showProfile("achievements")') {{ $t('achievements') }}
a.dropdown-item.dropdown-separated(@click='showProfile("profile")') {{ $t('profile') }}
router-link.dropdown-item(:to="{name: 'site'}") {{ $t('settings') }}
router-link.dropdown-item.dropdown-separated(:to="{name: 'subscription'}") {{ $t('subscription') }}
a.nav-link.dropdown-item.dropdown-separated(to="/", @click.prevent='logout()') {{ $t('logout') }}
li(v-if='!this.user.purchased.plan.customerId', @click='showBuyGemsModal("subscribe")')
.dropdown-item.text-center
h3.purple {{ $t('needMoreGems') }}
span.small-text {{ $t('needMoreGemsInfo') }}
img.float-left.align-self-end(src='~assets/images/gem-rain.png')
button.btn.btn-primary.btn-lg.learn-button Learn More
img.float-right.align-self-end(src='~assets/images/gold-rain.png')
.d-flex.align-items-center
.item-with-icon(v-if="userHourglasses > 0")
.svg-icon(v-html="icons.hourglasses", v-b-tooltip.hover.bottom="$t('mysticHourglassesTooltip')")
span {{ userHourglasses }}
.item-with-icon
.svg-icon.gem(v-html="icons.gem", @click='showBuyGemsModal("gems")', v-b-tooltip.hover.bottom="$t('gems')")
span {{userGems | roundBigNumber}}
.item-with-icon.gold
.svg-icon(v-html="icons.gold", v-b-tooltip.hover.bottom="$t('gold')")
span {{Math.floor(user.stats.gp * 100) / 100}}
a.item-with-icon(@click="sync", v-b-tooltip.hover.bottom="$t('sync')")
.svg-icon(v-html="icons.sync")
notification-menu.item-with-icon
user-dropdown.item-with-icon
b-nav-toggle(target='nav_collapse')
</template>
@@ -165,40 +143,13 @@ div
}
}
// Make the dropdown menu open on hover
.dropdown:hover .dropdown-menu {
display: block;
margin-top: 0; // remove the gap so it doesn't close
// Make the dropdown menu open on hover
.dropdown:hover .dropdown-menu {
display: block;
margin-top: 0; // remove the gap so it doesn't close
}
.dropdown + .dropdown {
margin-left: 0px;
}
.dropdown-separated {
border-bottom: 1px solid $gray-500;
}
.user-dropdown {
width: 14.75em;
}
.learn-button {
margin: 0.75em 0.75em 0.75em 1em;
}
.purple {
color: $purple-200;
}
.small-text {
color: $gray-200;
font-style: normal;
display: block;
white-space: normal;
}
.dropdown-menu:not(.user-dropdown) {
.dropdown-menu {
background: $purple-200;
border-radius: 0px;
border: none;
@@ -230,81 +181,48 @@ div
}
}
.dropdown + .dropdown {
margin-left: 0px;
}
.item-with-icon {
color: $white;
font-size: 16px;
font-weight: normal;
padding-top: 16px;
padding-left: 16px;
white-space: nowrap;
span {
font-weight: bold;
}
&:hover .svg-icon {
&.gold {
margin-right: 24px;
}
&:hover /deep/ .svg-icon {
color: $white;
}
.svg-icon {
& /deep/ .svg-icon {
color: $header-color;
vertical-align: bottom;
display: inline-block;
width: 20px;
height: 20px;
margin-right: 8px;
margin-left: 8px;
width: 24px;
height: 24px;
margin-right: 12px;
margin-left: 12px;
}
}
.item-notifications, .item-user {
padding-right: 12.5px;
padding-left: 12.5px;
color: $header-color;
transition: none;
.svg-icon {
margin-right: 0px;
}
}
.item-user .edit-avatar {
h3 {
color: $gray-10;
margin-bottom: 0px;
}
padding-top: 16px;
padding-bottom: 16px;
.menu-icon {
margin-left: 24px;
}
.gem:hover {
cursor: pointer;
}
.message-count {
background-color: $blue-50;
border-radius: 50%;
height: 20px;
width: 20px;
float: right;
color: $white;
text-align: center;
font-weight: bold;
font-size: 12px;
}
.message-count.top-count {
background-color: $red-50;
position: absolute;
right: 0;
top: .5em;
padding: .2em;
}
</style>
<script>
import axios from 'axios';
import bNavToggle from 'bootstrap-vue/lib/components/nav-toggle';
import bCollapse from 'bootstrap-vue/lib/components/collapse';
@@ -313,17 +231,18 @@ import * as Analytics from 'client/libs/analytics';
import gemIcon from 'assets/svg/gem.svg';
import goldIcon from 'assets/svg/gold.svg';
import syncIcon from 'assets/svg/sync.svg';
import userIcon from 'assets/svg/user.svg';
import svgHourglasses from 'assets/svg/hourglass.svg';
import logo from 'assets/svg/logo.svg';
import InboxModal from './userMenu/inbox.vue';
import notificationMenu from './notificationMenu';
import creatorIntro from './creatorIntro';
import profile from './userMenu/profile';
import markPMSRead from 'common/script/ops/markPMSRead';
import InboxModal from '../userMenu/inbox.vue';
import notificationMenu from './notificationsDropdown';
import creatorIntro from '../creatorIntro';
import profile from '../userMenu/profile';
import userDropdown from './userDropdown';
import bTooltip from 'bootstrap-vue/lib/directives/tooltip';
export default {
components: {
userDropdown,
InboxModal,
notificationMenu,
creatorIntro,
@@ -331,12 +250,15 @@ export default {
bNavToggle,
bCollapse,
},
directives: {
bTooltip,
},
data () {
return {
isUserDropdownOpen: false,
icons: Object.freeze({
gem: gemIcon,
gold: goldIcon,
user: userIcon,
hourglasses: svgHourglasses,
sync: syncIcon,
logo,
@@ -357,31 +279,15 @@ export default {
this.getUserGroupPlans();
},
methods: {
toggleUserDropdown () {
this.isUserDropdownOpen = !this.isUserDropdownOpen;
},
sync () {
return Promise.all([
this.$store.dispatch('user:fetch', {forceLoad: true}),
this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true}),
]);
},
logout () {
this.$store.dispatch('auth:logout');
},
showInbox () {
markPMSRead(this.user);
axios.post('/api/v3/user/mark-pms-read');
this.$root.$emit('show::modal', 'inbox-modal');
},
showAvatar (startingPage, subpage) {
this.$store.state.avatarEditorOptions.editingUser = true;
this.$store.state.avatarEditorOptions.startingPage = startingPage;
this.$store.state.avatarEditorOptions.subpage = subpage;
this.$root.$emit('show::modal', 'avatar-modal');
},
showProfile (startingPage) {
this.$store.state.profileUser = this.user;
this.$store.state.profileOptions.startingPage = startingPage;
this.$root.$emit('show::modal', 'profile');
},
async getUserGroupPlans () {
this.$store.state.groupPlans = await this.$store.dispatch('guilds:getGroupPlans');
},

View File

@@ -0,0 +1,27 @@
<template lang="pug" functional>
span.message-count(:class="{'top-count': props.top === true}") {{props.count}}
</template>
<style lang="scss">
@import '~client/assets/scss/colors.scss';
.message-count {
background-color: $blue-50;
border-radius: 50%;
height: 20px;
width: 20px;
float: right;
color: $white;
text-align: center;
font-weight: bold;
font-size: 12px;
}
.message-count.top-count {
position: absolute;
right: 0.3em;
top: -0.8em;
padding: 0.2em;
background-color: $red-50;
}
</style>

View File

@@ -1,15 +1,17 @@
<template lang="pug">
div.item-with-icon.item-notifications.dropdown
span.message-count.top-count(v-if='notificationsCount > 0') {{ notificationsCount }}
.svg-icon.notifications(v-html="icons.notifications")
.dropdown-menu.dropdown-menu-right.user-dropdown
menu-dropdown.item-notifications(:right="true")
div(slot="dropdown-toggle")
div
message-count(v-if='notificationsCount > 0', :count="notificationsCount", :top="true")
.svg-icon.notifications(v-html="icons.notifications")
div(slot="dropdown-content")
h4.dropdown-item.dropdown-separated(v-if='!hasNoNotifications()') {{ $t('notifications') }}
h4.dropdown-item.toolbar-notifs-no-messages(v-if='hasNoNotifications()') {{ $t('noNotifications') }}
a.dropdown-item(v-if='user.party.quest && user.party.quest.RSVPNeeded')
div {{ $t('invitedTo', {name: quests.quests[user.party.quest.key].text()}) }}
div
button.btn.btn-primary(@click='questAccept(user.party._id)') Accept
button.btn.btn-primary(@click='questReject(user.party._id)') Reject
button.btn.btn-primary(@click.stop='questAccept(user.party._id)') Accept
button.btn.btn-primary(@click.stop='questReject(user.party._id)') Reject
a.dropdown-item(v-if='user.purchased.plan.mysteryItems.length', @click='go("/inventory/items")')
span.glyphicon.glyphicon-gift
span {{ $t('newSubscriberItem') }}
@@ -18,20 +20,19 @@ div.item-with-icon.item-notifications.dropdown
span.glyphicon.glyphicon-user
span {{ $t('invitedTo', {name: party.name}) }}
div
button.btn.btn-primary(@click='accept(party, index, "party")') Accept
button.btn.btn-primary(@click='reject(party, index, "party")') Reject
button.btn.btn-primary(@click.stop='accept(party, index, "party")') Accept
button.btn.btn-primary(@click.stop='reject(party, index, "party")') Reject
a.dropdown-item(v-if='user.flags.cardReceived', @click='go("/inventory/items")')
span.glyphicon.glyphicon-envelope
span {{ $t('cardReceived') }}
a.dropdown-item(@click='clearCards()', :popover="$t('clear')",
popover-placement='right', popover-trigger='mouseenter', popover-append-to-body='true')
a.dropdown-item(@click.stop='clearCards()')
a.dropdown-item(v-for='(guild, index) in user.invitations.guilds')
div
span.glyphicon.glyphicon-user
span {{ $t('invitedTo', {name: guild.name}) }}
div
button.btn.btn-primary(@click='accept(guild, index, "guild")') Accept
button.btn.btn-primary(@click='reject(guild, index, "guild")') Reject
button.btn.btn-primary(@click.stop='accept(guild, index, "guild")') Accept
button.btn.btn-primary(@click.stop='reject(guild, index, "guild")') Reject
a.dropdown-item(v-if='user.flags.classSelected && !user.preferences.disableClasses && user.stats.points',
@click='go("/user/profile")')
span.glyphicon.glyphicon-plus-sign
@@ -40,130 +41,40 @@ div.item-with-icon.item-notifications.dropdown
span(@click='navigateToGroup(message.key)')
span.glyphicon.glyphicon-comment
span {{message.name}}
span.clear-button(@click='clearMessages(message.key)', :popover="$t('clear')",
popover-placement='right', popover-trigger='mouseenter', popover-append-to-body='true') Clear
span.clear-button(@click.stop='clearMessages(message.key)') Clear
a.dropdown-item(v-for='notification in groupNotifications', :key='notification.id')
span(:class="groupApprovalNotificationIcon(notification)")
span {{notification.data.message}}
span.clear-button(@click='viewGroupApprovalNotification(notification)', :popover="$t('clear')",
popover-placement='right', popover-trigger='mouseenter', popover-append-to-body='true') Clear
span.clear-button(@click.stop='viewGroupApprovalNotification(notification)') Clear
</template>
<style lang='scss' scoped>
@import '~client/assets/scss/colors.scss';
.message-count {
background-color: $blue-50;
border-radius: 50%;
height: 20px;
width: 20px;
float: right;
color: $white;
text-align: center;
font-weight: bold;
font-size: 12px;
}
.message-count.top-count {
position: absolute;
right: -.5em;
top: .5em;
padding: .2em;
background-color: $red-50;
}
.clear-button {
margin-left: .5em;
}
.item-notifications {
width: 44px;
}
.item-notifications:hover {
cursor: pointer;
}
.notifications {
color: $header-color;
vertical-align: bottom;
display: inline-block;
width: 20px;
height: 20px;
margin-right: 8px;
margin-left: 8px;
margin-top: .2em;
}
.item-with-icon:hover {
.svg-icon {
color: $white;
}
}
.user-dropdown {
max-height: 350px;
overflow: auto;
}
/* @TODO: Move to shared css */
.dropdown:hover .dropdown-menu {
display: block;
margin-top: 0; // remove the gap so it doesn't close
}
.dropdown + .dropdown {
margin-left: 0px;
}
.dropdown-separated {
border-bottom: 1px solid $gray-500;
}
.dropdown-menu:not(.user-dropdown) {
background: $purple-200;
border-radius: 0px;
border: none;
box-shadow: none;
padding: 0px;
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
.dropdown-item {
font-size: 16px;
box-shadow: none;
color: $white;
border: none;
line-height: 1.5;
&.active {
background: $purple-300;
}
&:hover {
background: $purple-300;
&:last-child {
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
}
}
}
}
.clear-button {
margin-left: .5em;
}
</style>
<script>
import axios from 'axios';
import isEmpty from 'lodash/isEmpty';
import map from 'lodash/map';
// import bTooltip from 'bootstrap-vue/lib/directives/tooltip';
import { mapState } from 'client/libs/store';
import * as Analytics from 'client/libs/analytics';
import quests from 'common/script/content/quests';
import notificationsIcon from 'assets/svg/notifications.svg';
import MenuDropdown from '../ui/customMenuDropdown';
import MessageCount from './messageCount';
export default {
components: {
MenuDropdown,
MessageCount,
},
directives: {
// bTooltip,
},
data () {
return {
icons: Object.freeze({

View File

@@ -0,0 +1,126 @@
<template lang="pug">
menu-dropdown.item-user(:right="true")
div(slot="dropdown-toggle")
div(v-b-tooltip.hover.bottom="$t('user')")
message-count(v-if='user.inbox.newMessages > 0', :count="user.inbox.newMessages", :top="true")
.svg-icon.user(v-html="icons.user")
.user-dropdown(slot="dropdown-content")
a.dropdown-item.edit-avatar.dropdown-separated(@click='showAvatar()')
h3 {{ user.profile.name }}
span.small-text {{ $t('editAvatar') }}
a.nav-link.dropdown-item.dropdown-separated(@click.prevent='showInbox()')
| {{ $t('messages') }}
message-count(v-if='user.inbox.newMessages > 0', :count="user.inbox.newMessages")
a.dropdown-item(@click='showAvatar("backgrounds", "2017")') {{ $t('backgrounds') }}
a.dropdown-item(@click='showProfile("stats")') {{ $t('stats') }}
a.dropdown-item(@click='showProfile("achievements")') {{ $t('achievements') }}
a.dropdown-item.dropdown-separated(@click='showProfile("profile")') {{ $t('profile') }}
router-link.dropdown-item(:to="{name: 'site'}") {{ $t('settings') }}
router-link.dropdown-item.dropdown-separated(:to="{name: 'subscription'}") {{ $t('subscription') }}
a.nav-link.dropdown-item.dropdown-separated(@click.prevent='logout()') {{ $t('logout') }}
li(v-if='!this.user.purchased.plan.customerId', @click='showBuyGemsModal("subscribe")')
.dropdown-item.text-center
h3.purple {{ $t('needMoreGems') }}
span.small-text {{ $t('needMoreGemsInfo') }}
img.float-left.align-self-end(src='~assets/images/gem-rain.png')
button.btn.btn-primary.btn-lg.learn-button Learn More
img.float-right.align-self-end(src='~assets/images/gold-rain.png')
</template>
<style lang='scss' scoped>
@import '~client/assets/scss/colors.scss';
.edit-avatar {
h3 {
color: $gray-10;
margin-bottom: 0px;
}
padding-top: 16px;
padding-bottom: 16px;
}
.user-dropdown {
width: 14.75em;
}
.learn-button {
margin: 0.75em 0.75em 0.75em 1em;
}
.purple {
color: $purple-200;
}
.small-text {
color: $gray-200;
font-style: normal;
display: block;
white-space: normal;
font-weight: bold;
}
</style>
<script>
import { mapState } from 'client/libs/store';
import * as Analytics from 'client/libs/analytics';
import userIcon from 'assets/svg/user.svg';
import MenuDropdown from '../ui/customMenuDropdown';
import axios from 'axios';
import markPMSRead from 'common/script/ops/markPMSRead';
import MessageCount from './messageCount';
import bTooltip from 'bootstrap-vue/lib/directives/tooltip';
export default {
components: {
MenuDropdown,
MessageCount,
},
directives: {
bTooltip,
},
data () {
return {
icons: Object.freeze({
user: userIcon,
}),
};
},
computed: {
...mapState({user: 'user.data'}),
},
methods: {
showAvatar (startingPage, subpage) {
this.$store.state.avatarEditorOptions.editingUser = true;
this.$store.state.avatarEditorOptions.startingPage = startingPage;
this.$store.state.avatarEditorOptions.subpage = subpage;
this.$root.$emit('show::modal', 'avatar-modal');
},
showInbox () {
markPMSRead(this.user);
axios.post('/api/v3/user/mark-pms-read');
this.$root.$emit('show::modal', 'inbox-modal');
},
showProfile (startingPage) {
this.$store.state.profileUser = this.user;
this.$store.state.profileOptions.startingPage = startingPage;
this.$root.$emit('show::modal', 'profile');
},
showBuyGemsModal (startingPage) {
this.$store.state.gemModalOptions.startingPage = startingPage;
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Gems > User Dropdown',
});
this.$root.$emit('show::modal', 'buy-gems', {alreadyTracked: true});
},
logout () {
this.$store.dispatch('auth:logout');
},
},
};
</script>

View File

@@ -79,9 +79,9 @@
:showPopover="flatGear[activeItems[group]] && Boolean(flatGear[activeItems[group]].text)",
@click="equipItem(flatGear[activeItems[group]])",
)
template(slot="popoverContent", scope="context")
template(slot="popoverContent", slot-scope="context")
equipmentAttributesPopover(:item="context.item")
template(slot="itemBadge", scope="context")
template(slot="itemBadge", slot-scope="context")
starBadge(
:selected="true",
:show="!costume || user.preferences.costume",
@@ -105,7 +105,7 @@
:type="group.key",
:noItemsLabel="$t('noGearItemsOfType', { type: group.label })"
)
template(slot="item", scope="context")
template(slot="item", slot-scope="context")
item(
:item="context.item",
:itemContentClass="'shop_' + context.item.key",
@@ -113,13 +113,13 @@
:key="context.item.key",
@click="openEquipDialog(context.item)"
)
template(slot="itemBadge", scope="context")
template(slot="itemBadge", slot-scope="context")
starBadge(
:selected="activeItems[context.item.type] === context.item.key",
:show="!costume || user.preferences.costume",
@click="equipItem(context.item)",
)
template(slot="popoverContent", scope="context")
template(slot="popoverContent", slot-scope="context")
equipmentAttributesPopover(:item="context.item")
equipGearModal(

View File

@@ -43,7 +43,7 @@
:type="group.key",
:noItemsLabel="$t('noGearItemsOfType', { type: $t(group.key) })"
)
template(slot="item", scope="context")
template(slot="item", slot-scope="context")
item(
:item="context.item",
:key="context.item.key",
@@ -57,10 +57,10 @@
@click="onEggClicked($event, context.item)",
)
template(slot="popoverContent", scope="context")
template(slot="popoverContent", slot-scope="context")
h4.popover-content-title {{ context.item.text }}
.popover-content-text(v-if="currentDraggingPotion == null") {{ context.item.notes }}
template(slot="itemBadge", scope="context")
template(slot="itemBadge", slot-scope="context")
countBadge(
:show="true",
:count="context.item.quantity"
@@ -74,7 +74,7 @@
:type="group.key",
:noItemsLabel="$t('noGearItemsOfType', { type: $t(group.key) })"
)
template(slot="item", scope="context")
template(slot="item", slot-scope="context")
item(
:item="context.item",
:key="context.item.key",
@@ -88,10 +88,10 @@
@click="onPotionClicked($event, context.item)"
)
template(slot="popoverContent", scope="context")
template(slot="popoverContent", slot-scope="context")
h4.popover-content-title {{ context.item.text }}
.popover-content-text {{ context.item.notes }}
template(slot="itemBadge", scope="context")
template(slot="itemBadge", slot-scope="context")
countBadge(
:show="true",
:count="context.item.quantity"
@@ -105,7 +105,7 @@
:type="group.key",
:noItemsLabel="$t('noGearItemsOfType', { type: $t(group.key) })"
)
template(slot="item", scope="context")
template(slot="item", slot-scope="context")
item(
:item="context.item",
:key="context.item.key",
@@ -113,7 +113,7 @@
:showPopover="currentDraggingPotion == null",
@click="itemClicked(group.key, context.item)",
)
template(slot="popoverContent", scope="context")
template(slot="popoverContent", slot-scope="context")
div.questPopover(v-if="group.key === 'quests'")
h4.popover-content-title {{ context.item.text }}
questInfo(:quest="context.item")
@@ -121,7 +121,7 @@
div(v-else)
h4.popover-content-title {{ context.item.text }}
.popover-content-text(v-html="context.item.notes")
template(slot="itemBadge", scope="context")
template(slot="itemBadge", slot-scope="context")
countBadge(
:show="true",
:count="context.item.quantity"

View File

@@ -112,7 +112,7 @@
div(:class="'Pet_Egg_'+item.eggKey")
div(v-else)
h4.popover-content-title {{ item.name }}
template(slot="itemBadge", scope="context")
template(slot="itemBadge", slot-scope="context")
starBadge(:selected="item.key === currentPet", :show="item.isOwned()", @click="selectPet(item)")
.btn.btn-flat.btn-show-more(@click="setShowMore(petGroup.key)", v-if='petGroup.key !== "specialPets"')
@@ -144,7 +144,7 @@
)
span(slot="popoverContent")
h4.popover-content-title {{ item.name }}
template(slot="itemBadge", scope="context")
template(slot="itemBadge", slot-scope="context")
starBadge(
:selected="item.key === currentMount",
:show="item.isOwned()",
@@ -187,7 +187,7 @@
:itemWidth=94,
:itemMargin=24,
)
template(slot="item", scope="context")
template(slot="item", slot-scope="context")
foodItem(
:item="context.item",
:itemCount="userItems.food[context.item.key]",

View File

@@ -20,17 +20,17 @@
.is-buffed(v-if="isBuffed")
.svg-icon(v-html="icons.buff")
span.small-text.character-level {{ characterLevel }}
.progress-container
.progress-container(b-tooltip.hover.bottom="$t('health')")
.svg-icon(v-html="icons.health")
.progress
.progress-bar.bg-health(:style="{width: `${percent(member.stats.hp, MAX_HEALTH)}%`}")
span.small-text {{member.stats.hp | statFloor}} / {{MAX_HEALTH}}
.progress-container
.progress-container(b-tooltip.hover.bottom="$t('experience')")
.svg-icon(v-html="icons.experience")
.progress
.progress-bar.bg-experience(:style="{width: `${percent(member.stats.exp, toNextLevel)}%`}")
span.small-text {{member.stats.exp | statFloor}} / {{toNextLevel}}
.progress-container(v-if="hasClass")
.progress-container(v-if="hasClass", b-tooltip.hover.bottom="$t('mana')")
.svg-icon(v-html="icons.mana")
.progress
.progress-bar.bg-mana(:style="{width: `${percent(member.stats.mp, maxMP)}%`}")
@@ -186,6 +186,7 @@ import Profile from './userMenu/profile';
import { toNextLevel } from '../../common/script/statHelpers';
import statsComputed from '../../common/script/libs/statsComputed';
import percent from '../../common/script/libs/percent';
// import bTooltip from 'bootstrap-vue/lib/directives/tooltip';
import buffIcon from 'assets/svg/buff.svg';
import healthIcon from 'assets/svg/health.svg';
@@ -198,6 +199,9 @@ export default {
Profile,
ClassBadge,
},
directives: {
// bTooltip,
},
props: {
member: {
type: Object,

View File

@@ -257,7 +257,7 @@ export default {
this.mp(mana);
},
userLvl (after, before) {
if (after <= before || this.isRunningYesterdailies) return;
if (after <= before || this.$store.state.isRunningYesterdailies) return;
this.showLevelUpNotifications(after);
},
userClassSelect (after) {
@@ -285,7 +285,6 @@ export default {
this.$root.$emit('show::modal', 'quest-invitation');
},
},
mounted () {
Promise.all([
this.$store.dispatch('user:fetch'),
@@ -345,7 +344,7 @@ export default {
this.$root.$emit('playSound', sound);
},
checkNextCron: throttle(function checkNextCron () {
if (!this.isRunningYesterdailies && this.nextCron && Date.now() > this.nextCron) {
if (!this.$store.state.isRunningYesterdailies && this.nextCron && Date.now() > this.nextCron) {
Promise.all([
this.$store.dispatch('user:fetch', {forceLoad: true}),
this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true}),
@@ -367,11 +366,11 @@ export default {
// Setup a listener that executes 10 seconds after the next cron time
this.nextCron = Number(nextCron.format('x'));
this.isRunningYesterdailies = false;
this.$store.state.isRunningYesterdailies = false;
},
async runYesterDailies () {
if (this.isRunningYesterdailies) return;
this.isRunningYesterdailies = true;
if (this.$store.state.isRunningYesterdailies) return;
this.$store.state.isRunningYesterdailies = true;
if (!this.user.needsCron) {
this.handleUserNotifications(this.user.notifications);
@@ -412,7 +411,7 @@ export default {
this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true}),
]);
if (this.levelBeforeYesterdailies < this.user.stats.lvl) {
if (this.levelBeforeYesterdailies > 0 && this.levelBeforeYesterdailies < this.user.stats.lvl) {
this.showLevelUpNotifications(this.user.stats.lvl);
}

View File

@@ -150,11 +150,11 @@
p.benefits(v-markdown='$t("earnGemsMonthly", {cap:45})')
p.benefits(v-markdown='$t("receiveMysticHourglasses", {amount:4})')
button.btn.btn-primary(@click='subscriptionPlan = "basic_12mo"') {{ subscriptionPlan === "basic_12mo" ? $t('selected') : $t('select') }}
.row.text-center
.row.text-center(v-if='subscriptionPlan')
h2.mx-auto.text-payment {{ $t('choosePaymentMethod') }}
.row.text-center
a.mx-auto {{ $t('haveCouponCode') }}
.card-deck
.card-deck(v-if='subscriptionPlan')
.card.text-center.payment-method
.card-body(@click='showStripe({subscription: subscriptionPlan})')
.mx-auto(v-html='icons.creditCard', style='"height: 56px; width: 159px; margin-top: 1em;"')

View File

@@ -94,12 +94,12 @@ export default {
this.user.achievements.streak = clone(this.restoreValues.achievements.streak);
let settings = {
'stats.hp': this.restoreValues.stats.hp,
'stats.exp': this.restoreValues.stats.exp,
'stats.gp': this.restoreValues.stats.gp,
'stats.lvl': this.restoreValues.stats.lvl,
'stats.mp': this.restoreValues.stats.mp,
'achievements.streak': this.restoreValues.achievements.streak,
'stats.hp': Number(this.restoreValues.stats.hp),
'stats.exp': Number(this.restoreValues.stats.exp),
'stats.gp': Number(this.restoreValues.stats.gp),
'stats.lvl': Number(this.restoreValues.stats.lvl),
'stats.mp': Number(this.restoreValues.stats.mp),
'achievements.streak': Number(this.restoreValues.achievements.streak),
};
this.$store.dispatch('user:set', settings);

View File

@@ -53,7 +53,7 @@
:popoverPosition="'top'",
@click="featuredItemSelected(item)"
)
template(slot="itemBadge", scope="ctx")
template(slot="itemBadge", slot-scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
@click.prevent.stop="togglePinned(ctx.item)"
@@ -101,7 +101,7 @@
:type="'gear'",
:noItemsLabel="$t('noGearItemsOfClass')"
)
template(slot="item", scope="ctx")
template(slot="item", slot-scope="ctx")
shopItem(
:key="ctx.item.key",
:item="ctx.item",
@@ -110,7 +110,7 @@
@click="gearSelected(ctx.item)"
)
template(slot="itemBadge", scope="ctx")
template(slot="itemBadge", slot-scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
@click.prevent.stop="togglePinned(ctx.item)"
@@ -151,13 +151,13 @@
strong(v-if='item.key === "gem" && gemsLeft === 0') {{ $t('maxBuyGems') }}
h4.popover-content-title {{ item.text }}
template(slot="itemBadge", scope="ctx")
template(slot="itemBadge", slot-scope="ctx")
countBadge(
v-if="item.showCount != false",
:show="userItems[item.purchaseType][item.key] != 0",
:count="userItems[item.purchaseType][item.key] || 0"
)
.gems-left(v-if='item.key === "gem"')
.badge.badge-pill.badge-purple.gems-left(v-if='item.key === "gem"')
| {{ gemsLeft }}
span.badge.badge-pill.badge-item.badge-svg(
@@ -196,14 +196,14 @@
:itemWidth=94,
:itemMargin=24,
)
template(slot="item", scope="ctx")
template(slot="item", slot-scope="ctx")
item(
:item="ctx.item",
:itemContentClass="getItemClass(selectedDrawerItemType, ctx.item.key)",
popoverPosition="top",
@click="selectedItemToSell = ctx.item"
)
template(slot="itemBadge", scope="ctx")
template(slot="itemBadge", slot-scope="ctx")
countBadge(
:show="true",
:count="userItems[drawerTabs[selectedDrawerTab].contentType][ctx.item.key] || 0"
@@ -218,13 +218,13 @@
:text="selectedItemToSell != null ? getItemName(selectedDrawerItemType, selectedItemToSell) : ''",
@change="resetItemToSell($event)"
)
template(slot="item", scope="ctx")
template(slot="item", slot-scope="ctx")
item.flat(
:item="ctx.item",
:itemContentClass="getItemClass(selectedDrawerItemType, ctx.item.key)",
:showPopover="false"
)
template(slot="itemBadge", scope="ctx")
template(slot="itemBadge", slot-scope="ctx")
countBadge(
:show="true",
:count="userItems[drawerTabs[selectedDrawerTab].contentType][ctx.item.key] || 0"
@@ -356,17 +356,8 @@
}
.market .gems-left {
position: absolute;
right: -.5em;
top: -.5em;
color: $white;
background: $purple-200;
padding: .15em;
text-align: center;
width: 24px;
height: 24px;
border-radius: 50%;
box-shadow: 0 1px 1px 0 rgba($black, 0.12);
}
</style>

View File

@@ -54,12 +54,12 @@
:popoverPosition="'top'",
@click="selectItem(item)"
)
template(slot="popoverContent", scope="ctx")
template(slot="popoverContent", slot-scope="ctx")
div.questPopover
h4.popover-content-title {{ item.text }}
questInfo(:quest="item")
template(slot="itemBadge", scope="ctx")
template(slot="itemBadge", slot-scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
@click.prevent.stop="togglePinned(ctx.item)"
@@ -94,7 +94,7 @@
:itemMargin=24,
:type="'pet_quests'",
)
template(slot="item", scope="ctx")
template(slot="item", slot-scope="ctx")
shopItem(
:key="ctx.item.key",
:item="ctx.item",
@@ -104,12 +104,12 @@
:emptyItem="false",
@click="selectItem(ctx.item)"
)
span(slot="popoverContent", scope="ctx")
span(slot="popoverContent", slot-scope="ctx")
div.questPopover
h4.popover-content-title {{ ctx.item.text }}
questInfo(:quest="ctx.item")
template(slot="itemBadge", scope="ctx")
template(slot="itemBadge", slot-scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
@click.prevent.stop="togglePinned(ctx.item)"
@@ -145,7 +145,7 @@
.popover-content-text(v-if='item.lvl > user.stats.lvl') {{ `${$t('mustLvlQuest', {level: item.lvl})}` }}
questInfo(v-if='!item.locked', :quest="item")
template(slot="itemBadge", scope="ctx")
template(slot="itemBadge", slot-scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
@click.prevent.stop="togglePinned(ctx.item)"
@@ -172,7 +172,7 @@
h4.popover-content-title {{ item.text }}
questInfo(:quest="item")
template(slot="itemBadge", scope="ctx")
template(slot="itemBadge", slot-scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
@click.prevent.stop="togglePinned(ctx.item)"
@@ -190,7 +190,7 @@
:withPin="true",
@change="resetItemToBuy($event)",
)
template(slot="item", scope="ctx")
template(slot="item", slot-scope="ctx")
item.flat(
:item="ctx.item",
:itemContentClass="ctx.item.class",

View File

@@ -57,7 +57,7 @@
@click="itemSelected(item)"
)
h1.mb-0.page-header(v-once) {{ $t('seasonalShop') }}
h1.mb-0.page-header(v-once, v-if='seasonal.opened') {{ $t('seasonalShop') }}
.clearfix(v-if="seasonal.opened")
h2.float-left
@@ -97,7 +97,7 @@
:showEventBadge="false",
@click="itemSelected(item)"
)
template(slot="itemBadge", scope="ctx")
template(slot="itemBadge", slot-scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
@click.prevent.stop="togglePinned(ctx.item)"

View File

@@ -18,8 +18,7 @@ div
slot(name="itemImage", :item="item")
div.price
span.svg-icon.inline.icon-16(v-html="icons[currencyClass]")
span.svg-icon.inline.icon-16(v-html="icons[currencyClass]", v-once)
span.price-label(:class="currencyClass", v-once) {{ getPrice() }}
b-popover(
:target="itemId",

View File

@@ -63,7 +63,7 @@
:itemMargin=24,
:type="category.identifier",
)
template(slot="item", scope="ctx")
template(slot="item", slot-scope="ctx")
shopItem(
:key="ctx.item.key",
:item="ctx.item",
@@ -72,11 +72,11 @@
:emptyItem="false",
@click="selectItemToBuy(ctx.item)"
)
span(slot="popoverContent", scope="ctx")
span(slot="popoverContent", slot-scope="ctx")
div
h4.popover-content-title {{ ctx.item.text }}
template(slot="itemBadge", scope="ctx")
template(slot="itemBadge", slot-scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
v-if="ctx.item.pinType !== 'IGNORE'",
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",

Some files were not shown because too many files have changed in this diff Show More