mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-14 13:17:24 +01:00
lint common
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -4476,9 +4476,9 @@
|
||||
}
|
||||
},
|
||||
"eslint-config-habitrpg": {
|
||||
"version": "6.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-habitrpg/-/eslint-config-habitrpg-6.0.7.tgz",
|
||||
"integrity": "sha512-hda2IKsfwE/94CjLtPI3Cgsq0capQCFKCiqPPNM7foeO/E6cZpXCSvMHdGt5TXy66DQxF5h+kz4xcheHdcaRcA==",
|
||||
"version": "6.0.8",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-habitrpg/-/eslint-config-habitrpg-6.0.8.tgz",
|
||||
"integrity": "sha512-jQ62H3+Gkie4CK8uFfV37SX+yNs6yu+SHP27hIYIlnZ21HCaQnabEQfhrYzbpQUn9fNd2y1gUNqGOIG8ph84vg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eslint": "^6.5.1",
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"npm": "^6"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js --fix ./website/server",
|
||||
"lint": "eslint --ext .js --fix ./website/common",
|
||||
"test": "npm run lint && gulp test && gulp apidoc",
|
||||
"test:build": "gulp test:prepare:build",
|
||||
"test:api-v3": "gulp test:api-v3",
|
||||
@@ -103,7 +103,7 @@
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chalk": "^2.4.1",
|
||||
"eslint": "^6.5.1",
|
||||
"eslint-config-habitrpg": "^6.0.7",
|
||||
"eslint-config-habitrpg": "^6.0.8",
|
||||
"eslint-plugin-mocha": "^5.0.0",
|
||||
"expect.js": "^0.3.1",
|
||||
"istanbul": "^1.1.0-alpha.1",
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/* global env:true, rm:true, mkdir:true, cp:true */
|
||||
|
||||
// https://github.com/shelljs/shelljs
|
||||
require('shelljs/global');
|
||||
|
||||
const path = require('path');
|
||||
const config = require('./config');
|
||||
const ora = require('ora');
|
||||
const webpack = require('webpack');
|
||||
const webpackConfig = require('./webpack.prod.conf');
|
||||
|
||||
module.exports = function webpackProductionBuild (callback) {
|
||||
env.NODE_ENV = 'production';
|
||||
|
||||
console.log( // eslint-disable-line no-console
|
||||
' Tip:\n' +
|
||||
' Built files are meant to be served over an HTTP server.\n' +
|
||||
' Opening index.html over file:// won\'t work.\n'
|
||||
);
|
||||
|
||||
const spinner = ora('building for production...');
|
||||
spinner.start();
|
||||
|
||||
const assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory);
|
||||
rm('-rf', assetsPath);
|
||||
mkdir('-p', assetsPath);
|
||||
cp('-R', config.build.staticAssetsDirectory, assetsPath);
|
||||
|
||||
webpack(webpackConfig, (err, stats) => {
|
||||
spinner.stop();
|
||||
|
||||
const output = `${stats.toString({
|
||||
colors: true,
|
||||
modules: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
chunkModules: false,
|
||||
})}\n`;
|
||||
|
||||
if (callback) {
|
||||
return err ? callback(err) : callback(null, output);
|
||||
} else {
|
||||
if (err) throw err;
|
||||
process.stdout.write(output);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
const prodEnv = require('./prod.env');
|
||||
|
||||
module.exports = merge(prodEnv, {
|
||||
NODE_ENV: '"development"',
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
// see http://vuejs-templates.github.io/webpack for documentation.
|
||||
const path = require('path');
|
||||
const staticAssetsDirectory = './website/static/.'; // The folder where static files (not processed) live
|
||||
const prodEnv = require('./prod.env');
|
||||
const devEnv = require('./dev.env');
|
||||
const nconf = require('nconf');
|
||||
const setupNconf = require('../../website/server/libs/setupNconf');
|
||||
|
||||
let configFile = path.join(path.resolve(__dirname, '../../config.json'));
|
||||
|
||||
setupNconf(configFile);
|
||||
|
||||
const DEV_BASE_URL = nconf.get('BASE_URL');
|
||||
|
||||
module.exports = {
|
||||
build: {
|
||||
env: prodEnv,
|
||||
index: path.resolve(__dirname, '../../dist-client/index.html'),
|
||||
assetsRoot: path.resolve(__dirname, '../../dist-client'),
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '/',
|
||||
staticAssetsDirectory,
|
||||
productionSourceMap: true,
|
||||
// Gzip off by default as many popular static hosts such as
|
||||
// Surge or Netlify already gzip all static assets for you.
|
||||
// Before setting to `true`, make sure to:
|
||||
// npm install --save-dev compression-webpack-plugin
|
||||
productionGzip: false,
|
||||
productionGzipExtensions: ['js', 'css'],
|
||||
// Run the build command with an extra argument to
|
||||
// View the bundle analyzer report after build finishes:
|
||||
// `npm run client:build --report`
|
||||
// Set to `true` or `false` to always turn it on or off
|
||||
bundleAnalyzerReport: process.env.npm_config_report, // eslint-disable-line no-process-env
|
||||
},
|
||||
dev: {
|
||||
env: devEnv,
|
||||
port: 8080,
|
||||
autoOpenBrowser: true,
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '/',
|
||||
staticAssetsDirectory,
|
||||
proxyTable: {
|
||||
// proxy all requests to the server at IP:PORT as specified in the top-level config
|
||||
'/api/v3': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/api/v4': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/stripe': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/amazon': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/paypal': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/logout-server': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/export': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
// CSS Sourcemaps off by default because relative paths are "buggy"
|
||||
// with this option, according to the CSS-Loader README
|
||||
// (https://github.com/webpack/css-loader#sourcemaps)
|
||||
// In our experience, they generally work as expected,
|
||||
// just be aware of this issue when enabling this option.
|
||||
cssSourceMap: false,
|
||||
},
|
||||
};
|
||||
@@ -1,45 +0,0 @@
|
||||
const nconf = require('nconf');
|
||||
const { join, resolve } = require('path');
|
||||
const setupNconf = require('../../website/server/libs/setupNconf');
|
||||
|
||||
const PATH_TO_CONFIG = join(resolve(__dirname, '../../config.json'));
|
||||
let configFile = PATH_TO_CONFIG;
|
||||
|
||||
setupNconf(configFile);
|
||||
|
||||
// @TODO: Check if we can import from client. Items like admin emails can be imported
|
||||
// and that should be prefered
|
||||
|
||||
// To avoid stringifying more data then we need,
|
||||
// items from `env` used on the client will have to be specified in this array
|
||||
// @TODO: Do we need? const CLIENT_VARS = ['language', 'isStaticPage', 'availableLanguages', 'translations',
|
||||
// 'FACEBOOK_KEY', 'GOOGLE_CLIENT_ID', 'NODE_ENV', 'BASE_URL', 'GA_ID',
|
||||
// 'AMAZON_PAYMENTS', 'STRIPE_PUB_KEY', 'AMPLITUDE_KEY',
|
||||
// 'worldDmg', 'mods', 'IS_MOBILE'];
|
||||
|
||||
const AMAZON_SELLER_ID = nconf.get('AMAZON_PAYMENTS_SELLER_ID');
|
||||
const AMAZON_CLIENT_ID = nconf.get('AMAZON_PAYMENTS_CLIENT_ID');
|
||||
const AMAZON_MODE = nconf.get('AMAZON_PAYMENTS_MODE');
|
||||
|
||||
let env = {
|
||||
NODE_ENV: '"production"',
|
||||
// clientVars: CLIENT_VARS,
|
||||
AMAZON_PAYMENTS: {
|
||||
SELLER_ID: `"${AMAZON_SELLER_ID}"`,
|
||||
CLIENT_ID: `"${AMAZON_CLIENT_ID}"`,
|
||||
MODE: `"${AMAZON_MODE}"`,
|
||||
},
|
||||
EMAILS: {
|
||||
COMMUNITY_MANAGER_EMAIL: `"${nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL')}"`,
|
||||
TECH_ASSISTANCE_EMAIL: `"${nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL')}"`,
|
||||
PRESS_ENQUIRY_EMAIL: `"${nconf.get('EMAILS_PRESS_ENQUIRY_EMAIL')}"`,
|
||||
},
|
||||
};
|
||||
|
||||
'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY GOOGLE_CLIENT_ID AMPLITUDE_KEY LOGGLY_CLIENT_TOKEN'
|
||||
.split(' ')
|
||||
.forEach(key => {
|
||||
env[key] = `"${nconf.get(key)}"`;
|
||||
});
|
||||
|
||||
module.exports = env;
|
||||
@@ -1,6 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
const devEnv = require('./dev.env');
|
||||
|
||||
module.exports = merge(devEnv, {
|
||||
NODE_ENV: '"test"',
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
/* global window:true */
|
||||
|
||||
require('eventsource-polyfill');
|
||||
const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true&overlay=false');
|
||||
|
||||
hotClient.subscribe(event => {
|
||||
if (event.action === 'reload') {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
@@ -1,72 +0,0 @@
|
||||
/* eslint-disable no-process-env, no-console */
|
||||
|
||||
const path = require('path');
|
||||
const express = require('express');
|
||||
const webpack = require('webpack');
|
||||
const config = require('./config');
|
||||
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV);
|
||||
}
|
||||
|
||||
const proxyMiddleware = require('http-proxy-middleware');
|
||||
const webpackConfig = process.env.NODE_ENV === 'test' ?
|
||||
require('./webpack.prod.conf') :
|
||||
require('./webpack.dev.conf');
|
||||
|
||||
// default port where dev server listens for incoming traffic
|
||||
const port = process.env.PORT || config.dev.port;
|
||||
// Define HTTP proxies to your custom API backend
|
||||
// https://github.com/chimurai/http-proxy-middleware
|
||||
const proxyTable = config.dev.proxyTable;
|
||||
|
||||
const app = express();
|
||||
const compiler = webpack(webpackConfig);
|
||||
|
||||
const devMiddleware = require('webpack-dev-middleware')(compiler, {
|
||||
publicPath: webpackConfig.output.publicPath,
|
||||
stats: {
|
||||
colors: true,
|
||||
chunks: false,
|
||||
},
|
||||
});
|
||||
|
||||
const hotMiddleware = require('webpack-hot-middleware')(compiler);
|
||||
// force page reload when html-webpack-plugin template changes
|
||||
compiler.plugin('compilation', (compilation) => {
|
||||
compilation.plugin('html-webpack-plugin-after-emit', (data, cb) => {
|
||||
hotMiddleware.publish({ action: 'reload' });
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
// proxy api requests
|
||||
Object.keys(proxyTable).forEach((context) => {
|
||||
let options = proxyTable[context];
|
||||
if (typeof options === 'string') {
|
||||
options = { target: options };
|
||||
}
|
||||
app.use(proxyMiddleware(options.filter || context, options));
|
||||
});
|
||||
|
||||
// handle fallback for HTML5 history API
|
||||
app.use(require('connect-history-api-fallback')());
|
||||
|
||||
// serve webpack bundle output
|
||||
app.use(devMiddleware);
|
||||
|
||||
// enable hot-reload and state-preserving
|
||||
// compilation error display
|
||||
app.use(hotMiddleware);
|
||||
|
||||
// serve pure static assets
|
||||
const staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory);
|
||||
app.use(staticPath, express.static(config.dev.staticAssetsDirectory));
|
||||
|
||||
module.exports = app.listen(port, (err) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
console.log(`Listening at http://localhost:${port}\n`);
|
||||
});
|
||||
@@ -1,66 +0,0 @@
|
||||
/* eslint-disable no-process-env, no-console */
|
||||
|
||||
const path = require('path');
|
||||
const config = require('./config');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
|
||||
exports.assetsPath = (_path) => {
|
||||
const assetsSubDirectory = process.env.NODE_ENV === 'production' ?
|
||||
config.build.assetsSubDirectory :
|
||||
config.dev.assetsSubDirectory;
|
||||
return path.posix.join(assetsSubDirectory, _path);
|
||||
};
|
||||
|
||||
exports.cssLoaders = function cssLoaders (options) {
|
||||
options = options || {};
|
||||
// generate loader string to be used with extract text plugin
|
||||
function generateLoaders (loaders) {
|
||||
let sourceLoader = loaders.map((loader) => {
|
||||
let extraParamChar;
|
||||
if (/\?/.test(loader)) {
|
||||
loader = loader.replace(/\?/, '-loader?');
|
||||
extraParamChar = '&';
|
||||
} else {
|
||||
loader = `${loader}-loader`;
|
||||
extraParamChar = '?';
|
||||
}
|
||||
return loader + (options.sourceMap ? `${extraParamChar}sourceMap` : '');
|
||||
}).join('!');
|
||||
|
||||
// Extract CSS when that option is specified
|
||||
// (which is the case during production build)
|
||||
if (options.extract) {
|
||||
return ExtractTextPlugin.extract({
|
||||
use: sourceLoader,
|
||||
fallback: 'vue-style-loader',
|
||||
});
|
||||
} else {
|
||||
return ['vue-style-loader', sourceLoader].join('!');
|
||||
}
|
||||
}
|
||||
|
||||
// http://vuejs.github.io/vue-loader/en/configurations/extract-css.html
|
||||
return {
|
||||
css: generateLoaders(['css']),
|
||||
postcss: generateLoaders(['css']),
|
||||
less: generateLoaders(['css', 'less']),
|
||||
sass: generateLoaders(['css', 'sass?indentedSyntax']),
|
||||
scss: generateLoaders(['css', 'sass']),
|
||||
stylus: generateLoaders(['css', 'stylus']),
|
||||
styl: generateLoaders(['css', 'stylus']),
|
||||
};
|
||||
};
|
||||
|
||||
// Generate loaders for standalone style files (outside of .vue)
|
||||
exports.styleLoaders = (options) => {
|
||||
const output = [];
|
||||
const loaders = exports.cssLoaders(options);
|
||||
for (let extension in loaders) {
|
||||
const loader = loaders[extension];
|
||||
output.push({
|
||||
test: new RegExp(`\\.${extension}$`),
|
||||
loader,
|
||||
});
|
||||
}
|
||||
return output;
|
||||
};
|
||||
@@ -1,154 +0,0 @@
|
||||
/* eslint-disable no-process-env, no-console */
|
||||
|
||||
const path = require('path');
|
||||
const config = require('./config');
|
||||
const utils = require('./utils');
|
||||
const webpack = require('webpack');
|
||||
const projectRoot = path.resolve(__dirname, '../');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const postcssEasyImport = require('postcss-easy-import');
|
||||
const IS_PROD = process.env.NODE_ENV === 'production';
|
||||
|
||||
const baseConfig = {
|
||||
entry: {
|
||||
app: ['babel-polyfill', './website/client/main.js'],
|
||||
},
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
publicPath: IS_PROD ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
|
||||
filename: '[name].js',
|
||||
devtoolModuleFilenameTemplate (info) {
|
||||
// Fix source maps, code from
|
||||
// https://github.com/Darkside73/bbsmile.com.ua/commit/3596d3c42ef91b69d8380359c3e8908edc08acdb
|
||||
let filename = info.resourcePath;
|
||||
if (info.resource.match(/\.vue$/) && !info.allLoaders.match(/type=script/)) {
|
||||
filename = 'generated';
|
||||
}
|
||||
|
||||
return filename;
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['*', '.js', '.vue', '.json'],
|
||||
modules: [
|
||||
path.join(projectRoot, 'website'),
|
||||
path.join(projectRoot, 'test/client/unit'),
|
||||
path.join(projectRoot, 'node_modules'),
|
||||
],
|
||||
alias: {
|
||||
website: path.resolve(projectRoot, 'website'),
|
||||
common: path.resolve(projectRoot, 'website/common'),
|
||||
client: path.resolve(projectRoot, 'website/client'),
|
||||
assets: path.resolve(projectRoot, 'website/client/assets'),
|
||||
components: path.resolve(projectRoot, 'website/client/components'),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ContextReplacementPlugin(/moment[\\\/]locale$/, /^\.\/(NOT_EXISTING)$/),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
options: {
|
||||
loaders: utils.cssLoaders({
|
||||
sourceMap: IS_PROD ?
|
||||
config.build.productionSourceMap :
|
||||
config.dev.cssSourceMap,
|
||||
extract: IS_PROD,
|
||||
}),
|
||||
postcss: [
|
||||
autoprefixer({
|
||||
overrideBrowserslist: ['last 2 versions'],
|
||||
}),
|
||||
postcssEasyImport(),
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
include: [
|
||||
path.join(projectRoot, 'test'),
|
||||
path.join(projectRoot, 'website'),
|
||||
path.join(projectRoot, 'node_modules', 'bootstrap-vue'),
|
||||
],
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
query: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('images/[name].[hash:7].[ext]'),
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
query: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('fonts/[name].[hash:7].[ext]'),
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
use: [
|
||||
{ loader: 'svg-inline-loader' },
|
||||
{
|
||||
loader: 'svgo-loader',
|
||||
options: {
|
||||
plugins: [
|
||||
{removeViewBox: false},
|
||||
{convertPathData: {noSpaceAfterFlags: false}},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
exclude: [path.resolve(projectRoot, 'website/client/assets/svg/for-css')],
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'svg-url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('svg/[name].[hash:7].[ext]'),
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'svgo-loader',
|
||||
options: {
|
||||
plugins: [
|
||||
{removeViewBox: false},
|
||||
{convertPathData: {noSpaceAfterFlags: false}},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
include: [path.resolve(projectRoot, 'website/client/assets/svg/for-css')],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
if (!IS_PROD) {
|
||||
const eslintFriendlyFormatter = require('eslint-friendly-formatter'); // eslint-disable-line global-require
|
||||
|
||||
baseConfig.module.rules.unshift({
|
||||
test: /\.(js|vue)$/,
|
||||
loader: 'eslint-loader',
|
||||
enforce: 'pre',
|
||||
include: projectRoot,
|
||||
options: {
|
||||
formatter: eslintFriendlyFormatter,
|
||||
emitWarning: true,
|
||||
},
|
||||
exclude: /node_modules/,
|
||||
});
|
||||
}
|
||||
module.exports = baseConfig;
|
||||
@@ -1,32 +0,0 @@
|
||||
const config = require('./config');
|
||||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge');
|
||||
const utils = require('./utils');
|
||||
const baseWebpackConfig = require('./webpack.base.conf');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
// add hot-reload related code to entry chunks
|
||||
Object.keys(baseWebpackConfig.entry).forEach((name) => {
|
||||
baseWebpackConfig.entry[name] = baseWebpackConfig.entry[name].concat('./webpack/dev-client');
|
||||
});
|
||||
|
||||
module.exports = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }),
|
||||
},
|
||||
// cheap-module-eval-source-map is faster for development
|
||||
devtool: '#cheap-module-eval-source-map',
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': config.dev.env,
|
||||
}),
|
||||
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
// https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: './website/client/index.html',
|
||||
inject: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -1,101 +0,0 @@
|
||||
/* eslint-disable no-process-env, no-console */
|
||||
|
||||
const path = require('path');
|
||||
const config = require('./config');
|
||||
const utils = require('./utils');
|
||||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge');
|
||||
const baseWebpackConfig = require('./webpack.base.conf');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const env = process.env.NODE_ENV === 'test' ?
|
||||
require('./config/test.env') :
|
||||
config.build.env;
|
||||
|
||||
const webpackConfig = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true }),
|
||||
},
|
||||
devtool: config.build.productionSourceMap ? '#source-map' : false,
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: utils.assetsPath('js/[name].[chunkhash].js'),
|
||||
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js'),
|
||||
},
|
||||
plugins: [
|
||||
// http://vuejs.github.io/vue-loader/workflow/production.html
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': env,
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false,
|
||||
},
|
||||
sourceMap: true,
|
||||
}),
|
||||
// extract css into its own file
|
||||
new ExtractTextPlugin({
|
||||
filename: utils.assetsPath('css/[name].[contenthash].css'),
|
||||
}),
|
||||
// generate dist index.html with correct asset hash for caching.
|
||||
// you can customize output by editing /index.html
|
||||
// see https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: process.env.NODE_ENV === 'test' ?
|
||||
'index.html' :
|
||||
config.build.index,
|
||||
template: './website/client/index.html',
|
||||
inject: true,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeAttributeQuotes: true,
|
||||
// more options:
|
||||
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||
},
|
||||
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||
chunksSortMode: 'dependency',
|
||||
}),
|
||||
// split vendor js into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
minChunks (scriptModule) {
|
||||
// any required modules inside node_modules are extracted to vendor
|
||||
return (
|
||||
scriptModule.resource &&
|
||||
/\.js$/.test(scriptModule.resource) &&
|
||||
scriptModule.resource.indexOf(
|
||||
path.join(__dirname, '../node_modules')
|
||||
) === 0
|
||||
);
|
||||
},
|
||||
}),
|
||||
// extract webpack runtime and module manifest to its own file in order to
|
||||
// prevent vendor hash from being updated whenever app bundle is updated
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'manifest',
|
||||
chunks: ['vendor'],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
if (config.build.productionGzip) {
|
||||
const CompressionWebpackPlugin = require('compression-webpack-plugin'); // eslint-disable-line global-require
|
||||
|
||||
webpackConfig.plugins.push(
|
||||
new CompressionWebpackPlugin({
|
||||
asset: '[path].gz[query]',
|
||||
algorithm: 'gzip',
|
||||
test: new RegExp(`\\.(${config.build.productionGzipExtensions.join('|')})$`),
|
||||
threshold: 10240,
|
||||
minRatio: 0.8,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (config.build.bundleAnalyzerReport) {
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // eslint-disable-line global-require
|
||||
webpackConfig.plugins.push(new BundleAnalyzerPlugin());
|
||||
}
|
||||
|
||||
module.exports = webpackConfig;
|
||||
@@ -1,24 +0,0 @@
|
||||
// This is the webpack config used for unit tests.
|
||||
const merge = require('webpack-merge');
|
||||
const baseConfig = require('./webpack.base.conf');
|
||||
const utils = require('./utils');
|
||||
const webpack = require('webpack');
|
||||
const testEnv = require('./config/test.env');
|
||||
|
||||
const webpackConfig = merge(baseConfig, {
|
||||
// use inline sourcemap for karma-sourcemap-loader
|
||||
module: {
|
||||
rules: utils.styleLoaders(),
|
||||
},
|
||||
devtool: '#inline-source-map',
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': testEnv,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
// no need for app entry during tests
|
||||
delete webpackConfig.entry;
|
||||
|
||||
module.exports = webpackConfig;
|
||||
@@ -1,27 +1,26 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
node: true,
|
||||
},
|
||||
'extends': [
|
||||
'plugin:vue/essential',
|
||||
'eslint:recommended'
|
||||
extends: [
|
||||
'habitrpg/lib/vue',
|
||||
],
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
},
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint'
|
||||
parser: 'babel-eslint',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'**/__tests__/*.{j,t}s?(x)'
|
||||
'**/__tests__/*.{j,t}s?(x)',
|
||||
],
|
||||
env: {
|
||||
mocha: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
mocha: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
'@vue/cli-plugin-babel/preset',
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -201,8 +201,7 @@ import bannedAccountModal from '@/components/bannedAccountModal';
|
||||
const COMMUNITY_MANAGER_EMAIL = process.env.EMAILS_COMMUNITY_MANAGER_EMAIL; // eslint-disable-line
|
||||
|
||||
export default {
|
||||
mixins: [notifications, spellsMixin],
|
||||
name: 'app',
|
||||
name: 'App',
|
||||
components: {
|
||||
AppMenu,
|
||||
AppHeader,
|
||||
@@ -217,6 +216,7 @@ export default {
|
||||
subCancelModalConfirm,
|
||||
subCanceledModal,
|
||||
},
|
||||
mixins: [notifications, spellsMixin],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
@@ -235,9 +235,9 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapState(['isUserLoggedIn', 'browserTimezoneOffset', 'isUserLoaded']),
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
isStaticPage () {
|
||||
return this.$route.meta.requiresLogin === false ? true : false;
|
||||
return this.$route.meta.requiresLogin === false;
|
||||
},
|
||||
castingSpell () {
|
||||
return this.$store.state.spellOptions.castingSpell;
|
||||
@@ -256,14 +256,14 @@ export default {
|
||||
},
|
||||
},
|
||||
created () {
|
||||
this.$root.$on('playSound', (sound) => {
|
||||
let theme = this.user.preferences.sound;
|
||||
this.$root.$on('playSound', sound => {
|
||||
const theme = this.user.preferences.sound;
|
||||
|
||||
if (!theme || theme === 'off') {
|
||||
return;
|
||||
}
|
||||
|
||||
let file = `/static/audio/${theme}/${sound}`;
|
||||
const file = `/static/audio/${theme}/${sound}`;
|
||||
|
||||
if (this.audioSuffix === null) {
|
||||
this.audioSource = document.createElement('source');
|
||||
@@ -284,12 +284,12 @@ export default {
|
||||
});
|
||||
|
||||
// @TODO: I'm not sure these should be at the app level. Can we move these back into shop/inventory or maybe they need a lateral move?
|
||||
this.$root.$on('buyModal::showItem', (item) => {
|
||||
this.$root.$on('buyModal::showItem', item => {
|
||||
this.selectedItemToBuy = item;
|
||||
this.$root.$emit('bv::show::modal', 'buy-modal');
|
||||
});
|
||||
|
||||
this.$root.$on('selectMembersModal::showItem', (item) => {
|
||||
this.$root.$on('selectMembersModal::showItem', item => {
|
||||
this.selectedSpellToBuy = item;
|
||||
this.$root.$emit('bv::show::modal', 'select-member-modal');
|
||||
});
|
||||
@@ -301,18 +301,18 @@ export default {
|
||||
});
|
||||
|
||||
// Set up Error interceptors
|
||||
axios.interceptors.response.use((response) => {
|
||||
axios.interceptors.response.use(response => {
|
||||
if (this.user && response.data && response.data.notifications) {
|
||||
this.$set(this.user, 'notifications', response.data.notifications);
|
||||
}
|
||||
return response;
|
||||
}, (error) => {
|
||||
}, error => {
|
||||
if (error.response.status >= 400) {
|
||||
this.checkForBannedUser(error);
|
||||
|
||||
// Don't show errors from getting user details. These users have delete their account,
|
||||
// but their chat message still exists.
|
||||
let configExists = Boolean(error.response) && Boolean(error.response.config);
|
||||
const configExists = Boolean(error.response) && Boolean(error.response.config);
|
||||
if (configExists && error.response.config.method === 'get' && error.response.config.url.indexOf('/api/v4/members/') !== -1) {
|
||||
// @TODO: We resolve the promise because we need our caching to cache this user as tried
|
||||
// Chat paging should help this, but maybe we can also find another solution..
|
||||
@@ -333,11 +333,11 @@ export default {
|
||||
let snackbarTimeout = false;
|
||||
if (error.response.status === 502) snackbarTimeout = true;
|
||||
|
||||
let errorsToShow = [];
|
||||
const errorsToShow = [];
|
||||
// show only the first error for each param
|
||||
let paramErrorsFound = {};
|
||||
const paramErrorsFound = {};
|
||||
if (errorData.errors) {
|
||||
for (let e of errorData.errors) {
|
||||
for (const e of errorData.errors) {
|
||||
if (!paramErrorsFound[e.param]) {
|
||||
errorsToShow.push(e.message);
|
||||
paramErrorsFound[e.param] = true;
|
||||
@@ -362,11 +362,11 @@ export default {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
axios.interceptors.response.use((response) => {
|
||||
axios.interceptors.response.use(response => {
|
||||
// Verify that the user was not updated from another browser/app/client
|
||||
// If it was, sync
|
||||
const url = response.config.url;
|
||||
const method = response.config.method;
|
||||
const { url } = response.config;
|
||||
const { method } = response.config;
|
||||
|
||||
const isApiCall = url.indexOf('api/v4') !== -1;
|
||||
const userV = response.data && response.data.userV;
|
||||
@@ -380,14 +380,14 @@ export default {
|
||||
const isUserSync = url.indexOf('/api/v4/user') === 0 && method === 'get';
|
||||
const isTasksSync = url.indexOf('/api/v4/tasks/user') === 0 && method === 'get';
|
||||
// exclude chat seen requests because with real time chat they would be too many
|
||||
const isChatSeen = url.indexOf('/chat/seen') !== -1 && method === 'post';
|
||||
const isChatSeen = url.indexOf('/chat/seen') !== -1 && method === 'post';
|
||||
// exclude POST /api/v4/cron because the user is synced automatically after cron runs
|
||||
|
||||
// Something has changed on the user object that was not tracked here, sync the user
|
||||
if (userV - oldUserV > 1 && !isCron && !isChatSeen && !isUserSync && !isTasksSync) {
|
||||
Promise.all([
|
||||
this.$store.dispatch('user:fetch', {forceLoad: true}),
|
||||
this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true}),
|
||||
this.$store.dispatch('user:fetch', { forceLoad: true }),
|
||||
this.$store.dispatch('tasks:fetchUserTasks', { forceLoad: true }),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -407,7 +407,7 @@ export default {
|
||||
});
|
||||
|
||||
// Setup listener for title
|
||||
this.$store.watch(state => state.title, (title) => {
|
||||
this.$store.watch(state => state.title, title => {
|
||||
document.title = title;
|
||||
});
|
||||
this.$nextTick(() => {
|
||||
@@ -446,7 +446,7 @@ export default {
|
||||
// Load external scripts after the app has been rendered
|
||||
setupPayments();
|
||||
});
|
||||
}).catch((err) => {
|
||||
}).catch(err => {
|
||||
console.error('Impossible to fetch user. Clean up localStorage and refresh.', err); // eslint-disable-line no-console
|
||||
});
|
||||
} else {
|
||||
@@ -491,17 +491,17 @@ export default {
|
||||
// Manage modals
|
||||
this.$root.$on('bv::show::modal', (modalId, data = {}) => {
|
||||
if (data.fromRoot) return;
|
||||
const modalStack = this.$store.state.modalStack;
|
||||
const { modalStack } = this.$store.state;
|
||||
|
||||
this.trackGemPurchase(modalId, data);
|
||||
|
||||
// Add new modal to the stack
|
||||
const prev = modalStack[modalStack.length - 1];
|
||||
const prevId = prev ? prev.modalId : undefined;
|
||||
modalStack.push({modalId, prev: prevId});
|
||||
modalStack.push({ modalId, prev: prevId });
|
||||
});
|
||||
|
||||
this.$root.$on('bv::modal::hidden', (bvEvent) => {
|
||||
this.$root.$on('bv::modal::hidden', bvEvent => {
|
||||
let modalId = bvEvent.target && bvEvent.target.id;
|
||||
|
||||
// sometimes the target isn't passed to the hidden event, fallback is the vueTarget
|
||||
@@ -513,7 +513,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const modalStack = this.$store.state.modalStack;
|
||||
const { modalStack } = this.$store.state;
|
||||
|
||||
const modalOnTop = modalStack[modalStack.length - 1];
|
||||
|
||||
@@ -529,7 +529,7 @@ export default {
|
||||
// Get previous modal
|
||||
const modalBefore = modalOnTop ? modalOnTop.prev : undefined;
|
||||
|
||||
if (modalBefore) this.$root.$emit('bv::show::modal', modalBefore, {fromRoot: true});
|
||||
if (modalBefore) this.$root.$emit('bv::show::modal', modalBefore, { fromRoot: true });
|
||||
});
|
||||
},
|
||||
validStack (modalStack) {
|
||||
@@ -537,7 +537,7 @@ export default {
|
||||
const modalCount = {};
|
||||
const prevAndCurrent = 2;
|
||||
|
||||
for (let index in modalStack) {
|
||||
for (const index in modalStack) {
|
||||
const current = modalStack[index];
|
||||
|
||||
if (!modalCount[current.modalId]) modalCount[current.modalId] = 0;
|
||||
@@ -574,11 +574,9 @@ export default {
|
||||
this.selectedItemToBuy = item;
|
||||
},
|
||||
genericPurchase (item) {
|
||||
if (!item)
|
||||
return false;
|
||||
if (!item) return false;
|
||||
|
||||
if (['card', 'debuffPotion'].includes(item.purchaseType))
|
||||
return false;
|
||||
if (['card', 'debuffPotion'].includes(item.purchaseType)) return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
@@ -609,7 +607,7 @@ export default {
|
||||
this.selectedSpellToBuy = null;
|
||||
|
||||
if (this.user.party._id) {
|
||||
this.$store.dispatch('party:getMembers', {forceLoad: true});
|
||||
this.$store.dispatch('party:getMembers', { forceLoad: true });
|
||||
}
|
||||
|
||||
this.$root.$emit('bv::hide::modal', 'select-member-modal');
|
||||
|
||||
@@ -16,10 +16,9 @@ export default {
|
||||
...mapState(['isUserLoggedIn']),
|
||||
contactUsLink () {
|
||||
if (this.isUserLoggedIn) {
|
||||
return {name: 'guild', params: {groupId: 'a29da26b-37de-4a71-b0c6-48e72a900dac'} };
|
||||
} else {
|
||||
return {name: 'contact'};
|
||||
return { name: 'guild', params: { groupId: 'a29da26b-37de-4a71-b0c6-48e72a900dac' } };
|
||||
}
|
||||
return { name: 'contact' };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
|
||||
<script>
|
||||
// @TODO:
|
||||
let BASE_URL = 'https://habitica.com';
|
||||
import twitter from '@/assets/svg/twitter.svg';
|
||||
import facebook from '@/assets/svg/facebook.svg';
|
||||
|
||||
const BASE_URL = 'https://habitica.com';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
let tweet = this.$t('achievementShare');
|
||||
const tweet = this.$t('achievementShare');
|
||||
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
|
||||
@@ -136,6 +136,9 @@ export default {
|
||||
classes: 'content.classes',
|
||||
}),
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
@@ -147,16 +150,13 @@ export default {
|
||||
selectedClass: 'warrior',
|
||||
};
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'choose-class');
|
||||
},
|
||||
clickSelectClass (heroClass) {
|
||||
if (this.user.flags.classSelected && !confirm(this.$t('changeClassConfirmCost'))) return;
|
||||
this.$store.dispatch('user:changeClass', {query: {class: heroClass}});
|
||||
this.$store.dispatch('user:changeClass', { query: { class: heroClass } });
|
||||
},
|
||||
clickDisableClasses () {
|
||||
this.$store.dispatch('user:disableClasses');
|
||||
@@ -170,13 +170,13 @@ export default {
|
||||
shield: 'shield_special_fall2019Rogue',
|
||||
weapon: 'weapon_special_fall2019Rogue',
|
||||
};
|
||||
} else if (heroClass === 'wizard') {
|
||||
} if (heroClass === 'wizard') {
|
||||
return {
|
||||
armor: 'armor_special_fall2019Mage',
|
||||
head: 'head_special_fall2019Mage',
|
||||
weapon: 'weapon_special_fall2019Mage',
|
||||
};
|
||||
} else if (heroClass === 'healer') {
|
||||
} if (heroClass === 'healer') {
|
||||
return {
|
||||
armor: 'armor_special_fall2019Healer',
|
||||
eyewear: 'eyewear_special_fall2019Healer',
|
||||
@@ -184,14 +184,13 @@ export default {
|
||||
shield: 'shield_special_fall2019Healer',
|
||||
weapon: 'weapon_special_fall2019Healer',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
armor: 'armor_special_fall2019Warrior',
|
||||
head: 'head_special_fall2019Warrior',
|
||||
shield: 'shield_special_fall2019Warrior',
|
||||
weapon: 'weapon_special_fall2019Warrior',
|
||||
};
|
||||
}
|
||||
return {
|
||||
armor: 'armor_special_fall2019Warrior',
|
||||
head: 'head_special_fall2019Warrior',
|
||||
shield: 'shield_special_fall2019Warrior',
|
||||
weapon: 'weapon_special_fall2019Warrior',
|
||||
};
|
||||
},
|
||||
selectionBox (selectedClass, heroClass) {
|
||||
if (selectedClass === heroClass) {
|
||||
|
||||
@@ -34,7 +34,7 @@ export default {
|
||||
achievementAvatar,
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
|
||||
@@ -41,7 +41,7 @@ import axios from 'axios';
|
||||
import Avatar from '../avatar';
|
||||
import { mapState } from '@/libs/store';
|
||||
import percent from '@/../../common/script/libs/percent';
|
||||
import {MAX_HEALTH as maxHealth} from '@/../../common/script/constants';
|
||||
import { MAX_HEALTH as maxHealth } from '@/../../common/script/constants';
|
||||
import revive from '@/../../common/script/ops/revive';
|
||||
|
||||
export default {
|
||||
@@ -54,7 +54,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
barStyle () {
|
||||
return {
|
||||
width: `${percent(this.user.stats.hp, maxHealth)}%`,
|
||||
|
||||
@@ -30,7 +30,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
firstDropText () {
|
||||
return this.$t('firstDrop', {
|
||||
eggText: this.eggs.all.Wolf.text(),
|
||||
|
||||
@@ -19,24 +19,24 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
|
||||
import {mapState} from '@/libs/store';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
},
|
||||
props: ['data'],
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'generic-achievement');
|
||||
},
|
||||
props: ['data'],
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'generic-achievement');
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -19,28 +19,28 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
|
||||
import {mapState} from '@/libs/store';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: `${this.$t('modalAchievement')} ${this.$t('achievementJustAddWater')}`,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'just-add-water');
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: `${this.$t('modalAchievement')} ${this.$t('achievementJustAddWater')}`,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'just-add-water');
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -116,20 +116,20 @@
|
||||
<script>
|
||||
import Avatar from '../avatar';
|
||||
import { mapState } from '@/libs/store';
|
||||
import {MAX_HEALTH as maxHealth} from '@/../../common/script/constants';
|
||||
import { MAX_HEALTH as maxHealth } from '@/../../common/script/constants';
|
||||
import styleHelper from '@/mixins/styleHelper';
|
||||
import twitter from '@/assets/svg/twitter.svg';
|
||||
import facebook from '@/assets/svg/facebook.svg';
|
||||
|
||||
let BASE_URL = 'https://habitica.com';
|
||||
const BASE_URL = 'https://habitica.com';
|
||||
|
||||
export default {
|
||||
mixins: [styleHelper],
|
||||
components: {
|
||||
Avatar,
|
||||
},
|
||||
mixins: [styleHelper],
|
||||
data () {
|
||||
let tweet = this.$t('levelUpShare');
|
||||
const tweet = this.$t('levelUpShare');
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
twitter,
|
||||
@@ -147,7 +147,7 @@ export default {
|
||||
this.loadWidgets();
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
showAllocation () {
|
||||
return this.$store.getters['members:hasClass'](this.user) && !this.user.preferences.automaticAllocation;
|
||||
},
|
||||
|
||||
@@ -19,28 +19,28 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
|
||||
import {mapState} from '@/libs/store';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: `${this.$t('modalAchievement')} ${this.$t('achievementLostMasterclasser')}`,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'lost-masterclasser');
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: `${this.$t('modalAchievement')} ${this.$t('achievementLostMasterclasser')}`,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'lost-masterclasser');
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
import Avatar from '../avatar';
|
||||
import { mapState } from '@/libs/store';
|
||||
import percent from '@/../../common/script/libs/percent';
|
||||
import {MAX_HEALTH as maxHealth} from '@/../../common/script/constants';
|
||||
import { MAX_HEALTH as maxHealth } from '@/../../common/script/constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -71,7 +71,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
barStyle () {
|
||||
return {
|
||||
width: `${percent(this.user.stats.hp, maxHealth)}%`,
|
||||
|
||||
@@ -19,28 +19,28 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
|
||||
import {mapState} from '@/libs/store';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: `${this.$t('modalAchievement')} ${this.$t('achievementMindOverMatter')}`,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'mind-over-matter');
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: `${this.$t('modalAchievement')} ${this.$t('achievementMindOverMatter')}`,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'mind-over-matter');
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -24,37 +24,37 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import { mapState } from '@/libs/store';
|
||||
import axios from 'axios';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
html: '',
|
||||
};
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
html: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
async mounted () {
|
||||
this.$root.$on('bv::show::modal', async modalId => {
|
||||
if (modalId !== 'new-stuff') return;
|
||||
const response = await axios.get('/api/v4/news');
|
||||
this.html = response.data.html;
|
||||
});
|
||||
},
|
||||
destroyed () {
|
||||
this.$root.$off('bv::show::modal');
|
||||
},
|
||||
methods: {
|
||||
tellMeLater () {
|
||||
this.$store.dispatch('user:newStuffLater');
|
||||
this.$root.$emit('bv::hide::modal', 'new-stuff');
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
dismissAlert () {
|
||||
this.$store.dispatch('user:set', { 'flags.newStuff': false });
|
||||
this.$root.$emit('bv::hide::modal', 'new-stuff');
|
||||
},
|
||||
async mounted () {
|
||||
this.$root.$on('bv::show::modal', async (modalId) => {
|
||||
if (modalId !== 'new-stuff') return;
|
||||
let response = await axios.get('/api/v4/news');
|
||||
this.html = response.data.html;
|
||||
});
|
||||
},
|
||||
destroyed () {
|
||||
this.$root.$off('bv::show::modal');
|
||||
},
|
||||
methods: {
|
||||
tellMeLater () {
|
||||
this.$store.dispatch('user:newStuffLater');
|
||||
this.$root.$emit('bv::hide::modal', 'new-stuff');
|
||||
},
|
||||
dismissAlert () {
|
||||
this.$store.dispatch('user:set', {'flags.newStuff': false});
|
||||
this.$root.$emit('bv::hide::modal', 'new-stuff');
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -28,7 +28,7 @@ import questDialogDrops from '@/components/shops/quests/questDialogDrops';
|
||||
|
||||
import { mapState } from '@/libs/store';
|
||||
import percent from '@/../../common/script/libs/percent';
|
||||
import {MAX_HEALTH as maxHealth} from '@/../../common/script/constants';
|
||||
import { MAX_HEALTH as maxHealth } from '@/../../common/script/constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -41,7 +41,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
questData () {
|
||||
return this.quests.quests[this.user.party.quest.completed];
|
||||
},
|
||||
@@ -62,7 +62,7 @@ export default {
|
||||
this.close();
|
||||
},
|
||||
hide () {
|
||||
this.$store.dispatch('user:set', {'party.quest.completed': ''});
|
||||
this.$store.dispatch('user:set', { 'party.quest.completed': '' });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
import * as quests from '@/../../common/script/content/quests';
|
||||
import { mapState } from '@/libs/store';
|
||||
import percent from '@/../../common/script/libs/percent';
|
||||
import {MAX_HEALTH as maxHealth} from '@/../../common/script/constants';
|
||||
import { MAX_HEALTH as maxHealth } from '@/../../common/script/constants';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
@@ -58,7 +58,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
barStyle () {
|
||||
return {
|
||||
width: `${percent(this.user.stats.hp, maxHealth)}%`,
|
||||
|
||||
@@ -24,23 +24,23 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
|
||||
import {mapState} from '@/libs/store';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'rebirth');
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'rebirth');
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -21,7 +21,7 @@ import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
|
||||
@@ -38,15 +38,15 @@ export default {
|
||||
achievementAvatar,
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'streak');
|
||||
},
|
||||
suppressModals () {
|
||||
let surpress = this.user.preferences.suppressModals.streak ? true : false;
|
||||
this.$store.dispatch('user:set', {'preferences.suppressModals.streak': surpress});
|
||||
const surpress = !!this.user.preferences.suppressModals.streak;
|
||||
this.$store.dispatch('user:set', { 'preferences.suppressModals.streak': surpress });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ export default {
|
||||
achievementAvatar,
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
|
||||
@@ -49,10 +49,10 @@ export default {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
data () {
|
||||
let tweet = this.$t('wonChallengeShare');
|
||||
const tweet = this.$t('wonChallengeShare');
|
||||
return {
|
||||
tweet,
|
||||
};
|
||||
|
||||
@@ -295,7 +295,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
...mapState(['isUserLoaded']),
|
||||
getDataDisplayToolUrl () {
|
||||
const base = 'https://oldgods.net/habitrpg/habitrpg_user_data_display.html';
|
||||
@@ -318,7 +318,7 @@ export default {
|
||||
async addMissedDay (numberOfDays) {
|
||||
if (!confirm(`Are you sure you want to reset the day by ${numberOfDays} day(s)?`)) return;
|
||||
|
||||
let date = moment(this.user.lastCron).subtract(numberOfDays, 'days').toDate();
|
||||
const date = moment(this.user.lastCron).subtract(numberOfDays, 'days').toDate();
|
||||
|
||||
await axios.post('/api/v4/debug/set-cron', {
|
||||
lastCron: date,
|
||||
@@ -349,18 +349,18 @@ export default {
|
||||
addLevelsAndGold () {
|
||||
this.$store.dispatch('user:set', {
|
||||
'stats.exp': this.user.stats.exp + 10000,
|
||||
'stats.gp': this.user.stats.gp + 10000,
|
||||
'stats.mp': this.user.stats.mp + 10000,
|
||||
'stats.gp': this.user.stats.gp + 10000,
|
||||
'stats.mp': this.user.stats.mp + 10000,
|
||||
});
|
||||
},
|
||||
addExp () {
|
||||
// @TODO: Name these variables better
|
||||
let exp = 0;
|
||||
let five = 10 * this.user.stats.lvl;
|
||||
let four = Math.pow(this.user.stats.lvl, 2) * 0.25;
|
||||
let three = four + five + 139.75;
|
||||
let two = three / 10;
|
||||
let one = Math.round(two) * 10;
|
||||
const five = 10 * this.user.stats.lvl;
|
||||
const four = Math.pow(this.user.stats.lvl, 2) * 0.25;
|
||||
const three = four + five + 139.75;
|
||||
const two = three / 10;
|
||||
const one = Math.round(two) * 10;
|
||||
exp = this.user.stats.exp + one;
|
||||
|
||||
this.$store.dispatch('user:set', {
|
||||
@@ -394,7 +394,7 @@ export default {
|
||||
eventAction: 'click',
|
||||
eventLabel: 'Gems > Donate',
|
||||
});
|
||||
this.$root.$emit('bv::show::modal', 'buy-gems', {alreadyTracked: true});
|
||||
this.$root.$emit('bv::show::modal', 'buy-gems', { alreadyTracked: true });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
|
||||
<script>
|
||||
import hello from 'hellojs';
|
||||
import { setUpAxios } from '@/libs/auth';
|
||||
import debounce from 'lodash/debounce';
|
||||
import isEmail from 'validator/lib/isEmail';
|
||||
import { setUpAxios } from '@/libs/auth';
|
||||
|
||||
import facebookSquareIcon from '@/assets/svg/facebook-square.svg';
|
||||
import googleIcon from '@/assets/svg/google.svg';
|
||||
@@ -90,7 +90,7 @@ import googleIcon from '@/assets/svg/google.svg';
|
||||
export default {
|
||||
name: 'AuthForm',
|
||||
data () {
|
||||
let data = {
|
||||
const data = {
|
||||
registering: true,
|
||||
username: '',
|
||||
email: '',
|
||||
@@ -106,13 +106,6 @@ export default {
|
||||
|
||||
return data;
|
||||
},
|
||||
mounted () {
|
||||
hello.init({
|
||||
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line
|
||||
// windows: WINDOWS_CLIENT_ID,
|
||||
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
emailValid () {
|
||||
if (this.email.length <= 3) return false;
|
||||
@@ -141,6 +134,13 @@ export default {
|
||||
this.validateUsername(this.username);
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
hello.init({
|
||||
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line
|
||||
// windows: WINDOWS_CLIENT_ID,
|
||||
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
// eslint-disable-next-line func-names
|
||||
validateUsername: debounce(function (username) {
|
||||
@@ -165,7 +165,7 @@ export default {
|
||||
|
||||
try {
|
||||
const redirectUrl = `${window.location.protocol}//${window.location.host}`;
|
||||
let auth = await hello(network).login({
|
||||
const auth = await hello(network).login({
|
||||
scope: 'email',
|
||||
redirect_uri: redirectUrl, // eslint-disable-line camelcase
|
||||
});
|
||||
@@ -212,7 +212,7 @@ export default {
|
||||
async finishAuth () {
|
||||
setUpAxios();
|
||||
|
||||
await this.$store.dispatch('user:fetch', {forceLoad: true});
|
||||
await this.$store.dispatch('user:fetch', { forceLoad: true });
|
||||
|
||||
this.$emit('authenticate');
|
||||
},
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script>
|
||||
export default {
|
||||
components: {},
|
||||
methods: {
|
||||
async logout () {
|
||||
return await this.$store.dispatch('auth:logout');
|
||||
},
|
||||
export default {
|
||||
components: {},
|
||||
created () {
|
||||
this.logout();
|
||||
},
|
||||
methods: {
|
||||
async logout () {
|
||||
return await this.$store.dispatch('auth:logout');
|
||||
},
|
||||
created () {
|
||||
this.logout();
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -304,7 +304,7 @@ import googleIcon from '@/assets/svg/google.svg';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
let data = {
|
||||
const data = {
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
@@ -364,29 +364,22 @@ export default {
|
||||
return !this.passwordConfirmValid;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
hello.init({
|
||||
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line
|
||||
// windows: WINDOWS_CLIENT_ID,
|
||||
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
$route: {
|
||||
handler () {
|
||||
if (this.resetPasswordSetNewOne) {
|
||||
const query = this.$route.query;
|
||||
const code = query.code;
|
||||
const hasError = query.hasError === 'true' ? true : false;
|
||||
const { query } = this.$route;
|
||||
const { code } = query;
|
||||
const hasError = query.hasError === 'true';
|
||||
if (hasError) {
|
||||
alert(query.message);
|
||||
this.$router.push({name: 'login'});
|
||||
this.$router.push({ name: 'login' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!code) {
|
||||
alert(this.$t('invalidPasswordResetCode'));
|
||||
this.$router.push({name: 'login'});
|
||||
this.$router.push({ name: 'login' });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -400,6 +393,13 @@ export default {
|
||||
this.validateUsername(this.username);
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
hello.init({
|
||||
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line
|
||||
// windows: WINDOWS_CLIENT_ID,
|
||||
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
// eslint-disable-next-line func-names
|
||||
validateUsername: debounce(function (username) {
|
||||
@@ -488,7 +488,7 @@ export default {
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
const redirectUrl = `${window.location.protocol}//${window.location.host}`;
|
||||
let auth = await hello(network).login({
|
||||
const auth = await hello(network).login({
|
||||
scope: 'email',
|
||||
// explicitly pass the redirect url or it might redirect to /home
|
||||
redirect_uri: redirectUrl, // eslint-disable-line camelcase
|
||||
@@ -568,7 +568,7 @@ export default {
|
||||
this.passwordConfirm = '';
|
||||
this.resetPasswordSetNewOneData.code = '';
|
||||
this.resetPasswordSetNewOneData.hasError = false;
|
||||
this.$router.push({name: 'login'});
|
||||
this.$router.push({ name: 'login' });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -146,9 +146,9 @@ export default {
|
||||
return val;
|
||||
},
|
||||
backgroundClass () {
|
||||
let background = this.member.preferences.background;
|
||||
const { background } = this.member.preferences;
|
||||
|
||||
let allowToShowBackground = !this.avatarOnly || this.withBackground;
|
||||
const allowToShowBackground = !this.avatarOnly || this.withBackground;
|
||||
|
||||
if (this.overrideAvatarGear && this.overrideAvatarGear.background) {
|
||||
return `background_${this.overrideAvatarGear.background}`;
|
||||
@@ -169,7 +169,7 @@ export default {
|
||||
};
|
||||
},
|
||||
skinClass () {
|
||||
let baseClass = `skin_${this.member.preferences.skin}`;
|
||||
const baseClass = `skin_${this.member.preferences.skin}`;
|
||||
|
||||
return `${baseClass}${this.member.preferences.sleep ? '_sleep' : ''}`;
|
||||
},
|
||||
@@ -196,19 +196,19 @@ export default {
|
||||
},
|
||||
hideGear (gearType) {
|
||||
if (gearType === 'weapon') {
|
||||
let equippedWeapon = this.member.items.gear[this.costumeClass][gearType];
|
||||
const equippedWeapon = this.member.items.gear[this.costumeClass][gearType];
|
||||
|
||||
if (!equippedWeapon) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let equippedIsTwoHanded = this.flatGear[equippedWeapon].twoHanded;
|
||||
let hasOverrideShield = this.overrideAvatarGear && this.overrideAvatarGear.shield;
|
||||
const equippedIsTwoHanded = this.flatGear[equippedWeapon].twoHanded;
|
||||
const hasOverrideShield = this.overrideAvatarGear && this.overrideAvatarGear.shield;
|
||||
|
||||
return equippedIsTwoHanded && hasOverrideShield;
|
||||
} else if (gearType === 'shield') {
|
||||
let overrideWeapon = this.overrideAvatarGear && this.overrideAvatarGear.weapon;
|
||||
let overrideIsTwoHanded = overrideWeapon && this.flatGear[overrideWeapon].twoHanded;
|
||||
} if (gearType === 'shield') {
|
||||
const overrideWeapon = this.overrideAvatarGear && this.overrideAvatarGear.weapon;
|
||||
const overrideIsTwoHanded = overrideWeapon && this.flatGear[overrideWeapon].twoHanded;
|
||||
|
||||
return overrideIsTwoHanded;
|
||||
}
|
||||
@@ -218,10 +218,9 @@ export default {
|
||||
this.$root.$emit('castEnd', this.member, 'user', e);
|
||||
},
|
||||
showAvatar () {
|
||||
if (!this.showVisualBuffs)
|
||||
return true;
|
||||
if (!this.showVisualBuffs) return true;
|
||||
|
||||
let buffs = this.member.stats.buffs;
|
||||
const { buffs } = this.member.stats;
|
||||
|
||||
return !buffs.snowball && !buffs.spookySparkles && !buffs.shinySeed && !buffs.seafoam;
|
||||
},
|
||||
|
||||
@@ -21,70 +21,70 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import appearance from '@/../../common/script/content/appearance';
|
||||
import {subPageMixin} from '../../mixins/subPage';
|
||||
import {userStateMixin} from '../../mixins/userState';
|
||||
import {avatarEditorUtilies} from '../../mixins/avatarEditUtilities';
|
||||
import subMenu from './sub-menu';
|
||||
import customizeOptions from './customize-options';
|
||||
import gem from '@/assets/svg/gem.svg';
|
||||
import appearance from '@/../../common/script/content/appearance';
|
||||
import { subPageMixin } from '../../mixins/subPage';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
import { avatarEditorUtilies } from '../../mixins/avatarEditUtilities';
|
||||
import subMenu from './sub-menu';
|
||||
import customizeOptions from './customize-options';
|
||||
import gem from '@/assets/svg/gem.svg';
|
||||
|
||||
const freeShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price === 0);
|
||||
const specialShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price !== 0);
|
||||
const freeShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price === 0);
|
||||
const specialShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price !== 0);
|
||||
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'editing',
|
||||
],
|
||||
components: {
|
||||
subMenu,
|
||||
customizeOptions,
|
||||
export default {
|
||||
components: {
|
||||
subMenu,
|
||||
customizeOptions,
|
||||
},
|
||||
mixins: [
|
||||
subPageMixin,
|
||||
userStateMixin,
|
||||
avatarEditorUtilies,
|
||||
],
|
||||
props: [
|
||||
'editing',
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
specialShirtKeys,
|
||||
icons: Object.freeze({
|
||||
gem,
|
||||
}),
|
||||
items: [
|
||||
{
|
||||
id: 'size',
|
||||
label: this.$t('size'),
|
||||
},
|
||||
{
|
||||
id: 'shirt',
|
||||
label: this.$t('shirt'),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
sizes () {
|
||||
return ['slim', 'broad'].map(s => this.mapKeysToFreeOption(s, 'size'));
|
||||
},
|
||||
mixins: [
|
||||
subPageMixin,
|
||||
userStateMixin,
|
||||
avatarEditorUtilies,
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
specialShirtKeys,
|
||||
icons: Object.freeze({
|
||||
gem,
|
||||
}),
|
||||
items: [
|
||||
{
|
||||
id: 'size',
|
||||
label: this.$t('size'),
|
||||
},
|
||||
{
|
||||
id: 'shirt',
|
||||
label: this.$t('shirt'),
|
||||
},
|
||||
],
|
||||
};
|
||||
freeShirts () {
|
||||
return freeShirtKeys.map(s => this.mapKeysToFreeOption(s, 'shirt'));
|
||||
},
|
||||
computed: {
|
||||
sizes () {
|
||||
return ['slim', 'broad'].map(s => this.mapKeysToFreeOption(s, 'size'));
|
||||
},
|
||||
freeShirts () {
|
||||
return freeShirtKeys.map(s => this.mapKeysToFreeOption(s, 'shirt'));
|
||||
},
|
||||
specialShirts () {
|
||||
specialShirts () {
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
let keys = this.specialShirtKeys;
|
||||
let options = keys.map(key => this.mapKeysToOption(key, 'shirt'));
|
||||
return options;
|
||||
},
|
||||
const keys = this.specialShirtKeys;
|
||||
const options = keys.map(key => this.mapKeysToOption(key, 'shirt'));
|
||||
return options;
|
||||
},
|
||||
mounted () {
|
||||
this.changeSubPage('size');
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
mounted () {
|
||||
this.changeSubPage('size');
|
||||
},
|
||||
methods: {
|
||||
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -23,29 +23,29 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gem from '@/assets/svg/gem.svg';
|
||||
import gold from '@/assets/svg/gold.svg';
|
||||
import {avatarEditorUtilies} from '../../mixins/avatarEditUtilities';
|
||||
import gem from '@/assets/svg/gem.svg';
|
||||
import gold from '@/assets/svg/gold.svg';
|
||||
import { avatarEditorUtilies } from '../../mixins/avatarEditUtilities';
|
||||
|
||||
export default {
|
||||
props: ['items', 'currentValue', 'fullSet'],
|
||||
mixins: [
|
||||
avatarEditorUtilies,
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
gem,
|
||||
gold,
|
||||
}),
|
||||
};
|
||||
export default {
|
||||
mixins: [
|
||||
avatarEditorUtilies,
|
||||
],
|
||||
props: ['items', 'currentValue', 'fullSet'],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
gem,
|
||||
gold,
|
||||
}),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
unlock () {
|
||||
this.$emit('unlock');
|
||||
},
|
||||
methods: {
|
||||
unlock () {
|
||||
this.$emit('unlock');
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -36,235 +36,228 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import appearance from '@/../../common/script/content/appearance';
|
||||
import {subPageMixin} from '../../mixins/subPage';
|
||||
import {userStateMixin} from '../../mixins/userState';
|
||||
import {avatarEditorUtilies} from '../../mixins/avatarEditUtilities';
|
||||
import subMenu from './sub-menu';
|
||||
import customizeOptions from './customize-options';
|
||||
import gem from '@/assets/svg/gem.svg';
|
||||
import appearance from '@/../../common/script/content/appearance';
|
||||
import { subPageMixin } from '../../mixins/subPage';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
import { avatarEditorUtilies } from '../../mixins/avatarEditUtilities';
|
||||
import subMenu from './sub-menu';
|
||||
import customizeOptions from './customize-options';
|
||||
import gem from '@/assets/svg/gem.svg';
|
||||
|
||||
const freeShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price === 0);
|
||||
const specialShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price !== 0);
|
||||
const freeShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price === 0);
|
||||
const specialShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price !== 0);
|
||||
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'editing',
|
||||
],
|
||||
components: {
|
||||
subMenu,
|
||||
customizeOptions,
|
||||
},
|
||||
mixins: [
|
||||
subPageMixin,
|
||||
userStateMixin,
|
||||
avatarEditorUtilies,
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
animalItemKeys: {
|
||||
back: ['bearTail', 'cactusTail', 'foxTail', 'lionTail', 'pandaTail', 'pigTail', 'tigerTail', 'wolfTail'],
|
||||
headAccessory: ['bearEars', 'cactusEars', 'foxEars', 'lionEars', 'pandaEars', 'pigEars', 'tigerEars', 'wolfEars'],
|
||||
export default {
|
||||
components: {
|
||||
subMenu,
|
||||
customizeOptions,
|
||||
},
|
||||
mixins: [
|
||||
subPageMixin,
|
||||
userStateMixin,
|
||||
avatarEditorUtilies,
|
||||
],
|
||||
props: [
|
||||
'editing',
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
animalItemKeys: {
|
||||
back: ['bearTail', 'cactusTail', 'foxTail', 'lionTail', 'pandaTail', 'pigTail', 'tigerTail', 'wolfTail'],
|
||||
headAccessory: ['bearEars', 'cactusEars', 'foxEars', 'lionEars', 'pandaEars', 'pigEars', 'tigerEars', 'wolfEars'],
|
||||
},
|
||||
chairKeys: ['none', 'black', 'blue', 'green', 'pink', 'red', 'yellow', 'handleless_black', 'handleless_blue', 'handleless_green', 'handleless_pink', 'handleless_red', 'handleless_yellow'],
|
||||
specialShirtKeys,
|
||||
icons: Object.freeze({
|
||||
gem,
|
||||
}),
|
||||
items: [
|
||||
{
|
||||
id: 'size',
|
||||
label: this.$t('size'),
|
||||
},
|
||||
chairKeys: ['none', 'black', 'blue', 'green', 'pink', 'red', 'yellow', 'handleless_black', 'handleless_blue', 'handleless_green', 'handleless_pink', 'handleless_red', 'handleless_yellow'],
|
||||
specialShirtKeys,
|
||||
icons: Object.freeze({
|
||||
gem,
|
||||
}),
|
||||
items: [
|
||||
{
|
||||
id: 'size',
|
||||
label: this.$t('size'),
|
||||
},
|
||||
{
|
||||
id: 'shirt',
|
||||
label: this.$t('shirt'),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
extraSubMenuItems () {
|
||||
const items = [];
|
||||
{
|
||||
id: 'shirt',
|
||||
label: this.$t('shirt'),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
extraSubMenuItems () {
|
||||
const items = [];
|
||||
|
||||
if (this.editing) {
|
||||
items.push({
|
||||
id: 'glasses',
|
||||
label: this.$t('glasses'),
|
||||
});
|
||||
}
|
||||
|
||||
items.push(
|
||||
{
|
||||
id: 'wheelchair',
|
||||
label: this.$t('wheelchair'),
|
||||
},
|
||||
{
|
||||
id: 'flower',
|
||||
label: this.$t('accent'),
|
||||
},
|
||||
);
|
||||
|
||||
if (this.editing) {
|
||||
items.push({
|
||||
id: 'ears',
|
||||
label: this.$t('animalEars'),
|
||||
});
|
||||
|
||||
items.push({
|
||||
id: 'tails',
|
||||
label: this.$t('animalTails'),
|
||||
});
|
||||
|
||||
items.push({
|
||||
id: 'headband',
|
||||
label: this.$t('headband'),
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
eyewear () {
|
||||
let keys = [
|
||||
'blackTopFrame', 'blueTopFrame', 'greenTopFrame', 'pinkTopFrame', 'redTopFrame', 'whiteTopFrame', 'yellowTopFrame',
|
||||
'blackHalfMoon', 'blueHalfMoon', 'greenHalfMoon', 'pinkHalfMoon', 'redHalfMoon', 'whiteHalfMoon', 'yellowHalfMoon',
|
||||
];
|
||||
let options = keys.map(key => {
|
||||
let newKey = `eyewear_special_${key}`;
|
||||
let option = {};
|
||||
option.key = key;
|
||||
option.active = this.user.preferences.costume ? this.user.items.gear.costume.eyewear === newKey : this.user.items.gear.equipped.eyewear === newKey;
|
||||
option.class = `eyewear_special_${key}`;
|
||||
option.click = () => {
|
||||
let type = this.user.preferences.costume ? 'costume' : 'equipped';
|
||||
|
||||
return this.equip(newKey, type);
|
||||
};
|
||||
return option;
|
||||
if (this.editing) {
|
||||
items.push({
|
||||
id: 'glasses',
|
||||
label: this.$t('glasses'),
|
||||
});
|
||||
return options;
|
||||
},
|
||||
freeShirts () {
|
||||
return freeShirtKeys.map(s => this.mapKeysToFreeOption(s, 'shirt'));
|
||||
},
|
||||
specialShirts () {
|
||||
}
|
||||
|
||||
items.push(
|
||||
{
|
||||
id: 'wheelchair',
|
||||
label: this.$t('wheelchair'),
|
||||
},
|
||||
{
|
||||
id: 'flower',
|
||||
label: this.$t('accent'),
|
||||
},
|
||||
);
|
||||
|
||||
if (this.editing) {
|
||||
items.push({
|
||||
id: 'ears',
|
||||
label: this.$t('animalEars'),
|
||||
});
|
||||
|
||||
items.push({
|
||||
id: 'tails',
|
||||
label: this.$t('animalTails'),
|
||||
});
|
||||
|
||||
items.push({
|
||||
id: 'headband',
|
||||
label: this.$t('headband'),
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
eyewear () {
|
||||
const keys = [
|
||||
'blackTopFrame', 'blueTopFrame', 'greenTopFrame', 'pinkTopFrame', 'redTopFrame', 'whiteTopFrame', 'yellowTopFrame',
|
||||
'blackHalfMoon', 'blueHalfMoon', 'greenHalfMoon', 'pinkHalfMoon', 'redHalfMoon', 'whiteHalfMoon', 'yellowHalfMoon',
|
||||
];
|
||||
const options = keys.map(key => {
|
||||
const newKey = `eyewear_special_${key}`;
|
||||
const option = {};
|
||||
option.key = key;
|
||||
option.active = this.user.preferences.costume ? this.user.items.gear.costume.eyewear === newKey : this.user.items.gear.equipped.eyewear === newKey;
|
||||
option.class = `eyewear_special_${key}`;
|
||||
option.click = () => {
|
||||
const type = this.user.preferences.costume ? 'costume' : 'equipped';
|
||||
|
||||
return this.equip(newKey, type);
|
||||
};
|
||||
return option;
|
||||
});
|
||||
return options;
|
||||
},
|
||||
freeShirts () {
|
||||
return freeShirtKeys.map(s => this.mapKeysToFreeOption(s, 'shirt'));
|
||||
},
|
||||
specialShirts () {
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
let keys = this.specialShirtKeys;
|
||||
let options = keys.map(key => this.mapKeysToOption(key, 'shirt'));
|
||||
return options;
|
||||
},
|
||||
headbands () {
|
||||
let keys = ['blackHeadband', 'blueHeadband', 'greenHeadband', 'pinkHeadband', 'redHeadband', 'whiteHeadband', 'yellowHeadband'];
|
||||
let options = keys.map(key => {
|
||||
let newKey = `headAccessory_special_${key}`;
|
||||
let option = {};
|
||||
option.key = key;
|
||||
option.active = this.user.preferences.costume ? this.user.items.gear.costume.headAccessory === newKey : this.user.items.gear.equipped.headAccessory === newKey;
|
||||
option.class = `headAccessory_special_${option.key} headband`;
|
||||
option.click = () => {
|
||||
let type = this.user.preferences.costume ? 'costume' : 'equipped';
|
||||
return this.equip(newKey, type);
|
||||
};
|
||||
return option;
|
||||
});
|
||||
return options;
|
||||
},
|
||||
chairs () {
|
||||
let options = this.chairKeys.map(key => {
|
||||
let option = {};
|
||||
option.key = key;
|
||||
if (key === 'none') {
|
||||
option.none = true;
|
||||
}
|
||||
option.active = this.user.preferences.chair === key;
|
||||
option.class = `button_chair_${key} chair ${key.includes('handleless_') ? 'handleless' : ''}`;
|
||||
option.click = () => {
|
||||
return this.set({'preferences.chair': key});
|
||||
};
|
||||
return option;
|
||||
});
|
||||
return options;
|
||||
},
|
||||
flowers () {
|
||||
let keys = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
|
||||
let options = keys.map(key => {
|
||||
let option = {};
|
||||
option.key = key;
|
||||
if (key === 0) {
|
||||
option.none = true;
|
||||
}
|
||||
option.active = this.user.preferences.hair.flower === key;
|
||||
option.class = `hair_flower_${key} flower`;
|
||||
option.click = () => {
|
||||
return this.set({'preferences.hair.flower': key});
|
||||
};
|
||||
return option;
|
||||
});
|
||||
return options;
|
||||
},
|
||||
const keys = this.specialShirtKeys;
|
||||
const options = keys.map(key => this.mapKeysToOption(key, 'shirt'));
|
||||
return options;
|
||||
},
|
||||
mounted () {
|
||||
this.changeSubPage(this.extraSubMenuItems[0].id);
|
||||
headbands () {
|
||||
const keys = ['blackHeadband', 'blueHeadband', 'greenHeadband', 'pinkHeadband', 'redHeadband', 'whiteHeadband', 'yellowHeadband'];
|
||||
const options = keys.map(key => {
|
||||
const newKey = `headAccessory_special_${key}`;
|
||||
const option = {};
|
||||
option.key = key;
|
||||
option.active = this.user.preferences.costume ? this.user.items.gear.costume.headAccessory === newKey : this.user.items.gear.equipped.headAccessory === newKey;
|
||||
option.class = `headAccessory_special_${option.key} headband`;
|
||||
option.click = () => {
|
||||
const type = this.user.preferences.costume ? 'costume' : 'equipped';
|
||||
return this.equip(newKey, type);
|
||||
};
|
||||
return option;
|
||||
});
|
||||
return options;
|
||||
},
|
||||
methods: {
|
||||
animalItems (category) {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
chairs () {
|
||||
const options = this.chairKeys.map(key => {
|
||||
const option = {};
|
||||
option.key = key;
|
||||
if (key === 'none') {
|
||||
option.none = true;
|
||||
}
|
||||
option.active = this.user.preferences.chair === key;
|
||||
option.class = `button_chair_${key} chair ${key.includes('handleless_') ? 'handleless' : ''}`;
|
||||
option.click = () => this.set({ 'preferences.chair': key });
|
||||
return option;
|
||||
});
|
||||
return options;
|
||||
},
|
||||
flowers () {
|
||||
const keys = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
|
||||
const options = keys.map(key => {
|
||||
const option = {};
|
||||
option.key = key;
|
||||
if (key === 0) {
|
||||
option.none = true;
|
||||
}
|
||||
option.active = this.user.preferences.hair.flower === key;
|
||||
option.class = `hair_flower_${key} flower`;
|
||||
option.click = () => this.set({ 'preferences.hair.flower': key });
|
||||
return option;
|
||||
});
|
||||
return options;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.changeSubPage(this.extraSubMenuItems[0].id);
|
||||
},
|
||||
methods: {
|
||||
animalItems (category) {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
let keys = this.animalItemKeys[category];
|
||||
let options = keys.map(key => {
|
||||
let newKey = `${category}_special_${key}`;
|
||||
let userPurchased = this.user.items.gear.owned[newKey];
|
||||
const keys = this.animalItemKeys[category];
|
||||
const options = keys.map(key => {
|
||||
const newKey = `${category}_special_${key}`;
|
||||
const userPurchased = this.user.items.gear.owned[newKey];
|
||||
|
||||
let option = {};
|
||||
option.key = key;
|
||||
option.active = this.user.preferences.costume ? this.user.items.gear.costume[category] === newKey : this.user.items.gear.equipped[category] === newKey;
|
||||
option.class = `headAccessory_special_${option.key} ${category}`;
|
||||
if (category === 'back') {
|
||||
option.class = `icon_back_special_${option.key} back`;
|
||||
}
|
||||
option.gemLocked = userPurchased === undefined;
|
||||
option.goldLocked = userPurchased === false;
|
||||
if (option.goldLocked) {
|
||||
option.gold = 20;
|
||||
}
|
||||
const option = {};
|
||||
option.key = key;
|
||||
option.active = this.user.preferences.costume ? this.user.items.gear.costume[category] === newKey : this.user.items.gear.equipped[category] === newKey;
|
||||
option.class = `headAccessory_special_${option.key} ${category}`;
|
||||
if (category === 'back') {
|
||||
option.class = `icon_back_special_${option.key} back`;
|
||||
}
|
||||
option.gemLocked = userPurchased === undefined;
|
||||
option.goldLocked = userPurchased === false;
|
||||
if (option.goldLocked) {
|
||||
option.gold = 20;
|
||||
}
|
||||
if (option.gemLocked) {
|
||||
option.gem = 2;
|
||||
}
|
||||
option.locked = option.gemLocked || option.goldLocked;
|
||||
option.click = () => {
|
||||
if (option.gemLocked) {
|
||||
option.gem = 2;
|
||||
return this.unlock(`items.gear.owned.${newKey}`);
|
||||
} if (option.goldLocked) {
|
||||
return this.buy(newKey);
|
||||
}
|
||||
option.locked = option.gemLocked || option.goldLocked;
|
||||
option.click = () => {
|
||||
if (option.gemLocked) {
|
||||
return this.unlock(`items.gear.owned.${newKey}`);
|
||||
} else if (option.goldLocked) {
|
||||
return this.buy(newKey);
|
||||
} else {
|
||||
let type = this.user.preferences.costume ? 'costume' : 'equipped';
|
||||
return this.equip(newKey, type);
|
||||
}
|
||||
};
|
||||
return option;
|
||||
});
|
||||
return options;
|
||||
},
|
||||
animalItemsUnlockString (category) {
|
||||
const keys = this.animalItemKeys[category].map(key => {
|
||||
return `items.gear.owned.${category}_special_${key}`;
|
||||
});
|
||||
const type = this.user.preferences.costume ? 'costume' : 'equipped';
|
||||
return this.equip(newKey, type);
|
||||
};
|
||||
return option;
|
||||
});
|
||||
return options;
|
||||
},
|
||||
animalItemsUnlockString (category) {
|
||||
const keys = this.animalItemKeys[category].map(key => `items.gear.owned.${category}_special_${key}`);
|
||||
|
||||
return keys.join(',');
|
||||
},
|
||||
animalItemsOwned (category) {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
return keys.join(',');
|
||||
},
|
||||
animalItemsOwned (category) {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
|
||||
let own = true;
|
||||
this.animalItemKeys[category].forEach(key => {
|
||||
if (this.user.items.gear.owned[`${category}_special_${key}`] === undefined) own = false;
|
||||
});
|
||||
return own;
|
||||
},
|
||||
let own = true;
|
||||
this.animalItemKeys[category].forEach(key => {
|
||||
if (this.user.items.gear.owned[`${category}_special_${key}`] === undefined) own = false;
|
||||
});
|
||||
return own;
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -42,298 +42,286 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import appearance from '@/../../common/script/content/appearance';
|
||||
import {subPageMixin} from '../../mixins/subPage';
|
||||
import {userStateMixin} from '../../mixins/userState';
|
||||
import {avatarEditorUtilies} from '../../mixins/avatarEditUtilities';
|
||||
import subMenu from './sub-menu';
|
||||
import customizeOptions from './customize-options';
|
||||
import gem from '@/assets/svg/gem.svg';
|
||||
import appearanceSets from '@/../../common/script/content/appearance/sets';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
import appearance from '@/../../common/script/content/appearance';
|
||||
import { subPageMixin } from '../../mixins/subPage';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
import { avatarEditorUtilies } from '../../mixins/avatarEditUtilities';
|
||||
import subMenu from './sub-menu';
|
||||
import customizeOptions from './customize-options';
|
||||
import gem from '@/assets/svg/gem.svg';
|
||||
import appearanceSets from '@/../../common/script/content/appearance/sets';
|
||||
|
||||
const hairColorBySet = groupBy(appearance.hair.color, 'set.key');
|
||||
const freeHairColorKeys = hairColorBySet[undefined].map(s => s.key);
|
||||
const hairColorBySet = groupBy(appearance.hair.color, 'set.key');
|
||||
const freeHairColorKeys = hairColorBySet[undefined].map(s => s.key);
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'editing',
|
||||
],
|
||||
components: {
|
||||
subMenu,
|
||||
customizeOptions,
|
||||
},
|
||||
mixins: [
|
||||
subPageMixin,
|
||||
userStateMixin,
|
||||
avatarEditorUtilies,
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
freeHairColorKeys,
|
||||
icons: Object.freeze({
|
||||
gem,
|
||||
}),
|
||||
baseHair1: [1, 3],
|
||||
baseHair2Keys: [2, 4, 5, 6, 7, 8],
|
||||
baseHair3Keys: [9, 10, 11, 12, 13, 14],
|
||||
baseHair4Keys: [15, 16, 17, 18, 19, 20],
|
||||
baseHair5Keys: [1, 2],
|
||||
baseHair6Keys: [1, 2, 3],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hairSubMenuItems () {
|
||||
const items = [
|
||||
{
|
||||
id: 'color',
|
||||
label: this.$t('color'),
|
||||
},
|
||||
{
|
||||
id: 'bangs',
|
||||
label: this.$t('bangs'),
|
||||
},
|
||||
{
|
||||
id: 'style',
|
||||
label: this.$t('style'),
|
||||
},
|
||||
];
|
||||
export default {
|
||||
components: {
|
||||
subMenu,
|
||||
customizeOptions,
|
||||
},
|
||||
mixins: [
|
||||
subPageMixin,
|
||||
userStateMixin,
|
||||
avatarEditorUtilies,
|
||||
],
|
||||
props: [
|
||||
'editing',
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
freeHairColorKeys,
|
||||
icons: Object.freeze({
|
||||
gem,
|
||||
}),
|
||||
baseHair1: [1, 3],
|
||||
baseHair2Keys: [2, 4, 5, 6, 7, 8],
|
||||
baseHair3Keys: [9, 10, 11, 12, 13, 14],
|
||||
baseHair4Keys: [15, 16, 17, 18, 19, 20],
|
||||
baseHair5Keys: [1, 2],
|
||||
baseHair6Keys: [1, 2, 3],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hairSubMenuItems () {
|
||||
const items = [
|
||||
{
|
||||
id: 'color',
|
||||
label: this.$t('color'),
|
||||
},
|
||||
{
|
||||
id: 'bangs',
|
||||
label: this.$t('bangs'),
|
||||
},
|
||||
{
|
||||
id: 'style',
|
||||
label: this.$t('style'),
|
||||
},
|
||||
];
|
||||
|
||||
if (this.editing) {
|
||||
items.push({
|
||||
id: 'facialhair',
|
||||
label: this.$t('facialhair'),
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
freeHairColors () {
|
||||
return freeHairColorKeys.map(s => this.mapKeysToFreeOption(s, 'hair', 'color'));
|
||||
},
|
||||
seasonalHairColors () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
|
||||
let seasonalHairColors = [];
|
||||
for (let key in hairColorBySet) {
|
||||
let set = hairColorBySet[key];
|
||||
|
||||
let keys = set.map(item => {
|
||||
return item.key;
|
||||
});
|
||||
|
||||
let options = keys.map(optionKey => {
|
||||
const option = this.mapKeysToOption(optionKey, 'hair', 'color', key);
|
||||
return option;
|
||||
});
|
||||
|
||||
let text = this.$t(key);
|
||||
if (appearanceSets[key] && appearanceSets[key].text) {
|
||||
text = appearanceSets[key].text();
|
||||
}
|
||||
|
||||
let compiledSet = {
|
||||
key,
|
||||
options,
|
||||
keys,
|
||||
text,
|
||||
};
|
||||
seasonalHairColors.push(compiledSet);
|
||||
}
|
||||
|
||||
return seasonalHairColors;
|
||||
},
|
||||
premiumHairColors () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
let keys = this.premiumHairColorKeys;
|
||||
let options = keys.map(key => {
|
||||
return this.mapKeysToOption(key, 'hair', 'color');
|
||||
if (this.editing) {
|
||||
items.push({
|
||||
id: 'facialhair',
|
||||
label: this.$t('facialhair'),
|
||||
});
|
||||
return options;
|
||||
},
|
||||
baseHair2 () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
freeHairColors () {
|
||||
return freeHairColorKeys.map(s => this.mapKeysToFreeOption(s, 'hair', 'color'));
|
||||
},
|
||||
seasonalHairColors () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
let keys = this.baseHair2Keys;
|
||||
let options = keys.map(key => {
|
||||
return this.mapKeysToOption(key, 'hair', 'base');
|
||||
});
|
||||
return options;
|
||||
},
|
||||
baseHair3 () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
let keys = this.baseHair3Keys;
|
||||
let options = keys.map(key => {
|
||||
const option = this.mapKeysToOption(key, 'hair', 'base');
|
||||
|
||||
const seasonalHairColors = [];
|
||||
for (const key in hairColorBySet) {
|
||||
const set = hairColorBySet[key];
|
||||
|
||||
const keys = set.map(item => item.key);
|
||||
|
||||
const options = keys.map(optionKey => {
|
||||
const option = this.mapKeysToOption(optionKey, 'hair', 'color', key);
|
||||
return option;
|
||||
});
|
||||
return options;
|
||||
},
|
||||
baseHair4 () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
let keys = this.baseHair4Keys;
|
||||
let options = keys.map(key => {
|
||||
return this.mapKeysToOption(key, 'hair', 'base');
|
||||
});
|
||||
return options;
|
||||
},
|
||||
baseHair5 () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
let keys = this.baseHair5Keys;
|
||||
let options = keys.map(key => {
|
||||
return this.mapKeysToOption(key, 'hair', 'mustache');
|
||||
});
|
||||
return options;
|
||||
},
|
||||
baseHair6 () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
let keys = this.baseHair6Keys;
|
||||
let options = keys.map(key => {
|
||||
return this.mapKeysToOption(key, 'hair', 'beard');
|
||||
});
|
||||
return options;
|
||||
},
|
||||
hairBangs () {
|
||||
const none = this.mapKeysToFreeOption(0, 'hair', 'bangs');
|
||||
none.none = true;
|
||||
|
||||
const options = [1, 2, 3, 4].map(s => this.mapKeysToFreeOption(s, 'hair', 'bangs'));
|
||||
|
||||
return [none, ...options];
|
||||
},
|
||||
mustacheList () {
|
||||
const noneOption = this.mapKeysToFreeOption(0, 'hair', 'mustache');
|
||||
noneOption.none = true;
|
||||
|
||||
return [noneOption, ...this.baseHair5];
|
||||
},
|
||||
beardList () {
|
||||
const noneOption = this.mapKeysToFreeOption(0, 'hair', 'beard');
|
||||
noneOption.none = true;
|
||||
|
||||
return [noneOption, ...this.baseHair6];
|
||||
},
|
||||
styleSets () {
|
||||
const sets = [];
|
||||
|
||||
const emptyHairBase = {
|
||||
...this.mapKeysToFreeOption(0, 'hair', 'base'),
|
||||
none: true,
|
||||
};
|
||||
|
||||
if (this.editing) {
|
||||
sets.push({
|
||||
fullSet: !this.userOwnsSet('hair', this.baseHair3Keys, 'base'),
|
||||
unlock: () => this.unlock(`hair.base.${this.baseHair3Keys.join(',hair.base.')}`),
|
||||
options: [
|
||||
emptyHairBase,
|
||||
...this.baseHair3,
|
||||
],
|
||||
});
|
||||
|
||||
sets.push({
|
||||
fullSet: !this.userOwnsSet('hair', this.baseHair4Keys, 'base'),
|
||||
unlock: () => this.unlock(`hair.base.${this.baseHair4Keys.join(',hair.base.')}`),
|
||||
options: [
|
||||
...this.baseHair4,
|
||||
],
|
||||
});
|
||||
let text = this.$t(key);
|
||||
if (appearanceSets[key] && appearanceSets[key].text) {
|
||||
text = appearanceSets[key].text();
|
||||
}
|
||||
|
||||
const compiledSet = {
|
||||
key,
|
||||
options,
|
||||
keys,
|
||||
text,
|
||||
};
|
||||
seasonalHairColors.push(compiledSet);
|
||||
}
|
||||
|
||||
return seasonalHairColors;
|
||||
},
|
||||
premiumHairColors () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
const keys = this.premiumHairColorKeys;
|
||||
const options = keys.map(key => this.mapKeysToOption(key, 'hair', 'color'));
|
||||
return options;
|
||||
},
|
||||
baseHair2 () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
const keys = this.baseHair2Keys;
|
||||
const options = keys.map(key => this.mapKeysToOption(key, 'hair', 'base'));
|
||||
return options;
|
||||
},
|
||||
baseHair3 () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
const keys = this.baseHair3Keys;
|
||||
const options = keys.map(key => {
|
||||
const option = this.mapKeysToOption(key, 'hair', 'base');
|
||||
return option;
|
||||
});
|
||||
return options;
|
||||
},
|
||||
baseHair4 () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
const keys = this.baseHair4Keys;
|
||||
const options = keys.map(key => this.mapKeysToOption(key, 'hair', 'base'));
|
||||
return options;
|
||||
},
|
||||
baseHair5 () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
const keys = this.baseHair5Keys;
|
||||
const options = keys.map(key => this.mapKeysToOption(key, 'hair', 'mustache'));
|
||||
return options;
|
||||
},
|
||||
baseHair6 () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
const keys = this.baseHair6Keys;
|
||||
const options = keys.map(key => this.mapKeysToOption(key, 'hair', 'beard'));
|
||||
return options;
|
||||
},
|
||||
hairBangs () {
|
||||
const none = this.mapKeysToFreeOption(0, 'hair', 'bangs');
|
||||
none.none = true;
|
||||
|
||||
const options = [1, 2, 3, 4].map(s => this.mapKeysToFreeOption(s, 'hair', 'bangs'));
|
||||
|
||||
return [none, ...options];
|
||||
},
|
||||
mustacheList () {
|
||||
const noneOption = this.mapKeysToFreeOption(0, 'hair', 'mustache');
|
||||
noneOption.none = true;
|
||||
|
||||
return [noneOption, ...this.baseHair5];
|
||||
},
|
||||
beardList () {
|
||||
const noneOption = this.mapKeysToFreeOption(0, 'hair', 'beard');
|
||||
noneOption.none = true;
|
||||
|
||||
return [noneOption, ...this.baseHair6];
|
||||
},
|
||||
styleSets () {
|
||||
const sets = [];
|
||||
|
||||
const emptyHairBase = {
|
||||
...this.mapKeysToFreeOption(0, 'hair', 'base'),
|
||||
none: true,
|
||||
};
|
||||
|
||||
if (this.editing) {
|
||||
sets.push({
|
||||
fullSet: !this.userOwnsSet('hair', this.baseHair3Keys, 'base'),
|
||||
unlock: () => this.unlock(`hair.base.${this.baseHair3Keys.join(',hair.base.')}`),
|
||||
options: [
|
||||
emptyHairBase,
|
||||
...this.baseHair1.map(key => this.mapKeysToFreeOption(key, 'hair', 'base')),
|
||||
...this.baseHair3,
|
||||
],
|
||||
});
|
||||
|
||||
if (this.editing) {
|
||||
sets.push({
|
||||
fullSet: !this.userOwnsSet('hair', this.baseHair2Keys, 'base'),
|
||||
unlock: () => this.unlock(`hair.base.${this.baseHair2Keys.join(',hair.base.')}`),
|
||||
options: [
|
||||
...this.baseHair2,
|
||||
],
|
||||
});
|
||||
}
|
||||
sets.push({
|
||||
fullSet: !this.userOwnsSet('hair', this.baseHair4Keys, 'base'),
|
||||
unlock: () => this.unlock(`hair.base.${this.baseHair4Keys.join(',hair.base.')}`),
|
||||
options: [
|
||||
...this.baseHair4,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
return sets;
|
||||
},
|
||||
sets.push({
|
||||
options: [
|
||||
emptyHairBase,
|
||||
...this.baseHair1.map(key => this.mapKeysToFreeOption(key, 'hair', 'base')),
|
||||
],
|
||||
});
|
||||
|
||||
if (this.editing) {
|
||||
sets.push({
|
||||
fullSet: !this.userOwnsSet('hair', this.baseHair2Keys, 'base'),
|
||||
unlock: () => this.unlock(`hair.base.${this.baseHair2Keys.join(',hair.base.')}`),
|
||||
options: [
|
||||
...this.baseHair2,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
return sets;
|
||||
},
|
||||
mounted () {
|
||||
this.changeSubPage('color');
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
},
|
||||
mounted () {
|
||||
this.changeSubPage('color');
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Allows you to find out whether you need the "Purchase All" button or not. If there are more than 2 unpurchased items, returns true, otherwise returns false.
|
||||
* @param {string} category - The selected category.
|
||||
* @param {string[]} keySets - The items keySets.
|
||||
* @param {string[]} [types] - The items types (subcategories). Optional.
|
||||
* @returns {boolean} - Determines whether the "Purchase All" button is needed (true) or not (false).
|
||||
*/
|
||||
isPurchaseAllNeeded (category, keySets, types) {
|
||||
const purchasedItemsLengths = [];
|
||||
// If item types are specified, count them
|
||||
if (types && types.length > 0) {
|
||||
// Types can be undefined, so we must check them.
|
||||
types.forEach((type) => {
|
||||
if (this.user.purchased[category][type]) {
|
||||
purchasedItemsLengths
|
||||
.push(Object.keys(this.user.purchased[category][type]).length);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let purchasedItemsCounter = 0;
|
||||
|
||||
// If types are not specified, recursively
|
||||
// search for purchased items in the category
|
||||
const findPurchasedItems = (item) => {
|
||||
if (typeof item === 'object') {
|
||||
Object.values(item)
|
||||
.forEach((innerItem) => {
|
||||
if (typeof innerItem === 'boolean' && innerItem === true) {
|
||||
purchasedItemsCounter += 1;
|
||||
}
|
||||
return findPurchasedItems(innerItem);
|
||||
});
|
||||
}
|
||||
return purchasedItemsCounter;
|
||||
};
|
||||
|
||||
findPurchasedItems(this.user.purchased[category]);
|
||||
if (purchasedItemsCounter > 0) {
|
||||
purchasedItemsLengths.push(purchasedItemsCounter);
|
||||
isPurchaseAllNeeded (category, keySets, types) {
|
||||
const purchasedItemsLengths = [];
|
||||
// If item types are specified, count them
|
||||
if (types && types.length > 0) {
|
||||
// Types can be undefined, so we must check them.
|
||||
types.forEach(type => {
|
||||
if (this.user.purchased[category][type]) {
|
||||
purchasedItemsLengths
|
||||
.push(Object.keys(this.user.purchased[category][type]).length);
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need to count the key sets (below)
|
||||
// if there are no purchased items at all.
|
||||
if (purchasedItemsLengths.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const allItemsLengths = [];
|
||||
// Key sets must be specify correctly.
|
||||
keySets.forEach((keySet) => {
|
||||
allItemsLengths.push(Object.keys(this[keySet]).length);
|
||||
});
|
||||
} else {
|
||||
let purchasedItemsCounter = 0;
|
||||
|
||||
// Simply sum all the length values and
|
||||
// write them into variables for the convenience.
|
||||
const allItems = allItemsLengths.reduce((acc, val) => acc + val);
|
||||
const purchasedItems = purchasedItemsLengths.reduce((acc, val) => acc + val);
|
||||
// If types are not specified, recursively
|
||||
// search for purchased items in the category
|
||||
const findPurchasedItems = item => {
|
||||
if (typeof item === 'object') {
|
||||
Object.values(item)
|
||||
.forEach(innerItem => {
|
||||
if (typeof innerItem === 'boolean' && innerItem === true) {
|
||||
purchasedItemsCounter += 1;
|
||||
}
|
||||
return findPurchasedItems(innerItem);
|
||||
});
|
||||
}
|
||||
return purchasedItemsCounter;
|
||||
};
|
||||
|
||||
const unpurchasedItems = allItems - purchasedItems;
|
||||
return unpurchasedItems > 2;
|
||||
},
|
||||
findPurchasedItems(this.user.purchased[category]);
|
||||
if (purchasedItemsCounter > 0) {
|
||||
purchasedItemsLengths.push(purchasedItemsCounter);
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need to count the key sets (below)
|
||||
// if there are no purchased items at all.
|
||||
if (purchasedItemsLengths.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const allItemsLengths = [];
|
||||
// Key sets must be specify correctly.
|
||||
keySets.forEach(keySet => {
|
||||
allItemsLengths.push(Object.keys(this[keySet]).length);
|
||||
});
|
||||
|
||||
// Simply sum all the length values and
|
||||
// write them into variables for the convenience.
|
||||
const allItems = allItemsLengths.reduce((acc, val) => acc + val);
|
||||
const purchasedItems = purchasedItemsLengths.reduce((acc, val) => acc + val);
|
||||
|
||||
const unpurchasedItems = allItems - purchasedItems;
|
||||
return unpurchasedItems > 2;
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -16,97 +16,95 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import appearance from '@/../../common/script/content/appearance';
|
||||
import {subPageMixin} from '../../mixins/subPage';
|
||||
import {userStateMixin} from '../../mixins/userState';
|
||||
import {avatarEditorUtilies} from '../../mixins/avatarEditUtilities';
|
||||
import appearanceSets from '@/../../common/script/content/appearance/sets';
|
||||
import subMenu from './sub-menu';
|
||||
import customizeOptions from './customize-options';
|
||||
import gem from '@/assets/svg/gem.svg';
|
||||
|
||||
import groupBy from 'lodash/groupBy';
|
||||
|
||||
const skinsBySet = groupBy(appearance.skin, 'set.key');
|
||||
|
||||
const freeSkinKeys = skinsBySet[undefined].map(s => s.key);
|
||||
|
||||
// const specialSkinKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price !== 0);
|
||||
import groupBy from 'lodash/groupBy';
|
||||
import appearance from '@/../../common/script/content/appearance';
|
||||
import { subPageMixin } from '../../mixins/subPage';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
import { avatarEditorUtilies } from '../../mixins/avatarEditUtilities';
|
||||
import appearanceSets from '@/../../common/script/content/appearance/sets';
|
||||
import subMenu from './sub-menu';
|
||||
import customizeOptions from './customize-options';
|
||||
import gem from '@/assets/svg/gem.svg';
|
||||
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'editing',
|
||||
],
|
||||
components: {
|
||||
subMenu,
|
||||
customizeOptions,
|
||||
const skinsBySet = groupBy(appearance.skin, 'set.key');
|
||||
|
||||
const freeSkinKeys = skinsBySet[undefined].map(s => s.key);
|
||||
|
||||
// const specialSkinKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price !== 0);
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
subMenu,
|
||||
customizeOptions,
|
||||
},
|
||||
mixins: [
|
||||
subPageMixin,
|
||||
userStateMixin,
|
||||
avatarEditorUtilies,
|
||||
],
|
||||
props: [
|
||||
'editing',
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
freeSkinKeys,
|
||||
icons: Object.freeze({
|
||||
gem,
|
||||
}),
|
||||
skinSubMenuItems: [
|
||||
{
|
||||
id: 'color',
|
||||
label: this.$t('color'),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
freeSkins () {
|
||||
return freeSkinKeys.map(s => this.mapKeysToFreeOption(s, 'skin'));
|
||||
},
|
||||
mixins: [
|
||||
subPageMixin,
|
||||
userStateMixin,
|
||||
avatarEditorUtilies,
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
freeSkinKeys,
|
||||
icons: Object.freeze({
|
||||
gem,
|
||||
}),
|
||||
skinSubMenuItems: [
|
||||
{
|
||||
id: 'color',
|
||||
label: this.$t('color'),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
freeSkins () {
|
||||
return freeSkinKeys.map(s => this.mapKeysToFreeOption(s, 'skin'));
|
||||
},
|
||||
seasonalSkins () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
seasonalSkins () {
|
||||
// @TODO: For some resonse when I use $set on the user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
|
||||
let seasonalSkins = [];
|
||||
for (let setKey in skinsBySet) {
|
||||
let set = skinsBySet[setKey];
|
||||
const seasonalSkins = [];
|
||||
for (const setKey in skinsBySet) {
|
||||
const set = skinsBySet[setKey];
|
||||
|
||||
let keys = set.map(item => {
|
||||
return item.key;
|
||||
});
|
||||
const keys = set.map(item => item.key);
|
||||
|
||||
let options = keys.map(optionKey => {
|
||||
const option = this.mapKeysToOption(optionKey, 'skin', '', setKey);
|
||||
const options = keys.map(optionKey => {
|
||||
const option = this.mapKeysToOption(optionKey, 'skin', '', setKey);
|
||||
|
||||
return option;
|
||||
});
|
||||
return option;
|
||||
});
|
||||
|
||||
let text = this.$t(setKey);
|
||||
if (appearanceSets[setKey] && appearanceSets[setKey].text) {
|
||||
text = appearanceSets[setKey].text();
|
||||
}
|
||||
|
||||
let compiledSet = {
|
||||
key: setKey,
|
||||
options,
|
||||
keys,
|
||||
text,
|
||||
};
|
||||
seasonalSkins.push(compiledSet);
|
||||
let text = this.$t(setKey);
|
||||
if (appearanceSets[setKey] && appearanceSets[setKey].text) {
|
||||
text = appearanceSets[setKey].text();
|
||||
}
|
||||
|
||||
return seasonalSkins;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.changeSubPage('color');
|
||||
},
|
||||
methods: {
|
||||
const compiledSet = {
|
||||
key: setKey,
|
||||
options,
|
||||
keys,
|
||||
text,
|
||||
};
|
||||
seasonalSkins.push(compiledSet);
|
||||
}
|
||||
|
||||
return seasonalSkins;
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.changeSubPage('color');
|
||||
},
|
||||
methods: {
|
||||
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['items', 'activeSubPage'],
|
||||
};
|
||||
export default {
|
||||
props: ['items', 'activeSubPage'],
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -13,22 +13,22 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
categories: {
|
||||
required: true,
|
||||
},
|
||||
owner: {
|
||||
default: false,
|
||||
},
|
||||
member: {
|
||||
default: false,
|
||||
},
|
||||
export default {
|
||||
props: {
|
||||
categories: {
|
||||
required: true,
|
||||
},
|
||||
methods: {
|
||||
isOfficial (category) {
|
||||
return category.name === 'habitica_official';
|
||||
},
|
||||
owner: {
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
member: {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isOfficial (category) {
|
||||
return category.name === 'habitica_official';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -167,8 +167,6 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const TASK_KEYS_TO_REMOVE = ['_id', 'completed', 'date', 'dateCompleted', 'history', 'id', 'streak', 'createdAt', 'challenge'];
|
||||
|
||||
import Vue from 'vue';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
@@ -194,9 +192,9 @@ import gemIcon from '@/assets/svg/gem.svg';
|
||||
import memberIcon from '@/assets/svg/member-icon.svg';
|
||||
import calendarIcon from '@/assets/svg/calendar.svg';
|
||||
|
||||
const TASK_KEYS_TO_REMOVE = ['_id', 'completed', 'date', 'dateCompleted', 'history', 'id', 'streak', 'createdAt', 'challenge'];
|
||||
|
||||
export default {
|
||||
props: ['challengeId'],
|
||||
mixins: [challengeMemberSearchMixin],
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
@@ -212,6 +210,8 @@ export default {
|
||||
userLink,
|
||||
groupLink,
|
||||
},
|
||||
mixins: [challengeMemberSearchMixin],
|
||||
props: ['challengeId'],
|
||||
data () {
|
||||
return {
|
||||
searchId: '',
|
||||
@@ -238,7 +238,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
isMember () {
|
||||
return this.user.challenges.indexOf(this.challenge._id) !== -1;
|
||||
},
|
||||
@@ -267,11 +267,11 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
cleanUpTask (task) {
|
||||
let cleansedTask = omit(task, TASK_KEYS_TO_REMOVE);
|
||||
const cleansedTask = omit(task, TASK_KEYS_TO_REMOVE);
|
||||
|
||||
// Copy checklists but reset to uncomplete and assign new id
|
||||
if (!cleansedTask.checklist) cleansedTask.checklist = [];
|
||||
cleansedTask.checklist.forEach((item) => {
|
||||
cleansedTask.checklist.forEach(item => {
|
||||
item.completed = false;
|
||||
item.id = uuid();
|
||||
});
|
||||
@@ -283,16 +283,16 @@ export default {
|
||||
return cleansedTask;
|
||||
},
|
||||
async loadChallenge () {
|
||||
this.challenge = await this.$store.dispatch('challenges:getChallenge', {challengeId: this.searchId});
|
||||
this.challenge = await this.$store.dispatch('challenges:getChallenge', { challengeId: this.searchId });
|
||||
this.members = await this.loadMembers({ challengeId: this.searchId, includeAllPublicFields: true });
|
||||
let tasks = await this.$store.dispatch('tasks:getChallengeTasks', {challengeId: this.searchId});
|
||||
const tasks = await this.$store.dispatch('tasks:getChallengeTasks', { challengeId: this.searchId });
|
||||
this.tasksByType = {
|
||||
habit: [],
|
||||
daily: [],
|
||||
todo: [],
|
||||
reward: [],
|
||||
};
|
||||
tasks.forEach((task) => {
|
||||
tasks.forEach(task => {
|
||||
this.tasksByType[task.type].push(task);
|
||||
});
|
||||
},
|
||||
@@ -322,7 +322,7 @@ export default {
|
||||
},
|
||||
createTask (type) {
|
||||
this.taskFormPurpose = 'create';
|
||||
this.creatingTask = taskDefaults({type, text: ''}, this.user);
|
||||
this.creatingTask = taskDefaults({ type, text: '' }, this.user);
|
||||
this.workingTask = this.creatingTask;
|
||||
// Necessary otherwise the first time the modal is not rendered
|
||||
Vue.nextTick(() => {
|
||||
@@ -337,15 +337,11 @@ export default {
|
||||
this.tasksByType[task.type].push(task);
|
||||
},
|
||||
taskEdited (task) {
|
||||
let index = findIndex(this.tasksByType[task.type], (taskItem) => {
|
||||
return taskItem._id === task._id;
|
||||
});
|
||||
const index = findIndex(this.tasksByType[task.type], taskItem => taskItem._id === task._id);
|
||||
this.tasksByType[task.type].splice(index, 1, task);
|
||||
},
|
||||
taskDestroyed (task) {
|
||||
let index = findIndex(this.tasksByType[task.type], (taskItem) => {
|
||||
return taskItem._id === task._id;
|
||||
});
|
||||
const index = findIndex(this.tasksByType[task.type], taskItem => taskItem._id === task._id);
|
||||
this.tasksByType[task.type].splice(index, 1);
|
||||
},
|
||||
showMemberModal () {
|
||||
@@ -360,8 +356,8 @@ export default {
|
||||
},
|
||||
async joinChallenge () {
|
||||
this.user.challenges.push(this.searchId);
|
||||
await this.$store.dispatch('challenges:joinChallenge', {challengeId: this.searchId});
|
||||
await this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true});
|
||||
await this.$store.dispatch('challenges:joinChallenge', { challengeId: this.searchId });
|
||||
await this.$store.dispatch('tasks:fetchUserTasks', { forceLoad: true });
|
||||
},
|
||||
async leaveChallenge () {
|
||||
this.$root.$emit('bv::show::modal', 'leave-challenge-modal');
|
||||
|
||||
@@ -303,89 +303,89 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { TAVERN_ID } from '@/../../common/script/constants';
|
||||
import userLink from '../userLink';
|
||||
import groupLink from '../groupLink';
|
||||
import categoryTags from '../categories/categoryTags';
|
||||
import markdownDirective from '@/directives/markdown';
|
||||
import {mapState} from '@/libs/store';
|
||||
import { TAVERN_ID } from '@/../../common/script/constants';
|
||||
import userLink from '../userLink';
|
||||
import groupLink from '../groupLink';
|
||||
import categoryTags from '../categories/categoryTags';
|
||||
import markdownDirective from '@/directives/markdown';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
import gemIcon from '@/assets/svg/gem.svg';
|
||||
import memberIcon from '@/assets/svg/member-icon.svg';
|
||||
import calendarIcon from '@/assets/svg/calendar.svg';
|
||||
import habitIcon from '@/assets/svg/habit.svg';
|
||||
import todoIcon from '@/assets/svg/todo.svg';
|
||||
import dailyIcon from '@/assets/svg/daily.svg';
|
||||
import rewardIcon from '@/assets/svg/reward.svg';
|
||||
import officialIcon from '@/assets/svg/official.svg';
|
||||
import gemIcon from '@/assets/svg/gem.svg';
|
||||
import memberIcon from '@/assets/svg/member-icon.svg';
|
||||
import calendarIcon from '@/assets/svg/calendar.svg';
|
||||
import habitIcon from '@/assets/svg/habit.svg';
|
||||
import todoIcon from '@/assets/svg/todo.svg';
|
||||
import dailyIcon from '@/assets/svg/daily.svg';
|
||||
import rewardIcon from '@/assets/svg/reward.svg';
|
||||
import officialIcon from '@/assets/svg/official.svg';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
challenge: {
|
||||
required: true,
|
||||
},
|
||||
fullLayout: {
|
||||
default: true,
|
||||
},
|
||||
export default {
|
||||
components: {
|
||||
userLink,
|
||||
groupLink,
|
||||
categoryTags,
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
props: {
|
||||
challenge: {
|
||||
required: true,
|
||||
},
|
||||
components: {
|
||||
userLink,
|
||||
groupLink,
|
||||
categoryTags,
|
||||
fullLayout: {
|
||||
default: true,
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
gemIcon,
|
||||
memberIcon,
|
||||
calendarIcon,
|
||||
officialIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
isOwner () {
|
||||
return this.challenge.leader && this.challenge.leader._id === this.user._id;
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
gemIcon,
|
||||
memberIcon,
|
||||
calendarIcon,
|
||||
officialIcon,
|
||||
}),
|
||||
};
|
||||
isMember () {
|
||||
return this.user.challenges.indexOf(this.challenge._id) !== -1;
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
isOwner () {
|
||||
return this.challenge.leader && this.challenge.leader._id === this.user._id;
|
||||
},
|
||||
isMember () {
|
||||
return this.user.challenges.indexOf(this.challenge._id) !== -1;
|
||||
},
|
||||
isOfficial () {
|
||||
return this.challenge.official || this.challenge.categories.map(category => category.slug).includes('habitica_official');
|
||||
},
|
||||
tasksData () {
|
||||
return [
|
||||
{
|
||||
icon: habitIcon,
|
||||
label: 'habit',
|
||||
value: this.challenge.tasksOrder.habits.length,
|
||||
},
|
||||
{
|
||||
icon: dailyIcon,
|
||||
label: 'daily',
|
||||
value: this.challenge.tasksOrder.dailys.length,
|
||||
},
|
||||
{
|
||||
icon: todoIcon,
|
||||
label: 'todo',
|
||||
value: this.challenge.tasksOrder.todos.length,
|
||||
},
|
||||
{
|
||||
icon: rewardIcon,
|
||||
label: 'reward',
|
||||
value: this.challenge.tasksOrder.rewards.length,
|
||||
},
|
||||
];
|
||||
},
|
||||
isOfficial () {
|
||||
return this.challenge.official || this.challenge.categories.map(category => category.slug).includes('habitica_official');
|
||||
},
|
||||
methods: {
|
||||
isTavern (group) {
|
||||
return group._id === TAVERN_ID;
|
||||
},
|
||||
tasksData () {
|
||||
return [
|
||||
{
|
||||
icon: habitIcon,
|
||||
label: 'habit',
|
||||
value: this.challenge.tasksOrder.habits.length,
|
||||
},
|
||||
{
|
||||
icon: dailyIcon,
|
||||
label: 'daily',
|
||||
value: this.challenge.tasksOrder.dailys.length,
|
||||
},
|
||||
{
|
||||
icon: todoIcon,
|
||||
label: 'todo',
|
||||
value: this.challenge.tasksOrder.todos.length,
|
||||
},
|
||||
{
|
||||
icon: rewardIcon,
|
||||
label: 'reward',
|
||||
value: this.challenge.tasksOrder.rewards.length,
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
isTavern (group) {
|
||||
return group._id === TAVERN_ID;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -23,10 +23,10 @@ import axios from 'axios';
|
||||
import Column from '../tasks/column';
|
||||
|
||||
export default {
|
||||
props: ['challengeId'],
|
||||
components: {
|
||||
TaskColumn: Column,
|
||||
},
|
||||
props: ['challengeId'],
|
||||
data () {
|
||||
return {
|
||||
columns: ['habit', 'daily', 'todo', 'reward'],
|
||||
@@ -41,18 +41,6 @@ export default {
|
||||
isAdmin: false,
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica:challenge:member-progress', (data) => {
|
||||
if (!data.progressMemberId) return;
|
||||
this.memberId = data.progressMemberId;
|
||||
this.isLeader = data.isLeader;
|
||||
this.isAdmin = data.isAdmin;
|
||||
this.$root.$emit('bv::show::modal', 'challenge-member-modal');
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$root.$off('habitica:challenge:member-progress');
|
||||
},
|
||||
watch: {
|
||||
async memberId (id) {
|
||||
if (!id) return;
|
||||
@@ -63,13 +51,25 @@ export default {
|
||||
reward: [],
|
||||
};
|
||||
|
||||
let response = await axios.get(`/api/v4/challenges/${this.challengeId}/members/${this.memberId}`);
|
||||
let tasks = response.data.data.tasks;
|
||||
tasks.forEach((task) => {
|
||||
const response = await axios.get(`/api/v4/challenges/${this.challengeId}/members/${this.memberId}`);
|
||||
const { tasks } = response.data.data;
|
||||
tasks.forEach(task => {
|
||||
this.tasksByType[task.type].push(task);
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica:challenge:member-progress', data => {
|
||||
if (!data.progressMemberId) return;
|
||||
this.memberId = data.progressMemberId;
|
||||
this.isLeader = data.isLeader;
|
||||
this.isAdmin = data.isAdmin;
|
||||
this.$root.$emit('bv::show::modal', 'challenge-member-modal');
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$root.$off('habitica:challenge:member-progress');
|
||||
},
|
||||
methods: {
|
||||
async closeChallenge () {
|
||||
this.challenge = await this.$store.dispatch('challenges:selectChallengeWinner', {
|
||||
|
||||
@@ -138,12 +138,12 @@ import { TAVERN_ID, MIN_SHORTNAME_SIZE_FOR_CHALLENGES, MAX_SUMMARY_SIZE_FOR_CHAL
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
props: ['groupId'],
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
props: ['groupId'],
|
||||
data () {
|
||||
let categoryOptions = [
|
||||
const categoryOptions = [
|
||||
{
|
||||
label: 'habitica_official',
|
||||
key: 'habitica_official',
|
||||
@@ -201,11 +201,11 @@ export default {
|
||||
key: 'time_management',
|
||||
},
|
||||
];
|
||||
let hashedCategories = {};
|
||||
categoryOptions.forEach((category) => {
|
||||
const hashedCategories = {};
|
||||
categoryOptions.forEach(category => {
|
||||
hashedCategories[category.key] = category.label;
|
||||
});
|
||||
let categoriesHashByKey = hashedCategories;
|
||||
const categoriesHashByKey = hashedCategories;
|
||||
|
||||
return {
|
||||
workingChallenge: {
|
||||
@@ -233,24 +233,6 @@ export default {
|
||||
groups: [],
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica:clone-challenge', (data) => {
|
||||
if (!data.challenge) return;
|
||||
this.cloning = true;
|
||||
this.cloningChallengeId = data.challenge._id;
|
||||
this.$store.state.challengeOptions.workingChallenge = Object.assign({}, this.$store.state.challengeOptions.workingChallenge, data.challenge);
|
||||
this.$root.$emit('bv::show::modal', 'challenge-modal');
|
||||
});
|
||||
this.$root.$on('habitica:update-challenge', (data) => {
|
||||
if (!data.challenge) return;
|
||||
this.cloning = false;
|
||||
this.$store.state.challengeOptions.workingChallenge = Object.assign({}, this.$store.state.challengeOptions.workingChallenge, data.challenge);
|
||||
this.$root.$emit('bv::show::modal', 'challenge-modal');
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$root.$off('habitica:clone-challenge');
|
||||
},
|
||||
watch: {
|
||||
user () {
|
||||
if (!this.challenge) this.workingChallenge.leader = this.user._id;
|
||||
@@ -262,8 +244,26 @@ export default {
|
||||
this.setUpWorkingChallenge();
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica:clone-challenge', data => {
|
||||
if (!data.challenge) return;
|
||||
this.cloning = true;
|
||||
this.cloningChallengeId = data.challenge._id;
|
||||
this.$store.state.challengeOptions.workingChallenge = { ...this.$store.state.challengeOptions.workingChallenge, ...data.challenge };
|
||||
this.$root.$emit('bv::show::modal', 'challenge-modal');
|
||||
});
|
||||
this.$root.$on('habitica:update-challenge', data => {
|
||||
if (!data.challenge) return;
|
||||
this.cloning = false;
|
||||
this.$store.state.challengeOptions.workingChallenge = { ...this.$store.state.challengeOptions.workingChallenge, ...data.challenge };
|
||||
this.$root.$emit('bv::show::modal', 'challenge-modal');
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$root.$off('habitica:clone-challenge');
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
creating () {
|
||||
return !this.workingChallenge.id;
|
||||
},
|
||||
@@ -274,19 +274,18 @@ export default {
|
||||
return this.$t('editingChallenge');
|
||||
},
|
||||
charactersRemaining () {
|
||||
let currentLength = this.workingChallenge.summary ? this.workingChallenge.summary.length : 0;
|
||||
const currentLength = this.workingChallenge.summary ? this.workingChallenge.summary.length : 0;
|
||||
return MAX_SUMMARY_SIZE_FOR_CHALLENGES - currentLength;
|
||||
},
|
||||
maxPrize () {
|
||||
let userBalance = this.user.balance || 0;
|
||||
userBalance = userBalance * 4;
|
||||
userBalance *= 4;
|
||||
|
||||
let groupBalance = 0;
|
||||
let group;
|
||||
this.groups.forEach(item => {
|
||||
if (item._id === this.workingChallenge.group) {
|
||||
group = item;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -301,14 +300,13 @@ export default {
|
||||
return 0;
|
||||
},
|
||||
insufficientGemsForTavernChallenge () {
|
||||
let balance = this.user.balance || 0;
|
||||
let isForTavern = this.workingChallenge.group === TAVERN_ID;
|
||||
const balance = this.user.balance || 0;
|
||||
const isForTavern = this.workingChallenge.group === TAVERN_ID;
|
||||
|
||||
if (isForTavern) {
|
||||
return balance <= 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
challenge () {
|
||||
return this.$store.state.challengeOptions.workingChallenge;
|
||||
@@ -342,7 +340,7 @@ export default {
|
||||
|
||||
if (!this.challenge) return;
|
||||
|
||||
this.workingChallenge = Object.assign({}, this.workingChallenge, this.challenge);
|
||||
this.workingChallenge = { ...this.workingChallenge, ...this.challenge };
|
||||
// @TODO: Should we use a separate field? I think the API expects `group` but it is confusing
|
||||
this.workingChallenge.group = this.workingChallenge.group._id;
|
||||
this.workingChallenge.categories = [];
|
||||
@@ -381,7 +379,7 @@ export default {
|
||||
async createChallenge () {
|
||||
this.loading = true;
|
||||
// @TODO: improve error handling, add it to updateChallenge, make errors translatable. Suggestion: `<% fieldName %> is required` where possible, where `fieldName` is inserted as the translatable string that's used for the field header.
|
||||
let errors = [];
|
||||
const errors = [];
|
||||
|
||||
if (!this.workingChallenge.name) errors.push(this.$t('nameRequired'));
|
||||
if (this.workingChallenge.shortName.length < MIN_SHORTNAME_SIZE_FOR_CHALLENGES) errors.push(this.$t('tagTooShort'));
|
||||
@@ -398,17 +396,17 @@ export default {
|
||||
}
|
||||
|
||||
this.workingChallenge.timestamp = new Date().getTime();
|
||||
let categoryKeys = this.workingChallenge.categories;
|
||||
let serverCategories = [];
|
||||
const categoryKeys = this.workingChallenge.categories;
|
||||
const serverCategories = [];
|
||||
categoryKeys.forEach(key => {
|
||||
let catName = this.categoriesHashByKey[key];
|
||||
const catName = this.categoriesHashByKey[key];
|
||||
serverCategories.push({
|
||||
slug: key,
|
||||
name: catName,
|
||||
});
|
||||
});
|
||||
|
||||
let challengeDetails = clone(this.workingChallenge);
|
||||
const challengeDetails = clone(this.workingChallenge);
|
||||
challengeDetails.categories = serverCategories;
|
||||
|
||||
let challenge;
|
||||
@@ -419,13 +417,11 @@ export default {
|
||||
});
|
||||
this.cloningChallengeId = '';
|
||||
} else {
|
||||
challenge = await this.$store.dispatch('challenges:createChallenge', {challenge: challengeDetails});
|
||||
challenge = await this.$store.dispatch('challenges:createChallenge', { challenge: challengeDetails });
|
||||
}
|
||||
|
||||
// Update Group Prize
|
||||
let challengeGroup = this.groups.find(group => {
|
||||
return group._id === this.workingChallenge.group;
|
||||
});
|
||||
const challengeGroup = this.groups.find(group => group._id === this.workingChallenge.group);
|
||||
|
||||
// @TODO: Share with server
|
||||
const prizeCost = this.workingChallenge.prize / 4;
|
||||
@@ -435,7 +431,7 @@ export default {
|
||||
// Group pays for all of prize
|
||||
} else if (challengeGroup && userIsLeader && challengeGroup.balance > 0) {
|
||||
// User pays remainder of prize cost after group
|
||||
let remainder = prizeCost - challengeGroup.balance;
|
||||
const remainder = prizeCost - challengeGroup.balance;
|
||||
this.user.balance -= remainder;
|
||||
} else {
|
||||
// User pays for all of prize
|
||||
@@ -449,24 +445,24 @@ export default {
|
||||
this.$router.push(`/challenges/${challenge._id}`);
|
||||
},
|
||||
updateChallenge () {
|
||||
let categoryKeys = this.workingChallenge.categories;
|
||||
let serverCategories = [];
|
||||
const categoryKeys = this.workingChallenge.categories;
|
||||
const serverCategories = [];
|
||||
categoryKeys.forEach(key => {
|
||||
let newKey = key.trim();
|
||||
let catName = this.categoriesHashByKey[newKey];
|
||||
const newKey = key.trim();
|
||||
const catName = this.categoriesHashByKey[newKey];
|
||||
serverCategories.push({
|
||||
slug: newKey,
|
||||
name: catName,
|
||||
});
|
||||
});
|
||||
|
||||
let challengeDetails = clone(this.workingChallenge);
|
||||
const challengeDetails = clone(this.workingChallenge);
|
||||
challengeDetails.categories = serverCategories;
|
||||
|
||||
this.$emit('updatedChallenge', {
|
||||
challenge: challengeDetails,
|
||||
});
|
||||
this.$store.dispatch('challenges:updateChallenge', {challenge: challengeDetails});
|
||||
this.$store.dispatch('challenges:updateChallenge', { challenge: challengeDetails });
|
||||
this.resetWorkingChallenge();
|
||||
this.$root.$emit('bv::hide::modal', 'challenge-modal');
|
||||
},
|
||||
|
||||
@@ -74,10 +74,10 @@ div
|
||||
import memberSearchDropdown from '@/components/members/memberSearchDropdown';
|
||||
|
||||
export default {
|
||||
props: ['challengeId', 'members', 'prize'],
|
||||
components: {
|
||||
memberSearchDropdown,
|
||||
},
|
||||
props: ['challengeId', 'members', 'prize'],
|
||||
data () {
|
||||
return {
|
||||
winner: {},
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import MugenScroll from 'vue-mugen-scroll';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
import Sidebar from './sidebar';
|
||||
@@ -75,18 +77,15 @@ import challengeUtilities from '@/mixins/challengeUtilities';
|
||||
|
||||
import positiveIcon from '@/assets/svg/positive.svg';
|
||||
|
||||
import MugenScroll from 'vue-mugen-scroll';
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
export default {
|
||||
mixins: [challengeUtilities],
|
||||
components: {
|
||||
Sidebar,
|
||||
ChallengeItem,
|
||||
challengeModal,
|
||||
MugenScroll,
|
||||
},
|
||||
mixins: [challengeUtilities],
|
||||
data () {
|
||||
return {
|
||||
loading: true,
|
||||
@@ -127,7 +126,7 @@ export default {
|
||||
this.loadChallenges();
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
filteredChallenges () {
|
||||
return this.challenges;
|
||||
},
|
||||
|
||||
@@ -38,55 +38,55 @@ div
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import challengeModal from './challengeModal';
|
||||
import {mapState} from '@/libs/store';
|
||||
import markdownDirective from '@/directives/markdown';
|
||||
import challengeModal from './challengeModal';
|
||||
import { mapState } from '@/libs/store';
|
||||
import markdownDirective from '@/directives/markdown';
|
||||
|
||||
import challengeItem from './challengeItem';
|
||||
import challengeIcon from '@/assets/svg/challenge.svg';
|
||||
import challengeItem from './challengeItem';
|
||||
import challengeIcon from '@/assets/svg/challenge.svg';
|
||||
|
||||
export default {
|
||||
props: ['groupId'],
|
||||
components: {
|
||||
challengeModal,
|
||||
challengeItem,
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
challenges: [],
|
||||
icons: Object.freeze({
|
||||
challengeIcon,
|
||||
}),
|
||||
groupIdForChallenges: '',
|
||||
};
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
mounted () {
|
||||
export default {
|
||||
components: {
|
||||
challengeModal,
|
||||
challengeItem,
|
||||
},
|
||||
props: ['groupId'],
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
challenges: [],
|
||||
icons: Object.freeze({
|
||||
challengeIcon,
|
||||
}),
|
||||
groupIdForChallenges: '',
|
||||
};
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
watch: {
|
||||
async groupId () {
|
||||
this.loadChallenges();
|
||||
},
|
||||
watch: {
|
||||
async groupId () {
|
||||
this.loadChallenges();
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.loadChallenges();
|
||||
},
|
||||
methods: {
|
||||
async loadChallenges () {
|
||||
this.groupIdForChallenges = this.groupId;
|
||||
if (this.groupId === 'party' && this.user.party._id) this.groupIdForChallenges = this.user.party._id;
|
||||
this.challenges = await this.$store.dispatch('challenges:getGroupChallenges', { groupId: this.groupIdForChallenges });
|
||||
},
|
||||
methods: {
|
||||
async loadChallenges () {
|
||||
this.groupIdForChallenges = this.groupId;
|
||||
if (this.groupId === 'party' && this.user.party._id) this.groupIdForChallenges = this.user.party._id;
|
||||
this.challenges = await this.$store.dispatch('challenges:getGroupChallenges', {groupId: this.groupIdForChallenges});
|
||||
},
|
||||
createChallenge () {
|
||||
this.$root.$emit('bv::show::modal', 'challenge-modal');
|
||||
},
|
||||
challengeCreated (challenge) {
|
||||
if (challenge.group._id !== this.groupIdForChallenges) return;
|
||||
this.challenges.push(challenge);
|
||||
},
|
||||
createChallenge () {
|
||||
this.$root.$emit('bv::show::modal', 'challenge-modal');
|
||||
},
|
||||
};
|
||||
challengeCreated (challenge) {
|
||||
if (challenge.group._id !== this.groupIdForChallenges) return;
|
||||
this.challenges.push(challenge);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -19,22 +19,20 @@ import { mapState } from '@/libs/store';
|
||||
import notifications from '@/mixins/notifications';
|
||||
|
||||
export default {
|
||||
props: ['challengeId'],
|
||||
mixins: [notifications],
|
||||
props: ['challengeId'],
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
async leaveChallenge (keep) {
|
||||
let index = findIndex(this.user.challenges, (id) => {
|
||||
return id === this.challengeId;
|
||||
});
|
||||
const index = findIndex(this.user.challenges, id => id === this.challengeId);
|
||||
this.user.challenges.splice(index, 1);
|
||||
await this.$store.dispatch('challenges:leaveChallenge', {
|
||||
challengeId: this.challengeId,
|
||||
keep,
|
||||
});
|
||||
await this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true});
|
||||
await this.$store.dispatch('tasks:fetchUserTasks', { forceLoad: true });
|
||||
this.close();
|
||||
},
|
||||
close () {
|
||||
|
||||
@@ -80,6 +80,8 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import MugenScroll from 'vue-mugen-scroll';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { mapState } from '@/libs/store';
|
||||
import Sidebar from './sidebar';
|
||||
import ChallengeItem from './challengeItem';
|
||||
@@ -88,18 +90,16 @@ import challengeUtilities from '@/mixins/challengeUtilities';
|
||||
|
||||
import challengeIcon from '@/assets/svg/challenge.svg';
|
||||
import positiveIcon from '@/assets/svg/positive.svg';
|
||||
import MugenScroll from 'vue-mugen-scroll';
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
export default {
|
||||
mixins: [challengeUtilities],
|
||||
components: {
|
||||
Sidebar,
|
||||
ChallengeItem,
|
||||
challengeModal,
|
||||
MugenScroll,
|
||||
},
|
||||
mixins: [challengeUtilities],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
@@ -141,15 +141,15 @@ export default {
|
||||
this.loadChallenges();
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
filteredChallenges () {
|
||||
const filters = this.filters;
|
||||
const user = this.user;
|
||||
const { filters } = this;
|
||||
const { user } = this;
|
||||
|
||||
return this.challenges.filter(challenge => {
|
||||
let isMember = true;
|
||||
|
||||
let filteringRole = filters.roles && filters.roles.length > 0;
|
||||
const filteringRole = filters.roles && filters.roles.length > 0;
|
||||
if (filteringRole && filters.roles.indexOf('participating') !== -1) {
|
||||
isMember = this.isMemberOfChallenge(user, challenge);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ import tier9 from '@/assets/svg/tier-staff.svg';
|
||||
import tierNPC from '@/assets/svg/tier-npc.svg';
|
||||
|
||||
export default {
|
||||
mixins: [styleHelper],
|
||||
props: ['selections', 'text', 'caretPosition', 'coords', 'chat', 'textbox'],
|
||||
data () {
|
||||
return {
|
||||
@@ -93,7 +94,7 @@ export default {
|
||||
computed: {
|
||||
autocompleteStyle () {
|
||||
function heightToUse (textBox, topCoords) {
|
||||
let textBoxHeight = textBox['user-entry'].clientHeight;
|
||||
const textBoxHeight = textBox['user-entry'].clientHeight;
|
||||
return topCoords < textBoxHeight ? topCoords + 30 : textBoxHeight + 10;
|
||||
}
|
||||
return {
|
||||
@@ -113,15 +114,10 @@ export default {
|
||||
this.currentSearch = this.atRegex.exec(this.text)[0]; // eslint-disable-line vue/no-side-effects-in-computed-properties
|
||||
this.currentSearch = this.currentSearch.substring(1, this.currentSearch.length); // eslint-disable-line vue/no-side-effects-in-computed-properties
|
||||
|
||||
return this.tmpSelections.filter((option) => {
|
||||
return option.displayName.toLowerCase().indexOf(this.currentSearch.toLowerCase()) !== -1 || option.username && option.username.toLowerCase().indexOf(this.currentSearch.toLowerCase()) !== -1;
|
||||
}).slice(0, 4);
|
||||
return this.tmpSelections.filter(option => option.displayName.toLowerCase().indexOf(this.currentSearch.toLowerCase()) !== -1 || option.username && option.username.toLowerCase().indexOf(this.currentSearch.toLowerCase()) !== -1).slice(0, 4);
|
||||
},
|
||||
|
||||
},
|
||||
mounted () {
|
||||
this.grabUserNames();
|
||||
},
|
||||
watch: {
|
||||
text (newText) {
|
||||
if (!newText[newText.length - 1] || newText[newText.length - 1] === ' ') {
|
||||
@@ -143,6 +139,9 @@ export default {
|
||||
this.grabUserNames();
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.grabUserNames();
|
||||
},
|
||||
methods: {
|
||||
resetDefaults () {
|
||||
// Mounted is not called when switching between group pages because they have the
|
||||
@@ -153,9 +152,9 @@ export default {
|
||||
this.resetSelection();
|
||||
},
|
||||
grabUserNames () {
|
||||
let usersThatMessage = groupBy(this.chat, 'user');
|
||||
for (let userKey in usersThatMessage) {
|
||||
let systemMessage = userKey === 'undefined';
|
||||
const usersThatMessage = groupBy(this.chat, 'user');
|
||||
for (const userKey in usersThatMessage) {
|
||||
const systemMessage = userKey === 'undefined';
|
||||
if (!systemMessage && this.tmpSelections.indexOf(userKey) === -1) {
|
||||
this.tmpSelections.push({
|
||||
displayName: userKey,
|
||||
@@ -204,18 +203,18 @@ export default {
|
||||
selectNext () {
|
||||
if (this.searchResults.length > 0) {
|
||||
this.clearHover();
|
||||
this.selected = this.selected === null ?
|
||||
0 :
|
||||
(this.selected + 1) % this.searchResults.length;
|
||||
this.selected = this.selected === null
|
||||
? 0
|
||||
: (this.selected + 1) % this.searchResults.length;
|
||||
this.searchResults[this.selected].hover = true;
|
||||
}
|
||||
},
|
||||
selectPrevious () {
|
||||
if (this.searchResults.length > 0) {
|
||||
this.clearHover();
|
||||
this.selected = this.selected === null ?
|
||||
this.searchResults.length - 1 :
|
||||
(this.selected - 1 + this.searchResults.length) % this.searchResults.length;
|
||||
this.selected = this.selected === null
|
||||
? this.searchResults.length - 1
|
||||
: (this.selected - 1 + this.searchResults.length) % this.searchResults.length;
|
||||
this.searchResults[this.selected].hover = true;
|
||||
}
|
||||
},
|
||||
@@ -231,6 +230,5 @@ export default {
|
||||
this.resetSelection();
|
||||
},
|
||||
},
|
||||
mixins: [styleHelper],
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -140,7 +140,16 @@ import { highlightUsers } from '../../libs/highlightUsers';
|
||||
import { CHAT_FLAG_LIMIT_FOR_HIDING, CHAT_FLAG_FROM_SHADOW_MUTE } from '@/../../common/script/constants';
|
||||
|
||||
export default {
|
||||
components: {userLink},
|
||||
components: { userLink },
|
||||
filters: {
|
||||
timeAgo (value) {
|
||||
return moment(value).fromNow();
|
||||
},
|
||||
date (value) {
|
||||
// @TODO: Vue doesn't support this so we cant user preference
|
||||
return moment(value).toDate().toString();
|
||||
},
|
||||
},
|
||||
props: {
|
||||
msg: {},
|
||||
inbox: {
|
||||
@@ -161,20 +170,11 @@ export default {
|
||||
reported: false,
|
||||
};
|
||||
},
|
||||
filters: {
|
||||
timeAgo (value) {
|
||||
return moment(value).fromNow();
|
||||
},
|
||||
date (value) {
|
||||
// @TODO: Vue doesn't support this so we cant user preference
|
||||
return moment(value).toDate().toString();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
isUserMentioned () {
|
||||
const message = this.msg;
|
||||
const user = this.user;
|
||||
const { user } = this;
|
||||
|
||||
if (message.hasOwnProperty('highlight')) return message.highlight;
|
||||
|
||||
@@ -190,7 +190,7 @@ export default {
|
||||
const pattern = `@(${escapedUsername}|${escapedDisplayName})(\\b)`;
|
||||
const precedingChar = messageText.substring(mentioned - 1, mentioned);
|
||||
if (mentioned === 0 || precedingChar.trim() === '' || precedingChar === '@') {
|
||||
let regex = new RegExp(pattern, 'i');
|
||||
const regex = new RegExp(pattern, 'i');
|
||||
message.highlight = regex.test(messageText);
|
||||
}
|
||||
|
||||
@@ -201,8 +201,8 @@ export default {
|
||||
if (!message.likes) return 0;
|
||||
|
||||
let likeCount = 0;
|
||||
for (let key in message.likes) {
|
||||
let like = message.likes[key];
|
||||
for (const key in message.likes) {
|
||||
const like = message.likes[key];
|
||||
if (like) likeCount += 1;
|
||||
}
|
||||
return likeCount;
|
||||
@@ -217,9 +217,14 @@ export default {
|
||||
return 'Message hidden (shadow-muted)';
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.CHAT_FLAG_LIMIT_FOR_HIDING = CHAT_FLAG_LIMIT_FOR_HIDING;
|
||||
this.CHAT_FLAG_FROM_SHADOW_MUTE = CHAT_FLAG_FROM_SHADOW_MUTE;
|
||||
this.$emit('chat-card-mounted', this.msg.id);
|
||||
},
|
||||
methods: {
|
||||
async like () {
|
||||
let message = cloneDeep(this.msg);
|
||||
const message = cloneDeep(this.msg);
|
||||
|
||||
await this.$store.dispatch('chat:like', {
|
||||
groupId: this.groupId,
|
||||
@@ -279,10 +284,5 @@ export default {
|
||||
return habiticaMarkdown.render(String(text));
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.CHAT_FLAG_LIMIT_FOR_HIDING = CHAT_FLAG_LIMIT_FOR_HIDING;
|
||||
this.CHAT_FLAG_FROM_SHADOW_MUTE = CHAT_FLAG_FROM_SHADOW_MUTE;
|
||||
this.$emit('chat-card-mounted', this.msg.id);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -140,15 +140,20 @@
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import axios from 'axios';
|
||||
import { mapState } from '@/libs/store';
|
||||
import debounce from 'lodash/debounce';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
import Avatar from '../avatar';
|
||||
import copyAsTodoModal from './copyAsTodoModal';
|
||||
import chatCard from './chatCard';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
copyAsTodoModal,
|
||||
chatCard,
|
||||
Avatar,
|
||||
},
|
||||
props: {
|
||||
chat: {},
|
||||
inbox: {
|
||||
@@ -162,20 +167,6 @@ export default {
|
||||
isLoading: Boolean,
|
||||
canLoadMore: Boolean,
|
||||
},
|
||||
components: {
|
||||
copyAsTodoModal,
|
||||
chatCard,
|
||||
Avatar,
|
||||
},
|
||||
mounted () {
|
||||
this.loadProfileCache();
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
currentDayDividerDisplay: moment().day(),
|
||||
@@ -187,8 +178,17 @@ export default {
|
||||
lastOffset: -1,
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.loadProfileCache();
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
// @TODO: We need a different lazy load mechnism.
|
||||
// But honestly, adding a paging route to chat would solve this
|
||||
messages () {
|
||||
@@ -201,7 +201,7 @@ export default {
|
||||
this.loadProfileCache(window.scrollY / 1000);
|
||||
},
|
||||
async triggerLoad () {
|
||||
const container = this.$refs.container;
|
||||
const { container } = this.$refs;
|
||||
|
||||
// get current offset
|
||||
this.lastOffset = container.scrollTop - (container.scrollHeight - container.clientHeight);
|
||||
@@ -226,7 +226,7 @@ export default {
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
|
||||
let promises = [];
|
||||
const promises = [];
|
||||
const noProfilesLoaded = Object.keys(this.cachedProfileData).length === 0;
|
||||
|
||||
// @TODO: write an explination
|
||||
@@ -237,9 +237,9 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
let aboutToCache = {};
|
||||
const aboutToCache = {};
|
||||
this.messages.forEach(message => {
|
||||
let uuid = message.uuid;
|
||||
const { uuid } = message;
|
||||
|
||||
if (message.userStyles) {
|
||||
this.$set(this.cachedProfileData, uuid, message.userStyles);
|
||||
@@ -253,21 +253,21 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
let results = await Promise.all(promises);
|
||||
const results = await Promise.all(promises);
|
||||
results.forEach(result => {
|
||||
// We could not load the user. Maybe they were deleted. So, let's cache empty so we don't try again
|
||||
if (!result || !result.data || result.status >= 400) {
|
||||
return;
|
||||
}
|
||||
|
||||
let userData = result.data.data;
|
||||
const userData = result.data.data;
|
||||
this.$set(this.cachedProfileData, userData._id, userData);
|
||||
});
|
||||
|
||||
// Merge in any attempts that were rejected so we don't attempt again
|
||||
for (let uuid in aboutToCache) {
|
||||
for (const uuid in aboutToCache) {
|
||||
if (!this.cachedProfileData[uuid]) {
|
||||
this.$set(this.cachedProfileData, uuid, {rejected: true});
|
||||
this.$set(this.cachedProfileData, uuid, { rejected: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,22 +293,21 @@ export default {
|
||||
type: 'error',
|
||||
timeout: false,
|
||||
});
|
||||
} else {
|
||||
this.cachedProfileData[memberId] = result.data.data;
|
||||
profile = result.data.data;
|
||||
}
|
||||
this.cachedProfileData[memberId] = result.data.data;
|
||||
profile = result.data.data;
|
||||
}
|
||||
|
||||
// Open the modal only if the data is available
|
||||
if (profile && !profile.rejected) {
|
||||
this.$router.push({name: 'userProfile', params: {userId: profile._id}});
|
||||
this.$router.push({ name: 'userProfile', params: { userId: profile._id } });
|
||||
}
|
||||
},
|
||||
itemWasMounted: debounce(function itemWasMounted () {
|
||||
itemWasMounted: debounce(function itemWasMounted () {
|
||||
if (this.handleScrollBack) {
|
||||
this.handleScrollBack = false;
|
||||
|
||||
const container = this.$refs.container;
|
||||
const { container } = this.$refs;
|
||||
const offset = container.scrollHeight - container.clientHeight;
|
||||
|
||||
const newOffset = offset + this.lastOffset;
|
||||
@@ -319,9 +318,7 @@ export default {
|
||||
}
|
||||
}, 50),
|
||||
messageLiked (message) {
|
||||
const chatIndex = findIndex(this.chat, chatMessage => {
|
||||
return chatMessage.id === message.id;
|
||||
});
|
||||
const chatIndex = findIndex(this.chat, chatMessage => chatMessage.id === message.id);
|
||||
this.chat.splice(chatIndex, 1, message);
|
||||
},
|
||||
messageRemoved (message) {
|
||||
@@ -330,9 +327,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const chatIndex = findIndex(this.chat, chatMessage => {
|
||||
return chatMessage.id === message.id;
|
||||
});
|
||||
const chatIndex = findIndex(this.chat, chatMessage => chatMessage.id === message.id);
|
||||
this.chat.splice(chatIndex, 1);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -59,11 +59,10 @@ export default {
|
||||
groupPath () {
|
||||
if (this.groupId === TAVERN_ID) {
|
||||
return `${baseUrl}/groups/tavern`;
|
||||
} else if (this.groupType === 'party') {
|
||||
} if (this.groupType === 'party') {
|
||||
return `${baseUrl}/party`;
|
||||
} else {
|
||||
return `${baseUrl}/groups/guild/${this.groupId}`;
|
||||
}
|
||||
return `${baseUrl}/groups/guild/${this.groupId}`;
|
||||
},
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'copyAsTodo');
|
||||
|
||||
@@ -66,15 +66,15 @@ import notifications from '@/mixins/notifications';
|
||||
import markdownDirective from '@/directives/markdown';
|
||||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
mixins: [notifications],
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
reportData () {
|
||||
let reportMessage = this.abuseObject.user;
|
||||
let isSystemMessage = this.abuseObject.uuid === 'system';
|
||||
const isSystemMessage = this.abuseObject.uuid === 'system';
|
||||
if (isSystemMessage) reportMessage = this.$t('systemMessage');
|
||||
return {
|
||||
name: `<span class='text-danger'>${reportMessage}</span>`,
|
||||
@@ -82,7 +82,7 @@ export default {
|
||||
},
|
||||
},
|
||||
data () {
|
||||
let abuseFlagModalBody = {
|
||||
const abuseFlagModalBody = {
|
||||
firstLinkStart: '<a href="/static/community-guidelines" target="_blank">',
|
||||
secondLinkStart: '<a href="/static/terms" target="_blank">',
|
||||
linkEnd: '</a>',
|
||||
@@ -108,7 +108,7 @@ export default {
|
||||
async reportAbuse () {
|
||||
this.text(this.$t(this.groupId === 'privateMessage' ? 'pmReported' : 'abuseReported'));
|
||||
|
||||
let result = await this.$store.dispatch('chat:flag', {
|
||||
const result = await this.$store.dispatch('chat:flag', {
|
||||
groupId: this.groupId,
|
||||
chatId: this.abuseObject.id,
|
||||
comment: this.reportComment,
|
||||
|
||||
@@ -731,12 +731,11 @@ import pin from '@/assets/svg/pin.svg';
|
||||
import arrowRight from '@/assets/svg/arrow_right.svg';
|
||||
import arrowLeft from '@/assets/svg/arrow_left.svg';
|
||||
import isPinned from '@/../../common/script/libs/isPinned';
|
||||
import {avatarEditorUtilies} from '../mixins/avatarEditUtilities';
|
||||
import { avatarEditorUtilies } from '../mixins/avatarEditUtilities';
|
||||
|
||||
import content from '@/../../common/script/content/index';
|
||||
|
||||
export default {
|
||||
mixins: [guide, notifications, avatarEditorUtilies],
|
||||
components: {
|
||||
avatar,
|
||||
toggleSwitch,
|
||||
@@ -748,13 +747,9 @@ export default {
|
||||
|
||||
subMenu,
|
||||
},
|
||||
mounted () {
|
||||
if (this.editing) this.modalPage = 2;
|
||||
// Buy modal is global, so we listen at root. I'd like to not
|
||||
this.$root.$on('buyModal::boughtItem', this.backgroundPurchased);
|
||||
},
|
||||
mixins: [guide, notifications, avatarEditorUtilies],
|
||||
data () {
|
||||
let backgroundShopSets = shops.getBackgroundShopSets();
|
||||
const backgroundShopSets = shops.getBackgroundShopSets();
|
||||
|
||||
return {
|
||||
loading: false,
|
||||
@@ -786,12 +781,10 @@ export default {
|
||||
},
|
||||
],
|
||||
|
||||
bgSubMenuItems: ['2019', '2018', '2017', '2016', '2015', '2014'].map(y =>
|
||||
({
|
||||
id: y,
|
||||
label: y,
|
||||
})
|
||||
),
|
||||
bgSubMenuItems: ['2019', '2018', '2017', '2016', '2015', '2014'].map(y => ({
|
||||
id: y,
|
||||
label: y,
|
||||
})),
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -806,8 +799,13 @@ export default {
|
||||
this.$store.state.avatarEditorOptions.subpage = '';
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
if (this.editing) this.modalPage = 2;
|
||||
// Buy modal is global, so we listen at root. I'd like to not
|
||||
this.$root.$on('buyModal::boughtItem', this.backgroundPurchased);
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
|
||||
|
||||
editing () {
|
||||
@@ -818,7 +816,7 @@ export default {
|
||||
},
|
||||
backgroundShopSetsByYear () {
|
||||
// @TODO: add dates to backgrounds
|
||||
let backgroundShopSetsByYear = {
|
||||
const backgroundShopSetsByYear = {
|
||||
2014: [],
|
||||
2015: [],
|
||||
2016: [],
|
||||
@@ -830,12 +828,12 @@ export default {
|
||||
// Hack to force update for now until we restructure the data
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
|
||||
this.backgroundShopSets.forEach((set) => {
|
||||
let year = set.identifier.substr(set.identifier.length - 4);
|
||||
this.backgroundShopSets.forEach(set => {
|
||||
const year = set.identifier.substr(set.identifier.length - 4);
|
||||
if (!backgroundShopSetsByYear[year]) return;
|
||||
|
||||
let setOwnedByUser = false;
|
||||
for (let key in set.items) {
|
||||
for (const key in set.items) {
|
||||
if (this.user.purchased.background[key]) setOwnedByUser = true;
|
||||
}
|
||||
set.userOwns = setOwnedByUser;
|
||||
@@ -845,13 +843,13 @@ export default {
|
||||
return backgroundShopSetsByYear;
|
||||
},
|
||||
ownedBackgrounds () {
|
||||
let ownedBackgrounds = [];
|
||||
const ownedBackgrounds = [];
|
||||
|
||||
// Hack to force update for now until we restructure the data
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
|
||||
this.backgroundShopSets.forEach((set) => {
|
||||
set.items.forEach((bg) => {
|
||||
this.backgroundShopSets.forEach(set => {
|
||||
set.items.forEach(bg => {
|
||||
if (this.user.purchased.background[bg.key]) {
|
||||
ownedBackgrounds.push(bg);
|
||||
}
|
||||
@@ -885,7 +883,7 @@ export default {
|
||||
this.loading = true;
|
||||
|
||||
let tasksToCreate = [
|
||||
...content.tasksByCategory.defaults.map(t => ({
|
||||
...content.tasksByCategory.defaults.map(t => ({
|
||||
...t,
|
||||
text: t.text(),
|
||||
notes: t.notes && t.notes(),
|
||||
@@ -900,8 +898,8 @@ export default {
|
||||
});
|
||||
|
||||
// @TODO: Move to the action
|
||||
let response = await axios.post('/api/v4/tasks/user', tasksToCreate);
|
||||
let tasks = response.data.data;
|
||||
const response = await axios.post('/api/v4/tasks/user', tasksToCreate);
|
||||
const tasks = response.data.data;
|
||||
tasks.forEach(task => {
|
||||
this.$store.state.user.data.tasksOrder[`${task.type}s`].unshift(task._id);
|
||||
this.$store.state.tasks.data[`${task.type}s`].unshift(task);
|
||||
@@ -926,7 +924,7 @@ export default {
|
||||
let setOwnedByUser = false;
|
||||
|
||||
for (let key in set) {
|
||||
let value = set[key];
|
||||
const value = set[key];
|
||||
if (type === 'background') key = value.key;
|
||||
if (this.user.purchased[type][key]) setOwnedByUser = true;
|
||||
}
|
||||
@@ -948,8 +946,8 @@ export default {
|
||||
return isPinned(this.user, bg);
|
||||
},
|
||||
togglePinned (bg) {
|
||||
if (!this.$store.dispatch('user:togglePinnedItem', {type: bg.pinType, path: bg.path})) {
|
||||
this.text(this.$t('unpinnedItem', {item: bg.text}));
|
||||
if (!this.$store.dispatch('user:togglePinnedItem', { type: bg.pinType, path: bg.path })) {
|
||||
this.text(this.$t('unpinnedItem', { item: bg.text }));
|
||||
}
|
||||
},
|
||||
backgroundSelected (bg) {
|
||||
|
||||
@@ -62,7 +62,7 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
isLeader () {
|
||||
return this.user._id === this.group.leader._id;
|
||||
},
|
||||
@@ -81,8 +81,8 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async loadGroup () {
|
||||
let group = await this.$store.dispatch('guilds:getGroup', {groupId: this.groupId});
|
||||
this.group = Object.assign({}, group);
|
||||
const group = await this.$store.dispatch('guilds:getGroup', { groupId: this.groupId });
|
||||
this.group = { ...group };
|
||||
},
|
||||
upgradeGroup () {
|
||||
this.$store.state.upgradingGroup = this.group;
|
||||
|
||||
@@ -79,10 +79,10 @@ import amazonButton from '@/components/payments/amazonButton';
|
||||
import creditCardIcon from '@/assets/svg/credit-card-icon.svg';
|
||||
|
||||
export default {
|
||||
mixins: [paymentsMixin],
|
||||
components: {
|
||||
amazonButton,
|
||||
},
|
||||
mixins: [paymentsMixin],
|
||||
data () {
|
||||
return {
|
||||
amazonPayments: {},
|
||||
@@ -110,7 +110,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
newGroupIsReady () {
|
||||
return Boolean(this.newGroup.name);
|
||||
},
|
||||
@@ -131,7 +131,7 @@ export default {
|
||||
},
|
||||
pay (paymentMethod) {
|
||||
const subscriptionKey = 'group_monthly';
|
||||
let paymentData = {
|
||||
const paymentData = {
|
||||
subscription: subscriptionKey,
|
||||
coupon: null,
|
||||
};
|
||||
|
||||
@@ -213,7 +213,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
shown () {
|
||||
|
||||
@@ -22,20 +22,18 @@ import SecondaryMenu from '@/components/secondaryMenu';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
props: ['groupId'],
|
||||
components: {
|
||||
SecondaryMenu,
|
||||
groupFormModal,
|
||||
},
|
||||
props: ['groupId'],
|
||||
computed: {
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
groupPlans: 'groupPlans',
|
||||
}),
|
||||
currentGroup () {
|
||||
let groupFound = this.groupPlans.find(group => {
|
||||
return group._id === this.groupId;
|
||||
});
|
||||
const groupFound = this.groupPlans.find(group => group._id === this.groupId);
|
||||
|
||||
return groupFound;
|
||||
},
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
import taskDefaults from '@/../../common/script/libs/taskDefaults';
|
||||
import TaskColumn from '../tasks/column';
|
||||
import TaskModal from '../tasks/taskModal';
|
||||
@@ -80,19 +84,15 @@ import dailyIcon from '@/assets/svg/daily.svg';
|
||||
import todoIcon from '@/assets/svg/todo.svg';
|
||||
import rewardIcon from '@/assets/svg/reward.svg';
|
||||
|
||||
import Vue from 'vue';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
props: ['groupId'],
|
||||
components: {
|
||||
TaskColumn,
|
||||
TaskModal,
|
||||
GroupPlanOverviewModal,
|
||||
},
|
||||
props: ['groupId'],
|
||||
data () {
|
||||
return {
|
||||
openCreateBtn: false,
|
||||
@@ -143,7 +143,7 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
tagsByType () {
|
||||
const userTags = this.user.tags;
|
||||
const tagsByType = {
|
||||
@@ -194,22 +194,22 @@ export default {
|
||||
groupId: this.searchId,
|
||||
});
|
||||
|
||||
let members = await this.$store.dispatch('members:getGroupMembers', {groupId: this.searchId});
|
||||
const members = await this.$store.dispatch('members:getGroupMembers', { groupId: this.searchId });
|
||||
this.group.members = members;
|
||||
|
||||
let tasks = await this.$store.dispatch('tasks:getGroupTasks', {
|
||||
const tasks = await this.$store.dispatch('tasks:getGroupTasks', {
|
||||
groupId: this.searchId,
|
||||
});
|
||||
|
||||
let groupedApprovals = await this.loadApprovals();
|
||||
const groupedApprovals = await this.loadApprovals();
|
||||
|
||||
tasks.forEach((task) => {
|
||||
tasks.forEach(task => {
|
||||
if (groupedApprovals[task._id] && groupedApprovals[task._id].length > 0) task.approvals = groupedApprovals[task._id];
|
||||
this.tasksByType[task.type].push(task);
|
||||
});
|
||||
},
|
||||
async loadApprovals () {
|
||||
let approvalRequests = await this.$store.dispatch('tasks:getGroupApprovals', {
|
||||
const approvalRequests = await this.$store.dispatch('tasks:getGroupApprovals', {
|
||||
groupId: this.searchId,
|
||||
});
|
||||
|
||||
@@ -229,10 +229,8 @@ export default {
|
||||
groupId: this.searchId,
|
||||
});
|
||||
|
||||
completedTodos.forEach((task) => {
|
||||
const existingTaskIndex = findIndex(this.tasksByType.todo, (todo) => {
|
||||
return todo._id === task._id;
|
||||
});
|
||||
completedTodos.forEach(task => {
|
||||
const existingTaskIndex = findIndex(this.tasksByType.todo, todo => todo._id === task._id);
|
||||
if (existingTaskIndex === -1) {
|
||||
this.tasksByType.todo.push(task);
|
||||
}
|
||||
@@ -241,7 +239,7 @@ export default {
|
||||
createTask (type) {
|
||||
this.openCreateBtn = false;
|
||||
this.taskFormPurpose = 'create';
|
||||
this.creatingTask = taskDefaults({type, text: ''}, this.user);
|
||||
this.creatingTask = taskDefaults({ type, text: '' }, this.user);
|
||||
this.workingTask = this.creatingTask;
|
||||
// Necessary otherwise the first time the modal is not rendered
|
||||
Vue.nextTick(() => {
|
||||
@@ -253,15 +251,11 @@ export default {
|
||||
this.tasksByType[task.type].push(task);
|
||||
},
|
||||
taskEdited (task) {
|
||||
let index = findIndex(this.tasksByType[task.type], (taskItem) => {
|
||||
return taskItem._id === task._id;
|
||||
});
|
||||
const index = findIndex(this.tasksByType[task.type], taskItem => taskItem._id === task._id);
|
||||
this.tasksByType[task.type].splice(index, 1, task);
|
||||
},
|
||||
taskDestroyed (task) {
|
||||
let index = findIndex(this.tasksByType[task.type], (taskItem) => {
|
||||
return taskItem._id === task._id;
|
||||
});
|
||||
const index = findIndex(this.tasksByType[task.type], taskItem => taskItem._id === task._id);
|
||||
this.tasksByType[task.type].splice(index, 1);
|
||||
},
|
||||
cancelTaskModal () {
|
||||
@@ -289,12 +283,12 @@ export default {
|
||||
this.closeFilterPanel();
|
||||
},
|
||||
applyFilters () {
|
||||
const temporarilySelectedTags = this.temporarilySelectedTags;
|
||||
const { temporarilySelectedTags } = this;
|
||||
this.selectedTags = temporarilySelectedTags.slice();
|
||||
this.closeFilterPanel();
|
||||
},
|
||||
toggleTag (tag) {
|
||||
const temporarilySelectedTags = this.temporarilySelectedTags;
|
||||
const { temporarilySelectedTags } = this;
|
||||
const tagI = temporarilySelectedTags.indexOf(tag.id);
|
||||
if (tagI === -1) {
|
||||
temporarilySelectedTags.push(tag.id);
|
||||
|
||||
@@ -6,20 +6,20 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { TAVERN_ID } from '@/../../common/script/constants';
|
||||
import { TAVERN_ID } from '@/../../common/script/constants';
|
||||
|
||||
export default {
|
||||
props: ['group'],
|
||||
methods: {
|
||||
goToGroup () {
|
||||
if (this.group.type === 'party') {
|
||||
this.$router.push({name: 'party'});
|
||||
} else if (this.group._id === TAVERN_ID) {
|
||||
this.$router.push({name: 'tavern'});
|
||||
} else {
|
||||
this.$router.push({name: 'guild', params: {groupId: this.group._id}});
|
||||
}
|
||||
},
|
||||
export default {
|
||||
props: ['group'],
|
||||
methods: {
|
||||
goToGroup () {
|
||||
if (this.group.type === 'party') {
|
||||
this.$router.push({ name: 'party' });
|
||||
} else if (this.group._id === TAVERN_ID) {
|
||||
this.$router.push({ name: 'tavern' });
|
||||
} else {
|
||||
this.$router.push({ name: 'guild', params: { groupId: this.group._id } });
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -48,190 +48,190 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import debounce from 'lodash/debounce';
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
import autocomplete from '../chat/autoComplete';
|
||||
import communityGuidelines from './communityGuidelines';
|
||||
import chatMessage from '../chat/chatMessages';
|
||||
import { mapState } from '@/libs/store';
|
||||
import markdownDirective from '@/directives/markdown';
|
||||
import autocomplete from '../chat/autoComplete';
|
||||
import communityGuidelines from './communityGuidelines';
|
||||
import chatMessage from '../chat/chatMessages';
|
||||
import { mapState } from '@/libs/store';
|
||||
import markdownDirective from '@/directives/markdown';
|
||||
|
||||
export default {
|
||||
props: ['label', 'group', 'placeholder'],
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
components: {
|
||||
autocomplete,
|
||||
communityGuidelines,
|
||||
chatMessage,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
newMessage: '',
|
||||
sending: false,
|
||||
caretPosition: 0,
|
||||
chat: {
|
||||
submitDisable: false,
|
||||
submitTimeout: null,
|
||||
},
|
||||
coords: {
|
||||
TOP: 0,
|
||||
LEFT: 0,
|
||||
},
|
||||
textbox: this.$refs,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
currentLength () {
|
||||
return this.newMessage.length;
|
||||
export default {
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
components: {
|
||||
autocomplete,
|
||||
communityGuidelines,
|
||||
chatMessage,
|
||||
},
|
||||
props: ['label', 'group', 'placeholder'],
|
||||
data () {
|
||||
return {
|
||||
newMessage: '',
|
||||
sending: false,
|
||||
caretPosition: 0,
|
||||
chat: {
|
||||
submitDisable: false,
|
||||
submitTimeout: null,
|
||||
},
|
||||
communityGuidelinesAccepted () {
|
||||
return this.user.flags.communityGuidelinesAccepted;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// https://medium.com/@_jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a
|
||||
getCoord (e, text) {
|
||||
this.caretPosition = text.selectionEnd;
|
||||
let div = document.createElement('div');
|
||||
let span = document.createElement('span');
|
||||
let copyStyle = getComputedStyle(text);
|
||||
|
||||
[].forEach.call(copyStyle, (prop) => {
|
||||
div.style[prop] = copyStyle[prop];
|
||||
});
|
||||
|
||||
div.style.position = 'absolute';
|
||||
document.body.appendChild(div);
|
||||
div.textContent = text.value.substr(0, this.caretPosition);
|
||||
span.textContent = text.value.substr(this.caretPosition) || '.';
|
||||
div.appendChild(span);
|
||||
this.coords = {
|
||||
TOP: span.offsetTop,
|
||||
LEFT: span.offsetLeft,
|
||||
};
|
||||
document.body.removeChild(div);
|
||||
},
|
||||
updateCarretPosition: debounce(function updateCarretPosition (eventUpdate) {
|
||||
this._updateCarretPosition(eventUpdate);
|
||||
}, 250),
|
||||
_updateCarretPosition (eventUpdate) {
|
||||
let text = eventUpdate.target;
|
||||
this.getCoord(eventUpdate, text);
|
||||
},
|
||||
async sendMessageShortcut () {
|
||||
// If the user recently pasted in the text field, don't submit
|
||||
if (!this.chat.submitDisable) {
|
||||
this.sendMessage();
|
||||
}
|
||||
},
|
||||
async sendMessage () {
|
||||
if (this.sending) return;
|
||||
this.sending = true;
|
||||
let response;
|
||||
|
||||
try {
|
||||
response = await this.$store.dispatch('chat:postChat', {
|
||||
group: this.group,
|
||||
message: this.newMessage,
|
||||
});
|
||||
} catch (e) {
|
||||
// catch exception to allow function to continue
|
||||
}
|
||||
|
||||
if (response) {
|
||||
this.group.chat.unshift(response.message);
|
||||
this.newMessage = '';
|
||||
}
|
||||
|
||||
this.sending = false;
|
||||
|
||||
// @TODO: I would like to not reload everytime we send. Why are we reloading?
|
||||
// The response has all the necessary data...
|
||||
let chat = await this.$store.dispatch('chat:getChat', {groupId: this.group._id});
|
||||
this.group.chat = chat;
|
||||
},
|
||||
|
||||
disableMessageSendShortcut () {
|
||||
// Some users were experiencing accidental sending of messages after pasting
|
||||
// So, after pasting, disable the shortcut for a second.
|
||||
this.chat.submitDisable = true;
|
||||
|
||||
if (this.chat.submitTimeout) {
|
||||
// If someone pastes during the disabled period, prevent early re-enable
|
||||
clearTimeout(this.chat.submitTimeout);
|
||||
this.chat.submitTimeout = null;
|
||||
}
|
||||
|
||||
this.chat.submitTimeout = window.setTimeout(() => {
|
||||
this.chat.submitTimeout = null;
|
||||
this.chat.submitDisable = false;
|
||||
}, 500);
|
||||
},
|
||||
|
||||
handleTab (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
if (e.shiftKey) {
|
||||
this.$refs.autocomplete.selectPrevious();
|
||||
} else {
|
||||
this.$refs.autocomplete.selectNext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleEscape (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
selectNextAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.selectNext();
|
||||
}
|
||||
},
|
||||
|
||||
selectPreviousAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.selectPrevious();
|
||||
}
|
||||
},
|
||||
|
||||
selectAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.makeSelection();
|
||||
}
|
||||
},
|
||||
|
||||
selectedAutocomplete (newText) {
|
||||
this.newMessage = newText;
|
||||
},
|
||||
|
||||
fetchRecentMessages () {
|
||||
this.$emit('fetchRecentMessages');
|
||||
},
|
||||
reverseChat () {
|
||||
this.group.chat.reverse();
|
||||
},
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
// Reset chat
|
||||
this.newMessage = '';
|
||||
this.coords = {
|
||||
coords: {
|
||||
TOP: 0,
|
||||
LEFT: 0,
|
||||
};
|
||||
|
||||
next();
|
||||
},
|
||||
textbox: this.$refs,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
currentLength () {
|
||||
return this.newMessage.length;
|
||||
},
|
||||
};
|
||||
communityGuidelinesAccepted () {
|
||||
return this.user.flags.communityGuidelinesAccepted;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// https://medium.com/@_jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a
|
||||
getCoord (e, text) {
|
||||
this.caretPosition = text.selectionEnd;
|
||||
const div = document.createElement('div');
|
||||
const span = document.createElement('span');
|
||||
const copyStyle = getComputedStyle(text);
|
||||
|
||||
[].forEach.call(copyStyle, prop => {
|
||||
div.style[prop] = copyStyle[prop];
|
||||
});
|
||||
|
||||
div.style.position = 'absolute';
|
||||
document.body.appendChild(div);
|
||||
div.textContent = text.value.substr(0, this.caretPosition);
|
||||
span.textContent = text.value.substr(this.caretPosition) || '.';
|
||||
div.appendChild(span);
|
||||
this.coords = {
|
||||
TOP: span.offsetTop,
|
||||
LEFT: span.offsetLeft,
|
||||
};
|
||||
document.body.removeChild(div);
|
||||
},
|
||||
updateCarretPosition: debounce(function updateCarretPosition (eventUpdate) {
|
||||
this._updateCarretPosition(eventUpdate);
|
||||
}, 250),
|
||||
_updateCarretPosition (eventUpdate) {
|
||||
const text = eventUpdate.target;
|
||||
this.getCoord(eventUpdate, text);
|
||||
},
|
||||
async sendMessageShortcut () {
|
||||
// If the user recently pasted in the text field, don't submit
|
||||
if (!this.chat.submitDisable) {
|
||||
this.sendMessage();
|
||||
}
|
||||
},
|
||||
async sendMessage () {
|
||||
if (this.sending) return;
|
||||
this.sending = true;
|
||||
let response;
|
||||
|
||||
try {
|
||||
response = await this.$store.dispatch('chat:postChat', {
|
||||
group: this.group,
|
||||
message: this.newMessage,
|
||||
});
|
||||
} catch (e) {
|
||||
// catch exception to allow function to continue
|
||||
}
|
||||
|
||||
if (response) {
|
||||
this.group.chat.unshift(response.message);
|
||||
this.newMessage = '';
|
||||
}
|
||||
|
||||
this.sending = false;
|
||||
|
||||
// @TODO: I would like to not reload everytime we send. Why are we reloading?
|
||||
// The response has all the necessary data...
|
||||
const chat = await this.$store.dispatch('chat:getChat', { groupId: this.group._id });
|
||||
this.group.chat = chat;
|
||||
},
|
||||
|
||||
disableMessageSendShortcut () {
|
||||
// Some users were experiencing accidental sending of messages after pasting
|
||||
// So, after pasting, disable the shortcut for a second.
|
||||
this.chat.submitDisable = true;
|
||||
|
||||
if (this.chat.submitTimeout) {
|
||||
// If someone pastes during the disabled period, prevent early re-enable
|
||||
clearTimeout(this.chat.submitTimeout);
|
||||
this.chat.submitTimeout = null;
|
||||
}
|
||||
|
||||
this.chat.submitTimeout = window.setTimeout(() => {
|
||||
this.chat.submitTimeout = null;
|
||||
this.chat.submitDisable = false;
|
||||
}, 500);
|
||||
},
|
||||
|
||||
handleTab (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
if (e.shiftKey) {
|
||||
this.$refs.autocomplete.selectPrevious();
|
||||
} else {
|
||||
this.$refs.autocomplete.selectNext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleEscape (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
selectNextAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.selectNext();
|
||||
}
|
||||
},
|
||||
|
||||
selectPreviousAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.selectPrevious();
|
||||
}
|
||||
},
|
||||
|
||||
selectAutocomplete (e) {
|
||||
if (this.$refs.autocomplete.searchActive) {
|
||||
e.preventDefault();
|
||||
this.$refs.autocomplete.makeSelection();
|
||||
}
|
||||
},
|
||||
|
||||
selectedAutocomplete (newText) {
|
||||
this.newMessage = newText;
|
||||
},
|
||||
|
||||
fetchRecentMessages () {
|
||||
this.$emit('fetchRecentMessages');
|
||||
},
|
||||
reverseChat () {
|
||||
this.group.chat.reverse();
|
||||
},
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
// Reset chat
|
||||
this.newMessage = '';
|
||||
this.coords = {
|
||||
TOP: 0,
|
||||
LEFT: 0,
|
||||
};
|
||||
|
||||
next();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -47,14 +47,14 @@ import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
communityGuidelinesAccepted () {
|
||||
return this.user.flags.communityGuidelinesAccepted;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
acceptCommunityGuidelines () {
|
||||
this.$store.dispatch('user:set', {'flags.communityGuidelinesAccepted': true});
|
||||
this.$store.dispatch('user:set', { 'flags.communityGuidelinesAccepted': true });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -140,55 +140,55 @@ b-modal#create-party-modal(size='lg', hide-footer=true)
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from '@/libs/store';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import notifications from '@/mixins/notifications';
|
||||
import { mapState } from '@/libs/store';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import notifications from '@/mixins/notifications';
|
||||
|
||||
import copyIcon from '@/assets/svg/copy.svg';
|
||||
import copyIcon from '@/assets/svg/copy.svg';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
copy: copyIcon,
|
||||
}),
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
copy: copyIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
async createParty () {
|
||||
const group = {
|
||||
type: 'party',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
methods: {
|
||||
async createParty () {
|
||||
let group = {
|
||||
type: 'party',
|
||||
};
|
||||
group.name = this.$t('possessiveParty', {name: this.user.profile.name});
|
||||
let party = await this.$store.dispatch('guilds:create', {group});
|
||||
this.$store.state.party.data = party;
|
||||
this.user.party._id = party._id;
|
||||
group.name = this.$t('possessiveParty', { name: this.user.profile.name });
|
||||
const party = await this.$store.dispatch('guilds:create', { group });
|
||||
this.$store.state.party.data = party;
|
||||
this.user.party._id = party._id;
|
||||
|
||||
Analytics.updateUser({
|
||||
partyID: party._id,
|
||||
partySize: 1,
|
||||
});
|
||||
Analytics.updateUser({
|
||||
partyID: party._id,
|
||||
partySize: 1,
|
||||
});
|
||||
|
||||
this.$root.$emit('bv::hide::modal', 'create-party-modal');
|
||||
this.$router.push('/party');
|
||||
},
|
||||
copyUsername () {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(this.user.auth.local.username);
|
||||
} else {
|
||||
let copyText = document.createElement('textarea');
|
||||
copyText.value = this.user.auth.local.username;
|
||||
document.body.appendChild(copyText);
|
||||
copyText.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(copyText);
|
||||
}
|
||||
this.text(this.$t('usernameCopied'));
|
||||
},
|
||||
this.$root.$emit('bv::hide::modal', 'create-party-modal');
|
||||
this.$router.push('/party');
|
||||
},
|
||||
mixins: [notifications],
|
||||
};
|
||||
copyUsername () {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(this.user.auth.local.username);
|
||||
} else {
|
||||
const copyText = document.createElement('textarea');
|
||||
copyText.value = this.user.auth.local.username;
|
||||
document.body.appendChild(copyText);
|
||||
copyText.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(copyText);
|
||||
}
|
||||
this.text(this.$t('usernameCopied'));
|
||||
},
|
||||
},
|
||||
mixins: [notifications],
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -64,7 +64,7 @@ import groupUtilities from '@/mixins/groupsUtilities';
|
||||
import positiveIcon from '@/assets/svg/positive.svg';
|
||||
|
||||
function _mapCategories (guilds) {
|
||||
guilds.forEach((guild) => {
|
||||
guilds.forEach(guild => {
|
||||
if (!guild.categories) return;
|
||||
guild.categorySlugs = guild.categories.map(cat => {
|
||||
if (!cat) return;
|
||||
@@ -74,8 +74,8 @@ function _mapCategories (guilds) {
|
||||
}
|
||||
|
||||
export default {
|
||||
mixins: [groupUtilities],
|
||||
components: { PublicGuildItem, MugenScroll, Sidebar },
|
||||
mixins: [groupUtilities],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
@@ -113,14 +113,12 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
filteredGuilds () {
|
||||
let search = this.search;
|
||||
let filters = this.filters;
|
||||
let user = this.$store.state.user.data;
|
||||
let filterGuild = this.filterGuild;
|
||||
const { search } = this;
|
||||
const { filters } = this;
|
||||
const user = this.$store.state.user.data;
|
||||
const { filterGuild } = this;
|
||||
// @TODO: Move this to the server
|
||||
return this.guilds.filter((guild) => {
|
||||
return filterGuild(guild, filters, search, user);
|
||||
});
|
||||
return this.guilds.filter(guild => filterGuild(guild, filters, search, user));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@@ -133,7 +131,7 @@ export default {
|
||||
|
||||
this.queryFilters.search = eventData.searchTerm;
|
||||
|
||||
let guilds = await this.$store.dispatch('guilds:getPublicGuilds', this.queryFilters);
|
||||
const guilds = await this.$store.dispatch('guilds:getPublicGuilds', this.queryFilters);
|
||||
_mapCategories(guilds);
|
||||
this.guilds = guilds;
|
||||
},
|
||||
@@ -157,7 +155,7 @@ export default {
|
||||
this.queryFilters.categories = eventData.categories.join(',');
|
||||
|
||||
// Role filters
|
||||
let filteringRole = eventData.roles && eventData.roles.length > 0;
|
||||
const filteringRole = eventData.roles && eventData.roles.length > 0;
|
||||
if (filteringRole && eventData.roles.indexOf('member') !== -1) {
|
||||
this.queryFilters.member = true;
|
||||
}
|
||||
@@ -182,7 +180,7 @@ export default {
|
||||
this.queryFilters.maxMemberCount = 99;
|
||||
}
|
||||
|
||||
let guilds = await this.$store.dispatch('guilds:getPublicGuilds', this.queryFilters);
|
||||
const guilds = await this.$store.dispatch('guilds:getPublicGuilds', this.queryFilters);
|
||||
_mapCategories(guilds);
|
||||
this.guilds = guilds;
|
||||
},
|
||||
@@ -194,7 +192,7 @@ export default {
|
||||
|
||||
this.loading = true;
|
||||
this.queryFilters.page = this.lastPageLoaded;
|
||||
let guilds = await this.$store.dispatch('guilds:getPublicGuilds', this.queryFilters);
|
||||
const guilds = await this.$store.dispatch('guilds:getPublicGuilds', this.queryFilters);
|
||||
if (guilds.length === 0) this.hasLoadedAllGuilds = true;
|
||||
|
||||
_mapCategories(guilds);
|
||||
|
||||
@@ -289,8 +289,6 @@ import silverGuildBadgeIcon from '@/assets/svg/silver-guild-badge-small.svg';
|
||||
import bronzeGuildBadgeIcon from '@/assets/svg/bronze-guild-badge-small.svg';
|
||||
|
||||
export default {
|
||||
mixins: [groupUtilities, styleHelper],
|
||||
props: ['groupId'],
|
||||
components: {
|
||||
membersModal,
|
||||
startQuestModal,
|
||||
@@ -307,6 +305,8 @@ export default {
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
mixins: [groupUtilities, styleHelper],
|
||||
props: ['groupId'],
|
||||
data () {
|
||||
return {
|
||||
searchId: '',
|
||||
@@ -333,7 +333,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
partyStore () {
|
||||
return this.$store.state.party;
|
||||
},
|
||||
@@ -353,16 +353,6 @@ export default {
|
||||
return this.group.memberCount > this.$store.state.constants.LARGE_GROUP_COUNT_MESSAGE_CUTOFF;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
if (this.isParty) this.searchId = 'party';
|
||||
if (!this.searchId) this.searchId = this.groupId;
|
||||
this.load();
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
this.$set(this, 'searchId', to.params.groupId);
|
||||
|
||||
next();
|
||||
},
|
||||
watch: {
|
||||
// call again the method if the route changes (when this route is already active)
|
||||
$route: 'fetchGuild',
|
||||
@@ -375,9 +365,19 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
if (this.isParty) this.searchId = 'party';
|
||||
if (!this.searchId) this.searchId = this.groupId;
|
||||
this.load();
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
this.$set(this, 'searchId', to.params.groupId);
|
||||
|
||||
next();
|
||||
},
|
||||
methods: {
|
||||
acceptCommunityGuidelines () {
|
||||
this.$store.dispatch('user:set', {'flags.communityGuidelinesAccepted': true});
|
||||
this.$store.dispatch('user:set', { 'flags.communityGuidelinesAccepted': true });
|
||||
},
|
||||
async load () {
|
||||
if (this.isParty) {
|
||||
@@ -393,7 +393,7 @@ export default {
|
||||
includeAllPublicFields: true,
|
||||
});
|
||||
this.$root.$on('updatedGroup', group => {
|
||||
let updatedGroup = extend(this.group, group);
|
||||
const updatedGroup = extend(this.group, group);
|
||||
this.$set(this.group, updatedGroup);
|
||||
});
|
||||
},
|
||||
@@ -442,7 +442,7 @@ export default {
|
||||
this.group = this.$store.state.party.data;
|
||||
this.checkForAchievements();
|
||||
} else {
|
||||
const group = await this.$store.dispatch('guilds:getGroup', {groupId: this.searchId});
|
||||
const group = await this.$store.dispatch('guilds:getGroup', { groupId: this.searchId });
|
||||
this.$set(this, 'group', group);
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ export default {
|
||||
if (this.hasUnreadMessages(groupId)) {
|
||||
// Delay by 1sec to make sure it returns after other requests that don't have the notification marked as read
|
||||
setTimeout(() => {
|
||||
this.$store.dispatch('chat:markChatSeen', {groupId});
|
||||
this.$store.dispatch('chat:markChatSeen', { groupId });
|
||||
this.$delete(this.user.newMessages, groupId);
|
||||
}, 1000);
|
||||
}
|
||||
@@ -458,9 +458,7 @@ export default {
|
||||
hasUnreadMessages (groupId) {
|
||||
if (this.user.newMessages[groupId]) return true;
|
||||
|
||||
return this.user.notifications.some(n => {
|
||||
return n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === groupId;
|
||||
});
|
||||
return this.user.notifications.some(n => n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === groupId);
|
||||
},
|
||||
checkForAchievements () {
|
||||
// Checks if user's party has reached 2 players for the first time.
|
||||
@@ -481,7 +479,7 @@ export default {
|
||||
if (this.group.cancelledPlan && !confirm(this.$t('aboutToJoinCancelledGroupPlan'))) {
|
||||
return;
|
||||
}
|
||||
await this.$store.dispatch('guilds:join', {groupId: this.group._id, type: 'guild'});
|
||||
await this.$store.dispatch('guilds:join', { groupId: this.group._id, type: 'guild' });
|
||||
},
|
||||
clickLeave () {
|
||||
Analytics.track({
|
||||
@@ -493,13 +491,13 @@ export default {
|
||||
|
||||
// @TODO: Get challenges and ask to keep or remove
|
||||
if (!confirm('Are you sure you want to leave?')) return;
|
||||
let keep = true;
|
||||
const keep = true;
|
||||
this.leave(keep);
|
||||
},
|
||||
async leave (keepTasks) {
|
||||
let keepChallenges = 'remain-in-challenges';
|
||||
const keepChallenges = 'remain-in-challenges';
|
||||
|
||||
let data = {
|
||||
const data = {
|
||||
groupId: this.group._id,
|
||||
keep: keepTasks,
|
||||
keepChallenges,
|
||||
@@ -507,14 +505,14 @@ export default {
|
||||
|
||||
if (this.isParty) {
|
||||
data.type = 'party';
|
||||
Analytics.updateUser({partySize: null, partyID: null});
|
||||
Analytics.updateUser({ partySize: null, partyID: null });
|
||||
this.$store.state.partyMembers = [];
|
||||
}
|
||||
|
||||
await this.$store.dispatch('guilds:leave', data);
|
||||
|
||||
if (this.isParty) {
|
||||
this.$router.push({name: 'tasks'});
|
||||
this.$router.push({ name: 'tasks' });
|
||||
}
|
||||
},
|
||||
upgradeGroup () {
|
||||
|
||||
@@ -185,7 +185,7 @@ export default {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
data () {
|
||||
let data = {
|
||||
const data = {
|
||||
workingGroup: {
|
||||
id: '',
|
||||
name: '',
|
||||
@@ -267,8 +267,8 @@ export default {
|
||||
membersToInvite: [],
|
||||
};
|
||||
|
||||
let hashedCategories = {};
|
||||
data.categoryOptions.forEach((category) => {
|
||||
const hashedCategories = {};
|
||||
data.categoryOptions.forEach(category => {
|
||||
hashedCategories[category.key] = category.label;
|
||||
});
|
||||
data.categoriesHashByKey = hashedCategories;
|
||||
@@ -281,12 +281,12 @@ export default {
|
||||
return data;
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
editingGroup () {
|
||||
return this.$store.state.editingGroup;
|
||||
},
|
||||
charactersRemaining () {
|
||||
let currentLength = this.workingGroup.summary ? this.workingGroup.summary.length : 0;
|
||||
const currentLength = this.workingGroup.summary ? this.workingGroup.summary.length : 0;
|
||||
return MAX_SUMMARY_SIZE_FOR_GUILDS - currentLength;
|
||||
},
|
||||
title () {
|
||||
@@ -304,7 +304,7 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
editingGroup () {
|
||||
let editingGroup = this.editingGroup;
|
||||
const { editingGroup } = this;
|
||||
|
||||
if (!editingGroup._id) {
|
||||
this.resetWorkingGroup();
|
||||
@@ -339,7 +339,7 @@ export default {
|
||||
methods: {
|
||||
async getMembers () {
|
||||
if (!this.workingGroup.id) return;
|
||||
let members = await this.$store.dispatch('members:getGroupMembers', {
|
||||
const members = await this.$store.dispatch('members:getGroupMembers', {
|
||||
groupId: this.workingGroup.id,
|
||||
includeAllPublicFields: true,
|
||||
});
|
||||
@@ -367,7 +367,7 @@ export default {
|
||||
// @TODO return $rootScope.openModal('buyGems', {track:"Gems > Gems > Create Group"});
|
||||
}
|
||||
|
||||
let errors = [];
|
||||
const errors = [];
|
||||
|
||||
if (!this.workingGroup.name) errors.push(this.$t('nameRequired'));
|
||||
if (!this.workingGroup.summary) errors.push(this.$t('summaryRequired'));
|
||||
@@ -391,10 +391,10 @@ export default {
|
||||
challenges: this.workingGroup.onlyLeaderCreatesChallenges,
|
||||
};
|
||||
|
||||
let categoryKeys = this.workingGroup.categories;
|
||||
let serverCategories = [];
|
||||
const categoryKeys = this.workingGroup.categories;
|
||||
const serverCategories = [];
|
||||
categoryKeys.forEach(key => {
|
||||
let catName = this.categoriesHashByKey[key];
|
||||
const catName = this.categoriesHashByKey[key];
|
||||
serverCategories.push({
|
||||
slug: key,
|
||||
name: catName,
|
||||
@@ -402,16 +402,16 @@ export default {
|
||||
});
|
||||
this.workingGroup.categories = serverCategories;
|
||||
|
||||
let groupData = Object.assign({}, this.workingGroup);
|
||||
const groupData = { ...this.workingGroup };
|
||||
|
||||
let newgroup;
|
||||
if (groupData.id) {
|
||||
await this.$store.dispatch('guilds:update', {group: groupData});
|
||||
await this.$store.dispatch('guilds:update', { group: groupData });
|
||||
this.$root.$emit('updatedGroup', this.workingGroup);
|
||||
// @TODO: this doesn't work because of the async resource
|
||||
// if (updatedGroup.type === 'party') this.$store.state.party = {data: updatedGroup};
|
||||
} else {
|
||||
newgroup = await this.$store.dispatch('guilds:create', {group: groupData});
|
||||
newgroup = await this.$store.dispatch('guilds:create', { group: groupData });
|
||||
this.$store.state.user.data.balance -= 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -297,10 +297,10 @@ import creditCardIcon from '@/assets/svg/credit-card-icon.svg';
|
||||
import amazonButton from '@/components/payments/amazonButton';
|
||||
|
||||
export default {
|
||||
mixins: [paymentsMixin],
|
||||
components: {
|
||||
amazonButton,
|
||||
},
|
||||
mixins: [paymentsMixin],
|
||||
data () {
|
||||
return {
|
||||
amazonPayments: {},
|
||||
@@ -341,7 +341,7 @@ export default {
|
||||
return this.$store.state.upgradingGroup;
|
||||
},
|
||||
// @TODO: can we move this to payment mixin?
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
launchModal () {
|
||||
|
||||
@@ -90,111 +90,103 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from '@/libs/store';
|
||||
import clone from 'lodash/clone';
|
||||
import debounce from 'lodash/debounce';
|
||||
import filter from 'lodash/filter';
|
||||
import forEach from 'lodash/forEach';
|
||||
import isEmail from 'validator/lib/isEmail';
|
||||
import isUUID from 'validator/lib/isUUID';
|
||||
import notifications from '@/mixins/notifications';
|
||||
import positiveIcon from '@/assets/svg/positive.svg';
|
||||
import clone from 'lodash/clone';
|
||||
import debounce from 'lodash/debounce';
|
||||
import filter from 'lodash/filter';
|
||||
import forEach from 'lodash/forEach';
|
||||
import isEmail from 'validator/lib/isEmail';
|
||||
import isUUID from 'validator/lib/isUUID';
|
||||
import { mapState } from '@/libs/store';
|
||||
import notifications from '@/mixins/notifications';
|
||||
import positiveIcon from '@/assets/svg/positive.svg';
|
||||
|
||||
const INVITE_DEFAULTS = {text: '', error: null, valid: null};
|
||||
const INVITE_DEFAULTS = { text: '', error: null, valid: null };
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
cannotSubmit () {
|
||||
const filteredInvites = filter(this.invites, (invite) => {
|
||||
return invite.text.length > 0 && !invite.valid;
|
||||
});
|
||||
if (filteredInvites.length > 0) return true;
|
||||
return false;
|
||||
},
|
||||
inviter () {
|
||||
return this.user.profile.name;
|
||||
},
|
||||
export default {
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
cannotSubmit () {
|
||||
const filteredInvites = filter(this.invites, invite => invite.text.length > 0 && !invite.valid);
|
||||
if (filteredInvites.length > 0) return true;
|
||||
return false;
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
invites: [clone(INVITE_DEFAULTS), clone(INVITE_DEFAULTS)],
|
||||
icons: Object.freeze({
|
||||
positiveIcon,
|
||||
}),
|
||||
};
|
||||
inviter () {
|
||||
return this.user.profile.name;
|
||||
},
|
||||
methods: {
|
||||
checkInviteList: debounce(function checkList () {
|
||||
this.invites = filter(this.invites, (invite, index) => {
|
||||
return invite.text.length > 0 || index === this.invites.length - 1;
|
||||
});
|
||||
while (this.invites.length < 2) this.invites.push(clone(INVITE_DEFAULTS));
|
||||
forEach(this.invites, (value, index) => {
|
||||
if (value.text.length < 1 || isEmail(value.text)) {
|
||||
return this.fillErrors(index);
|
||||
}
|
||||
if (isUUID(value.text)) {
|
||||
this.$store.dispatch('user:userLookup', {uuid: value.text})
|
||||
.then(res => {
|
||||
return this.fillErrors(index, res);
|
||||
});
|
||||
} else {
|
||||
let searchUsername = value.text;
|
||||
if (searchUsername[0] === '@') searchUsername = searchUsername.slice(1, searchUsername.length);
|
||||
this.$store.dispatch('user:userLookup', {username: searchUsername})
|
||||
.then(res => {
|
||||
return this.fillErrors(index, res);
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 250),
|
||||
expandInviteList () {
|
||||
if (this.invites[this.invites.length - 1].text.length > 0) this.invites.push(clone(INVITE_DEFAULTS));
|
||||
},
|
||||
fillErrors (index, res) {
|
||||
if (!res || res.status === 200) {
|
||||
this.invites[index].error = null;
|
||||
if (this.invites[index].text.length < 1) return this.invites[index].valid = null;
|
||||
return this.invites[index].valid = true;
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
invites: [clone(INVITE_DEFAULTS), clone(INVITE_DEFAULTS)],
|
||||
icons: Object.freeze({
|
||||
positiveIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
checkInviteList: debounce(function checkList () {
|
||||
this.invites = filter(this.invites, (invite, index) => invite.text.length > 0 || index === this.invites.length - 1);
|
||||
while (this.invites.length < 2) this.invites.push(clone(INVITE_DEFAULTS));
|
||||
forEach(this.invites, (value, index) => {
|
||||
if (value.text.length < 1 || isEmail(value.text)) {
|
||||
return this.fillErrors(index);
|
||||
}
|
||||
this.invites[index].error = res.response.data.message;
|
||||
return this.invites[index].valid = false;
|
||||
},
|
||||
close () {
|
||||
this.invites = [clone(INVITE_DEFAULTS), clone(INVITE_DEFAULTS)];
|
||||
this.$root.$emit('bv::hide::modal', 'invite-modal');
|
||||
},
|
||||
async sendInvites () {
|
||||
let invitationDetails = {
|
||||
inviter: this.inviter,
|
||||
emails: [],
|
||||
uuids: [],
|
||||
usernames: [],
|
||||
};
|
||||
forEach(this.invites, (invite) => {
|
||||
if (invite.text.length < 1) return;
|
||||
if (isEmail(invite.text)) {
|
||||
invitationDetails.emails.push({email: invite.text});
|
||||
} else if (isUUID(invite.text)) {
|
||||
invitationDetails.uuids.push(invite.text);
|
||||
} else {
|
||||
invitationDetails.usernames.push(invite.text);
|
||||
}
|
||||
});
|
||||
await this.$store.dispatch('guilds:invite', {
|
||||
invitationDetails,
|
||||
groupId: this.group._id,
|
||||
});
|
||||
|
||||
const invitesSent = invitationDetails.emails.length + invitationDetails.uuids.length + invitationDetails.usernames.length;
|
||||
let invitationString = invitesSent > 1 ? 'invitationsSent' : 'invitationSent';
|
||||
|
||||
this.text(this.$t(invitationString));
|
||||
this.close();
|
||||
},
|
||||
if (isUUID(value.text)) {
|
||||
this.$store.dispatch('user:userLookup', { uuid: value.text })
|
||||
.then(res => this.fillErrors(index, res));
|
||||
} else {
|
||||
let searchUsername = value.text;
|
||||
if (searchUsername[0] === '@') searchUsername = searchUsername.slice(1, searchUsername.length);
|
||||
this.$store.dispatch('user:userLookup', { username: searchUsername })
|
||||
.then(res => this.fillErrors(index, res));
|
||||
}
|
||||
});
|
||||
}, 250),
|
||||
expandInviteList () {
|
||||
if (this.invites[this.invites.length - 1].text.length > 0) this.invites.push(clone(INVITE_DEFAULTS));
|
||||
},
|
||||
mixins: [notifications],
|
||||
props: ['group', 'groupType'],
|
||||
};
|
||||
fillErrors (index, res) {
|
||||
if (!res || res.status === 200) {
|
||||
this.invites[index].error = null;
|
||||
if (this.invites[index].text.length < 1) return this.invites[index].valid = null;
|
||||
return this.invites[index].valid = true;
|
||||
}
|
||||
this.invites[index].error = res.response.data.message;
|
||||
return this.invites[index].valid = false;
|
||||
},
|
||||
close () {
|
||||
this.invites = [clone(INVITE_DEFAULTS), clone(INVITE_DEFAULTS)];
|
||||
this.$root.$emit('bv::hide::modal', 'invite-modal');
|
||||
},
|
||||
async sendInvites () {
|
||||
const invitationDetails = {
|
||||
inviter: this.inviter,
|
||||
emails: [],
|
||||
uuids: [],
|
||||
usernames: [],
|
||||
};
|
||||
forEach(this.invites, invite => {
|
||||
if (invite.text.length < 1) return;
|
||||
if (isEmail(invite.text)) {
|
||||
invitationDetails.emails.push({ email: invite.text });
|
||||
} else if (isUUID(invite.text)) {
|
||||
invitationDetails.uuids.push(invite.text);
|
||||
} else {
|
||||
invitationDetails.usernames.push(invite.text);
|
||||
}
|
||||
});
|
||||
await this.$store.dispatch('guilds:invite', {
|
||||
invitationDetails,
|
||||
groupId: this.group._id,
|
||||
});
|
||||
|
||||
const invitesSent = invitationDetails.emails.length + invitationDetails.uuids.length + invitationDetails.usernames.length;
|
||||
const invitationString = invitesSent > 1 ? 'invitationsSent' : 'invitationSent';
|
||||
|
||||
this.text(this.$t(invitationString));
|
||||
this.close();
|
||||
},
|
||||
},
|
||||
mixins: [notifications],
|
||||
props: ['group', 'groupType'],
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -214,11 +214,11 @@ import starIcon from '@/assets/members/star.svg';
|
||||
import dots from '@/assets/svg/dots.svg';
|
||||
|
||||
export default {
|
||||
props: ['hideBadge'],
|
||||
components: {
|
||||
MemberDetails,
|
||||
removeMemberModal,
|
||||
},
|
||||
props: ['hideBadge'],
|
||||
data () {
|
||||
return {
|
||||
sortOption: {},
|
||||
@@ -278,7 +278,7 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica:show-member-modal', (data) => {
|
||||
this.$root.$on('habitica:show-member-modal', data => {
|
||||
// @TODO: Remove store
|
||||
this.$store.state.memberModalOptions.challengeId = data.challengeId;
|
||||
this.$store.state.memberModalOptions.groupId = data.groupId;
|
||||
@@ -294,7 +294,7 @@ export default {
|
||||
this.$root.$off('habitica:show-member-modal');
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
isLeader () {
|
||||
if (!this.group || !this.group.leader) return false;
|
||||
return this.user._id === this.group.leader || this.user._id === this.group.leader._id;
|
||||
@@ -370,10 +370,10 @@ export default {
|
||||
});
|
||||
},
|
||||
async getMembers () {
|
||||
let groupId = this.groupId;
|
||||
const { groupId } = this;
|
||||
|
||||
if (groupId && groupId !== 'challenge') {
|
||||
let invites = await this.$store.dispatch('members:getGroupInvites', {
|
||||
const invites = await this.$store.dispatch('members:getGroupInvites', {
|
||||
groupId,
|
||||
includeAllPublicFields: true,
|
||||
});
|
||||
@@ -383,7 +383,7 @@ export default {
|
||||
this.members = this.$store.state.memberModalOptions.viewingMembers;
|
||||
},
|
||||
async clickMember (uid, forceShow) {
|
||||
let user = this.$store.state.user.data;
|
||||
const user = this.$store.state.user.data;
|
||||
|
||||
if (user._id === uid && !forceShow) {
|
||||
if (this.$route.name === 'tasks') {
|
||||
@@ -409,7 +409,7 @@ export default {
|
||||
memberRemoved () {
|
||||
this.members.splice(this.memberToRemove.index, 1);
|
||||
this.group.memberCount -= 1;
|
||||
this.memberToRemove = {};
|
||||
this.memberToRemove = {};
|
||||
},
|
||||
async quickReply (uid) {
|
||||
this.memberToReply = uid;
|
||||
@@ -459,7 +459,7 @@ export default {
|
||||
const lastMember = this.members[this.members.length - 1];
|
||||
if (!lastMember) return;
|
||||
|
||||
let newMembers = await this.$store.state.memberModalOptions.fetchMoreMembers({
|
||||
const newMembers = await this.$store.state.memberModalOptions.fetchMoreMembers({
|
||||
challengeId: this.challengeId,
|
||||
groupId: this.groupId,
|
||||
lastMemberId: lastMember._id,
|
||||
@@ -483,10 +483,10 @@ export default {
|
||||
this.viewMembers();
|
||||
},
|
||||
async promoteToLeader (member) {
|
||||
let groupData = Object.assign({}, this.group);
|
||||
const groupData = { ...this.group };
|
||||
|
||||
groupData.leader = member._id;
|
||||
await this.$store.dispatch('guilds:update', {group: groupData});
|
||||
await this.$store.dispatch('guilds:update', { group: groupData });
|
||||
|
||||
alert(this.$t('leaderChanged'));
|
||||
|
||||
|
||||
@@ -61,10 +61,10 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import MugenScroll from 'vue-mugen-scroll';
|
||||
import { mapState } from '@/libs/store';
|
||||
import groupUtilities from '@/mixins/groupsUtilities';
|
||||
|
||||
import MugenScroll from 'vue-mugen-scroll';
|
||||
|
||||
import PublicGuildItem from './publicGuildItem';
|
||||
import Sidebar from './sidebar';
|
||||
@@ -73,8 +73,8 @@ import greyBadgeIcon from '@/assets/svg/grey-badge.svg';
|
||||
import positiveIcon from '@/assets/svg/positive.svg';
|
||||
|
||||
export default {
|
||||
mixins: [groupUtilities],
|
||||
components: { PublicGuildItem, MugenScroll, Sidebar },
|
||||
mixins: [groupUtilities],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
@@ -109,15 +109,13 @@ export default {
|
||||
guilds: 'myGuilds',
|
||||
}),
|
||||
filteredGuilds () {
|
||||
let search = this.search;
|
||||
let filters = this.filters;
|
||||
let user = this.$store.state.user.data;
|
||||
let filterGuild = this.filterGuild;
|
||||
return this.guilds.filter((guild) => {
|
||||
const { search } = this;
|
||||
const { filters } = this;
|
||||
const user = this.$store.state.user.data;
|
||||
const { filterGuild } = this;
|
||||
return this.guilds.filter(guild => {
|
||||
if (guild.categories) {
|
||||
guild.categorySlugs = guild.categories.map(cat => {
|
||||
return cat.slug;
|
||||
});
|
||||
guild.categorySlugs = guild.categories.map(cat => cat.slug);
|
||||
}
|
||||
return filterGuild(guild, filters, search, user);
|
||||
});
|
||||
|
||||
@@ -71,16 +71,16 @@ import { mapGetters } from '@/libs/store';
|
||||
import MemberDetails from '../memberDetails';
|
||||
|
||||
export default {
|
||||
props: ['group'],
|
||||
components: {
|
||||
MemberDetails,
|
||||
},
|
||||
props: ['group'],
|
||||
computed: {
|
||||
...mapGetters({
|
||||
partyMembers: 'party:members',
|
||||
}),
|
||||
participants () {
|
||||
let partyMembers = this.partyMembers || [];
|
||||
const partyMembers = this.partyMembers || [];
|
||||
return partyMembers.filter(member => this.group.quest.members[member._id] === true);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -144,16 +144,16 @@ import bronzeGuildBadgeIcon from '@/assets/svg/bronze-guild-badge-large.svg';
|
||||
import { MAX_SUMMARY_SIZE_FOR_GUILDS } from '@/../../common/script/constants';
|
||||
|
||||
export default {
|
||||
mixins: [groupUtilities],
|
||||
directives: {
|
||||
markdown,
|
||||
},
|
||||
props: ['guild', 'displayLeave', 'displayGemBank'],
|
||||
components: {
|
||||
categoryTags,
|
||||
},
|
||||
mixins: [groupUtilities],
|
||||
props: ['guild', 'displayLeave', 'displayGemBank'],
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
isOwner () {
|
||||
return this.guild.leader && this.guild.leader === this.user._id;
|
||||
},
|
||||
@@ -174,9 +174,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
showSuggested (guildId) {
|
||||
let habiticaHelpingGuildId = '5481ccf3-5d2d-48a9-a871-70a7380cee5a';
|
||||
let sixtyDaysAgoFromNow = moment().subtract(60, 'days');
|
||||
let isUserNew = moment(this.user.auth.timestamps.created).isAfter(sixtyDaysAgoFromNow);
|
||||
const habiticaHelpingGuildId = '5481ccf3-5d2d-48a9-a871-70a7380cee5a';
|
||||
const sixtyDaysAgoFromNow = moment().subtract(60, 'days');
|
||||
const isUserNew = moment(this.user.auth.timestamps.created).isAfter(sixtyDaysAgoFromNow);
|
||||
return guildId === habiticaHelpingGuildId && isUserNew;
|
||||
},
|
||||
async join () {
|
||||
@@ -184,11 +184,11 @@ export default {
|
||||
if (this.guild.cancelledPlan && !confirm(window.env.t('aboutToJoinCancelledGroupPlan'))) {
|
||||
return;
|
||||
}
|
||||
await this.$store.dispatch('guilds:join', {groupId: this.guild._id, type: 'guild'});
|
||||
await this.$store.dispatch('guilds:join', { groupId: this.guild._id, type: 'guild' });
|
||||
},
|
||||
async leave () {
|
||||
// @TODO: ask about challenges when we add challenges
|
||||
await this.$store.dispatch('guilds:leave', {groupId: this.guild._id, type: 'myGuilds'});
|
||||
await this.$store.dispatch('guilds:leave', { groupId: this.guild._id, type: 'myGuilds' });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -149,11 +149,11 @@ import questDialogDrops from '../shops/quests/questDialogDrops';
|
||||
import questDialogContent from '../shops/quests/questDialogContent';
|
||||
|
||||
export default {
|
||||
props: ['group'],
|
||||
components: {
|
||||
questDialogDrops,
|
||||
questDialogContent,
|
||||
},
|
||||
props: ['group'],
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
@@ -181,38 +181,36 @@ export default {
|
||||
return quests.quests[this.group.quest.key];
|
||||
},
|
||||
members () {
|
||||
let partyMembers = this.partyMembers || [];
|
||||
return partyMembers.map(member => {
|
||||
return {
|
||||
name: member.profile.name,
|
||||
accepted: this.group.quest.members[member._id],
|
||||
};
|
||||
});
|
||||
const partyMembers = this.partyMembers || [];
|
||||
return partyMembers.map(member => ({
|
||||
name: member.profile.name,
|
||||
accepted: this.group.quest.members[member._id],
|
||||
}));
|
||||
},
|
||||
canEditQuest () {
|
||||
if (!this.group.quest) return false;
|
||||
let isQuestLeader = this.group.quest.leader === this.user._id;
|
||||
let isPartyLeader = this.group.leader._id === this.user._id;
|
||||
const isQuestLeader = this.group.quest.leader === this.user._id;
|
||||
const isPartyLeader = this.group.leader._id === this.user._id;
|
||||
return isQuestLeader || isPartyLeader;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async questConfirm () {
|
||||
let count = 0;
|
||||
for (let uuid in this.group.quest.members) {
|
||||
for (const uuid in this.group.quest.members) {
|
||||
if (this.group.quest.members[uuid]) count += 1;
|
||||
}
|
||||
if (!confirm(this.$t('questConfirm', { questmembers: count, totalmembers: this.group.memberCount}))) return;
|
||||
if (!confirm(this.$t('questConfirm', { questmembers: count, totalmembers: this.group.memberCount }))) return;
|
||||
this.questForceStart();
|
||||
},
|
||||
async questForceStart () {
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/force-start'});
|
||||
const quest = await this.$store.dispatch('quests:sendAction', { groupId: this.group._id, action: 'quests/force-start' });
|
||||
this.group.quest = quest;
|
||||
this.close();
|
||||
},
|
||||
async questCancel () {
|
||||
if (!confirm(this.$t('sureCancel'))) return;
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/cancel'});
|
||||
const quest = await this.$store.dispatch('quests:sendAction', { groupId: this.group._id, action: 'quests/cancel' });
|
||||
this.group.quest = quest;
|
||||
this.close();
|
||||
},
|
||||
|
||||
@@ -198,10 +198,10 @@ import sidebarSection from '../sidebarSection';
|
||||
import questIcon from '@/assets/svg/quest.svg';
|
||||
|
||||
export default {
|
||||
props: ['group'],
|
||||
components: {
|
||||
sidebarSection,
|
||||
},
|
||||
props: ['group'],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
@@ -210,7 +210,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
userIsOnQuest () {
|
||||
if (!this.group.quest || !this.group.quest.members) return false;
|
||||
return Boolean(this.group.quest.members[this.user._id]);
|
||||
@@ -230,20 +230,20 @@ export default {
|
||||
},
|
||||
canEditQuest () {
|
||||
if (!this.group.quest) return false;
|
||||
let isQuestLeader = this.group.quest.leader === this.user._id;
|
||||
let isPartyLeader = this.group.leader._id === this.user._id;
|
||||
const isQuestLeader = this.group.quest.leader === this.user._id;
|
||||
const isPartyLeader = this.group.leader._id === this.user._id;
|
||||
return isQuestLeader || isPartyLeader;
|
||||
},
|
||||
isMemberOfPendingQuest () {
|
||||
let userid = this.user._id;
|
||||
let group = this.group;
|
||||
const userid = this.user._id;
|
||||
const { group } = this;
|
||||
if (!group.quest || !group.quest.members) return false;
|
||||
if (group.quest.active) return false; // quest is started, not pending
|
||||
return userid in group.quest.members && group.quest.members[userid] !== false;
|
||||
},
|
||||
isMemberOfRunningQuest () {
|
||||
let userid = this.user._id;
|
||||
let group = this.group;
|
||||
const userid = this.user._id;
|
||||
const { group } = this;
|
||||
if (!group.quest || !group.quest.members) return false;
|
||||
if (!group.quest.active) return false; // quest is pending, not started
|
||||
return group.quest.members[userid];
|
||||
@@ -253,7 +253,7 @@ export default {
|
||||
|
||||
if (!this.group || !this.group.quest) return count;
|
||||
|
||||
for (let uuid in this.group.quest.members) {
|
||||
for (const uuid in this.group.quest.members) {
|
||||
if (this.group.quest.members[uuid]) count += 1;
|
||||
}
|
||||
|
||||
@@ -273,20 +273,20 @@ export default {
|
||||
async questAbort () {
|
||||
if (!confirm(this.$t('sureAbort'))) return;
|
||||
if (!confirm(this.$t('doubleSureAbort'))) return;
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/abort'});
|
||||
const quest = await this.$store.dispatch('quests:sendAction', { groupId: this.group._id, action: 'quests/abort' });
|
||||
this.group.quest = quest;
|
||||
},
|
||||
async questLeave () {
|
||||
if (!confirm(this.$t('sureLeave'))) return;
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/leave'});
|
||||
const quest = await this.$store.dispatch('quests:sendAction', { groupId: this.group._id, action: 'quests/leave' });
|
||||
this.group.quest = quest;
|
||||
},
|
||||
async questAccept (partyId) {
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: partyId, action: 'quests/accept'});
|
||||
const quest = await this.$store.dispatch('quests:sendAction', { groupId: partyId, action: 'quests/accept' });
|
||||
this.user.party.quest = quest;
|
||||
},
|
||||
async questReject (partyId) {
|
||||
let quest = await this.$store.dispatch('quests:sendAction', {groupId: partyId, action: 'quests/reject'});
|
||||
const quest = await this.$store.dispatch('quests:sendAction', { groupId: partyId, action: 'quests/reject' });
|
||||
this.user.party.quest = quest;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -123,12 +123,12 @@ import questDialogContent from '../shops/quests/questDialogContent';
|
||||
import QuestInfo from '../shops/quests/questInfo';
|
||||
|
||||
export default {
|
||||
props: ['group'],
|
||||
components: {
|
||||
questDialogDrops,
|
||||
questDialogContent,
|
||||
QuestInfo,
|
||||
},
|
||||
props: ['group'],
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
@@ -162,7 +162,7 @@ export default {
|
||||
this.$root.$off('selectQuest', this.selectQuest);
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
questData () {
|
||||
return quests.quests[this.selectedQuest];
|
||||
},
|
||||
@@ -179,11 +179,11 @@ export default {
|
||||
partySize: this.group.memberCount,
|
||||
});
|
||||
|
||||
let groupId = this.group._id || this.user.party._id;
|
||||
const groupId = this.group._id || this.user.party._id;
|
||||
|
||||
const key = this.selectedQuest;
|
||||
try {
|
||||
const response = await this.$store.dispatch('guilds:inviteToQuest', {groupId, key});
|
||||
const response = await this.$store.dispatch('guilds:inviteToQuest', { groupId, key });
|
||||
const quest = response.data.data;
|
||||
|
||||
if (this.$store.state.party.data) this.$store.state.party.data.quest = quest;
|
||||
|
||||
@@ -501,14 +501,14 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
questData () {
|
||||
if (!this.group.quest) return {};
|
||||
return quests.quests[this.group.quest.key];
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
this.group = await this.$store.dispatch('guilds:getGroup', {groupId: TAVERN_ID});
|
||||
this.group = await this.$store.dispatch('guilds:getGroup', { groupId: TAVERN_ID });
|
||||
},
|
||||
methods: {
|
||||
modForm () {
|
||||
@@ -543,7 +543,7 @@ export default {
|
||||
}
|
||||
},
|
||||
async fetchRecentMessages () {
|
||||
this.group = await this.$store.dispatch('guilds:getGroup', {groupId: TAVERN_ID});
|
||||
this.group = await this.$store.dispatch('guilds:getGroup', { groupId: TAVERN_ID });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -119,10 +119,13 @@ import notifications from '@/mixins/notifications';
|
||||
import userLink from '../userLink';
|
||||
|
||||
export default {
|
||||
mixins: [notifications, styleHelper],
|
||||
components: {
|
||||
userLink,
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
mixins: [notifications, styleHelper],
|
||||
data () {
|
||||
return {
|
||||
heroes: [],
|
||||
@@ -141,14 +144,11 @@ export default {
|
||||
expandAuth: false,
|
||||
};
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
async mounted () {
|
||||
this.heroes = await this.$store.dispatch('hall:getHeroes');
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
getAllItemPaths () {
|
||||
@@ -172,7 +172,7 @@ export default {
|
||||
getFormattedItemReference (pathPrefix, itemKeys, values) {
|
||||
let finishedString = '\n'.concat('path: ', pathPrefix, ', ', 'value: {', values, '}\n');
|
||||
|
||||
each(itemKeys, (key) => {
|
||||
each(itemKeys, key => {
|
||||
finishedString = finishedString.concat('\t', pathPrefix, '.', key, '\n');
|
||||
});
|
||||
|
||||
@@ -180,8 +180,8 @@ export default {
|
||||
},
|
||||
async loadHero (uuid, heroIndex) {
|
||||
this.currentHeroIndex = heroIndex;
|
||||
let hero = await this.$store.dispatch('hall:getHero', { uuid });
|
||||
this.hero = Object.assign({}, hero);
|
||||
const hero = await this.$store.dispatch('hall:getHero', { uuid });
|
||||
this.hero = { ...hero };
|
||||
if (!this.hero.flags) {
|
||||
this.hero.flags = {
|
||||
chatRevoked: false,
|
||||
@@ -192,8 +192,8 @@ export default {
|
||||
this.expandAuth = false;
|
||||
},
|
||||
async saveHero () {
|
||||
this.hero.contributor.admin = this.hero.contributor.level > 7 ? true : false;
|
||||
let heroUpdated = await this.$store.dispatch('hall:updateHero', { heroDetails: this.hero });
|
||||
this.hero.contributor.admin = this.hero.contributor.level > 7;
|
||||
const heroUpdated = await this.$store.dispatch('hall:updateHero', { heroDetails: this.hero });
|
||||
this.text('User updated');
|
||||
this.hero = {};
|
||||
this.heroID = -1;
|
||||
|
||||
@@ -32,7 +32,7 @@ export default {
|
||||
this.patrons = await this.$store.dispatch('hall:getPatrons', { page: 0 });
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
// @TODO: Import member modal - clickMember()
|
||||
|
||||
@@ -178,7 +178,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('inviteModal::inviteToGroup', (group) => {
|
||||
this.$root.$on('inviteModal::inviteToGroup', group => {
|
||||
this.inviteModalGroup = group;
|
||||
this.inviteModalGroupType = group.type === 'guild' ? 'Guild' : 'Party';
|
||||
this.$root.$emit('bv::show::modal', 'invite-modal');
|
||||
|
||||
@@ -488,7 +488,7 @@ export default {
|
||||
eventLabel: 'Gems > Toolbar',
|
||||
});
|
||||
|
||||
this.$root.$emit('bv::show::modal', 'buy-gems', {alreadyTracked: true});
|
||||
this.$root.$emit('bv::show::modal', 'buy-gems', { alreadyTracked: true });
|
||||
},
|
||||
dropdownDesktop (hover) {
|
||||
if (this.isDesktop() && hover.target.classList.contains('droppable')) {
|
||||
@@ -499,7 +499,7 @@ export default {
|
||||
this.dropdown(click.currentTarget.parentElement);
|
||||
},
|
||||
dropdown (element) {
|
||||
let droppedElement = document.getElementsByClassName('down')[0];
|
||||
const droppedElement = document.getElementsByClassName('down')[0];
|
||||
if (droppedElement && droppedElement !== element) {
|
||||
droppedElement.classList.remove('down');
|
||||
if (droppedElement.lastChild) {
|
||||
|
||||
@@ -130,7 +130,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
isNotBailey () {
|
||||
return this.notification.type !== 'NEW_STUFF';
|
||||
},
|
||||
@@ -141,7 +141,7 @@ export default {
|
||||
}),
|
||||
clicked () {
|
||||
if (this.readAfterClick === true) {
|
||||
this.readNotification({notificationId: this.notification.id});
|
||||
this.readNotification({ notificationId: this.notification.id });
|
||||
}
|
||||
|
||||
this.$emit('click');
|
||||
@@ -149,12 +149,12 @@ export default {
|
||||
remove () {
|
||||
if (this.notification.type === 'NEW_CHAT_MESSAGE') {
|
||||
const groupId = this.notification.data.group.id;
|
||||
this.$store.dispatch('chat:markChatSeen', {groupId});
|
||||
this.$store.dispatch('chat:markChatSeen', { groupId });
|
||||
if (this.user.newMessages[groupId]) {
|
||||
this.$delete(this.user.newMessages, groupId);
|
||||
}
|
||||
} else {
|
||||
this.readNotification({notificationId: this.notification.id});
|
||||
this.readNotification({ notificationId: this.notification.id });
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,10 +14,10 @@ base-notification(
|
||||
import BaseNotification from './base';
|
||||
|
||||
export default {
|
||||
props: ['notification', 'canRemove'],
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
computed: {
|
||||
cardString () {
|
||||
return this.$t(`${this.notification.data.card}Card`);
|
||||
@@ -28,7 +28,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
action () {
|
||||
this.$router.push({name: 'items'});
|
||||
this.$router.push({ name: 'items' });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -18,13 +18,13 @@ import { mapState } from '@/libs/store';
|
||||
import sync from '@/mixins/sync';
|
||||
|
||||
export default {
|
||||
mixins: [sync],
|
||||
props: ['notification', 'canRemove'],
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
mixins: [sync],
|
||||
props: ['notification', 'canRemove'],
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
// Check that the notification has all the necessary data (old ones are missing some fields)
|
||||
notificationHasData () {
|
||||
return Boolean(this.notification.data.groupTaskId && this.notification.data.userId);
|
||||
@@ -33,14 +33,17 @@ export default {
|
||||
methods: {
|
||||
action () {
|
||||
const groupId = this.notification.data.group.id;
|
||||
this.$router.push({ name: 'groupPlanDetailTaskInformation', params: { groupId }});
|
||||
this.$router.push({ name: 'groupPlanDetailTaskInformation', params: { groupId } });
|
||||
},
|
||||
async approve () {
|
||||
// Redirect users to the group tasks page if the notification doesn't have data
|
||||
if (!this.notificationHasData) {
|
||||
this.$router.push({ name: 'groupPlanDetailTaskInformation', params: {
|
||||
groupId: this.notification.data.groupId,
|
||||
}});
|
||||
this.$router.push({
|
||||
name: 'groupPlanDetailTaskInformation',
|
||||
params: {
|
||||
groupId: this.notification.data.groupId,
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -55,9 +58,12 @@ export default {
|
||||
async needsWork () {
|
||||
// Redirect users to the group tasks page if the notification doesn't have data
|
||||
if (!this.notificationHasData) {
|
||||
this.$router.push({ name: 'groupPlanDetailTaskInformation', params: {
|
||||
groupId: this.notification.data.groupId,
|
||||
}});
|
||||
this.$router.push({
|
||||
name: 'groupPlanDetailTaskInformation',
|
||||
params: {
|
||||
groupId: this.notification.data.groupId,
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,14 +13,14 @@ base-notification(
|
||||
import BaseNotification from './base';
|
||||
|
||||
export default {
|
||||
props: ['notification', 'canRemove'],
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
methods: {
|
||||
action () {
|
||||
const groupId = this.notification.data.groupId;
|
||||
this.$router.push({ name: 'groupPlanDetailTaskInformation', params: { groupId }});
|
||||
const { groupId } = this.notification.data;
|
||||
this.$router.push({ name: 'groupPlanDetailTaskInformation', params: { groupId } });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseNotification from './base';
|
||||
import BaseNotification from './base';
|
||||
|
||||
export default {
|
||||
props: ['notification', 'canRemove'],
|
||||
components: {
|
||||
BaseNotification,
|
||||
export default {
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
methods: {
|
||||
action () {
|
||||
this.$router.push({ name: 'tasks' });
|
||||
},
|
||||
methods: {
|
||||
action () {
|
||||
this.$router.push({ name: 'tasks'});
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -13,14 +13,14 @@ base-notification(
|
||||
import BaseNotification from './base';
|
||||
|
||||
export default {
|
||||
props: ['notification', 'canRemove'],
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
methods: {
|
||||
action () {
|
||||
const groupId = this.notification.data.groupId;
|
||||
this.$router.push({ name: 'groupPlanDetailTaskInformation', params: { groupId }});
|
||||
const { groupId } = this.notification.data;
|
||||
this.$router.push({ name: 'groupPlanDetailTaskInformation', params: { groupId } });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -13,14 +13,14 @@ base-notification(
|
||||
import BaseNotification from './base';
|
||||
|
||||
export default {
|
||||
props: ['notification', 'canRemove'],
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
methods: {
|
||||
action () {
|
||||
const groupId = this.notification.data.group.id;
|
||||
this.$router.push({ name: 'groupPlanDetailTaskInformation', params: { groupId }});
|
||||
this.$router.push({ name: 'groupPlanDetailTaskInformation', params: { groupId } });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -17,12 +17,12 @@ import BaseNotification from './base';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
props: ['notification', 'canRemove'],
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({ user: 'user.data' }),
|
||||
isPublicGuild () {
|
||||
if (this.notification.data.publicGuild === true) return true;
|
||||
return false;
|
||||
@@ -31,10 +31,9 @@ export default {
|
||||
const guild = this.notification.data.name;
|
||||
|
||||
if (this.isPublicGuild) {
|
||||
return this.$t('invitedToPublicGuild', {guild});
|
||||
} else {
|
||||
return this.$t('invitedToPrivateGuild', {guild});
|
||||
return this.$t('invitedToPublicGuild', { guild });
|
||||
}
|
||||
return this.$t('invitedToPrivateGuild', { guild });
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@@ -52,11 +51,11 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.$store.dispatch('guilds:join', {groupId: group.id, type: 'guild'});
|
||||
await this.$store.dispatch('guilds:join', { groupId: group.id, type: 'guild' });
|
||||
this.$router.push({ name: 'guild', params: { groupId: group.id } });
|
||||
},
|
||||
reject () {
|
||||
this.$store.dispatch('guilds:rejectInvite', {groupId: this.notification.data.id, type: 'guild'});
|
||||
this.$store.dispatch('guilds:rejectInvite', { groupId: this.notification.data.id, type: 'guild' });
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
@@ -9,22 +9,22 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseNotification from './base';
|
||||
import BaseNotification from './base';
|
||||
|
||||
export default {
|
||||
props: ['notification', 'canRemove'],
|
||||
components: {
|
||||
BaseNotification,
|
||||
export default {
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
computed: {
|
||||
achievementString () {
|
||||
return `<strong>${this.$t('achievement')}</strong>: ${this.$t('achievementJustAddWater')}`;
|
||||
},
|
||||
computed: {
|
||||
achievementString () {
|
||||
return `<strong>${this.$t('achievement')}</strong>: ${this.$t('achievementJustAddWater')}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
action () {
|
||||
this.$root.$emit('bv::show::modal', 'just-add-water');
|
||||
},
|
||||
methods: {
|
||||
action () {
|
||||
this.$root.$emit('bv::show::modal', 'just-add-water');
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user