Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e0e448250 | |||
| 3377f8a916 | |||
| 22f83d09c4 | |||
| 496534ab4b | |||
| 7325d2020e | |||
| 1a0d39e566 | |||
| dc62ab7577 | |||
| c4e5633e48 | |||
| d977656e96 | |||
| b0a980d56e | |||
| 3d75c99f8d | |||
| 0aa16d7021 | |||
| 4fa3046104 | |||
| fda4be01b8 | |||
| 67436fbef1 | |||
| ffb92a5faa | |||
| 4a44d60fac |
@@ -47,10 +47,5 @@ gulp.task('build:prod', [
|
||||
'build:server',
|
||||
'prepare:staticNewStuff',
|
||||
'build:client',
|
||||
], (done) => {
|
||||
runSequence(
|
||||
'grunt-build:prod',
|
||||
'apidoc',
|
||||
done
|
||||
);
|
||||
});
|
||||
'apidoc',
|
||||
]);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var updateStore = require('../website/common/script/libs/updateStore');
|
||||
import { selectGearToPin } from '../website/common/script/ops/pinnedGearUtils';
|
||||
|
||||
var getItemInfo = require('../website/common/script/libs/getItemInfo');
|
||||
|
||||
var migrationName = '20170928_redesign_launch.js';
|
||||
@@ -69,7 +70,7 @@ function updateUser (user) {
|
||||
|
||||
var set = {'migration': migrationName};
|
||||
|
||||
var oldRewardsList = updateStore(user);
|
||||
var oldRewardsList = selectGearToPin(user);
|
||||
var newPinnedItems = [
|
||||
{
|
||||
type: 'armoire',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "4.1.2",
|
||||
"version": "4.2.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -68,6 +68,15 @@
|
||||
"@types/mime": "1.3.1"
|
||||
}
|
||||
},
|
||||
"JSONStream": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
|
||||
"integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=",
|
||||
"requires": {
|
||||
"jsonparse": "1.3.1",
|
||||
"through": "2.3.8"
|
||||
}
|
||||
},
|
||||
"abbrev": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz",
|
||||
@@ -782,6 +791,14 @@
|
||||
"is-buffer": "1.1.5"
|
||||
}
|
||||
},
|
||||
"axios-progress-bar": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/axios-progress-bar/-/axios-progress-bar-0.1.7.tgz",
|
||||
"integrity": "sha512-xStxJUtcQUH0ulLni5qc8YwvNMTUjO7rmUTsvnxS1bD2GJKEemozP6sJ5OeoC6jjd6MS5FZyx5BlkU/lD8JLUw==",
|
||||
"requires": {
|
||||
"nprogress": "0.2.0"
|
||||
}
|
||||
},
|
||||
"babel-code-frame": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
||||
@@ -2325,9 +2342,9 @@
|
||||
"resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.2.tgz",
|
||||
"integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=",
|
||||
"requires": {
|
||||
"JSONStream": "1.3.1",
|
||||
"combine-source-map": "0.7.2",
|
||||
"defined": "1.0.0",
|
||||
"JSONStream": "1.3.1",
|
||||
"through2": "2.0.3",
|
||||
"umd": "3.0.1"
|
||||
}
|
||||
@@ -2357,6 +2374,7 @@
|
||||
"resolved": "https://registry.npmjs.org/browserify/-/browserify-12.0.2.tgz",
|
||||
"integrity": "sha1-V/IeXm4wj/WYfE2v1EhAsrmPehk=",
|
||||
"requires": {
|
||||
"JSONStream": "1.3.1",
|
||||
"assert": "1.3.0",
|
||||
"browser-pack": "6.0.2",
|
||||
"browser-resolve": "1.11.2",
|
||||
@@ -2378,7 +2396,6 @@
|
||||
"inherits": "2.0.3",
|
||||
"insert-module-globals": "7.0.1",
|
||||
"isarray": "0.0.1",
|
||||
"JSONStream": "1.3.1",
|
||||
"labeled-stream-splicer": "2.0.0",
|
||||
"module-deps": "4.1.1",
|
||||
"os-browserify": "0.1.2",
|
||||
@@ -7389,13 +7406,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.0.1"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
@@ -7405,6 +7415,13 @@
|
||||
"strip-ansi": "3.0.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.0.1"
|
||||
}
|
||||
},
|
||||
"stringstream": {
|
||||
"version": "0.0.5",
|
||||
"bundled": true,
|
||||
@@ -10353,10 +10370,10 @@
|
||||
"resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz",
|
||||
"integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=",
|
||||
"requires": {
|
||||
"JSONStream": "1.3.1",
|
||||
"combine-source-map": "0.7.2",
|
||||
"concat-stream": "1.5.2",
|
||||
"is-buffer": "1.1.5",
|
||||
"JSONStream": "1.3.1",
|
||||
"lexical-scope": "1.2.0",
|
||||
"process": "0.11.10",
|
||||
"through2": "2.0.3",
|
||||
@@ -11349,15 +11366,6 @@
|
||||
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
|
||||
"integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk="
|
||||
},
|
||||
"JSONStream": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
|
||||
"integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=",
|
||||
"requires": {
|
||||
"jsonparse": "1.3.1",
|
||||
"through": "2.3.8"
|
||||
}
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||
@@ -13257,6 +13265,7 @@
|
||||
"resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz",
|
||||
"integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=",
|
||||
"requires": {
|
||||
"JSONStream": "1.3.1",
|
||||
"browser-resolve": "1.11.2",
|
||||
"cached-path-relative": "1.0.1",
|
||||
"concat-stream": "1.5.2",
|
||||
@@ -13264,7 +13273,6 @@
|
||||
"detective": "4.5.0",
|
||||
"duplexer2": "0.1.4",
|
||||
"inherits": "2.0.3",
|
||||
"JSONStream": "1.3.1",
|
||||
"parents": "1.0.1",
|
||||
"readable-stream": "2.0.6",
|
||||
"resolve": "1.4.0",
|
||||
@@ -14640,6 +14648,11 @@
|
||||
"set-blocking": "2.0.0"
|
||||
}
|
||||
},
|
||||
"nprogress": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
|
||||
"integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E="
|
||||
},
|
||||
"nth-check": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz",
|
||||
@@ -17234,22 +17247,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"require_optional": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
|
||||
"integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
|
||||
"requires": {
|
||||
"resolve-from": "2.0.0",
|
||||
"semver": "5.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
||||
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"require-again": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-again/-/require-again-2.0.0.tgz",
|
||||
@@ -17289,6 +17286,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"require_optional": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
|
||||
"integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
|
||||
"requires": {
|
||||
"resolve-from": "2.0.0",
|
||||
"semver": "5.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
||||
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
@@ -18655,11 +18668,6 @@
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||
},
|
||||
"string-length": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-0.1.2.tgz",
|
||||
@@ -18693,6 +18701,11 @@
|
||||
"strip-ansi": "3.0.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||
},
|
||||
"stringify-object": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-1.0.1.tgz",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "4.1.2",
|
||||
"version": "4.2.0",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@slack/client": "^3.8.1",
|
||||
@@ -14,6 +14,7 @@
|
||||
"autoprefixer": "^6.4.0",
|
||||
"aws-sdk": "^2.0.25",
|
||||
"axios": "^0.16.0",
|
||||
"axios-progress-bar": "^0.1.7",
|
||||
"babel-core": "^6.0.0",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-loader": "^6.0.0",
|
||||
|
||||
@@ -105,7 +105,12 @@ const baseConfig = {
|
||||
{
|
||||
test: /\.svg$/,
|
||||
use: [
|
||||
{ loader: 'svg-url-loader' },
|
||||
{
|
||||
loader: 'svg-url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
},
|
||||
},
|
||||
{ loader: 'svgo-loader' },
|
||||
],
|
||||
include: [path.resolve(projectRoot, 'website/client/assets/svg/for-css')],
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import { loadProgressBar } from 'axios-progress-bar';
|
||||
|
||||
import AppMenu from './components/appMenu';
|
||||
import AppHeader from './components/appHeader';
|
||||
import AppFooter from './components/appFooter';
|
||||
@@ -129,6 +131,7 @@ export default {
|
||||
this.$refs.sound.load();
|
||||
});
|
||||
|
||||
// @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.selectedItemToBuy = item;
|
||||
this.$root.$emit('show::modal', 'buy-modal');
|
||||
@@ -140,6 +143,9 @@ export default {
|
||||
});
|
||||
|
||||
// @TODO split up this file, it's too big
|
||||
|
||||
loadProgressBar();
|
||||
|
||||
// Set up Error interceptors
|
||||
axios.interceptors.response.use((response) => {
|
||||
if (this.user && response.data && response.data.notifications) {
|
||||
@@ -320,10 +326,12 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
memberSelected (member) {
|
||||
async memberSelected (member) {
|
||||
this.$store.dispatch('user:castSpell', {key: this.selectedSpellToBuy.key, targetId: member.id});
|
||||
this.selectedSpellToBuy = null;
|
||||
|
||||
this.$store.dispatch('party:getMembers', {forceLoad: true});
|
||||
|
||||
this.$root.$emit('hide::modal', 'select-member-modal');
|
||||
},
|
||||
hideLoadingScreen () {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
@import './loading-screen';
|
||||
|
||||
// Global styles
|
||||
@import './misc';
|
||||
@import './typography';
|
||||
@import './markdown';
|
||||
@import './form';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
.items > div {
|
||||
display: inline-block;
|
||||
margin-right: 23px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.items > div:last-of-type {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
.expand-toggle:after {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
content: "";
|
||||
border-bottom: 4px solid transparent;
|
||||
border-top: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
|
||||
.expand-toggle.open:after {
|
||||
border-top: 4px solid;
|
||||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid transparent;
|
||||
border-bottom: 0;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="8" viewBox="0 0 12 8">
|
||||
<path fill="#878190" fill-rule="evenodd" d="M3 6h9v2H3V6zm0-3h7v2H3V3zm0-3h5v2H3V0zM0 6h2v2H0V6zm0-3h2v2H0V3zm0-3h2v2H0V0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 219 B |
@@ -29,11 +29,27 @@ div
|
||||
br
|
||||
// TODO link to party creation or party page if partying solo
|
||||
button.btn.btn-primary(@click='openPartyModal()') {{ partyMembers && partyMembers.length > 1 ? $t('startAParty') : $t('inviteFriends') }}
|
||||
a.useMobileApp(v-if="isAndroidMobile()", v-once, href="https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica") {{ $t('useMobileApps') }}
|
||||
a.useMobileApp(v-if="isIOSMobile()", v-once, href="https://itunes.apple.com/us/app/habitica-gamified-task-manager/id994882113?mt=8") {{ $t('useMobileApps') }}
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.useMobileApp {
|
||||
background: red;
|
||||
color: white;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
margin: 10px 5px 0 0;
|
||||
height: 64px;
|
||||
text-align: center;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#app-header {
|
||||
padding-left: 14px;
|
||||
margin-top: 56px;
|
||||
@@ -117,6 +133,7 @@ export default {
|
||||
user: 'user:data',
|
||||
partyMembers: 'party:members',
|
||||
}),
|
||||
|
||||
showHeader () {
|
||||
if (this.$store.state.hideHeader) return false;
|
||||
return true;
|
||||
@@ -129,6 +146,12 @@ export default {
|
||||
...mapActions({
|
||||
getPartyMembers: 'party:getMembers',
|
||||
}),
|
||||
isAndroidMobile () {
|
||||
return navigator.userAgent.match(/Android/i);
|
||||
},
|
||||
isIOSMobile () {
|
||||
return navigator.userAgent.match(/iPhone|iPad|iPod/i);
|
||||
},
|
||||
expandMember (memberId) {
|
||||
if (this.expandedMember === memberId) {
|
||||
this.expandedMember = null;
|
||||
|
||||
@@ -3,9 +3,10 @@ div
|
||||
inbox-modal
|
||||
creator-intro
|
||||
profile
|
||||
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-lg
|
||||
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-md
|
||||
.navbar-header
|
||||
.logo.svg-icon(v-html="icons.logo")
|
||||
.logo.svg-icon.hidden-lg-down(v-html="icons.logo")
|
||||
.svg-icon.gryphon.hidden-xl-up
|
||||
b-collapse#nav_collapse.collapse.navbar-collapse(is-nav)
|
||||
ul.navbar-nav.mr-auto
|
||||
router-link.nav-item(tag="li", :to="{name: 'tasks'}", exact)
|
||||
@@ -93,58 +94,34 @@ div
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
@import '~client/assets/scss/utils.scss';
|
||||
|
||||
/* Less than Desktops and laptops ----------- */
|
||||
@media only screen and (max-width : 1224px) {
|
||||
#nav_collapse {
|
||||
background: $purple-100;
|
||||
margin-top: 1em;
|
||||
margin-left: 70%;
|
||||
padding-bottom: 1em;
|
||||
|
||||
a {
|
||||
padding: .5em !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute !important;
|
||||
left: -10em;
|
||||
top: -.5em;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width : 1224px) and (min-width: 1200px) {
|
||||
#nav_collapse {
|
||||
margin-top: 37em !important;
|
||||
|
||||
a {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-collapse.collapse {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.navbar-collapse.collapse.show {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.navbar-toggler, .navbar-nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navbar-toggleable-lg .navbar-collapse {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1280px) {
|
||||
.nav-link {
|
||||
padding: .8em 1em !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.navbar-header {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.gryphon {
|
||||
background-image: url('~assets/images/melior@3x.png');
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-size: cover;
|
||||
color: $white;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 990px) {
|
||||
#nav_collapse {
|
||||
margin-top: 1.3em;
|
||||
background-color: $purple-200;
|
||||
}
|
||||
}
|
||||
|
||||
nav.navbar {
|
||||
background: $purple-100 url(~assets/svg/for-css/bits.svg) right no-repeat;
|
||||
padding-left: 25px;
|
||||
@@ -354,7 +331,6 @@ export default {
|
||||
hourglasses: svgHourglasses,
|
||||
logo,
|
||||
}),
|
||||
groupPlans: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -364,6 +340,7 @@ export default {
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
userHourglasses: 'user.data.purchased.plan.consecutive.trinkets',
|
||||
groupPlans: 'groupPlans',
|
||||
}),
|
||||
},
|
||||
mounted () {
|
||||
@@ -390,7 +367,7 @@ export default {
|
||||
this.$root.$emit('show::modal', 'profile');
|
||||
},
|
||||
async getUserGroupPlans () {
|
||||
this.groupPlans = await this.$store.dispatch('guilds:getGroupPlans');
|
||||
this.$store.state.groupPlans = await this.$store.dispatch('guilds:getGroupPlans');
|
||||
},
|
||||
openPartyModal () {
|
||||
this.$root.$emit('show::modal', 'create-party-modal');
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
b-dropdown.create-dropdown(text="Select a Participant")
|
||||
b-dropdown-item(v-for="member in members", :key="member._id", @click="openMemberProgressModal(member._id)")
|
||||
| {{ member.profile.name }}
|
||||
span(v-if='isLeader')
|
||||
span(v-if='isLeader || isAdmin')
|
||||
b-dropdown.create-dropdown(:text="$t('addTaskToChallenge')", :variant="'success'")
|
||||
b-dropdown-item(v-for="type in columns", :key="type", @click="createTask(type)")
|
||||
| {{$t(type)}}
|
||||
@@ -64,13 +64,13 @@
|
||||
button.btn.btn-success(v-once, @click='joinChallenge()') {{$t('joinChallenge')}}
|
||||
div(v-if='isMember')
|
||||
button.btn.btn-danger(v-once, @click='leaveChallenge()') {{$t('leaveChallenge')}}
|
||||
div(v-if='isLeader')
|
||||
div(v-if='isLeader || isAdmin')
|
||||
button.btn.btn-secondary(v-once, @click='edit()') {{$t('editChallenge')}}
|
||||
div(v-if='isLeader')
|
||||
div(v-if='isLeader || isAdmin')
|
||||
button.btn.btn-danger(v-once, @click='closeChallenge()') {{$t('endChallenge')}}
|
||||
div(v-if='isLeader || isAdmin')
|
||||
button.btn.btn-secondary(v-once, @click='exportChallengeCsv()') {{$t('exportChallengeCsv')}}
|
||||
div(v-if='isLeader')
|
||||
div(v-if='isLeader || isAdmin')
|
||||
button.btn.btn-secondary(v-once, @click='cloneChallenge()') {{$t('clone')}}
|
||||
.description-section
|
||||
h2 {{$t('challengeSummary')}}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
.form-group
|
||||
label
|
||||
strong(v-once) {{$t('challengeDescription')}} *
|
||||
a.float-right {{ $t('markdownFormattingHelp') }}
|
||||
a.float-right(v-markdown='$t("markdownFormattingHelp")')
|
||||
textarea.description-textarea.form-control(:placeholder="$t('challengeDescriptionPlaceholder')", v-model="workingChallenge.description")
|
||||
.form-group(v-if='creating')
|
||||
label
|
||||
@@ -136,6 +136,8 @@ import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||
import bFormInput from 'bootstrap-vue/lib/components/form-input';
|
||||
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
|
||||
import { TAVERN_ID, MIN_SHORTNAME_SIZE_FOR_CHALLENGES, MAX_SUMMARY_SIZE_FOR_CHALLENGES } from '../../../common/script/constants';
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
@@ -147,6 +149,9 @@ export default {
|
||||
bDropdownItem,
|
||||
bFormInput,
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
data () {
|
||||
let categoryOptions = [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<template lang="pug">
|
||||
.row.standard-page(v-if='groupIsSubscribed && isLeader')
|
||||
.col-12.col-md-6.offset-md-3
|
||||
table.table.alert.alert-info
|
||||
tr(v-if='group.purchased.plan.dateTerminated')
|
||||
td.alert.alert-warning
|
||||
span.noninteractive-button.btn-danger {{ $t('canceledGroupPlan') }}
|
||||
i.glyphicon.glyphicon-time {{ $t('groupPlanCanceled') }}
|
||||
strong {{dateTerminated}}
|
||||
tr(v-if='!group.purchased.plan.dateTerminated')
|
||||
td
|
||||
h3 {{ $t('paymentDetails') }}
|
||||
p(v-if='group.purchased.plan.planId') {{ $t('groupSubscriptionPrice') }}
|
||||
tr(v-if='group.purchased.plan.extraMonths')
|
||||
td
|
||||
span.glyphicon.glyphicon-credit-card
|
||||
| {{ $t('purchasedGroupPlanPlanExtraMonths', purchasedGroupPlanPlanExtraMonths) }}
|
||||
tr(v-if='group.purchased.plan.consecutive.count || group.purchased.plan.consecutive.offset')
|
||||
td
|
||||
span.glyphicon.glyphicon-forward
|
||||
| {{ $t('consecutiveSubscription') }}
|
||||
ul.list-unstyled
|
||||
li {{ $t('consecutiveMonths') }} {{group.purchased.plan.consecutive.count + group.purchased.plan.consecutive.offset}}
|
||||
li {{ $t('gemCapExtra') }} {{group.purchased.plan.consecutive.gemCapExtra}}
|
||||
li {{ $t('mysticHourglasses') }} {{group.purchased.plan.consecutive.trinkets}}
|
||||
.col-12.col-md-6.offset-md-3
|
||||
.btn.btn-primary(v-if='!group.purchased.plan.dateTerminated && group.purchased.plan.paymentMethod === "Stripe"',
|
||||
@click='showStripeEdit({groupId: group.id})') {{ $t('subUpdateCard') }}
|
||||
.btn.btn-sm.btn-danger(v-if='!group.purchased.plan.dateTerminated',
|
||||
@click='cancelSubscription({group: group})') {{ $t('cancelGroupSub') }}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import paymentsMixin from 'client/mixins/payments';
|
||||
|
||||
export default {
|
||||
mixins: [paymentsMixin],
|
||||
props: ['groupId'],
|
||||
data () {
|
||||
return {
|
||||
group: {},
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.loadGroup();
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
isLeader () {
|
||||
return this.user._id === this.group.leader._id;
|
||||
},
|
||||
groupIsSubscribed () {
|
||||
return this.group.purchased && this.group.purchased.plan && this.group.purchased.plan.customerId;
|
||||
},
|
||||
dateTerminated () {
|
||||
if (!this.user.preferences || !this.user.preferences.dateFormat) return moment(this.group.purchased.plan.dateTerminated);
|
||||
return moment(this.group.purchased.plan.dateTerminated).format(this.user.preferences.dateFormat.toUpperCase());
|
||||
},
|
||||
purchasedGroupPlanPlanExtraMonths () {
|
||||
return {
|
||||
months: parseFloat(this.group.purchased.plan.extraMonths).toFixed(2),
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async loadGroup () {
|
||||
let group = await this.$store.dispatch('guilds:getGroup', {groupId: this.groupId});
|
||||
this.group = Object.assign({}, group);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -2,9 +2,14 @@
|
||||
.row
|
||||
secondary-menu.col-12
|
||||
router-link.nav-link(:to="{name: 'groupPlanDetailTaskInformation', params: {groupId}}",
|
||||
exact, :class="{'active': $route.name === 'groupPlanDetailTaskInformation'}") Task Board
|
||||
exact, :class="{'active': $route.name === 'groupPlanDetailTaskInformation'}") {{ $t('groupTaskBoard') }}
|
||||
router-link.nav-link(:to="{name: 'groupPlanDetailInformation', params: {groupId}}",
|
||||
exact, :class="{'active': $route.name === 'groupPlanDetailInformation'}") Group Information
|
||||
exact, :class="{'active': $route.name === 'groupPlanDetailInformation'}") {{ $t('groupInformation') }}
|
||||
router-link.nav-link(
|
||||
v-if='isLeader',
|
||||
:to="{name: 'groupPlanBilling', params: {groupId}}",
|
||||
exact,
|
||||
:class="{'active': $route.name === 'groupPlanBilling'}") {{ $t('groupBilling') }}
|
||||
|
||||
.col-12
|
||||
router-view
|
||||
@@ -12,11 +17,26 @@
|
||||
|
||||
<script>
|
||||
import SecondaryMenu from 'client/components/secondaryMenu';
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
export default {
|
||||
props: ['groupId'],
|
||||
components: {
|
||||
SecondaryMenu,
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
groupPlans: 'groupPlans',
|
||||
}),
|
||||
isLeader () {
|
||||
let groupFound = this.groupPlans.find(group => {
|
||||
return group._id === this.groupId;
|
||||
});
|
||||
|
||||
if (!groupFound) return false;
|
||||
return groupFound.leader === this.user._id;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -349,15 +349,21 @@ export default {
|
||||
groupId: this.searchId,
|
||||
});
|
||||
|
||||
let groupedApprovals = this.loadApprovals();
|
||||
|
||||
tasks.forEach((task) => {
|
||||
if (groupedApprovals.length > 0) task.approvals = groupedApprovals[task._id];
|
||||
this.tasksByType[task.type].push(task);
|
||||
});
|
||||
},
|
||||
async loadApprovals () {
|
||||
if (this.group.leader._id !== this.user._id) return [];
|
||||
|
||||
let approvalRequests = await this.$store.dispatch('tasks:getGroupApprovals', {
|
||||
groupId: this.searchId,
|
||||
});
|
||||
let groupedApprovals = groupBy(approvalRequests, 'group.taskId');
|
||||
|
||||
tasks.forEach((task) => {
|
||||
task.approvals = groupedApprovals[task._id];
|
||||
this.tasksByType[task.type].push(task);
|
||||
});
|
||||
return groupBy(approvalRequests, 'group.taskId');
|
||||
},
|
||||
editTask (task) {
|
||||
this.taskFormPurpose = 'edit';
|
||||
|
||||
@@ -56,15 +56,16 @@
|
||||
button.btn.btn-success(class='btn-success', v-if='isLeader && !group.purchased.active', @click='upgradeGroup()')
|
||||
| {{ $t('upgrade') }}
|
||||
.button-container
|
||||
button.btn.btn-primary(b-btn, @click="updateGuild", v-once, v-if='isLeader') {{ $t('edit') }}
|
||||
button.btn.btn-primary(b-btn, @click="updateGuild", v-once, v-if='isLeader || isAdmin') {{ $t('edit') }}
|
||||
.button-container
|
||||
button.btn.btn-success(class='btn-success', v-if='!isMember', @click='join()') {{ $t('join') }}
|
||||
.button-container
|
||||
button.btn.btn-primary(v-once, @click='showInviteModal()') {{$t('invite')}}
|
||||
// @TODO: hide the invitation button if there's an active group plan and the player is not the leader
|
||||
.button-container
|
||||
// @TODO: V2 button.btn.btn-primary(v-once, v-if='!isLeader') {{$t('messageGuildLeader')}}
|
||||
// @TODO: V2 button.btn.btn-primary(v-once, v-if='!isLeader') {{$t('messageGuildLeader')}} // Suggest making the button visible to the leader too - useful for them to test how the feature works or to send a note to themself. -- Alys
|
||||
.button-container
|
||||
// @TODO: V2 button.btn.btn-primary(v-once, v-if='isMember && !isParty') {{$t('donateGems')}}
|
||||
// @TODO: V2 button.btn.btn-primary(v-once, v-if='isMember && !isParty') {{$t('donateGems')}} // Suggest removing the isMember restriction - it's okay if non-members donate to a public guild. Also probably allow it for parties if parties can buy imagery. -- Alys
|
||||
|
||||
.section-header(v-if='isParty')
|
||||
.row
|
||||
@@ -81,7 +82,7 @@
|
||||
.svg-icon(v-html="icons.questIcon")
|
||||
h4(v-once) {{ $t('youAreNotOnQuest') }}
|
||||
p(v-once) {{ $t('questDescription') }}
|
||||
button.btn.btn-secondary(v-once, @click="openStartQuestModal()", v-if='isLeader') {{ $t('startAQuest') }}
|
||||
button.btn.btn-secondary(v-once, @click="openStartQuestModal()") {{ $t('startAQuest') }}
|
||||
.row.quest-active-section(v-if='isParty && onPendingQuest && !onActiveQuest')
|
||||
.col-2
|
||||
.quest(:class='`inventory_quest_scroll_${questData.key}`')
|
||||
@@ -129,7 +130,7 @@
|
||||
.col-6
|
||||
span.float-left
|
||||
| Rage {{questData.boss.rage.value}}
|
||||
button.btn.btn-secondary(v-once, @click="questAbort()", v-if='isLeader') {{ $t('abort') }}
|
||||
button.btn.btn-secondary(v-once, @click="questAbort()", v-if='canEditQuest') {{ $t('abort') }}
|
||||
|
||||
.section-header(v-if='!isParty')
|
||||
.row
|
||||
@@ -562,12 +563,17 @@ export default {
|
||||
isLeader () {
|
||||
return this.user._id === this.group.leader._id;
|
||||
},
|
||||
isAdmin () {
|
||||
return Boolean(this.user.contributor.admin);
|
||||
},
|
||||
isMember () {
|
||||
return this.isMemberOfGroup(this.user, this.group);
|
||||
},
|
||||
canEditQuest () {
|
||||
let isQuestLeader = this.group.quest && this.group.quest.leader === this.user._id;
|
||||
return isQuestLeader;
|
||||
if (!this.group.quest) return false;
|
||||
let isQuestLeader = this.group.quest.leader === this.user._id;
|
||||
let isPartyLeader = this.group.leader._id === this.user._id;
|
||||
return isQuestLeader || isPartyLeader;
|
||||
},
|
||||
isMemberOfPendingQuest () {
|
||||
let userid = this.user._id;
|
||||
@@ -710,6 +716,9 @@ export default {
|
||||
fetchRecentMessages () {
|
||||
this.fetchGuild();
|
||||
},
|
||||
reverseChat () {
|
||||
this.group.chat.reverse();
|
||||
},
|
||||
updateGuild () {
|
||||
this.$store.state.editingGroup = this.group;
|
||||
this.$root.$emit('show::modal', 'guild-form');
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
.form-group
|
||||
label
|
||||
strong(v-once) {{$t('groupDescription')}} *
|
||||
a.float-right {{ $t('markdownFormattingHelp') }}
|
||||
a.float-right(v-markdown='$t("markdownFormattingHelp")')
|
||||
textarea.form-control.description-textarea(type="text", textarea, :placeholder="isParty ? $t('partyDescriptionPlaceholder') : $t('guildDescriptionPlaceholder')", v-model="workingGroup.description")
|
||||
|
||||
.form-group(v-if='creatingParty && !workingGroup.id')
|
||||
@@ -177,6 +177,7 @@ import bTooltip from 'bootstrap-vue/lib/components/tooltip';
|
||||
|
||||
import { mapState } from 'client/libs/store';
|
||||
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
import gemIcon from 'assets/svg/gem.svg';
|
||||
import informationIcon from 'assets/svg/information.svg';
|
||||
|
||||
@@ -198,6 +199,9 @@ export default {
|
||||
bTooltip,
|
||||
toggleSwitch,
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
data () {
|
||||
let data = {
|
||||
workingGroup: {
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
.pending.float-right(v-if='member.accepted === null') {{ $t('pending') }}
|
||||
div(v-if='questData')
|
||||
questDialogContent(:item="questData")
|
||||
div.text-center.actions
|
||||
div.text-center.actions(v-if='canEditQuest')
|
||||
div
|
||||
button.btn.btn-secondary(v-once, @click="questForceStart()") {{ $t('begin') }}
|
||||
// @TODO don't allow the party leader to start the quest until the leader has accepted or rejected the invitation (users get confused and think "begin" means "join quest")
|
||||
div
|
||||
.cancel(v-once, @click="questCancel()") {{ $t('cancel') }}
|
||||
.side-panel(v-if='questData')
|
||||
@@ -189,6 +190,12 @@ 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;
|
||||
return isQuestLeader || isPartyLeader;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async questForceStart () {
|
||||
|
||||
@@ -127,10 +127,7 @@
|
||||
:count="context.item.quantity"
|
||||
)
|
||||
|
||||
hatchedPetDialog(
|
||||
:pet="hatchedPet",
|
||||
@closed="closeHatchedPetDialog()"
|
||||
)
|
||||
hatchedPetDialog()
|
||||
|
||||
div.hatchingPotionInfo(ref="draggingPotionInfo")
|
||||
div(v-if="currentDraggingPotion != null")
|
||||
@@ -251,7 +248,6 @@ export default {
|
||||
|
||||
currentDraggingPotion: null,
|
||||
potionClickMode: false,
|
||||
hatchedPet: null,
|
||||
cardOptions: {
|
||||
cardType: '',
|
||||
messageOptions: 0,
|
||||
@@ -345,7 +341,7 @@ export default {
|
||||
},
|
||||
hatchPet (potion, egg) {
|
||||
this.$store.dispatch('common:hatch', {egg: egg.key, hatchingPotion: potion.key});
|
||||
this.hatchedPet = createAnimal(egg, potion, 'pet', this.content, this.user.items);
|
||||
this.$root.$emit('hatchedPet::open', createAnimal(egg, potion, 'pet', this.content, this.user.items));
|
||||
},
|
||||
onDragEnd () {
|
||||
this.currentDraggingPotion = null;
|
||||
@@ -405,9 +401,6 @@ export default {
|
||||
this.potionClickMode = false;
|
||||
}
|
||||
},
|
||||
closeHatchedPetDialog () {
|
||||
this.hatchedPet = null;
|
||||
},
|
||||
|
||||
async itemClicked (groupKey, item) {
|
||||
if (item.type && item.type === 'card') {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<template lang="pug">
|
||||
|
||||
b-modal#hatchedPet-modal(
|
||||
:visible="true",
|
||||
v-if="pet != null",
|
||||
:hide-header="true"
|
||||
)
|
||||
div.content
|
||||
div.dialog-header.title You hatched a new pet!
|
||||
div.content(v-if="pet != null")
|
||||
div.dialog-header.title(v-once) {{ $t('hatchedPetGeneric') }}
|
||||
|
||||
|
||||
div.inner-content
|
||||
@@ -45,7 +43,7 @@
|
||||
}
|
||||
|
||||
.inner-content {
|
||||
margin: 33px auto auto;
|
||||
margin: 24px auto auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -77,16 +75,33 @@
|
||||
components: {
|
||||
bModal,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
pet: null,
|
||||
};
|
||||
},
|
||||
created () {
|
||||
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('hatchedPet::open', this.openDialog);
|
||||
},
|
||||
destroyed () {
|
||||
this.$root.$off('hatchedPet::open', this.openDialog);
|
||||
},
|
||||
methods: {
|
||||
openDialog (item) {
|
||||
this.pet = item;
|
||||
this.$root.$emit('show::modal', 'hatchedPet-modal');
|
||||
},
|
||||
|
||||
close () {
|
||||
this.$emit('closed', this.item);
|
||||
this.$root.$emit('hide::modal', 'hatchedPet-modal');
|
||||
this.pet = null;
|
||||
},
|
||||
},
|
||||
props: {
|
||||
pet: {
|
||||
type: Object,
|
||||
},
|
||||
hideText: {
|
||||
type: Boolean,
|
||||
},
|
||||
|
||||
@@ -210,7 +210,6 @@
|
||||
div.content-text(v-once) {{ $t('welcomeStableText') }}
|
||||
|
||||
b-modal#hatching-modal(
|
||||
:visible="hatchablePet != null",
|
||||
@change="resetHatchablePet($event)"
|
||||
)
|
||||
div.content(v-if="hatchablePet")
|
||||
@@ -230,9 +229,7 @@
|
||||
button.btn.btn-secondary.btn-flat(@click="closeHatchPetDialog()") {{ $t('cancel') }}
|
||||
|
||||
hatchedPetDialog(
|
||||
:pet="hatchedPet",
|
||||
:hideText="true",
|
||||
@closed="closeHatchedPetDialog()"
|
||||
)
|
||||
|
||||
div.foodInfo(ref="dragginFoodInfo")
|
||||
@@ -337,11 +334,6 @@
|
||||
padding-right:0;
|
||||
}
|
||||
|
||||
.drawer-container {
|
||||
// 3% padding + 252px sidebar width
|
||||
left: calc(3% + 252px) !important;
|
||||
}
|
||||
|
||||
.svg-icon.inline.icon-16 {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
@@ -392,7 +384,6 @@
|
||||
}
|
||||
|
||||
.title {
|
||||
height: 24px;
|
||||
margin-top: 24px;
|
||||
font-family: Roboto;
|
||||
font-size: 20px;
|
||||
@@ -588,7 +579,6 @@
|
||||
highlightPet: '',
|
||||
|
||||
hatchablePet: null,
|
||||
hatchedPet: null,
|
||||
foodClickMode: false,
|
||||
currentDraggingFood: null,
|
||||
|
||||
@@ -918,7 +908,8 @@
|
||||
|
||||
hatchPet (pet) {
|
||||
this.$store.dispatch('common:hatch', {egg: pet.eggKey, hatchingPotion: pet.potionKey});
|
||||
this.hatchedPet = pet;
|
||||
|
||||
this.$root.$emit('hatchedPet::open', pet);
|
||||
this.closeHatchPetDialog();
|
||||
},
|
||||
|
||||
@@ -974,6 +965,8 @@
|
||||
}
|
||||
// opens the hatch dialog
|
||||
this.hatchablePet = pet;
|
||||
|
||||
this.$root.$emit('show::modal', 'hatching-modal');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -988,9 +981,6 @@
|
||||
closeHatchPetDialog () {
|
||||
this.$root.$emit('hide::modal', 'hatching-modal');
|
||||
},
|
||||
closeHatchedPetDialog () {
|
||||
this.hatchedPet = null;
|
||||
},
|
||||
|
||||
resetHatchablePet ($event) {
|
||||
if (!$event) {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<template lang="pug">
|
||||
div.item-with-icon.item-notifications.dropdown
|
||||
span.message-count.top-count(v-if='notificationsCount > 0') {{ notificationsCount }}
|
||||
.svg-icon.notifications(v-html="icons.notifications")
|
||||
// span.glyphicon(:class='iconClasses()')
|
||||
// span.notification-counter(v-if='getNotificationsCount()') {{getNotificationsCount()}}
|
||||
.dropdown-menu.dropdown-menu-right.user-dropdown
|
||||
h4.dropdown-item.dropdown-separated(v-if='!hasNoNotifications()') {{ $t('notifications') }}
|
||||
h4.dropdown-item.toolbar-notifs-no-messages(v-if='hasNoNotifications()') {{ $t('noNotifications') }}
|
||||
@@ -58,6 +57,25 @@ div.item-with-icon.item-notifications.dropdown
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.message-count {
|
||||
background-color: #46a7d9;
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
float: right;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.message-count.top-count {
|
||||
position: absolute;
|
||||
right: -.5em;
|
||||
top: .5em;
|
||||
padding: .2em;
|
||||
}
|
||||
|
||||
.clear-button {
|
||||
margin-left: .5em;
|
||||
}
|
||||
@@ -168,6 +186,31 @@ export default {
|
||||
}
|
||||
return userNewMessages;
|
||||
},
|
||||
notificationsCount () {
|
||||
let count = 0;
|
||||
|
||||
if (this.user.invitations.parties) {
|
||||
count += this.user.invitations.parties.length;
|
||||
}
|
||||
|
||||
if (this.user.purchased.plan && this.user.purchased.plan.mysteryItems.length) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (this.user.invitations.guilds) {
|
||||
count += this.user.invitations.guilds.length;
|
||||
}
|
||||
|
||||
if (this.user.flags.classSelected && !this.user.preferences.disableClasses && this.user.stats.points) {
|
||||
count += this.user.stats.points > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (this.userNewMessages) {
|
||||
count += Object.keys(this.userNewMessages).length;
|
||||
}
|
||||
|
||||
return count;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// @TODO: I hate this function, we can do better with a hashmap
|
||||
@@ -237,31 +280,6 @@ export default {
|
||||
clearCards () {
|
||||
this.$store.dispatch('chat:clearCards');
|
||||
},
|
||||
getNotificationsCount () {
|
||||
let count = 0;
|
||||
|
||||
if (this.user.invitations.parties) {
|
||||
count += this.user.invitations.parties.length;
|
||||
}
|
||||
|
||||
if (this.user.purchased.plan && this.user.purchased.plan.mysteryItems.length) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (this.user.invitations.guilds) {
|
||||
count += this.user.invitations.guilds.length;
|
||||
}
|
||||
|
||||
if (this.user.flags.classSelected && !this.user.preferences.disableClasses && this.user.stats.points) {
|
||||
count += this.user.stats.points > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (this.userNewMessages) {
|
||||
count += Object.keys(this.userNewMessages).length;
|
||||
}
|
||||
|
||||
return count;
|
||||
},
|
||||
iconClasses () {
|
||||
return this.selectNotificationValue(
|
||||
'glyphicon-gift',
|
||||
|
||||
@@ -158,6 +158,7 @@ export default {
|
||||
|
||||
return {
|
||||
yesterDailies: [],
|
||||
levelBeforeYesterdailies: 0,
|
||||
notificationData: {},
|
||||
unlockLevels,
|
||||
lastShownNotifications,
|
||||
@@ -256,12 +257,8 @@ export default {
|
||||
this.mp(mana);
|
||||
},
|
||||
userLvl (after, before) {
|
||||
if (after <= before) return;
|
||||
this.lvl();
|
||||
this.playSound('Level_Up');
|
||||
if (this.user._tmp && this.user._tmp.drop && this.user._tmp.drop.type === 'Quest') return;
|
||||
if (this.unlockLevels[`${after}`]) return;
|
||||
if (!this.user.preferences.suppressModals.levelUp) this.$root.$emit('show::modal', 'level-up');
|
||||
if (after <= before || this.isRunningYesterdailies) return;
|
||||
this.showLevelUpNotifications(after);
|
||||
},
|
||||
userClassSelect (after) {
|
||||
if (!after) return;
|
||||
@@ -334,6 +331,13 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
showLevelUpNotifications (newlevel) {
|
||||
this.lvl();
|
||||
this.playSound('Level_Up');
|
||||
if (this.user._tmp && this.user._tmp.drop && this.user._tmp.drop.type === 'Quest') return;
|
||||
if (this.unlockLevels[`${newlevel}`]) return;
|
||||
if (!this.user.preferences.suppressModals.levelUp) this.$root.$emit('show::modal', 'level-up');
|
||||
},
|
||||
playSound (sound) {
|
||||
this.$root.$emit('playSound', sound);
|
||||
},
|
||||
@@ -390,6 +394,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.levelBeforeYesterdailies = this.user.stats.lvl;
|
||||
this.$root.$emit('show::modal', 'yesterdaily');
|
||||
},
|
||||
async runYesterDailiesAction () {
|
||||
@@ -405,6 +410,10 @@ export default {
|
||||
this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true}),
|
||||
]);
|
||||
|
||||
if (this.levelBeforeYesterdailies < this.user.stats.lvl) {
|
||||
this.showLevelUpNotifications(this.user.stats.lvl);
|
||||
}
|
||||
|
||||
this.handleUserNotifications(this.user.notifications);
|
||||
this.scheduleNextCron();
|
||||
},
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
.nav-item(@click='selectedPage = "subscribe"', :class="{active: selectedPage === 'subscribe'}") {{ $t('subscribe') }}
|
||||
.nav-item(@click='selectedPage = "gems"', :class="{active: selectedPage === 'gems'}") {{ $t('buyGems') }}
|
||||
div(v-show='selectedPage === "gems"')
|
||||
div(v-if='hasSubscription')
|
||||
.row.text-center
|
||||
h2.mx-auto.text-leadin {{ $t('subscriptionAlreadySubscribedLeadIn') }}
|
||||
.row.text-center
|
||||
.col-6.offset-3
|
||||
p {{ $t("gemsPurchaseNote") }}
|
||||
.row.text-center
|
||||
h2.mx-auto.text-leadin {{ $t('gemBenefitLeadin') }}
|
||||
.row
|
||||
|
||||
@@ -112,7 +112,6 @@ import filter from 'lodash/filter';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import min from 'lodash/min';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import encodeParams from 'client/libs/encodeParams';
|
||||
|
||||
import subscriptionBlocks from '../../../common/script/content/subscriptionBlocks';
|
||||
import planGemLimits from '../../../common/script/libs/planGemLimits';
|
||||
@@ -261,42 +260,6 @@ export default {
|
||||
subs.basic_6mo.discount = true;
|
||||
subs.google_6mo.discount = false;
|
||||
},
|
||||
async cancelSubscription (config) {
|
||||
if (config && config.group && !confirm(this.$t('confirmCancelGroupPlan'))) return;
|
||||
if (!confirm(this.$t('sureCancelSub'))) return;
|
||||
|
||||
let group;
|
||||
if (config && config.group) {
|
||||
group = config.group;
|
||||
}
|
||||
|
||||
let paymentMethod = this.user.purchased.plan.paymentMethod;
|
||||
if (group) {
|
||||
paymentMethod = group.purchased.plan.paymentMethod;
|
||||
}
|
||||
|
||||
if (paymentMethod === 'Amazon Payments') {
|
||||
paymentMethod = 'amazon';
|
||||
} else {
|
||||
paymentMethod = paymentMethod.toLowerCase();
|
||||
}
|
||||
|
||||
let queryParams = {
|
||||
_id: this.user._id,
|
||||
apiToken: this.credentials.API_TOKEN,
|
||||
noRedirect: true,
|
||||
};
|
||||
|
||||
if (group) {
|
||||
queryParams.groupId = group._id;
|
||||
}
|
||||
|
||||
let cancelUrl = `/${paymentMethod}/subscribe/cancel?${encodeParams(queryParams)}`;
|
||||
await axios.get(cancelUrl);
|
||||
// Success
|
||||
alert(this.$t('paypalCanceled'));
|
||||
this.$router.push('/');
|
||||
},
|
||||
getCancelSubInfo () {
|
||||
// @TODO: String 'cancelSubInfoGroup Plan' not found. ?
|
||||
return this.$t(`cancelSubInfo${this.user.purchased.plan.paymentMethod}`);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
span.svg-icon.inline.icon-10(aria-hidden="true", v-html="icons.close", @click="hideDialog()")
|
||||
|
||||
div.content(v-if="item != null")
|
||||
|
||||
div.inner-content
|
||||
slot(name="item", :item="item")
|
||||
div(v-if="showAvatar")
|
||||
@@ -46,6 +45,11 @@
|
||||
span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getPriceClass()]")
|
||||
span.value(:class="getPriceClass()") {{ item.value }}
|
||||
|
||||
.gems-left(v-if='item.key === "gem"')
|
||||
strong(v-if='gemsLeft > 0') {{ gemsLeft }} {{ $t('gemsRemaining') }}
|
||||
strong(v-if='gemsLeft === 0') {{ $t('maxBuyGems') }}
|
||||
|
||||
|
||||
button.btn.btn-primary(
|
||||
@click="purchaseGems()",
|
||||
v-if="getPriceClass() === 'gems' && !this.enoughCurrency(getPriceClass(), item.value)"
|
||||
@@ -54,6 +58,7 @@
|
||||
button.btn.btn-primary(
|
||||
@click="buyItem()",
|
||||
v-else,
|
||||
:disabled='item.key === "gem" && gemsLeft === 0',
|
||||
:class="{'notEnough': !preventHealthPotion || !this.enoughCurrency(getPriceClass(), item.value)}"
|
||||
) {{ $t('buyNow') }}
|
||||
|
||||
@@ -203,12 +208,18 @@
|
||||
.bordered {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.gems-left {
|
||||
margin-top: .5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import * as Analytics from 'client/libs/analytics';
|
||||
import spellsMixin from 'client/mixins/spells';
|
||||
import planGemLimits from 'common/script/libs/planGemLimits';
|
||||
|
||||
import svgClose from 'assets/svg/close.svg';
|
||||
import svgGold from 'assets/svg/gold.svg';
|
||||
@@ -220,6 +231,7 @@
|
||||
import BalanceInfo from './balanceInfo.vue';
|
||||
import currencyMixin from './_currencyMixin';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
@@ -233,7 +245,7 @@
|
||||
import moment from 'moment';
|
||||
|
||||
export default {
|
||||
mixins: [currencyMixin, notifications],
|
||||
mixins: [currencyMixin, notifications, spellsMixin, buyMixin],
|
||||
components: {
|
||||
bModal,
|
||||
BalanceInfo,
|
||||
@@ -290,6 +302,10 @@
|
||||
limitedString () {
|
||||
return this.$t('limitedOffer', {date: moment(seasonalShopConfig.dateRange.end).format('LL')});
|
||||
},
|
||||
gemsLeft () {
|
||||
if (!this.user.purchased.plan) return 0;
|
||||
return planGemLimits.convCap + this.user.purchased.plan.consecutive.gemCapExtra - this.user.purchased.plan.gemsBought;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
item: function itemChanged () {
|
||||
@@ -301,17 +317,11 @@
|
||||
this.$emit('change', $event);
|
||||
},
|
||||
buyItem () {
|
||||
if (this.genericPurchase) {
|
||||
this.$store.dispatch('shops:genericPurchase', {
|
||||
pinType: this.item.pinType,
|
||||
type: this.item.purchaseType,
|
||||
key: this.item.key,
|
||||
currency: this.item.currency,
|
||||
});
|
||||
|
||||
if (this.item.cast) {
|
||||
this.castStart(this.item);
|
||||
} else if (this.genericPurchase) {
|
||||
this.makeGenericPurchase(this.item);
|
||||
this.purchased(this.item.text);
|
||||
this.$root.$emit('buyModal::boughtItem', this.item);
|
||||
this.$root.$emit('playSound', 'Reward');
|
||||
}
|
||||
|
||||
this.$emit('buyPressed', this.item);
|
||||
|
||||
@@ -383,6 +383,8 @@
|
||||
const sortGearTypes = ['sortByType', 'sortByPrice', 'sortByCon', 'sortByPer', 'sortByStr', 'sortByInt'];
|
||||
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
import currencyMixin from '../_currencyMixin';
|
||||
|
||||
const sortGearTypeMap = {
|
||||
sortByType: 'type',
|
||||
@@ -393,7 +395,7 @@
|
||||
};
|
||||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
mixins: [notifications, buyMixin, currencyMixin],
|
||||
components: {
|
||||
ShopItem,
|
||||
Item,
|
||||
@@ -694,6 +696,11 @@ export default {
|
||||
}
|
||||
},
|
||||
itemSelected (item) {
|
||||
if (item.purchaseType !== 'gear' && this.$store.state.recentlyPurchased[item.key]) {
|
||||
this.makeGenericPurchase(item);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$root.$emit('buyModal::showItem', item);
|
||||
},
|
||||
featuredItemSelected (item) {
|
||||
|
||||
@@ -43,11 +43,9 @@
|
||||
:currencyNeeded="priceType",
|
||||
:amountNeeded="item.value"
|
||||
).float-right
|
||||
|
||||
|
||||
</template>
|
||||
<style lang="scss">
|
||||
|
||||
<style lang="scss">
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
@import '~client/assets/scss/modal.scss';
|
||||
|
||||
@@ -173,7 +171,6 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import {mapState} from 'client/libs/store';
|
||||
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
@@ -188,12 +185,13 @@
|
||||
import currencyMixin from '../_currencyMixin';
|
||||
import QuestInfo from './questInfo.vue';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
|
||||
import questDialogDrops from './questDialogDrops';
|
||||
import questDialogContent from './questDialogContent';
|
||||
|
||||
export default {
|
||||
mixins: [currencyMixin, notifications],
|
||||
mixins: [currencyMixin, notifications, buyMixin],
|
||||
components: {
|
||||
bModal,
|
||||
BalanceInfo,
|
||||
@@ -243,15 +241,8 @@
|
||||
this.$emit('change', $event);
|
||||
},
|
||||
buyItem () {
|
||||
this.$store.dispatch('shops:genericPurchase', {
|
||||
pinType: this.item.pinType,
|
||||
type: this.item.purchaseType,
|
||||
key: this.item.key,
|
||||
currency: this.item.currency,
|
||||
});
|
||||
this.makeGenericPurchase(this.item, 'buyQuestModal');
|
||||
this.purchased(this.item.text);
|
||||
this.$root.$emit('playSound', 'Reward');
|
||||
this.$emit('buyPressed', this.item);
|
||||
this.hideDialog();
|
||||
},
|
||||
togglePinned () {
|
||||
|
||||
@@ -328,6 +328,8 @@
|
||||
import ItemRows from 'client/components/ui/itemRows';
|
||||
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
||||
import Avatar from 'client/components/avatar';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
import currencyMixin from '../_currencyMixin';
|
||||
|
||||
import BuyModal from './buyQuestModal.vue';
|
||||
import QuestInfo from './questInfo.vue';
|
||||
@@ -348,6 +350,7 @@
|
||||
import _map from 'lodash/map';
|
||||
|
||||
export default {
|
||||
mixins: [buyMixin, currencyMixin],
|
||||
components: {
|
||||
ShopItem,
|
||||
Item,
|
||||
@@ -470,6 +473,11 @@ export default {
|
||||
selectItem (item) {
|
||||
this.selectedItemToBuy = item;
|
||||
|
||||
if (this.$store.state.recentlyPurchased[item.key]) {
|
||||
this.makeGenericPurchase(item);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$root.$emit('show::modal', 'buy-quest-modal');
|
||||
},
|
||||
},
|
||||
|
||||
@@ -274,6 +274,8 @@
|
||||
import ItemRows from 'client/components/ui/itemRows';
|
||||
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
||||
import Avatar from 'client/components/avatar';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
import currencyMixin from '../_currencyMixin';
|
||||
|
||||
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
@@ -302,6 +304,7 @@
|
||||
import shops from 'common/script/libs/shops';
|
||||
|
||||
export default {
|
||||
mixins: [buyMixin, currencyMixin],
|
||||
components: {
|
||||
ShopItem,
|
||||
Item,
|
||||
@@ -485,9 +488,14 @@
|
||||
}
|
||||
},
|
||||
itemSelected (item) {
|
||||
if (!item.locked) {
|
||||
this.$root.$emit('buyModal::showItem', item);
|
||||
if (item.locked) return;
|
||||
|
||||
if (this.$store.state.recentlyPurchased[item.key]) {
|
||||
this.makeGenericPurchase(item);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$root.$emit('buyModal::showItem', item);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -71,9 +71,6 @@
|
||||
p.span
|
||||
span {{ $t('marketing4Lead3-2') }}
|
||||
a.btn.btn-primary(href='/static/plans',target='_blank') {{ $t('contactUs') }}
|
||||
p.span
|
||||
span {{ $t('marketing4Lead3-3') }}
|
||||
a.btn.btn-primary(href='/static/videos') {{ $t('watchVideos') }}
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
|
||||
@@ -46,6 +46,11 @@
|
||||
min-height: 556px;
|
||||
}
|
||||
|
||||
.task-wrapper {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.task-wrapper + .reward-items {
|
||||
margin-top: 16px;
|
||||
}
|
||||
@@ -104,7 +109,7 @@
|
||||
.column-background {
|
||||
position: absolute;
|
||||
bottom: 32px;
|
||||
z-index: 7;
|
||||
z-index: 1;
|
||||
|
||||
&.initial-description {
|
||||
top: 30%;
|
||||
@@ -160,19 +165,25 @@
|
||||
<script>
|
||||
import Task from './task';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import throttle from 'lodash/throttle';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
|
||||
import sortable from 'client/directives/sortable.directive';
|
||||
import buyMixin from 'client/mixins/buy';
|
||||
import { mapState, mapActions } from 'client/libs/store';
|
||||
import shopItem from '../shops/shopItem';
|
||||
|
||||
import { shouldDo } from 'common/script/cron';
|
||||
import inAppRewards from 'common/script/libs/inAppRewards';
|
||||
import spells from 'common/script/content/spells';
|
||||
|
||||
import habitIcon from 'assets/svg/habit.svg';
|
||||
import dailyIcon from 'assets/svg/daily.svg';
|
||||
import todoIcon from 'assets/svg/todo.svg';
|
||||
import rewardIcon from 'assets/svg/reward.svg';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import shopItem from '../shops/shopItem';
|
||||
import throttle from 'lodash/throttle';
|
||||
import sortable from 'client/directives/sortable.directive';
|
||||
|
||||
export default {
|
||||
mixins: [buyMixin],
|
||||
components: {
|
||||
Task,
|
||||
bModal,
|
||||
@@ -252,8 +263,30 @@ export default {
|
||||
},
|
||||
inAppRewards () {
|
||||
let watchRefresh = this.forceRefresh; // eslint-disable-line
|
||||
let rewards = inAppRewards(this.user);
|
||||
|
||||
return inAppRewards(this.user);
|
||||
|
||||
// Add season rewards if user is affected
|
||||
// @TODO: Add buff coniditional
|
||||
const seasonalSkills = {
|
||||
snowball: 'salt',
|
||||
spookySparkles: 'opaquePotion',
|
||||
shinySeed: 'petalFreePotion',
|
||||
seafoam: 'sand',
|
||||
};
|
||||
|
||||
for (let key in seasonalSkills) {
|
||||
if (this.user.stats.buffs[key]) {
|
||||
let debuff = seasonalSkills[key];
|
||||
let item = Object.assign({}, spells.special[debuff]);
|
||||
item.text = item.text();
|
||||
item.notes = item.notes();
|
||||
item.class = `shop_${key}`;
|
||||
rewards.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return rewards;
|
||||
},
|
||||
hasRewardsList () {
|
||||
return this.isUser === true && this.type === 'reward' && this.activeFilters[this.type].label !== 'custom';
|
||||
@@ -378,6 +411,14 @@ export default {
|
||||
}
|
||||
},
|
||||
openBuyDialog (rewardItem) {
|
||||
// Buy armoire and health potions immediately
|
||||
let itemsToPurchaseImmediately = ['potion', 'armoire'];
|
||||
if (itemsToPurchaseImmediately.indexOf(rewardItem.key) !== -1) {
|
||||
this.makeGenericPurchase(rewardItem);
|
||||
this.$emit('buyPressed', rewardItem);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rewardItem.purchaseType !== 'gear' || !rewardItem.locked) {
|
||||
this.$emit('openBuyDialog', rewardItem);
|
||||
}
|
||||
|
||||
@@ -148,14 +148,13 @@ div(v-if='user.stats.lvl > 10')
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import isArray from 'lodash/isArray';
|
||||
import bPopover from 'bootstrap-vue/lib/directives/popover';
|
||||
|
||||
import spells from '../../../common/script/content/spells';
|
||||
|
||||
import { mapState } from 'client/libs/store';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import spellsMixin from 'client/mixins/spells';
|
||||
import Drawer from 'client/components/ui/drawer';
|
||||
import MouseMoveDirective from 'client/directives/mouseposition.directive';
|
||||
|
||||
@@ -164,7 +163,7 @@ import quests from 'common/script/content/quests';
|
||||
import { CONSTANTS, setLocalSetting, getLocalSetting } from 'client/libs/userlocalManager';
|
||||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
mixins: [notifications, spellsMixin],
|
||||
components: {
|
||||
Drawer,
|
||||
},
|
||||
@@ -245,122 +244,6 @@ export default {
|
||||
|
||||
return notes;
|
||||
},
|
||||
async castStart (spell) {
|
||||
if (this.$store.state.spellOptions.castingSpell) {
|
||||
this.castCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.user.stats.mp < spell.mana) return this.text(this.$t('notEnoughMana'));
|
||||
|
||||
if (spell.immediateUse && this.user.stats.gp < spell.value) {
|
||||
return this.text('Not enough gold.');
|
||||
}
|
||||
|
||||
this.potionClickMode = true;
|
||||
this.applyingAction = true;
|
||||
this.$store.state.spellOptions.castingSpell = true;
|
||||
this.spell = spell;
|
||||
|
||||
if (spell.target === 'self') {
|
||||
this.castEnd(null, spell.target);
|
||||
} else if (spell.target === 'party') {
|
||||
if (!this.user.party._id) {
|
||||
let party = [this.user];
|
||||
this.castEnd(party, spell.target);
|
||||
return;
|
||||
}
|
||||
|
||||
let party = this.$store.state.party.members;
|
||||
party = isArray(party) ? party : [];
|
||||
party = party.concat(this.user);
|
||||
this.castEnd(party, spell.target);
|
||||
} else if (spell.target === 'tasks') {
|
||||
let userTasks = this.$store.state.tasks.data;
|
||||
// exclude rewards
|
||||
let tasks = userTasks.habits
|
||||
.concat(userTasks.dailys)
|
||||
.concat(userTasks.todos);
|
||||
// exclude challenge and group plan tasks
|
||||
tasks = tasks.filter((task) => {
|
||||
if (task.challenge && task.challenge.id && !task.challenge.broken) return false;
|
||||
if (task.group && task.group.id && !task.group.broken) return false;
|
||||
return true;
|
||||
});
|
||||
this.castEnd(tasks, spell.target);
|
||||
}
|
||||
},
|
||||
async castEnd (target, type) {
|
||||
if (!this.$store.state.spellOptions.castingSpell) return;
|
||||
let beforeQuestProgress = this.questProgress();
|
||||
|
||||
if (!this.applyingAction) return 'No applying action';
|
||||
|
||||
if (this.spell.target !== type) return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.type && target.type === 'reward') return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.challenge && target.challenge.id) return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.group && target.group.id) return this.text(this.$t('invalidTarget'));
|
||||
|
||||
// @TODO: just call castCancel?
|
||||
this.$store.state.spellOptions.castingSpell = false;
|
||||
this.potionClickMode = false;
|
||||
|
||||
this.spell.cast(this.user, target);
|
||||
// User.save(); // @TODO:
|
||||
|
||||
let spell = this.spell;
|
||||
let targetId = target ? target._id : null;
|
||||
this.spell = null;
|
||||
this.applyingAction = false;
|
||||
|
||||
let spellUrl = `/api/v3/user/class/cast/${spell.key}`;
|
||||
if (targetId) spellUrl += `?targetId=${targetId}`;
|
||||
|
||||
await axios.post(spellUrl);
|
||||
let msg = this.$t('youCast', {
|
||||
spell: spell.text(),
|
||||
});
|
||||
|
||||
switch (type) {
|
||||
case 'task':
|
||||
msg = this.$t('youCastTarget', {
|
||||
spell: spell.text(),
|
||||
target: target.text,
|
||||
});
|
||||
break;
|
||||
case 'user':
|
||||
msg = this.$t('youCastTarget', {
|
||||
spell: spell.text(),
|
||||
target: target.profile.name,
|
||||
});
|
||||
break;
|
||||
case 'party':
|
||||
msg = this.$t('youCastParty', {
|
||||
spell: spell.text(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
this.markdown(msg); // @TODO: mardown directive?
|
||||
// @TODO:
|
||||
let questProgress = this.questProgress() - beforeQuestProgress;
|
||||
if (questProgress > 0) {
|
||||
let userQuest = this.quests[this.user.party.quest.key];
|
||||
if (userQuest.boss) {
|
||||
this.quest('questDamage', questProgress.toFixed(1));
|
||||
} else if (userQuest.collection && userQuest.collect) {
|
||||
this.quest('questCollection', questProgress);
|
||||
}
|
||||
}
|
||||
// @TOOD: User.sync();
|
||||
},
|
||||
castCancel () {
|
||||
this.potionClickMode = false;
|
||||
this.applyingAction = false;
|
||||
this.spell = null;
|
||||
document.querySelector('body').style.cursor = 'initial';
|
||||
this.$store.state.spellOptions.castingSpell = false;
|
||||
},
|
||||
questProgress () {
|
||||
let user = this.user;
|
||||
if (!user.party.quest) return 0;
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
<template lang="pug">
|
||||
.tags-popup
|
||||
.tags-category.d-flex
|
||||
.tags-header
|
||||
strong(v-once) {{ $t('tags') }}
|
||||
.tags-list.container
|
||||
.row
|
||||
.col-4(v-for="tag in tags")
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value="tag.id", v-model="selectedTags")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(:title='tag.name') {{tag.name}}
|
||||
.tags-footer
|
||||
span.clear-tags(@click="clearTags()") {{$t("clearTags")}}
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.tags-popup {
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
max-width: 593px;
|
||||
z-index: 9999;
|
||||
background: $white;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.tags-category {
|
||||
border-bottom: 1px solid $gray-600;
|
||||
padding-bottom: 24px;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.tags-header {
|
||||
flex-basis: 96px;
|
||||
flex-shrink: 0;
|
||||
|
||||
a {
|
||||
font-size: 12px;
|
||||
line-height: 1.33;
|
||||
color: $blue-10;
|
||||
margin-top: 4px;
|
||||
|
||||
&:focus, &:hover, &:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags-list {
|
||||
.custom-control-description {
|
||||
color: $gray-50 !important;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: pre;
|
||||
width: 8em;
|
||||
}
|
||||
}
|
||||
|
||||
.tags-footer {
|
||||
border-top: 1px solid $gray-600;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.clear-tags {
|
||||
cursor: pointer;
|
||||
margin: 1.1em 0;
|
||||
color: $red-50;
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['tags', 'value'],
|
||||
data () {
|
||||
return {
|
||||
selectedTags: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
selectedTags () {
|
||||
this.$emit('input', this.selectedTags);
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.selectedTags = this.value;
|
||||
},
|
||||
methods: {
|
||||
clearTags () {
|
||||
this.selectedTags = [];
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -17,7 +17,16 @@
|
||||
h3.task-title(:class="{ 'has-notes': task.notes }", v-markdown="task.text")
|
||||
.task-notes.small-text(v-markdown="task.notes")
|
||||
.checklist(v-if="canViewchecklist")
|
||||
.d-inline-flex
|
||||
.collapse-checklist.d-flex.align-items-center.expand-toggle(
|
||||
v-if="isUser",
|
||||
@click="collapseChecklist(task)",
|
||||
:class="{open: !task.collapseChecklist}",
|
||||
)
|
||||
.svg-icon(v-html="icons.checklist")
|
||||
span {{ checklistProgress }}
|
||||
label.custom-control.custom-checkbox.checklist-item(
|
||||
v-if='!castingSpell && !task.collapseChecklist',
|
||||
v-for="item in task.checklist", :class="{'checklist-item-done': item.completed}",
|
||||
)
|
||||
input.custom-control-input(type="checkbox", :checked="item.completed", @change="toggleChecklistItem(item)")
|
||||
@@ -118,6 +127,26 @@
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.collapse-checklist {
|
||||
padding: 2px 6px;
|
||||
margin-bottom: 9px;
|
||||
border-radius: 1px;
|
||||
background-color: $gray-600;
|
||||
font-size: 10px;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
color: $gray-200;
|
||||
|
||||
span {
|
||||
margin: 0px 4px;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
width: 12px;
|
||||
height: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.checklist-item {
|
||||
color: $gray-50;
|
||||
font-size: 14px;
|
||||
@@ -307,6 +336,7 @@ import calendarIcon from 'assets/svg/calendar.svg';
|
||||
import challengeIcon from 'assets/svg/challenge.svg';
|
||||
import tagsIcon from 'assets/svg/tags.svg';
|
||||
import checkIcon from 'assets/svg/check.svg';
|
||||
import checklistIcon from 'assets/svg/checklist.svg';
|
||||
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
@@ -335,11 +365,15 @@ export default {
|
||||
challenge: challengeIcon,
|
||||
tags: tagsIcon,
|
||||
check: checkIcon,
|
||||
checklist: checklistIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
castingSpell: 'spellOptions.castingSpell',
|
||||
}),
|
||||
...mapGetters({
|
||||
getTagsFor: 'tasks:getTagsFor',
|
||||
getTaskClasses: 'tasks:getTaskClasses',
|
||||
@@ -349,6 +383,13 @@ export default {
|
||||
let userIsTaskUser = this.task.userId ? this.task.userId === this.user._id : true;
|
||||
return hasChecklist && userIsTaskUser;
|
||||
},
|
||||
checklistProgress () {
|
||||
const totalItems = this.task.checklist.length;
|
||||
const completedItems = this.task.checklist.reduce((total, item) => {
|
||||
return item.completed ? total + 1 : total;
|
||||
}, 0);
|
||||
return `${completedItems}/${totalItems}`;
|
||||
},
|
||||
leftControl () {
|
||||
const task = this.task;
|
||||
if (task.type === 'reward') return false;
|
||||
@@ -388,9 +429,13 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({scoreChecklistItem: 'tasks:scoreChecklistItem'}),
|
||||
...mapActions({
|
||||
scoreChecklistItem: 'tasks:scoreChecklistItem',
|
||||
collapseChecklist: 'tasks:collapseChecklist',
|
||||
}),
|
||||
toggleChecklistItem (item) {
|
||||
item.completed = !item.completed;
|
||||
if (this.castingSpell) return;
|
||||
item.completed = !item.completed; // @TODO this should go into the action?
|
||||
this.scoreChecklistItem({taskId: this.task._id, itemId: item.id});
|
||||
},
|
||||
edit (e, task) {
|
||||
@@ -407,6 +452,8 @@ export default {
|
||||
this.$root.$emit('castEnd', task, 'task', e);
|
||||
},
|
||||
async score (direction) {
|
||||
if (this.castingSpell) return;
|
||||
|
||||
// TODO move to an action
|
||||
const Content = this.$store.state.content;
|
||||
const user = this.user;
|
||||
|
||||
@@ -110,29 +110,22 @@
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description {{ $t('dayOfWeek') }}
|
||||
|
||||
.option(v-if="isUserTask")
|
||||
label(v-once) {{ $t('tags') }}
|
||||
.category-wrap(@click="showTagsSelect = !showTagsSelect")
|
||||
span.category-select(v-if='task.tags && task.tags.length === 0') {{$t('none')}}
|
||||
span.category-select(v-else)
|
||||
.category-label(v-for='tagName in getTagsFor(task)') {{tagName}}
|
||||
.category-box(v-if="showTagsSelect")
|
||||
.container
|
||||
.row
|
||||
.form-check.col-6(
|
||||
v-for="tag in user.tags",
|
||||
:key="tag.id",
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value="tag.id", v-model="task.tags")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ tag.name }}
|
||||
.row
|
||||
button.btn.btn-primary(@click="showTagsSelect = !showTagsSelect") {{$t('close')}}
|
||||
.tags-select.option(v-if="isUserTask")
|
||||
.tags-inline
|
||||
label(v-once) {{ $t('tags') }}
|
||||
.category-wrap(@click="showTagsSelect = !showTagsSelect", v-bind:class="{ active: showTagsSelect }")
|
||||
span.category-select(v-if='task.tags && task.tags.length === 0')
|
||||
.tags-none {{$t('none')}}
|
||||
.dropdown-toggle
|
||||
span.category-select(v-else)
|
||||
.category-label(v-for='tagName in truncatedSelectedTags', :title="tagName") {{ tagName }}
|
||||
.tags-more(v-if='remainingSelectedTags.length > 0') +{{ $t('more', { count: remainingSelectedTags.length }) }}
|
||||
.dropdown-toggle
|
||||
tags-popup(v-if="showTagsSelect", :tags="user.tags", v-model="task.tags")
|
||||
|
||||
.option(v-if="task.type === 'habit'")
|
||||
label(v-once) {{ $t('resetStreak') }}
|
||||
b-dropdown(:text="$t(task.frequency)")
|
||||
b-dropdown.streak-dropdown(:text="$t(task.frequency)")
|
||||
b-dropdown-item(v-for="frequency in ['daily', 'weekly', 'monthly']", :key="frequency", @click="task.frequency = frequency", :class="{active: task.frequency === frequency}")
|
||||
| {{ $t(frequency) }}
|
||||
|
||||
@@ -328,6 +321,88 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tags-select {
|
||||
position: relative;
|
||||
|
||||
.tags-inline {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.category-wrap {
|
||||
cursor: inherit;
|
||||
position: relative;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2px;
|
||||
margin-left: 4em;
|
||||
|
||||
&.active {
|
||||
border-color: $purple-500;
|
||||
|
||||
.category-select {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.category-select {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: .6em;
|
||||
padding-right: 2.8em;
|
||||
width: 100%;
|
||||
|
||||
.tags-none {
|
||||
margin: .26em 0 .26em .6em;
|
||||
|
||||
& + .dropdown-toggle {
|
||||
right: 1.3em;
|
||||
}
|
||||
}
|
||||
|
||||
.tags-more {
|
||||
color: #a5a1ac;
|
||||
flex: 0 1 auto;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
left: .5em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
top: .8em;
|
||||
}
|
||||
|
||||
.category-label {
|
||||
min-width: 68px;
|
||||
overflow: hidden;
|
||||
padding: .5em 1em;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 68px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags-popup {
|
||||
position: absolute;
|
||||
top: 3.5em;
|
||||
left: 6.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.streak-dropdown {
|
||||
margin-left: .5em;
|
||||
}
|
||||
|
||||
.checklist-group {
|
||||
border-top: 1px solid $gray-500;
|
||||
}
|
||||
@@ -418,6 +493,7 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import TagsPopup from './tagsPopup';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import { mapGetters, mapActions, mapState } from 'client/libs/store';
|
||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
@@ -441,6 +517,7 @@ import goldIcon from 'assets/svg/gold.svg';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TagsPopup,
|
||||
bModal,
|
||||
bDropdown,
|
||||
bDropdownItem,
|
||||
@@ -453,6 +530,7 @@ export default {
|
||||
props: ['task', 'purpose', 'challengeId', 'groupId'], // purpose is either create or edit, task is the task created or edited
|
||||
data () {
|
||||
return {
|
||||
maxTags: 3,
|
||||
showTagsSelect: false,
|
||||
showAssignedSelect: false,
|
||||
newChecklistItem: null,
|
||||
@@ -574,6 +652,15 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
selectedTags () {
|
||||
return this.getTagsFor(this.task);
|
||||
},
|
||||
truncatedSelectedTags () {
|
||||
return this.selectedTags.slice(0, this.maxTags);
|
||||
},
|
||||
remainingSelectedTags () {
|
||||
return this.selectedTags.slice(this.maxTags);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({saveTask: 'tasks:save', destroyTask: 'tasks:destroy', createTask: 'tasks:create'}),
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
max-width: 80%;
|
||||
|
||||
@media screen and (min-width: 1241px) {
|
||||
max-width: 968px;
|
||||
max-width: 978px;
|
||||
// 16.67% is the width of the .col-2 sidebar
|
||||
left: calc((100% + 16.67% - 968px) / 2);
|
||||
left: calc((100% + 16.67% - 978px) / 2);
|
||||
right: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
.fill-height {
|
||||
height: 38px; // button + margin + padding
|
||||
}
|
||||
|
||||
.item-rows {
|
||||
margin-right: -24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
export default {
|
||||
methods: {
|
||||
makeGenericPurchase (item, type = 'buyModal') {
|
||||
this.$store.dispatch('shops:genericPurchase', {
|
||||
pinType: item.pinType,
|
||||
type: item.purchaseType,
|
||||
key: item.key,
|
||||
currency: item.currency,
|
||||
});
|
||||
|
||||
if (item.purchaseType !== 'gear') {
|
||||
this.$store.state.recentlyPurchased[item.key] = true;
|
||||
}
|
||||
|
||||
this.$root.$emit('playSound', 'Reward');
|
||||
|
||||
if (type !== 'buyModal') {
|
||||
this.$emit('buyPressed', this.item);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$root.$emit('buyModal::boughtItem', item);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import axios from 'axios';
|
||||
const STRIPE_PUB_KEY = process.env.STRIPE_PUB_KEY; // eslint-disable-line
|
||||
import subscriptionBlocks from '../../common/script/content/subscriptionBlocks';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import encodeParams from 'client/libs/encodeParams';
|
||||
import notificationsMixin from 'client/mixins/notifications';
|
||||
|
||||
export default {
|
||||
@@ -170,5 +171,41 @@ export default {
|
||||
|
||||
this.$root.$emit('show::modal', 'amazon-payment');
|
||||
},
|
||||
async cancelSubscription (config) {
|
||||
if (config && config.group && !confirm(this.$t('confirmCancelGroupPlan'))) return;
|
||||
if (!confirm(this.$t('sureCancelSub'))) return;
|
||||
|
||||
let group;
|
||||
if (config && config.group) {
|
||||
group = config.group;
|
||||
}
|
||||
|
||||
let paymentMethod = this.user.purchased.plan.paymentMethod;
|
||||
if (group) {
|
||||
paymentMethod = group.purchased.plan.paymentMethod;
|
||||
}
|
||||
|
||||
if (paymentMethod === 'Amazon Payments') {
|
||||
paymentMethod = 'amazon';
|
||||
} else {
|
||||
paymentMethod = paymentMethod.toLowerCase();
|
||||
}
|
||||
|
||||
let queryParams = {
|
||||
_id: this.user._id,
|
||||
apiToken: this.credentials.API_TOKEN,
|
||||
noRedirect: true,
|
||||
};
|
||||
|
||||
if (group) {
|
||||
queryParams.groupId = group._id;
|
||||
}
|
||||
|
||||
let cancelUrl = `/${paymentMethod}/subscribe/cancel?${encodeParams(queryParams)}`;
|
||||
await axios.get(cancelUrl);
|
||||
// Success
|
||||
alert(this.$t('paypalCanceled'));
|
||||
this.$router.push('/');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import axios from 'axios';
|
||||
import isArray from 'lodash/isArray';
|
||||
|
||||
// @TODO: Let's separate some of the business logic out of Vue if possible
|
||||
export default {
|
||||
methods: {
|
||||
async castStart (spell) {
|
||||
if (this.$store.state.spellOptions.castingSpell) {
|
||||
this.castCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.user.stats.mp < spell.mana) return this.text(this.$t('notEnoughMana'));
|
||||
|
||||
if (spell.immediateUse && this.user.stats.gp < spell.value) {
|
||||
return this.text('Not enough gold.');
|
||||
}
|
||||
|
||||
this.potionClickMode = true;
|
||||
this.applyingAction = true;
|
||||
this.$store.state.spellOptions.castingSpell = true;
|
||||
this.spell = spell;
|
||||
|
||||
if (spell.target === 'self') {
|
||||
this.castEnd(null, spell.target);
|
||||
} else if (spell.target === 'party') {
|
||||
if (!this.user.party._id) {
|
||||
let party = [this.user];
|
||||
this.castEnd(party, spell.target);
|
||||
return;
|
||||
}
|
||||
|
||||
let party = this.$store.state.party.members;
|
||||
party = isArray(party) ? party : [];
|
||||
party = party.concat(this.user);
|
||||
this.castEnd(party, spell.target);
|
||||
} else if (spell.target === 'tasks') {
|
||||
let userTasks = this.$store.state.tasks.data;
|
||||
// exclude rewards
|
||||
let tasks = userTasks.habits
|
||||
.concat(userTasks.dailys)
|
||||
.concat(userTasks.todos);
|
||||
// exclude challenge and group plan tasks
|
||||
tasks = tasks.filter((task) => {
|
||||
if (task.challenge && task.challenge.id && !task.challenge.broken) return false;
|
||||
if (task.group && task.group.id && !task.group.broken) return false;
|
||||
return true;
|
||||
});
|
||||
this.castEnd(tasks, spell.target);
|
||||
}
|
||||
},
|
||||
async castEnd (target, type) {
|
||||
if (!this.$store.state.spellOptions.castingSpell) return;
|
||||
let beforeQuestProgress;
|
||||
if (this.spell.target === 'party') beforeQuestProgress = this.questProgress();
|
||||
|
||||
if (!this.applyingAction) return 'No applying action';
|
||||
|
||||
if (this.spell.target !== type) return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.type && target.type === 'reward') return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.challenge && target.challenge.id) return this.text(this.$t('invalidTarget'));
|
||||
if (target && target.group && target.group.id) return this.text(this.$t('invalidTarget'));
|
||||
|
||||
// @TODO: just call castCancel?
|
||||
this.$store.state.spellOptions.castingSpell = false;
|
||||
this.potionClickMode = false;
|
||||
|
||||
this.spell.cast(this.user, target);
|
||||
// User.save(); // @TODO:
|
||||
|
||||
let spell = this.spell;
|
||||
let targetId = target ? target._id : null;
|
||||
this.spell = null;
|
||||
this.applyingAction = false;
|
||||
|
||||
let spellUrl = `/api/v3/user/class/cast/${spell.key}`;
|
||||
if (targetId) spellUrl += `?targetId=${targetId}`;
|
||||
|
||||
let spellText = typeof spell.text === 'function' ? spell.text() : spell.text;
|
||||
|
||||
await axios.post(spellUrl);
|
||||
let msg = this.$t('youCast', {
|
||||
spell: spellText,
|
||||
});
|
||||
|
||||
switch (type) {
|
||||
case 'task':
|
||||
msg = this.$t('youCastTarget', {
|
||||
spell: spellText,
|
||||
target: target.text,
|
||||
});
|
||||
break;
|
||||
case 'user':
|
||||
msg = this.$t('youCastTarget', {
|
||||
spell: spellText,
|
||||
target: target.profile.name,
|
||||
});
|
||||
break;
|
||||
case 'party':
|
||||
msg = this.$t('youCastParty', {
|
||||
spell: spellText,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
this.markdown(msg); // @TODO: mardown directive?
|
||||
// @TODO:
|
||||
if (!beforeQuestProgress) return;
|
||||
let questProgress = this.questProgress() - beforeQuestProgress;
|
||||
if (questProgress > 0) {
|
||||
let userQuest = this.quests[this.user.party.quest.key];
|
||||
if (userQuest.boss) {
|
||||
this.quest('questDamage', questProgress.toFixed(1));
|
||||
} else if (userQuest.collection && userQuest.collect) {
|
||||
this.quest('questCollection', questProgress);
|
||||
}
|
||||
}
|
||||
// @TOOD: User.sync();
|
||||
},
|
||||
castCancel () {
|
||||
this.potionClickMode = false;
|
||||
this.applyingAction = false;
|
||||
this.spell = null;
|
||||
document.querySelector('body').style.cursor = 'initial';
|
||||
this.$store.state.spellOptions.castingSpell = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -9,14 +9,15 @@ import * as Analytics from 'client/libs/analytics';
|
||||
import ParentPage from './components/parentPage';
|
||||
|
||||
// Static Pages
|
||||
const StaticWrapper = () => import(/* webpackChunkName: "static" */'./components/static/staticWrapper');
|
||||
const StaticWrapper = () => import(/* webpackChunkName: "entry" */'./components/static/staticWrapper');
|
||||
const HomePage = () => import(/* webpackChunkName: "entry" */'./components/static/home');
|
||||
|
||||
const AppPage = () => import(/* webpackChunkName: "static" */'./components/static/app');
|
||||
const ClearBrowserDataPage = () => import(/* webpackChunkName: "static" */'./components/static/clearBrowserData');
|
||||
const CommunityGuidelinesPage = () => import(/* webpackChunkName: "static" */'./components/static/communityGuidelines');
|
||||
const ContactPage = () => import(/* webpackChunkName: "static" */'./components/static/contact');
|
||||
const FAQPage = () => import(/* webpackChunkName: "static" */'./components/static/faq');
|
||||
const FeaturesPage = () => import(/* webpackChunkName: "static" */'./components/static/features');
|
||||
const HomePage = () => import(/* webpackChunkName: "static" */'./components/static/home');
|
||||
const GroupPlansPage = () => import(/* webpackChunkName: "static" */'./components/static/groupPlans');
|
||||
const MerchPage = () => import(/* webpackChunkName: "static" */'./components/static/merch');
|
||||
const NewsPage = () => import(/* webpackChunkName: "static" */'./components/static/newStuff');
|
||||
@@ -72,6 +73,7 @@ const GroupPlansAppPage = () => import(/* webpackChunkName: "guilds" */ './compo
|
||||
// Group Plans
|
||||
const GroupPlanIndex = () => import(/* webpackChunkName: "group-plans" */ './components/group-plans/index');
|
||||
const GroupPlanTaskInformation = () => import(/* webpackChunkName: "group-plans" */ './components/group-plans/taskInformation');
|
||||
const GroupPlanBilling = () => import(/* webpackChunkName: "group-plans" */ './components/group-plans/billing');
|
||||
|
||||
// Challenges
|
||||
const ChallengeIndex = () => import(/* webpackChunkName: "challenges" */ './components/challenges/index');
|
||||
@@ -144,6 +146,12 @@ const router = new VueRouter({
|
||||
component: GroupPage,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
name: 'groupPlanBilling',
|
||||
path: '/group-plans/:groupId/billing',
|
||||
component: GroupPlanBilling,
|
||||
props: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -124,6 +124,13 @@ export async function scoreChecklistItem (store, {taskId, itemId}) {
|
||||
await axios.post(`/api/v3/tasks/${taskId}/checklist/${itemId}/score`);
|
||||
}
|
||||
|
||||
export async function collapseChecklist (store, task) {
|
||||
task.collapseChecklist = !task.collapseChecklist;
|
||||
await axios.put(`/api/v3/tasks/${task._id}`, {
|
||||
collapseChecklist: task.collapseChecklist,
|
||||
});
|
||||
}
|
||||
|
||||
export async function destroy (store, task) {
|
||||
const list = store.state.tasks.data[`${task.type}s`];
|
||||
const taskIndex = list.findIndex(t => t._id === task._id);
|
||||
|
||||
@@ -135,6 +135,8 @@ export default function () {
|
||||
userIdToMessage: '',
|
||||
brokenChallengeTask: {},
|
||||
equipmentDrawerOpen: true,
|
||||
recentlyPurchased: {},
|
||||
groupPlans: [],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
"shortName": "Short Name",
|
||||
"shortNamePlaceholder": "What short tag should be used to identify your Challenge?",
|
||||
"updateChallenge": "Update Challenge",
|
||||
"haveNoChallenges": "You don't have any Challenges",
|
||||
"haveNoChallenges": "This group has no Challenges",
|
||||
"loadMore": "Load More",
|
||||
"exportChallengeCsv": "Export Challenge",
|
||||
"editingChallenge": "Editing Challenge",
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
"optOutOfPMs": "Opt Out",
|
||||
"chooseClass": "Choose your Class",
|
||||
"chooseClassLearnMarkdown": "[Learn more about Habitica's class system](http://habitica.wikia.com/wiki/Class_System)",
|
||||
"optOutOfClassesText": "Can't be bothered with classes? Want to choose later? Opt out - you'll be a warrior with no special abilities. You can read about the class system later on the wiki and enable classes at any time under User -> Stats.",
|
||||
"optOutOfClassesText": "Can't be bothered with classes? Want to choose later? Opt out - you'll be a warrior with no special abilities. You can read about the class system later on the wiki and enable classes at any time under User Icon > Settings.",
|
||||
"selectClass": "Select <%= heroClass %>",
|
||||
"select": "Select",
|
||||
"stealth": "Stealth",
|
||||
@@ -196,7 +196,7 @@
|
||||
"int": "INT",
|
||||
"showQuickAllocation": "Show stat allocation",
|
||||
"hideQuickAllocation": "Hide stat allocation",
|
||||
"quickAllocationLevelPopover": "Each level earns you one point to assign to an attribute of your choice. You can do so manually, or let the game decide for you using one of the Automatic Allocation options found in User -> Stats.",
|
||||
"quickAllocationLevelPopover": "Each level earns you one point to assign to an attribute of your choice. You can do so manually, or let the game decide for you using one of the Automatic Allocation options found in User Icon > Stats.",
|
||||
"invalidAttribute": "\"<%= attr %>\" is not a valid attribute.",
|
||||
"notEnoughAttrPoints": "You don't have enough attribute points.",
|
||||
"style": "Style",
|
||||
|
||||
@@ -312,5 +312,6 @@
|
||||
"signup": "Sign Up",
|
||||
"getStarted": "Get Started",
|
||||
"mobileApps": "Mobile Apps",
|
||||
"learnMore": "Learn More"
|
||||
"learnMore": "Learn More",
|
||||
"useMobileApps": "Habitica is not optimized for a mobile browser. We recommend downloading our mobile apps."
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"subscriberItemText": "Each month, subscribers will receive a mystery item. This is usually released about one week before the end of the month. See the wiki's 'Mystery Item' page for more information.",
|
||||
"all": "All",
|
||||
"none": "None",
|
||||
"more": "<%= count %> more",
|
||||
"and": "and",
|
||||
"loginSuccess": "Login successful!",
|
||||
"youSure": "Are you sure?",
|
||||
@@ -125,7 +126,7 @@
|
||||
"error": "Error",
|
||||
"menu": "Menu",
|
||||
"notifications": "Notifications",
|
||||
"noNotifications": "You have no new messages.",
|
||||
"noNotifications": "You have no notifications.",
|
||||
"clear": "Clear",
|
||||
"endTour": "End Tour",
|
||||
"audioTheme": "Audio Theme",
|
||||
|
||||
@@ -358,7 +358,7 @@
|
||||
"guildSummaryPlaceholder": "Write a short description advertising your Guild to other Habiticans. What is the main purpose of your Guild and why should people join it? Try to include useful keywords in the summary so that Habiticans can easily find it when they search!",
|
||||
"groupDescription": "Description",
|
||||
"guildDescriptionPlaceholder": "Use this section to go into more detail about everything that Guild members should know about your Guild. Useful tips, helpful links, and encouraging statements all go here!",
|
||||
"markdownFormattingHelp": "Markdown formatting help",
|
||||
"markdownFormattingHelp": "[Markdown formatting help](http://habitica.wikia.com/wiki/Markdown_Cheat_Sheet)",
|
||||
"partyDescriptionPlaceholder": "This is our party's description. It describes what we do in this party. If you want to learn more about what we do in this party, read the description. Party on.",
|
||||
"guildGemCostInfo": "A Gem cost promotes high quality Guilds and is transferred into your Guild's bank.",
|
||||
"noGuildsTitle": "You aren't a member of any Guilds.",
|
||||
@@ -391,7 +391,10 @@
|
||||
"reverseChat": "Reverse Chat",
|
||||
"invites": "Invites",
|
||||
"details": "Details",
|
||||
"participantDesc": "Once all members have either accepted or declined, the Quest begins. Only those that clicked 'accept' will be able to participate in the Quest and receive the drops.",
|
||||
"participantDesc": "Once all members have either accepted or declined, the Quest begins. Only those who clicked 'accept' will be able to participate in the Quest and receive the rewards.",
|
||||
"groupGems": "Group Gems",
|
||||
"groupGemsDesc": "Guild Gems can be spent to make Challenges! In the future, you will be able to add more Guild Gems."
|
||||
"groupGemsDesc": "Guild Gems can be spent to make Challenges! In the future, you will be able to add more Guild Gems.",
|
||||
"groupTaskBoard": "Task Board",
|
||||
"groupInformation": "Group Information",
|
||||
"groupBilling": "Group Billing"
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
"useGems": "If you've got your eye on a pet, but can't wait any longer for it to drop, use Gems in <strong>Inventory > Market</strong> to buy one!",
|
||||
"hatchAPot": "Hatch a new <%= potion %> <%= egg %>?",
|
||||
"hatchedPet": "You hatched a new <%= potion %> <%= egg %>!",
|
||||
"hatchedPetGeneric": "You hatched a new pet!",
|
||||
"displayNow": "Display Now",
|
||||
"displayLater": "Display Later",
|
||||
"petNotOwned": "You do not own this pet.",
|
||||
|
||||
@@ -202,5 +202,7 @@
|
||||
"haveCouponCode": "Do you have a coupon code?",
|
||||
"subscriptionAlreadySubscribedLeadIn": "Thanks for subscribing!",
|
||||
"subscriptionAlreadySubscribed1": "To see your subscription details and cancel, renew, or change your subscription, please go to <a href='/user/settings/subscription'>User icon > Settings > Subscription</a>.",
|
||||
"purchaseAll": "Purchase All"
|
||||
"purchaseAll": "Purchase All",
|
||||
"gemsPurchaseNote": "Subscribers can buy gems for gold in the Market! For easy access, you can also pin the gem to your Rewards column.",
|
||||
"gemsRemaining": "gems remaining"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"unlockedReward": "You have received <%= reward %>",
|
||||
"earnedRewardForDevotion": "You have earned <%= reward %> for being committed to improving your life.",
|
||||
"nextRewardUnlocksIn": "Check-ins until your next prize: <%= numberOfCheckinsLeft %>",
|
||||
"nextRewardUnlocksIn": "Check-Ins until your next prize: <%= numberOfCheckinsLeft %>",
|
||||
"awesome": "Awesome!",
|
||||
"totalCount": "<%= count %> total count",
|
||||
"countLeft": "Check-ins until next reward: <%= count %>",
|
||||
"countLeft": "Check-Ins until next reward: <%= count %>",
|
||||
"incentivesDescription": "When it comes to building habits, consistency is key. Each day you check-in, you get closer to a prize.",
|
||||
"totalCheckins": "<%= count %> Check-Ins",
|
||||
"checkinEarned": "Your Check-In Counter went up!",
|
||||
|
||||
@@ -179,5 +179,6 @@ module.exports = {
|
||||
removePinnedItemsByOwnedGear,
|
||||
togglePinnedItem,
|
||||
removeItemByPath,
|
||||
selectGearToPin,
|
||||
isPinned,
|
||||
};
|
||||
|
||||
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 5.4 KiB |