New client many fixes (#8981)

* Footer style fixes

* Limited string display

* Fixed background reload

* Began adding more avatar items

* Fixed challenge updated cats and official to be seen by admins

* Fixed min prize

* Fixed required fields

* Added my challenges and find challenges to menu

* Removed nav to party page when have no party

* Updated user and notifications icon

* Added accept, reject and messages

* Added selfcare

* Underline links

* Added forgot password

* Fixed task adding

* Disabled habit options that should be

* Added markdown to tags

* Added confirm to delete

* Fixed cancel/delete style

* Fixed rounding

* Added gold icon

* Fixed task icon styles

* Fixed margin botton

* Fixed some repeat styles

* Fixed custom reward style

* Hide like count 0

* Added new tavern images

* Redirect to party page after create

* Hid leader options from non leaders

* Removed manager options from non group plan

* Fixed some nav styles

* Fixed overlay color

* Prevented edit data from being transfered to create

* Added hover state for spells

* Add performance fixes for chat avatars

* Fixed merge conflicts

* Updated fron navigation

* Fixed reg gryphon logo

* Began adding gem modal functionality

* Added purchase gems with gold

* Fixed restore

* Replaced description with summary

* Spells that target tasks fix

* Added initial challenge task load

* Fixed lint issue
This commit is contained in:
Keith Holliday
2017-08-22 21:58:13 -06:00
committed by GitHub
parent 69662f84df
commit f529a5c64c
42 changed files with 586 additions and 305 deletions

View File

@@ -21,6 +21,13 @@
} }
</style> </style>
<style>
.modal-backdrop.show {
opacity: 1 !important;
background-color: rgba(67, 40, 116, 0.9) !important;
}
</style>
<script> <script>
import axios from 'axios'; import axios from 'axios';
import AppMenu from './components/appMenu'; import AppMenu from './components/appMenu';

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="20" viewBox="0 0 24 20"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="20" viewBox="0 0 24 20">
<path fill="#C3C0C7" fill-rule="evenodd" d="M13 16h2v-2h-2v2zm-4 0h2v-2H9v2zm-4 0h2v-2H5v2zm12-4h2v-2h-2v2zm-4 0h2v-2h-2v2zm-4 0h2v-2H9v2zm13-4H2v8a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8zm2-2v10a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4h1V0h2v2h10V0h2v2h1a4 4 0 0 1 4 4zM5 12h2v-2H5v2z"/> <path fill-rule="evenodd" d="M13 16h2v-2h-2v2zm-4 0h2v-2H9v2zm-4 0h2v-2H5v2zm12-4h2v-2h-2v2zm-4 0h2v-2h-2v2zm-4 0h2v-2H9v2zm13-4H2v8a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8zm2-2v10a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4h1V0h2v2h10V0h2v2h1a4 4 0 0 1 4 4zM5 12h2v-2H5v2z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 382 B

After

Width:  |  Height:  |  Size: 367 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="20" viewBox="0 0 30 20"> <svg xmlns="http://www.w3.org/2000/svg" width="30" height="20" viewBox="0 0 30 20">
<path fill="#C3C0C7" fill-rule="evenodd" d="M11 11H9v2H7v-2H5V9h2V7h2v2h2v2zm8 0h6V9h-6v2zm9 5c0 1.103-.897 2-2 2H16V2h10c1.103 0 2 .897 2 2v12zM4 18c-1.103 0-2-.897-2-2V4c0-1.103.897-2 2-2h10v16H4zM26 0H4a4 4 0 0 0-4 4v12a4 4 0 0 0 4 4h22a4 4 0 0 0 4-4V4a4 4 0 0 0-4-4z"/> <path fill-rule="evenodd" d="M11 11H9v2H7v-2H5V9h2V7h2v2h2v2zm8 0h6V9h-6v2zm9 5c0 1.103-.897 2-2 2H16V2h10c1.103 0 2 .897 2 2v12zM4 18c-1.103 0-2-.897-2-2V4c0-1.103.897-2 2-2h10v16H4zM26 0H4a4 4 0 0 0-4 4v12a4 4 0 0 0 4 4h22a4 4 0 0 0 4-4V4a4 4 0 0 0-4-4z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 369 B

After

Width:  |  Height:  |  Size: 354 B

5
website/client/assets/svg/notifications.svg Executable file → Normal file
View File

@@ -1,4 +1,3 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>notifications</title> <path fill="#D5C8FF" fill-rule="evenodd" d="M21 0H3C1.3 0 0 1.3 0 3v14c0 1.7 1.3 3 3 3h4l2.9 3.1c1.2 1.2 3.1 1.2 4.2 0L17 20h4c1.7 0 3-1.3 3-3V3c0-1.7-1.3-3-3-3zm1 17c0 .6-.4 1-1 1h-4.9l-3.5 3.7c-.2.2-.4.3-.6.3-.2 0-.4-.1-.7-.3L7.9 18H3c-.6 0-1-.4-1-1V3c0-.6.4-1 1-1h18c.6 0 1 .4 1 1v14zM19 7H5V5h14v2zm0 4H5V9h14v2zm0 4H5v-2h14v2z"/>
<path d="M26 4h-20c-1.7 0-3 1.3-3 3v14c0 1.7 1.3 3 3 3h5l2.9 3.1c1.2 1.2 3.1 1.2 4.2 0l2.9-3.1h5c1.7 0 3-1.3 3-3v-14c0-1.7-1.3-3-3-3v0zM27 21c0 0.6-0.4 1-1 1h-5.9l-3.5 3.7c-0.2 0.2-0.4 0.3-0.6 0.3s-0.4-0.1-0.7-0.3l-3.4-3.7h-5.9c-0.6 0-1-0.4-1-1v-14c0-0.6 0.4-1 1-1h20c0.6 0 1 0.4 1 1v14zM24 11h-16v-2h16v2zM24 15h-16v-2h16v2zM24 19h-16v-2h16v2z"></path>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 487 B

After

Width:  |  Height:  |  Size: 430 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="20" viewBox="0 0 26 20"> <svg xmlns="http://www.w3.org/2000/svg" width="26" height="20" viewBox="0 0 26 20">
<path fill="#C3C0C7" fill-rule="evenodd" d="M24 10h-8V8h4a2 2 0 0 0 2-2V2c1.103 0 2 .897 2 2v6zm0 6a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-4h8v1a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-1h8v4zM2 4c0-1.103.897-2 2-2v4a2 2 0 0 0 2 2h4v2H2V4zm10 9h2V8h-2v5zm8-11v4H6V2h14zm2-2H4a4 4 0 0 0-4 4v12a4 4 0 0 0 4 4h18a4 4 0 0 0 4-4V4a4 4 0 0 0-4-4z"/> <path fill-rule="evenodd" d="M24 10h-8V8h4a2 2 0 0 0 2-2V2c1.103 0 2 .897 2 2v6zm0 6a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-4h8v1a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-1h8v4zM2 4c0-1.103.897-2 2-2v4a2 2 0 0 0 2 2h4v2H2V4zm10 9h2V8h-2v5zm8-11v4H6V2h14zm2-2H4a4 4 0 0 0-4 4v12a4 4 0 0 0 4 4h18a4 4 0 0 0 4-4V4a4 4 0 0 0-4-4z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 406 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<path fill="#C3C0C7" fill-rule="evenodd" d="M8.343 14.916c-.312 0-.61-.123-.831-.344l-3.831-3.831 1.662-1.662 2.934 2.934 5.938-6.929L16 6.613l-6.764 7.893a1.182 1.182 0 0 1-.848.409l-.045.001zM18 16c0 1.103-.897 2-2 2H4c-1.102 0-2-.897-2-2V4c0-1.103.898-2 2-2h12c1.103 0 2 .897 2 2v12zM16 0H4a4 4 0 0 0-4 4v12a4 4 0 0 0 4 4h12a4 4 0 0 0 4-4V4a4 4 0 0 0-4-4z"/> <path fill-rule="evenodd" d="M8.343 14.916c-.312 0-.61-.123-.831-.344l-3.831-3.831 1.662-1.662 2.934 2.934 5.938-6.929L16 6.613l-6.764 7.893a1.182 1.182 0 0 1-.848.409l-.045.001zM18 16c0 1.103-.897 2-2 2H4c-1.102 0-2-.897-2-2V4c0-1.103.898-2 2-2h12c1.103 0 2 .897 2 2v12zM16 0H4a4 4 0 0 0-4 4v12a4 4 0 0 0 4 4h12a4 4 0 0 0 4-4V4a4 4 0 0 0-4-4z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 442 B

5
website/client/assets/svg/user.svg Executable file → Normal file
View File

@@ -1,4 +1,3 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="24" viewBox="0 0 22 24">
<title>user</title> <path fill-rule="evenodd" d="M15 13h-.4c1.9-1.2 3.3-3.3 3.4-5.8.1-3.8-3.1-7.2-6.9-7.2C7.1 0 4 3.1 4 7c0 2.6 1.3 4.8 3.4 6H7c-3.9 0-7 3.1-7 7v1c0 1.7 1.3 3 3 3h16c1.7 0 3-1.3 3-3v-1c0-3.9-3.1-7-7-7zM6 7c0-2.8 2.2-5 5-5s5 2.2 5 5-2.2 5-5 5-5-2.2-5-5zm13 15H3c-.6 0-1-.4-1-1v-1c0-2.8 2.2-5 5-5h8c2.8 0 5 2.2 5 5v1c0 .6-.4 1-1 1z"/>
<path d="M20 17h-0.4c1.9-1.2 3.3-3.3 3.4-5.8 0.1-3.8-3.1-7.2-6.9-7.2-4 0-7.1 3.1-7.1 7 0 2.6 1.3 4.8 3.4 6h-0.4c-3.9 0-7 3.1-7 7v1c0 1.7 1.3 3 3 3h16c1.7 0 3-1.3 3-3v-1c0-3.9-3.1-7-7-7v0zM11 11c0-2.8 2.2-5 5-5s5 2.2 5 5c0 2.8-2.2 5-5 5s-5-2.2-5-5v0zM24 26h-16c-0.6 0-1-0.4-1-1v-1c0-2.8 2.2-5 5-5h8c2.8 0 5 2.2 5 5v1c0 0.6-0.4 1-1 1v0z"></path>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 469 B

After

Width:  |  Height:  |  Size: 424 B

View File

@@ -113,7 +113,8 @@
<style lang="scss" scoped> <style lang="scss" scoped>
footer { footer {
background-color: #e1e0e3; background-color: #e1e0e3;
height: 376px; min-height: 408px;
width: 100%;
padding-left: 6em; padding-left: 6em;
padding-right: 6em; padding-right: 6em;
padding-top: 3em; padding-top: 3em;

View File

@@ -1,7 +1,7 @@
<template lang="pug"> <template lang="pug">
#app-header.row #app-header.row
create-party-modal create-party-modal
members-modal(:group='user.party', :hide-badge="true") members-modal(:hide-badge="true")
member-details(:member="user") member-details(:member="user")
.view-party(v-if="user.party && user.party._id && partyMembers && partyMembers.length > 1") .view-party(v-if="user.party && user.party._id && partyMembers && partyMembers.length > 1")
// TODO button should open the party members modal // TODO button should open the party members modal
@@ -124,6 +124,8 @@ export default {
openPartyModal () { openPartyModal () {
if (this.user.party._id) { if (this.user.party._id) {
this.$store.state.groupId = this.user.party._id; this.$store.state.groupId = this.user.party._id;
// @TODO: do we need to fetch party?
this.$store.state.memberModalOptions.group = this.$store.state.party;
this.$root.$emit('show::modal', 'members-modal'); this.$root.$emit('show::modal', 'members-modal');
return; return;
} }

View File

@@ -22,7 +22,9 @@ div
router-link.dropdown-item(:to="{name: 'quests'}") {{ $t('quests') }} router-link.dropdown-item(:to="{name: 'quests'}") {{ $t('quests') }}
router-link.dropdown-item(:to="{name: 'seasonal'}") {{ $t('titleSeasonalShop') }} router-link.dropdown-item(:to="{name: 'seasonal'}") {{ $t('titleSeasonalShop') }}
router-link.dropdown-item(:to="{name: 'time'}") {{ $t('titleTimeTravelers') }} router-link.dropdown-item(:to="{name: 'time'}") {{ $t('titleTimeTravelers') }}
router-link.nav-item(tag="li", :to="{name: 'party'}") router-link.nav-item(tag="li", :to="{name: 'party'}", v-if='this.user.party._id')
a.nav-link(v-once) {{ $t('party') }}
.nav-item(@click='openPartyModal()', v-if='!this.user.party._id')
a.nav-link(v-once) {{ $t('party') }} a.nav-link(v-once) {{ $t('party') }}
router-link.nav-item.dropdown(tag="li", :to="{name: 'tavern'}", :class="{'active': $route.path.startsWith('/guilds')}") router-link.nav-item.dropdown(tag="li", :to="{name: 'tavern'}", :class="{'active': $route.path.startsWith('/guilds')}")
a.nav-link(v-once) {{ $t('guilds') }} a.nav-link(v-once) {{ $t('guilds') }}
@@ -30,15 +32,15 @@ div
router-link.dropdown-item(:to="{name: 'tavern'}") {{ $t('tavern') }} router-link.dropdown-item(:to="{name: 'tavern'}") {{ $t('tavern') }}
router-link.dropdown-item(:to="{name: 'myGuilds'}") {{ $t('myGuilds') }} router-link.dropdown-item(:to="{name: 'myGuilds'}") {{ $t('myGuilds') }}
router-link.dropdown-item(:to="{name: 'guildsDiscovery'}") {{ $t('guildsDiscovery') }} router-link.dropdown-item(:to="{name: 'guildsDiscovery'}") {{ $t('guildsDiscovery') }}
router-link.nav-item.dropdown( .nav-item.dropdown(tag="li", :class="{'active': $route.path.startsWith('/group-plans')}")
tag="li", a.nav-link(v-once) {{ $t('group') }}
:to="{name: 'groupPlan'}", .dropdown-menu
:class="{'active': $route.path.startsWith('/group-plan')}") router-link.dropdown-item(v-for='group in groupPlans', :key='group._id', :to="{name: 'groupPlanDetailTaskInformation', params: {groupId: group._id}}") {{ group.name }}
a.nav-link(v-once) {{ $t('group') }} router-link.nav-item.dropdown(tag="li", :to="{name: 'myChallenges'}", :class="{'active': $route.path.startsWith('/challenges')}")
.dropdown-menu
router-link.dropdown-item(v-for='group in groupPlans', :key='group._id', :to="{name: 'groupPlanDetailTaskInformation', params: {groupId: group._id}}") {{ group.name }}
router-link.nav-item(tag="li", :to="{name: 'myChallenges'}", exact)
a.nav-link(v-once) {{ $t('challenges') }} a.nav-link(v-once) {{ $t('challenges') }}
.dropdown-menu
router-link.dropdown-item(:to="{name: 'myChallenges'}") {{ $t('myChallenges') }}
router-link.dropdown-item(:to="{name: 'findChallenges'}") {{ $t('findChallenges') }}
router-link.nav-item.dropdown(tag="li", to="/help", :class="{'active': $route.path.startsWith('/help')}", :to="{name: 'faq'}") router-link.nav-item.dropdown(tag="li", to="/help", :class="{'active': $route.path.startsWith('/help')}", :to="{name: 'faq'}")
a.nav-link(v-once) {{ $t('help') }} a.nav-link(v-once) {{ $t('help') }}
.dropdown-menu .dropdown-menu
@@ -57,10 +59,10 @@ div
span {{userGems | roundBigNumber}} span {{userGems | roundBigNumber}}
.item-with-icon .item-with-icon
.svg-icon(v-html="icons.gold") .svg-icon(v-html="icons.gold")
span {{user.stats.gp | roundBigNumber}} span {{Math.floor(user.stats.gp * 100) / 100}}
notification-menu notification-menu
a.dropdown.item-with-icon.item-user a.dropdown.item-with-icon.item-user
.svg-icon(v-html="icons.user") .svg-icon.user(v-html="icons.user")
.dropdown-menu.dropdown-menu-right.user-dropdown .dropdown-menu.dropdown-menu-right.user-dropdown
a.dropdown-item.edit-avatar.dropdown-separated(@click='showAvatar()') a.dropdown-item.edit-avatar.dropdown-separated(@click='showAvatar()')
h3 {{ user.profile.name }} h3 {{ user.profile.name }}
@@ -173,11 +175,12 @@ div
padding-left: 16px; padding-left: 16px;
.svg-icon { .svg-icon {
vertical-align: middle; vertical-align: bottom;
width: 24px; display: inline-block;
height: 24px; width: 20px;
height: 20px;
margin-right: 8px; margin-right: 8px;
float: left; margin-left: 8px;
} }
} }
@@ -187,13 +190,13 @@ div
color: $header-color; color: $header-color;
transition: none; transition: none;
&:hover {
color: $white;
}
.svg-icon { .svg-icon {
margin-right: 0px; margin-right: 0px;
color: $white; color: $header-color;
&:hover {
color: $white;
}
} }
} }
@@ -276,6 +279,9 @@ export default {
async getUserGroupPlans () { async getUserGroupPlans () {
this.groupPlans = await this.$store.dispatch('guilds:getGroupPlans'); this.groupPlans = await this.$store.dispatch('guilds:getGroupPlans');
}, },
openPartyModal () {
this.$root.$emit('show::modal', 'create-party-modal');
},
showBuyGemsModal () { showBuyGemsModal () {
this.$root.$emit('show::modal', 'buy-gems'); this.$root.$emit('show::modal', 'buy-gems');
}, },

View File

@@ -3,10 +3,10 @@
#top-background #top-background
.seamless_stars_varied_opacity_repeat .seamless_stars_varied_opacity_repeat
form#login-form(v-on:submit.prevent='handleSubmit', @keyup.enter="handleSubmit") form#login-form(v-on:submit.prevent='handleSubmit', @keyup.enter="handleSubmit", v-if='!forgotPassword')
.text-center .text-center
div div
.svg-icon.gryphon(v-html="icons.gryphon") .svg-icon.gryphon
div div
.svg-icon.habitica-logo(v-html="icons.habiticaIcon") .svg-icon.habitica-logo(v-html="icons.habiticaIcon")
.form-group.row.text-center .form-group.row.text-center
@@ -29,6 +29,7 @@
input#emailInput.form-control(type='email', :placeholder='$t("emailPlaceholder")', v-model='email') input#emailInput.form-control(type='email', :placeholder='$t("emailPlaceholder")', v-model='email')
.form-group .form-group
label(for='passwordInput', v-once) {{$t('password')}} label(for='passwordInput', v-once) {{$t('password')}}
a.float-right.forgot-password(v-once, v-if='!registering', @click='forgotPassword = true') {{$t('forgotPassword')}}
input#passwordInput.form-control(type='password', :placeholder='$t("passwordPlaceholder")', v-model='password') input#passwordInput.form-control(type='password', :placeholder='$t("passwordPlaceholder")', v-model='password')
.form-group(v-if='registering') .form-group(v-if='registering')
label(for='confirmPasswordInput', v-once) {{$t('confirmPassword')}} label(for='confirmPasswordInput', v-once) {{$t('confirmPassword')}}
@@ -43,6 +44,21 @@
router-link(:to="{name: 'register'}", v-if='!registering', exact) router-link(:to="{name: 'register'}", v-if='!registering', exact)
a.toggle-link(v-once) Don't have an account? Join Habitica! a.toggle-link(v-once) Don't have an account? Join Habitica!
form#forgot-form(v-on:submit.prevent='handleSubmit', @keyup.enter="handleSubmit", v-if='forgotPassword')
.text-center
div
.svg-icon.gryphon
div
.svg-icon.habitica-logo(v-html="icons.habiticaIcon")
.header
h2 Email a Password Reset Link
p Enter the email address you used to register your Habitica account.
.form-group.row.text-center
label(for='usernameInput', v-once) {{$t('email')}}
input#usernameInput.form-control(type='text', :placeholder='$t("emailPlaceholder")', v-model='username')
.text-center
.btn.btn-info(@click='forgotPasswordLink()', v-once) {{$t('sendLink')}}
#bottom-wrap #bottom-wrap
#bottom-background #bottom-background
.seamless_mountains_demo_repeat .seamless_mountains_demo_repeat
@@ -56,6 +72,7 @@
small a { small a {
color: #fff; color: #fff;
text-decoration: underline;
} }
</style> </style>
@@ -85,7 +102,7 @@
color: $purple-400; color: $purple-400;
} }
#login-form { #login-form, #forgot-form {
margin: 0 auto; margin: 0 auto;
width: 40em; width: 40em;
padding-top: 5em; padding-top: 5em;
@@ -93,9 +110,20 @@
position: relative; position: relative;
z-index: 1; z-index: 1;
.header {
h2 {
color: $white;
}
color: $white;
}
.gryphon { .gryphon {
background-image: url('~assets/images/melior@3x.png');
width: 63.2px; width: 63.2px;
height: 69.4px; height: 69.4px;
background-size: cover;
color: $white; color: $white;
margin: 0 auto; margin: 0 auto;
} }
@@ -188,9 +216,14 @@
.toggle-link { .toggle-link {
color: #fff !important; color: #fff !important;
} }
.forgot-password {
color: #bda8ff !important;
}
</style> </style>
<script> <script>
import axios from 'axios';
import hello from 'hellojs'; import hello from 'hellojs';
import gryphon from 'assets/svg/gryphon.svg'; import gryphon from 'assets/svg/gryphon.svg';
@@ -205,6 +238,7 @@ export default {
email: '', email: '',
password: '', password: '',
passwordConfirm: '', passwordConfirm: '',
forgotPassword: false,
}; };
data.icons = Object.freeze({ data.icons = Object.freeze({
@@ -282,8 +316,25 @@ export default {
return; return;
} }
if (this.forgotPassword) {
this.forgotPasswordLink();
return;
}
this.login(); this.login();
}, },
async forgotPasswordLink () {
if (!this.username) {
alert('Email is required');
return;
}
await axios.post('/api/v3/user/reset-password', {
email: this.username,
});
alert(this.$t('newPassSent'));
},
}, },
}; };
</script> </script>

View File

@@ -250,13 +250,17 @@ export default {
this.tasksByType[task.type].splice(index, 1, task); this.tasksByType[task.type].splice(index, 1, task);
}, },
showMemberModal () { showMemberModal () {
this.$store.state.groupId = 'challenge'; // @TODO: change these terrible settings this.$store.state.memberModalOptions.groupId = 'challenge'; // @TODO: change these terrible settings
this.$store.state.viewingMembers = this.members; this.$store.state.memberModalOptions.group = this.group;
this.$store.state.memberModalOptions.viewingMembers = this.members;
this.$root.$emit('show::modal', 'members-modal'); this.$root.$emit('show::modal', 'members-modal');
}, },
async joinChallenge () { async joinChallenge () {
this.user.challenges.push(this.challengeId); this.user.challenges.push(this.challengeId);
await this.$store.dispatch('challenges:joinChallenge', {challengeId: this.challengeId}); await this.$store.dispatch('challenges:joinChallenge', {challengeId: this.challengeId});
// @TODO: this doesn't work because of asyncresource
let tasks = await this.$store.dispatch('tasks:fetchUserTasks');
this.$store.state.tasks.data = tasks.data;
}, },
async leaveChallenge () { async leaveChallenge () {
let keepChallenge = confirm('Do you want to keep challenge tasks?'); let keepChallenge = confirm('Do you want to keep challenge tasks?');

View File

@@ -24,7 +24,7 @@
div Challenge Prize div Challenge Prize
.row.description .row.description
.col-12 .col-12
| {{challenge.description}} | {{challenge.summary}}
.well.row .well.row
.col-3 .col-3
.count-details .count-details
@@ -104,7 +104,8 @@
.description { .description {
color: $gray-200; color: $gray-200;
margin-bottom: 2em; margin-top: 1em;
margin-bottom: 1em;
overflow: hidden; overflow: hidden;
} }

View File

@@ -34,9 +34,12 @@
.form-check( .form-check(
v-for="group in categoryOptions", v-for="group in categoryOptions",
:key="group.key", :key="group.key",
v-if='group.key !== "habitica_official" || user.contributor.admin'
) )
label.custom-control.custom-checkbox label.custom-control.custom-checkbox
input.custom-control-input(type="checkbox", :value="group.key" v-model="workingChallenge.categories") input.custom-control-input(type="checkbox",
:value='group.key',
v-model="workingChallenge.categories")
span.custom-control-indicator span.custom-control-indicator
span.custom-control-description(v-once) {{ $t(group.label) }} span.custom-control-description(v-once) {{ $t(group.label) }}
button.btn.btn-primary(@click.prevent="toggleCategorySelect") {{$t('close')}} button.btn.btn-primary(@click.prevent="toggleCategorySelect") {{$t('close')}}
@@ -47,7 +50,7 @@
.form-group .form-group
label label
strong(v-once) {{$t('prize')}} strong(v-once) {{$t('prize')}}
input(type='number', min='1', :max='maxPrize', v-model="workingChallenge.prize") input(type='number', :min='minPrize', :max='maxPrize', v-model="workingChallenge.prize")
.row.footer-wrap .row.footer-wrap
.col-12.text-center.submit-button-wrapper .col-12.text-center.submit-button-wrapper
.alert.alert-warning(v-if='insufficientGemsForTavernChallenge') .alert.alert-warning(v-if='insufficientGemsForTavernChallenge')
@@ -220,6 +223,13 @@ export default {
if (this.challenge) { if (this.challenge) {
Object.assign(this.workingChallenge, this.challenge); Object.assign(this.workingChallenge, this.challenge);
this.workingChallenge.categories = []; this.workingChallenge.categories = [];
if (this.challenge.categories) {
this.challenge.categories.forEach(category => {
this.workingChallenge.categories.push(category.slug);
});
}
this.creating = false; this.creating = false;
} }
}); });
@@ -230,6 +240,7 @@ export default {
this.groups.push({ this.groups.push({
name: party.name, name: party.name,
_id: party._id, _id: party._id,
privacy: 'private',
}); });
} }
@@ -256,7 +267,13 @@ export default {
userBalance = userBalance * 4; userBalance = userBalance * 4;
let groupBalance = 0; let groupBalance = 0;
let group = find(this.groups, { _id: this.workingChallenge.group }); let group;
this.groups.forEach(item => {
if (item._id === this.workingChallenge.group) {
group = item;
return;
}
});
if (group && group.balance && group.leader === this.user._id) { if (group && group.balance && group.leader === this.user._id) {
groupBalance = group.balance * 4; groupBalance = group.balance * 4;
@@ -264,6 +281,18 @@ export default {
return userBalance + groupBalance; return userBalance + groupBalance;
}, },
minPrize () {
let groupFound;
this.groups.forEach(group => {
if (group._id === this.workingChallenge.group) {
groupFound = group;
return;
}
});
if (groupFound && groupFound.privacy === 'private') return 0;
return 1;
},
insufficientGemsForTavernChallenge () { insufficientGemsForTavernChallenge () {
let balance = this.user.balance || 0; let balance = this.user.balance || 0;
let isForTavern = this.workingChallenge.group === TAVERN_ID; let isForTavern = this.workingChallenge.group === TAVERN_ID;
@@ -320,6 +349,17 @@ export default {
} }
}, },
updateChallenge () { updateChallenge () {
let categoryKeys = this.workingChallenge.categories;
let serverCategories = [];
categoryKeys.forEach(key => {
let catName = this.categoriesHashByKey[key];
serverCategories.push({
slug: key,
name: catName,
});
});
this.workingChallenge.categories = serverCategories;
this.$emit('updatedChallenge', { this.$emit('updatedChallenge', {
challenge: this.workingChallenge, challenge: this.workingChallenge,
}); });

View File

@@ -35,7 +35,7 @@
span.action(v-if='msg.uuid === user._id', @click='remove(msg, index)') span.action(v-if='msg.uuid === user._id', @click='remove(msg, index)')
.svg-icon(v-html="icons.delete") .svg-icon(v-html="icons.delete")
| {{$t('delete')}} | {{$t('delete')}}
span.action.float-right span.action.float-right(v-if='likeCount(msg) > 0')
.svg-icon(v-html="icons.liked") .svg-icon(v-html="icons.liked")
| + {{ likeCount(msg) }} | + {{ likeCount(msg) }}
.row(v-if='user._id === msg.uuid') .row(v-if='user._id === msg.uuid')
@@ -58,7 +58,7 @@
span.action(v-if='msg.uuid === user._id', @click='remove(msg, index)') span.action(v-if='msg.uuid === user._id', @click='remove(msg, index)')
.svg-icon(v-html="icons.delete") .svg-icon(v-html="icons.delete")
| {{$t('delete')}} | {{$t('delete')}}
span.action.float-right span.action.float-right(v-if='likeCount(msg) > 0')
.svg-icon(v-html="icons.liked") .svg-icon(v-html="icons.liked")
| + {{ likeCount(msg) }} | + {{ likeCount(msg) }}
.col-2 .col-2
@@ -128,6 +128,7 @@ import axios from 'axios';
import moment from 'moment'; import moment from 'moment';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import { mapState } from 'client/libs/store'; import { mapState } from 'client/libs/store';
import throttle from 'lodash/throttle';
import markdownDirective from 'client/directives/markdown'; import markdownDirective from 'client/directives/markdown';
import Avatar from '../avatar'; import Avatar from '../avatar';
import styleHelper from 'client/mixins/styleHelper'; import styleHelper from 'client/mixins/styleHelper';
@@ -155,6 +156,14 @@ export default {
mounted () { mounted () {
this.loadProfileCache(); this.loadProfileCache();
}, },
created () {
window.addEventListener('scroll', throttle(() => {
this.loadProfileCache(window.scrollY / 1000);
}, 1000));
},
destroyed () {
// window.removeEventListener('scroll', this.handleScroll);
},
data () { data () {
return { return {
icons: Object.freeze({ icons: Object.freeze({
@@ -167,6 +176,8 @@ export default {
copyingMessage: {}, copyingMessage: {},
currentDayDividerDisplay: moment().day(), currentDayDividerDisplay: moment().day(),
cachedProfileData: {}, cachedProfileData: {},
currentProfileLoadedCount: 0,
currentProfileLoadedEnd: 10,
}; };
}, },
filters: { filters: {
@@ -191,14 +202,22 @@ export default {
}, },
}, },
methods: { methods: {
async loadProfileCache () { async loadProfileCache (screenPosition) {
let promises = []; let promises = [];
// @TODO: write an explination
if (screenPosition && Math.floor(screenPosition) + 1 > this.currentProfileLoadedEnd / 10) {
this.currentProfileLoadedEnd = 10 * (Math.floor(screenPosition) + 1);
} else {
return;
}
this.messages.forEach(message => { this.messages.forEach(message => {
let uuid = message.uuid; let uuid = message.uuid;
if (uuid && !this.cachedProfileData[uuid]) { if (uuid && !this.cachedProfileData[uuid]) {
if (uuid === 'system') return; if (uuid === 'system' || this.currentProfileLoadedCount === this.currentProfileLoadedEnd) return;
promises.push(axios.get(`/api/v3/members/${uuid}`)); promises.push(axios.get(`/api/v3/members/${uuid}`));
this.currentProfileLoadedCount += 1;
} }
}); });

View File

@@ -53,8 +53,10 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.slim_shirt_pink.option(@click='set({"preferences.shirt":"pink"})', :class='{active: user.preferences.shirt === "pink"}') .slim_shirt_pink.option(@click='set({"preferences.shirt":"pink"})', :class='{active: user.preferences.shirt === "pink"}')
.slim_shirt_white.option(@click='set({"preferences.shirt":"white"})', :class='{active: user.preferences.shirt === "white"}') .slim_shirt_white.option(@click='set({"preferences.shirt":"white"})', :class='{active: user.preferences.shirt === "white"}')
.slim_shirt_yellow.option(@click='set({"preferences.shirt":"yellow"})', :class='{active: user.preferences.shirt === "yellow"}') .slim_shirt_yellow.option(@click='set({"preferences.shirt":"yellow"})', :class='{active: user.preferences.shirt === "yellow"}')
.col-12.premium-shirts(v-if='editing') .col-12.customize-options
.broad_shirt_convict.option(@click='set({"preferences.shirt":"convict"})', :class='{active: user.preferences.shirt === "convict"}') .option(v-for='option in ["convict", "cross", "fire", "horizon", "ocean", "purple", "rainbow", "redblue", "thunder", "tropical", "zombie"]',
:class='[`broad_shirt_${option}`, {active: user.preferences.shirt === option}]',
@click='set({"preferences.shirt": option})')
.section.customize-section(v-if='activeTopPage === "skin"') .section.customize-section(v-if='activeTopPage === "skin"')
.row.sub-menu.col-6.offset-3.text-center .row.sub-menu.col-6.offset-3.text-center
@@ -141,6 +143,13 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.hair_flower_4.option(@click='set({"preferences.hair.flower":4})', :class='{active: user.preferences.hair.flower === 4}') .hair_flower_4.option(@click='set({"preferences.hair.flower":4})', :class='{active: user.preferences.hair.flower === 4}')
.hair_flower_5.option(@click='set({"preferences.hair.flower":5})', :class='{active: user.preferences.hair.flower === 5}') .hair_flower_5.option(@click='set({"preferences.hair.flower":5})', :class='{active: user.preferences.hair.flower === 5}')
.hair_flower_6.option(@click='set({"preferences.hair.flower":6})', :class='{active: user.preferences.hair.flower === 6}') .hair_flower_6.option(@click='set({"preferences.hair.flower":6})', :class='{active: user.preferences.hair.flower === 6}')
.row(v-if='activeSubPage === "flower"')
.col-12.customize-options
// button.customize-option(ng-repeat='item in ::getGearArray("animal")', class='{{::item.key}}',
ng-class="{locked: user.items.gear.owned[item.key] == undefined, selectableInventory: user.preferences.costume ? user.items.gear.costume.headAccessory == item.key : user.items.gear.equipped.headAccessory == item.key}",
popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter',
popover-placement='right', popover-append-to-body='true',
ng-click='user.items.gear.owned[item.key] ? equip(item.key) : purchase(item.type,item)')
#backgrounds.section.container.customize-section(v-if='activeTopPage === "backgrounds"') #backgrounds.section.container.customize-section(v-if='activeTopPage === "backgrounds"')
.row.sub-menu.col-6.offset-3 .row.sub-menu.col-6.offset-3
@@ -172,6 +181,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
@click.prevent.stop="togglePinned(bg)" @click.prevent.stop="togglePinned(bg)"
) )
span.svg-icon.inline.icon-12.color(v-html="icons.pin") span.svg-icon.inline.icon-12.color(v-html="icons.pin")
.col-12.text-center(v-if='!ownsSet("background", set.items) && set.identifier !== "incentiveBackgrounds"') .col-12.text-center(v-if='!ownsSet("background", set.items) && set.identifier !== "incentiveBackgrounds"')
.gem-amount .gem-amount
.svg-icon.gem(v-html='icons.gem') .svg-icon.gem(v-html='icons.gem')
@@ -215,6 +225,11 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
input.custom-control-input(type="checkbox") input.custom-control-input(type="checkbox")
span.custom-control-indicator span.custom-control-indicator
span.custom-control-description(v-once) {{ $t('creativity') }} span.custom-control-description(v-once) {{ $t('creativity') }}
div
label.custom-control.custom-checkbox
input.custom-control-input(type="checkbox")
span.custom-control-indicator
span.custom-control-description(v-once) {{ $t('self_care') }}
.section.row.justin-message-section(:class='{top: modalPage > 1}') .section.row.justin-message-section(:class='{top: modalPage > 1}')
.col-9 .col-9
@@ -401,6 +416,10 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
border-radius: 2px; border-radius: 2px;
} }
.background:hover {
cursor: pointer;
}
.purchase-single { .purchase-single {
width: 141px; width: 141px;
margin: 0 auto; margin: 0 auto;
@@ -489,7 +508,6 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
} }
} }
.badge-svg { .badge-svg {
left: calc((100% - 18px) / 2); left: calc((100% - 18px) / 2);
cursor: pointer; cursor: pointer;
@@ -559,22 +577,9 @@ export default {
data () { data () {
let backgroundShopSets = getBackgroundShopSets(); let backgroundShopSets = getBackgroundShopSets();
// @TODO: add dates to backgrounds
let backgroundShopSetsByYear = {
2014: [],
2015: [],
2016: [],
2017: [],
};
backgroundShopSets.forEach((set) => {
let year = set.identifier.substr(set.identifier.length - 4);
if (!backgroundShopSetsByYear[year]) return;
backgroundShopSetsByYear[year].push(set);
});
return { return {
backgroundShopSets, backgroundShopSets,
backgroundShopSetsByYear, backgroundUpdate: new Date(),
icons: Object.freeze({ icons: Object.freeze({
logoPurple, logoPurple,
bodyIcon, bodyIcon,
@@ -610,6 +615,32 @@ export default {
startingPage () { startingPage () {
return this.$store.state.avatarEditorOptions.startingPage; return this.$store.state.avatarEditorOptions.startingPage;
}, },
backgroundShopSetsByYear () {
// @TODO: add dates to backgrounds
let backgroundShopSetsByYear = {
2014: [],
2015: [],
2016: [],
2017: [],
};
// 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);
if (!backgroundShopSetsByYear[year]) return;
let setOwnedByUser = false;
for (let key in set.items) {
if (this.user.purchased.background[key]) setOwnedByUser = true;
}
set.userOwns = setOwnedByUser;
backgroundShopSetsByYear[year].push(set);
});
return backgroundShopSetsByYear;
},
}, },
methods: { methods: {
prev () { prev () {
@@ -654,7 +685,6 @@ export default {
for (let key in set) { for (let key in set) {
let value = set[key]; let value = set[key];
if (type === 'background') key = value.key; if (type === 'background') key = value.key;
if (this.user.purchased[type][key]) setOwnedByUser = true; if (this.user.purchased[type][key]) setOwnedByUser = true;
} }
@@ -697,6 +727,7 @@ export default {
path, path,
}, },
}); });
this.backgroundUpdate = new Date();
} catch (e) { } catch (e) {
alert(e.message); alert(e.message);
} }

View File

@@ -172,9 +172,10 @@ export default {
}; };
group.name = this.$t('possessiveParty', {name: this.user.profile.name}); group.name = this.$t('possessiveParty', {name: this.user.profile.name});
let party = await this.$store.dispatch('guilds:create', {group}); let party = await this.$store.dispatch('guilds:create', {group});
this.$store.state.party = party; this.$store.state.party.data = party;
this.user.party._id = party._id; this.user.party._id = party._id;
this.$root.$emit('hide::modal', 'create-party-modal'); this.$root.$emit('hide::modal', 'create-party-modal');
this.$router.push('/party');
// @TODO: Analytics.updateUser({'partyID': $scope.group ._id, 'partySize': 1}); // @TODO: Analytics.updateUser({'partyID': $scope.group ._id, 'partySize': 1});
}, },
}, },

View File

@@ -120,6 +120,7 @@ export default {
this.loading = false; this.loading = false;
}, },
createGroup () { createGroup () {
this.$store.state.editingGroup = {};
this.$root.$emit('show::modal', 'guild-form'); this.$root.$emit('show::modal', 'guild-form');
}, },
}, },

View File

@@ -578,7 +578,8 @@ export default {
this.newMessage = newText; this.newMessage = newText;
}, },
showMemberModal () { showMemberModal () {
this.$store.state.groupId = this.group._id; this.$store.state.memberModalOptions.groupId = this.group._id;
this.$store.state.memberModalOptions.group = this.group;
this.$root.$emit('show::modal', 'members-modal'); this.$root.$emit('show::modal', 'members-modal');
}, },
async sendMessage () { async sendMessage () {
@@ -638,8 +639,7 @@ export default {
} }
}, },
async join () { async join () {
// @TODO: This needs to be in the notifications where users will now accept invites if (this.group.cancelledPlan && !confirm(this.$t('aboutToJoinCancelledGroupPlan'))) {
if (this.group.cancelledPlan && !confirm(window.env.t('aboutToJoinCancelledGroupPlan'))) {
return; return;
} }
await this.$store.dispatch('guilds:join', {guildId: this.group._id, type: 'myGuilds'}); await this.$store.dispatch('guilds:join', {guildId: this.group._id, type: 'myGuilds'});
@@ -685,11 +685,6 @@ export default {
await this.$store.dispatch('guilds:join', {groupId: this.group._id}); await this.$store.dispatch('guilds:join', {groupId: this.group._id});
}, },
// @TODO: Move to notificatin component
async reject () {
await this.$store.dispatch('guilds:rejectInvite', {groupId: this.group._id});
// User.sync();
},
clickStartQuest () { clickStartQuest () {
// Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Start a Quest'}); // Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Start a Quest'});
let hasQuests = find(this.user.items.quests, (quest) => { let hasQuests = find(this.user.items.quests, (quest) => {

View File

@@ -4,11 +4,11 @@
.form-group .form-group
label label
strong(v-once) {{$t('name')}} * strong(v-once) {{$t('name')}} *
b-form-input(type="text", :placeholder="$t('newGuildPlaceholder')", v-model="workingGuild.name") b-form-input(type="text", :placeholder="$t('newGuildPlaceholder')", v-model="workingGroup.name")
.form-group(v-if='workingGuild.id && members.length > 0') .form-group(v-if='workingGroup.id && members.length > 0')
label label
strong(v-once) {{$t('guildOrPartyLeader')}} * strong(v-once) {{$t('guildOrPartyLeader')}} *
select.form-control(v-model="workingGuild.newLeader") select.form-control(v-model="workingGroup.newLeader")
option(v-for='member in members', :value="member._id") {{ member.profile.name }} option(v-for='member in members', :value="member._id") {{ member.profile.name }}
.form-group .form-group
@@ -16,7 +16,7 @@
strong(v-once) {{$t('privacySettings')}} * strong(v-once) {{$t('privacySettings')}} *
br br
label.custom-control.custom-checkbox label.custom-control.custom-checkbox
input.custom-control-input(type="checkbox", v-model="workingGuild.onlyLeaderCreatesChallenges") input.custom-control-input(type="checkbox", v-model="workingGroup.onlyLeaderCreatesChallenges")
span.custom-control-indicator span.custom-control-indicator
span.custom-control-description(v-once) {{ $t('onlyLeaderCreatesChallenges') }} span.custom-control-description(v-once) {{ $t('onlyLeaderCreatesChallenges') }}
b-tooltip.icon(:content="$t('privateDescription')") b-tooltip.icon(:content="$t('privateDescription')")
@@ -24,13 +24,13 @@
// br // br
// @TODO Implement in V2 label.custom-control.custom-checkbox // @TODO Implement in V2 label.custom-control.custom-checkbox
input.custom-control-input(type="checkbox", v-model="workingGuild.guildLeaderCantBeMessaged") input.custom-control-input(type="checkbox", v-model="workingGroup.guildLeaderCantBeMessaged")
span.custom-control-indicator span.custom-control-indicator
span.custom-control-description(v-once) {{ $t('guildLeaderCantBeMessaged') }} span.custom-control-description(v-once) {{ $t('guildLeaderCantBeMessaged') }}
br br
label.custom-control.custom-checkbox(v-if='!isParty') label.custom-control.custom-checkbox(v-if='!isParty')
input.custom-control-input(type="checkbox", v-model="workingGuild.privateGuild") input.custom-control-input(type="checkbox", v-model="workingGroup.privateGuild")
span.custom-control-indicator span.custom-control-indicator
span.custom-control-description(v-once) {{ $t('privateGuild') }} span.custom-control-description(v-once) {{ $t('privateGuild') }}
b-tooltip.icon(:content="$t('privateDescription')") b-tooltip.icon(:content="$t('privateDescription')")
@@ -38,7 +38,7 @@
// br // br
// @TODO: Implement in v2 label.custom-control.custom-checkbox(v-if='!creatingParty') // @TODO: Implement in v2 label.custom-control.custom-checkbox(v-if='!creatingParty')
input.custom-control-input(type="checkbox", v-model="workingGuild.allowGuildInvationsFromNonMembers") input.custom-control-input(type="checkbox", v-model="workingGroup.allowGuildInvationsFromNonMembers")
span.custom-control-indicator span.custom-control-indicator
span.custom-control-description(v-once) {{ $t('allowGuildInvationsFromNonMembers') }} span.custom-control-description(v-once) {{ $t('allowGuildInvationsFromNonMembers') }}
@@ -46,16 +46,16 @@
label label
strong(v-once) {{$t('guildSummary')}} * strong(v-once) {{$t('guildSummary')}} *
div.summary-count {{charactersRemaining}} {{ $t('charactersRemaining') }} div.summary-count {{charactersRemaining}} {{ $t('charactersRemaining') }}
textarea.form-control.summary-textarea(:placeholder="$t('guildSummaryPlaceholder')", v-model="workingGuild.summary") textarea.form-control.summary-textarea(:placeholder="isParty ? $t('partyDescriptionPlaceHolder') : $t('guildSummaryPlaceholder')", v-model="workingGroup.summary")
// @TODO: need summary only for PUBLIC GUILDS, not for tavern, private guilds, or party // @TODO: need summary only for PUBLIC GUILDS, not for tavern, private guilds, or party
.form-group .form-group
label label
strong(v-once) {{$t('groupDescription')}} * strong(v-once) {{$t('groupDescription')}} *
a.float-right {{ $t('markdownFormattingHelp') }} a.float-right {{ $t('markdownFormattingHelp') }}
b-form-input.description-textarea(type="text", textarea, :placeholder="creatingParty ? $t('partyDescriptionPlaceholder') : $t('guildDescriptionPlaceholder')", v-model="workingGuild.description") b-form-input.description-textarea(type="text", textarea, :placeholder="isParty ? $t('partyDescriptionPlaceholder') : $t('guildDescriptionPlaceholder')", v-model="workingGroup.description")
.form-group(v-if='creatingParty && !workingGuild.id') .form-group(v-if='creatingParty && !workingGroup.id')
span span
toggleSwitch(:label="$t('inviteMembersNow')", v-model='inviteMembers') toggleSwitch(:label="$t('inviteMembersNow')", v-model='inviteMembers')
@@ -63,21 +63,21 @@
label label
strong(v-once) {{$t('categories')}} * strong(v-once) {{$t('categories')}} *
div.category-wrap(@click.prevent="toggleCategorySelect") div.category-wrap(@click.prevent="toggleCategorySelect")
span.category-select(v-if='workingGuild.categories.length === 0') {{$t('none')}} span.category-select(v-if='workingGroup.categories.length === 0') {{$t('none')}}
.category-label(v-for='category in workingGuild.categories') {{$t(categoriesHashByKey[category])}} .category-label(v-for='category in workingGroup.categories') {{$t(categoriesHashByKey[category])}}
.category-box(v-if="showCategorySelect") .category-box(v-if="showCategorySelect")
.form-check( .form-check(
v-for="group in categoryOptions", v-for="group in categoryOptions",
:key="group.key", :key="group.key",
) )
label.custom-control.custom-checkbox label.custom-control.custom-checkbox
input.custom-control-input(type="checkbox", :value="group.key", v-model="workingGuild.categories") input.custom-control-input(type="checkbox", :value="group.key", v-model="workingGroup.categories")
span.custom-control-indicator span.custom-control-indicator
span.custom-control-description(v-once) {{ $t(group.label) }} span.custom-control-description(v-once) {{ $t(group.label) }}
button.btn.btn-primary(@click.prevent="toggleCategorySelect") {{$t('close')}} button.btn.btn-primary(@click.prevent="toggleCategorySelect") {{$t('close')}}
// @TODO: need categories only for PUBLIC GUILDS, not for tavern, private guilds, or party // @TODO: need categories only for PUBLIC GUILDS, not for tavern, private guilds, or party
.form-group(v-if='inviteMembers && !workingGuild.id') .form-group(v-if='inviteMembers && !workingGroup.id')
label label
strong(v-once) Invite via Email or User ID strong(v-once) Invite via Email or User ID
p(v-once) {{$t('inviteMembersHowTo')}} * p(v-once) {{$t('inviteMembersHowTo')}} *
@@ -91,12 +91,12 @@
button(@click.prevent='addMemberToInvite()') Add button(@click.prevent='addMemberToInvite()') Add
.form-group.text-center .form-group.text-center
div.item-with-icon(v-if='!this.workingGuild.id') div.item-with-icon(v-if='!this.workingGroup.id')
.svg-icon(v-html="icons.gem") .svg-icon(v-html="icons.gem")
span.count 4 span.count 4
button.btn.btn-primary.btn-md(v-if='!workingGuild.id', :disabled='!workingGuild.name || !workingGuild.description') {{ creatingParty ? $t('createParty') : $t('createGuild') }} button.btn.btn-primary.btn-md(v-if='!workingGroup.id', :disabled='!workingGroup.name || !workingGroup.description') {{ creatingParty ? $t('createParty') : $t('createGuild') }}
button.btn.btn-primary.btn-md(v-if='workingGuild.id', :disabled='!workingGuild.name || !workingGuild.description') {{ isParty ? $t('updateParty') : $t('updateGuild') }} button.btn.btn-primary.btn-md(v-if='workingGroup.id', :disabled='!workingGroup.name || !workingGroup.description') {{ isParty ? $t('updateParty') : $t('updateGuild') }}
.gem-description(v-once, v-if='!this.workingGuild.id') {{ $t('guildGemCostInfo') }} .gem-description(v-once, v-if='!this.workingGroup.id') {{ $t('guildGemCostInfo') }}
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -187,7 +187,7 @@ export default {
}, },
data () { data () {
let data = { let data = {
workingGuild: { workingGroup: {
id: '', id: '',
name: '', name: '',
type: 'guild', type: 'guild',
@@ -286,25 +286,34 @@ export default {
// @TODO: do we need this? Maybe us computed. If we need, then make it on show a specific modal // @TODO: do we need this? Maybe us computed. If we need, then make it on show a specific modal
this.$root.$on('shown::modal', () => { this.$root.$on('shown::modal', () => {
let editingGroup = this.$store.state.editingGroup; let editingGroup = this.$store.state.editingGroup;
if (!editingGroup._id) return; if (!editingGroup._id) {
this.workingGuild.name = editingGroup.name; this.resetWorkingGroup();
this.workingGuild.type = editingGroup.type; return;
this.workingGuild.privacy = editingGroup.privacy; }
if (editingGroup.summary) this.workingGuild.summary = editingGroup.summary;
if (editingGroup.description) this.workingGuild.description = editingGroup.description; this.workingGroup.name = editingGroup.name;
if (editingGroup._id) this.workingGuild.id = editingGroup._id; this.workingGroup.type = editingGroup.type;
if (editingGroup.leader._id) this.workingGuild.newLeader = editingGroup.leader._id;
this.workingGroup.privateGuild = true;
if (editingGroup.privacy === 'public') {
this.workingGroup.privateGuild = false;
}
if (editingGroup.summary) this.workingGroup.summary = editingGroup.summary;
if (editingGroup.description) this.workingGroup.description = editingGroup.description;
if (editingGroup._id) this.workingGroup.id = editingGroup._id;
if (editingGroup.leader._id) this.workingGroup.newLeader = editingGroup.leader._id;
if (editingGroup._id) this.getMembers(); if (editingGroup._id) this.getMembers();
}); });
}, },
computed: { computed: {
charactersRemaining () { charactersRemaining () {
let currentLength = this.workingGuild.summary ? this.workingGuild.summary.length : 0; let currentLength = this.workingGroup.summary ? this.workingGroup.summary.length : 0;
return MAX_SUMMARY_SIZE_FOR_GUILDS - currentLength; return MAX_SUMMARY_SIZE_FOR_GUILDS - currentLength;
}, },
title () { title () {
if (this.creatingParty) return this.$t('createParty'); if (this.creatingParty) return this.$t('createParty');
if (!this.workingGuild.id) return this.$t('createGuild'); if (!this.workingGroup.id) return this.$t('createGuild');
if (this.isParty) return this.$t('updateParty'); if (this.isParty) return this.$t('updateParty');
return this.$t('updateGuild'); return this.$t('updateGuild');
}, },
@@ -312,14 +321,14 @@ export default {
return this.$store.state.groupFormOptions.createParty; return this.$store.state.groupFormOptions.createParty;
}, },
isParty () { isParty () {
return this.workingGuild.type === 'party'; return this.workingGroup.type === 'party';
}, },
}, },
methods: { methods: {
async getMembers () { async getMembers () {
if (!this.workingGuild.id) return; if (!this.workingGroup.id) return;
let members = await this.$store.dispatch('members:getGroupMembers', { let members = await this.$store.dispatch('members:getGroupMembers', {
groupId: this.workingGuild.id, groupId: this.workingGroup.id,
includeAllPublicFields: true, includeAllPublicFields: true,
}); });
this.members = members; this.members = members;
@@ -339,51 +348,51 @@ export default {
this.showCategorySelect = !this.showCategorySelect; this.showCategorySelect = !this.showCategorySelect;
}, },
async submit () { async submit () {
if (this.$store.state.user.data.balance < 1 && !this.workingGuild.id) { if (this.$store.state.user.data.balance < 1 && !this.workingGroup.id) {
// @TODO: Add proper notifications // @TODO: Add proper notifications
alert('Not enough gems'); alert('Not enough gems');
return; return;
// @TODO return $rootScope.openModal('buyGems', {track:"Gems > Create Group"}); // @TODO return $rootScope.openModal('buyGems', {track:"Gems > Create Group"});
} }
if (!this.workingGuild.name || !this.workingGuild.description) { if (!this.workingGroup.name || !this.workingGroup.description) {
// @TODO: Add proper notifications - split this out into two, make errors translatable. Suggestion: `<% fieldName %> is required` for all errors where possible, where `fieldName` is inserted as the translatable string that's used for the field header. // @TODO: Add proper notifications - split this out into two, make errors translatable. Suggestion: `<% fieldName %> is required` for all errors where possible, where `fieldName` is inserted as the translatable string that's used for the field header.
alert('Enter a name and description'); alert('Enter a name and description');
return; return;
} }
if (!this.workingGuild.summary) { if (!this.workingGroup.summary) {
// @TODO: Add proper notifications. Summary is mandatory for only public guilds (not tavern, private guilds, parties) // @TODO: Add proper notifications. Summary is mandatory for only public guilds (not tavern, private guilds, parties)
alert('Enter a summary'); alert('Enter a summary');
return; return;
} }
if (this.workingGuild.summary.length > MAX_SUMMARY_SIZE_FOR_GUILDS) { if (this.workingGroup.summary.length > MAX_SUMMARY_SIZE_FOR_GUILDS) {
// @TODO: Add proper notifications. Summary is mandatory for only public guilds (not tavern, private guilds, parties) // @TODO: Add proper notifications. Summary is mandatory for only public guilds (not tavern, private guilds, parties)
alert('Summary is too long'); alert('Summary is too long');
return; return;
} }
if (!this.workingGuild.categories || this.workingGuild.categories.length === 0) { if (!this.workingGroup.categories || this.workingGroup.categories.length === 0) {
// @TODO: Add proper notifications // @TODO: Add proper notifications
alert('One or more categories must be selected'); alert('One or more categories must be selected');
return; return;
} }
// @TODO: Add proper notifications // @TODO: Add proper notifications
if (!this.workingGuild.id && !confirm(this.$t('confirmGuild'))) return; if (!this.workingGroup.id && !confirm(this.$t('confirmGuild'))) return;
if (!this.workingGuild.privateGuild) { if (!this.workingGroup.privateGuild) {
this.workingGuild.privacy = 'public'; this.workingGroup.privacy = 'public';
} }
if (!this.workingGuild.onlyLeaderCreatesChallenges) { if (!this.workingGroup.onlyLeaderCreatesChallenges) {
this.workingGuild.leaderOnly = { this.workingGroup.leaderOnly = {
challenges: true, challenges: true,
}; };
} }
let categoryKeys = this.workingGuild.categories; let categoryKeys = this.workingGroup.categories;
let serverCategories = []; let serverCategories = [];
categoryKeys.forEach(key => { categoryKeys.forEach(key => {
let catName = this.categoriesHashByKey[key]; let catName = this.categoriesHashByKey[key];
@@ -392,22 +401,22 @@ export default {
name: catName, name: catName,
}); });
}); });
this.workingGuild.categories = serverCategories; this.workingGroup.categories = serverCategories;
let newgroup; let newgroup;
if (this.workingGuild.id) { if (this.workingGroup.id) {
await this.$store.dispatch('guilds:update', {group: this.workingGuild}); await this.$store.dispatch('guilds:update', {group: this.workingGroup});
this.$root.$emit('updatedGroup', this.workingGuild); this.$root.$emit('updatedGroup', this.workingGroup);
// @TODO: this doesn't work because of the async resource // @TODO: this doesn't work because of the async resource
// if (updatedGroup.type === 'party') this.$store.state.party = {data: updatedGroup}; // if (updatedGroup.type === 'party') this.$store.state.party = {data: updatedGroup};
} else { } else {
newgroup = await this.$store.dispatch('guilds:create', {group: this.workingGuild}); newgroup = await this.$store.dispatch('guilds:create', {group: this.workingGroup});
this.$store.state.user.data.balance -= 1; this.$store.state.user.data.balance -= 1;
} }
this.$store.state.editingGroup = {}; this.$store.state.editingGroup = {};
this.workingGuild = { this.workingGroup = {
name: '', name: '',
type: 'guild', type: 'guild',
privacy: 'private', privacy: 'private',
@@ -424,6 +433,19 @@ export default {
} }
this.$root.$emit('hide::modal', 'guild-form'); this.$root.$emit('hide::modal', 'guild-form');
}, },
resetWorkingGroup () {
this.workingGroup = {
name: '',
type: 'guild',
privacy: 'private',
description: '',
categories: [],
onlyLeaderCreatesChallenges: true,
guildLeaderCantBeMessaged: true,
privateGuild: true,
allowGuildInvationsFromNonMembers: true,
};
},
}, },
}; };
</script> </script>

View File

@@ -19,24 +19,24 @@ div
.col-8.offset-1 .col-8.offset-1
member-details(:member='member') member-details(:member='member')
.col-3.actions .col-3.actions
b-dropdown(:text="$t('sort')", right=true) b-dropdown(text="...", right=true)
b-dropdown-item(@click='sort(option.value)') b-dropdown-item(@click='sort(option.value)', v-if='isLeader')
span.dropdown-icon-item span.dropdown-icon-item
.svg-icon.inline(v-html="icons.removeIcon") .svg-icon.inline(v-html="icons.removeIcon", v-if='isLeader')
span.text {{$t('removeMember')}} span.text {{$t('removeMember')}}
b-dropdown-item(@click='sort(option.value)') b-dropdown-item(@click='sort(option.value)')
span.dropdown-icon-item span.dropdown-icon-item
.svg-icon.inline(v-html="icons.messageIcon") .svg-icon.inline(v-html="icons.messageIcon")
span.text {{$t('sendMessage')}} span.text {{$t('sendMessage')}}
b-dropdown-item(@click='sort(option.value)') b-dropdown-item(@click='sort(option.value)', v-if='isLeader')
span.dropdown-icon-item span.dropdown-icon-item
.svg-icon.inline(v-html="icons.starIcon") .svg-icon.inline(v-html="icons.starIcon")
span.text {{$t('promoteToLeader')}} span.text {{$t('promoteToLeader')}}
b-dropdown-item(@click='sort(option.value)') b-dropdown-item(@click='sort(option.value)', v-if='isLeader && groupIsSubscribed')
span.dropdown-icon-item span.dropdown-icon-item
.svg-icon.inline(v-html="icons.starIcon") .svg-icon.inline(v-html="icons.starIcon")
span.text {{$t('addManager')}} span.text {{$t('addManager')}}
b-dropdown-item(@click='sort(option.value)') b-dropdown-item(@click='sort(option.value)', v-if='isLeader && groupIsSubscribed')
span.dropdown-icon-item span.dropdown-icon-item
.svg-icon.inline(v-html="icons.removeIcon") .svg-icon.inline(v-html="icons.removeIcon")
span.text {{$t('removeManager2')}} span.text {{$t('removeManager2')}}
@@ -109,6 +109,7 @@ import sortBy from 'lodash/sortBy';
import bModal from 'bootstrap-vue/lib/components/modal'; import bModal from 'bootstrap-vue/lib/components/modal';
import bDropdown from 'bootstrap-vue/lib/components/dropdown'; import bDropdown from 'bootstrap-vue/lib/components/dropdown';
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item'; import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
import { mapState } from 'client/libs/store';
import MemberDetails from '../memberDetails'; import MemberDetails from '../memberDetails';
import removeIcon from 'assets/members/remove.svg'; import removeIcon from 'assets/members/remove.svg';
@@ -116,7 +117,7 @@ import messageIcon from 'assets/members/message.svg';
import starIcon from 'assets/members/star.svg'; import starIcon from 'assets/members/star.svg';
export default { export default {
props: ['group', 'hideBadge'], props: ['hideBadge'],
components: { components: {
bModal, bModal,
bDropdown, bDropdown,
@@ -160,6 +161,16 @@ export default {
}; };
}, },
computed: { computed: {
...mapState({user: 'user.data'}),
isLeader () {
return this.user._id === this.group.leader || this.user._id === this.group.leader._id;
},
groupIsSubscribed () {
return this.group.purchased.active;
},
group () {
return this.$store.state.memberModalOptions.group;
},
sortedMembers () { sortedMembers () {
let sortedMembers = this.members; let sortedMembers = this.members;
if (!this.sortOption) return sortedMembers; if (!this.sortOption) return sortedMembers;
@@ -182,7 +193,7 @@ export default {
}, },
methods: { methods: {
async getMembers () { async getMembers () {
let groupId = this.$store.state.groupId || this.group._id; let groupId = this.$store.state.memberModalOptions.groupId || this.group._id;
if (groupId && groupId !== 'challenge') { if (groupId && groupId !== 'challenge') {
let members = await this.$store.dispatch('members:getGroupMembers', { let members = await this.$store.dispatch('members:getGroupMembers', {
groupId, groupId,
@@ -191,7 +202,7 @@ export default {
this.members = members; this.members = members;
} }
if (this.$store.state.viewingMembers.length > 0) { if (this.$store.state.memberModalOptions.viewingMembers.length > 0) {
this.members = this.$store.state.viewingMembers; this.members = this.$store.state.viewingMembers;
} }
}, },

View File

@@ -136,6 +136,7 @@ export default {
this.loading = false; this.loading = false;
}, },
createGroup () { createGroup () {
this.$store.state.editingGroup = {};
this.$root.$emit('show::modal', 'guild-form'); this.$root.$emit('show::modal', 'guild-form');
}, },
}, },

View File

@@ -26,6 +26,7 @@
.col-md-4.sidebar .col-md-4.sidebar
.section .section
.grassy-meadow-backdrop .grassy-meadow-backdrop
.daniel_front
.sleep.below-header-sections .sleep.below-header-sections
strong(v-once) {{ $t('sleepDescription') }} strong(v-once) {{ $t('sleepDescription') }}
@@ -196,12 +197,21 @@
} }
.grassy-meadow-backdrop { .grassy-meadow-backdrop {
background-image: url('~assets/images/tavern_backdrop_web.png'); background-image: url('~assets/images/tavern_backdrop_web_backgroundtile.png');
background-size: cover; background-repeat: repeat-x;
width: 100%; width: 100%;
height: 246px; height: 246px;
} }
.daniel_front {
background-image: url('~assets/images/tavern_backdrop_web_daniel_and_props.png');
width: 100%;
height: 246px;
position: absolute;
top: 0;
margin: 0 auto;
}
.sleep { .sleep {
margin-top: 1em; margin-top: 1em;
} }

View File

@@ -1,6 +1,6 @@
<template lang="pug"> <template lang="pug">
.item-with-icon.item-notifications.dropdown div.item-with-icon.item-notifications.dropdown
.svg-icon(v-html="icons.notifications") .svg-icon.notifications(v-html="icons.notifications")
// span.glyphicon(:class='iconClasses()') // span.glyphicon(:class='iconClasses()')
// span.notification-counter(v-if='getNotificationsCount()') {{getNotificationsCount()}} // span.notification-counter(v-if='getNotificationsCount()') {{getNotificationsCount()}}
.dropdown-menu.dropdown-menu-right.user-dropdown .dropdown-menu.dropdown-menu-right.user-dropdown
@@ -9,24 +9,32 @@
a.dropdown-item(v-if='user.purchased.plan.mysteryItems.length', @click='go("/inventory/items")') a.dropdown-item(v-if='user.purchased.plan.mysteryItems.length', @click='go("/inventory/items")')
span.glyphicon.glyphicon-gift span.glyphicon.glyphicon-gift
span {{ $t('newSubscriberItem') }} span {{ $t('newSubscriberItem') }}
a.dropdown-item(v-for='party in user.invitations.parties', @click='go("/party")') a.dropdown-item(v-for='party in user.invitations.parties')
span.glyphicon.glyphicon-user div
span {{ $t('invitedTo', {name: party.name}) }} span.glyphicon.glyphicon-user
span {{ $t('invitedTo', {name: party.name}) }}
div
span(@click='accept(party)') Accept
span(@click='reject(party)') Reject
a.dropdown-item(v-if='user.flags.cardReceived', @click='go("/inventory/items")') a.dropdown-item(v-if='user.flags.cardReceived', @click='go("/inventory/items")')
span.glyphicon.glyphicon-envelope span.glyphicon.glyphicon-envelope
span {{ $t('cardReceived') }} span {{ $t('cardReceived') }}
a.dropdown-item(@click='clearCards()', :popover="$t('clear')", a.dropdown-item(@click='clearCards()', :popover="$t('clear')",
popover-placement='right', popover-trigger='mouseenter', popover-append-to-body='true') popover-placement='right', popover-trigger='mouseenter', popover-append-to-body='true')
a.dropdown-item(v-for='guild in user.invitations.guilds', @click='go("/groups/discovery")') a.dropdown-item(v-for='guild in user.invitations.guilds')
span.glyphicon.glyphicon-user div
span {{ $t('invitedTo', {name: guild.name}) }} span.glyphicon.glyphicon-user
span {{ $t('invitedTo', {name: guild.name}) }}
div
span(@click='accept(guild)') Accept
span(@click='reject(guild)') Reject
a.dropdown-item(v-if='user.flags.classSelected && !user.preferences.disableClasses && user.stats.points', a.dropdown-item(v-if='user.flags.classSelected && !user.preferences.disableClasses && user.stats.points',
@click='go("/user/profile")') @click='go("/user/profile")')
span.glyphicon.glyphicon-plus-sign span.glyphicon.glyphicon-plus-sign
span {{ $t('haveUnallocated', {points: user.stats.points}) }} span {{ $t('haveUnallocated', {points: user.stats.points}) }}
a.dropdown-item(v-for='(k,v) in user.newMessages', v-if='v.value', @click='navigateToGroup(k)') a.dropdown-item(v-for='(message, key) in user.newMessages', v-if='message.value', @click='navigateToGroup(key)')
span.glyphicon.glyphicon-comment span.glyphicon.glyphicon-comment
span {{v.name}} span {{message.name}}
a.dropdown-item(@click='clearMessages(k)', :popover="$t('clear')", popover-placement='right', popover-trigger='mouseenter',popover-append-to-body='true') a.dropdown-item(@click='clearMessages(k)', :popover="$t('clear')", popover-placement='right', popover-trigger='mouseenter',popover-append-to-body='true')
a.dropdown-item(v-for='(notification, index) in user.groupNotifications', @click='viewGroupApprovalNotification(notification, index, true)') a.dropdown-item(v-for='(notification, index) in user.groupNotifications', @click='viewGroupApprovalNotification(notification, index, true)')
span(:class="groupApprovalNotificationIcon(notification)") span(:class="groupApprovalNotificationIcon(notification)")
@@ -42,14 +50,25 @@
<style lang='scss' scoped> <style lang='scss' scoped>
@import '~client/assets/scss/colors.scss'; @import '~client/assets/scss/colors.scss';
.svg-icon {
width: 25px; .item-notifications {
width: 44px;
} }
.item-notifications:hover { .item-notifications:hover {
cursor: pointer; cursor: pointer;
} }
.notifications {
vertical-align: bottom;
display: inline-block;
width: 20px;
height: 20px;
margin-right: 8px;
margin-left: 8px;
margin-top: .2em;
}
/* @TODO: Move to shared css */ /* @TODO: Move to shared css */
.dropdown:hover .dropdown-menu { .dropdown:hover .dropdown-menu {
display: block; display: block;
@@ -231,7 +250,7 @@ export default {
// @TODO: USe notifications: User.readNotification(notification.id); // @TODO: USe notifications: User.readNotification(notification.id);
this.user.groupNotifications.splice(index, 1); this.user.groupNotifications.splice(index, 1);
return navigate; // @TODO: remove return navigate; // @TODO: remove
// @TODO: this.$route.go if (navigate) go('options.social.guilds.detail', {gid: notification.data.groupId}); // @TODO: this.$router.go if (navigate) go('options.social.guilds.detail', {gid: notification.data.groupId});
}, },
groupApprovalNotificationIcon (notification) { groupApprovalNotificationIcon (notification) {
if (notification.type === 'GROUP_TASK_APPROVAL') { if (notification.type === 'GROUP_TASK_APPROVAL') {
@@ -241,7 +260,7 @@ export default {
} }
}, },
go (path) { go (path) {
this.$route.push(path); this.$router.push(path);
}, },
navigateToGroup (key) { navigateToGroup (key) {
if (key === this.party._id || key === this.user.party._id) { if (key === this.party._id || key === this.user.party._id) {
@@ -250,6 +269,18 @@ export default {
} }
this.go(`/groups/guild/${key}`); this.go(`/groups/guild/${key}`);
}, },
async reject (group) {
await this.$store.dispatch('guilds:rejectInvite', {groupId: group.id});
// @TODO: User.sync();
},
async accept (group) {
if (group.cancelledPlan && !confirm(this.$t('aboutToJoinCancelledGroupPlan'))) {
return;
}
// @TODO: check for party , type: 'myGuilds'
await this.$store.dispatch('guilds:join', {guildId: group.id});
// this.user.guilds.push(this.group._id);
},
}, },
}; };
</script> </script>

View File

@@ -1,112 +1,111 @@
<template lang="pug"> <template lang="pug">
b-modal#buy-gems(title="Amazon", :hide-footer="true", size='lg') b-modal#buy-gems(title="Buy Gems", :hide-footer="true", size='lg')
.modal-body .modal-body
.buy-gems div(v-if='userReachedGemCap')
// @TODO: +gemButton(true) h2 {{ $t('buyGemsGold') }}
p {{ $t('maxBuyGems') }}
div(ng-if='user.purchased.plan.customerId && (user.purchased.plan.gemsBought >= User.user.purchased.plan.consecutive.gemCapExtra + Shared.planGemLimits.convCap)') .row(v-if='!userReachedGemCap')
.panel.panel-default .col-12
.panel-body h2 {{ $t('buyGemsGold') }}
h3 {{ $t('buyGemsGold') }} p {{ $t('subGemPop') }}
p {{ $t('maxBuyGems') }}
div(ng-if='user.purchased.plan.customerId && (user.purchased.plan.gemsBought < User.user.purchased.plan.consecutive.gemCapExtra + Shared.planGemLimits.convCap)') .col-4
.panel.panel-default button.btn.btn-primary(@click='purchase({ params: {type: "gems", key: "gem"} })')
.panel-body | Buy Gems for 20 Gold each
h3 {{ $t('buyGemsGold') }} span.Pet_Currency_Gem.inline-gems
p {{ $t('subGemPop') }} .badge.badge-success.stack-count {{planGemLimits.convCap + user.purchased.plan.consecutive.gemCapExtra - user.purchased.plan.gemsBought}}
.container-fluid .col-8
.row p {{ $t('buyGemsAllow1') }}
.col-md-3 | &nbsp; {{planGemLimits.convCap + user.purchased.plan.consecutive.gemCapExtra - user.purchased.plan.gemsBought}}&nbsp;
button.customize-option(ng-click='User.purchase({params:{type:"gems",key:"gem"}})') | {{ $t('buyGemsAllow2') }}
span.Pet_Currency_Gem.inline-gems .col-12
// @TODO: .badge.badge-success.stack-count {{Shared.planGemLimits.convCap + User.user.purchased.plan.consecutive.gemCapExtra - User.user.purchased.plan.gemsBought}} p(v-html="$t('seeSubscriptionDetails')")
.row(v-if='user.purchased.plan.customerId')
.col-12
h2 {{ $t('purchaseGemsSeparately') }}
.col-12.alert.alert-info
| $5 {{ $t('USD') }} = +20
.col-12
h3 {{ $t('paymentMethods') }}
button.purchase.btn.btn-primary(ng-click='Payments.showStripe({})') {{ $t('card') }}
a.purchase(href='/paypal/checkout?_id=${user._id}&apiToken=${User.settings.auth.apiToken}')
img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png',alt='Pay now with Paypal')
a.purchase(ng-click="Payments.amazonPayments.init({type: 'single'})")
img(src='https://payments.amazon.com/gp/cba/button', alt='Pay now with Amazon Payments')
.row(v-if='!user.purchased.plan.customerId')
.col-12
h2 {{ $t('purchaseGems') }}
.small
span.dashed-underline(popover="$t('donateText3')", popover-trigger='mouseenter', popover-placement='bottom')
| $5 {{ $t('USD') }}
span#TotalGemPrice.dashed-underline(popover="$t('donateText1')",
popover-trigger='mouseenter', ement='bottom')
| +20
span(class="Pet_Currency_Gem1x inline-gems")
.container-fluid
p p
| 20&nbsp; small.muted {{ $t('paymentMethods') }}
span.shop_gold a.purchase.btn.btn-primary(ng-click='Payments.showStripe({})') {{ $t('card') }}
.col-md-8 a.purchase(href='/paypal/checkout?_id=${user._id}&apiToken=${User.settings.auth.apiToken}')
.popover.right.gem-count-popover img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png',
.arrow alt='Pay now with Paypal')
.popover-content a.purchase(ng-click="Payments.amazonPayments.init({type: 'single'})")
p {{ $t('buyGemsAllow1') }} img(src='https://payments.amazon.com/gp/cba/button', alt='Pay now with Amazon Payments')
// @TOOD: | &nbsp;{{Shared.planGemLimits.convCap + User.user.purchased.plan.consecutive.gemCapExtra - User.user.purchased.plan.gemsBought}}&nbsp;
| {{ $t('buyGemsAllow2') }} .container-fluid
p {{ $t('seeSubscriptionDetails') }} h2 {{ $t('freeGemsTitle') }}
div(ng-if='user.purchased.plan.customerId') p {{ $t('subFreeGemsHow') }}
.well .well
h3 {{ $t('purchaseGemsSeparately') }} h3
.container-fluid .small {{ $t('buyGemsGoldTitle') }}
.row h3 {{ $t('becomeSubscriber') }}
.col-md-4.col-md-offset-4.alert.alert-info $5&nbsp;
| {{ $t('USD') }}
span#TotalGemPrice.dashed-underline(:popover="$t('donateText1')",
popover-trigger='mouseenter',popover-placement='bottom')
| +20
span(class="Pet_Currency_Gem1x inline-gems")
.container-fluid
.row
.col-md-10.col-md-offset-2
p
small.muted {{ $t('paymentMethods') }}
a.purchase.btn.btn-primary(ng-click='Payments.showStripe({})') {{ $t('card') }}
a.purchase(href='/paypal/checkout?_id=${user._id}&apiToken=${User.settings.auth.apiToken}')
img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png',alt='Pay now with Paypal')
a.purchase(ng-click="Payments.amazonPayments.init({type: 'single'})")
img(src='https://payments.amazon.com/gp/cba/button', alt='Pay now with Amazon Payments')
div(ng-if='!user.purchased.plan.customerId')
.panel.panel-default
.panel-body
h3 {{ $t('purchaseGems') }}
.small
span.dashed-underline(popover="$t('donateText3')", popover-trigger='mouseenter', popover-placement='bottom')
| {{ $t('donateText2') }}
.container-fluid
.row
.col-md-4.col-md-offset-4.alert.alert-info $5&nbsp;
| {{ $t('USD') }}
span#TotalGemPrice.dashed-underline(popover="$t('donateText1')",
popover-trigger='mouseenter', ement='bottom')
| +20
span(class="Pet_Currency_Gem1x inline-gems")
.container-fluid
.row
.col-md-10.col-md-offset-2
p
small.muted {{ $t('paymentMethods') }}
a.purchase.btn.btn-primary(ng-click='Payments.showStripe({})') {{ $t('card') }}
a.purchase(href='/paypal/checkout?_id=${user._id}&apiToken=${User.settings.auth.apiToken}')
img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png',
alt='Pay now with Paypal')
a.purchase(ng-click="Payments.amazonPayments.init({type: 'single'})")
img(src='https://payments.amazon.com/gp/cba/button', alt='Pay now with Amazon Payments')
.container-fluid div(ng-include="'partials/options.settings.subscription.html'", ng-controller='SettingsCtrl')
h3 {{ $t('freeGemsTitle') }}
p {{ $t('subFreeGemsHow') }}
.well .row(v-if='user.purchased.plan.customerId')
h3 .col-12
.small {{ $t('buyGemsGoldTitle') }} p(v-html="$t('seeSubscriptionDetails')")
h3 {{ $t('becomeSubscriber') }} .modal-footer
.col-12.text-center
div(ng-include="'partials/options.settings.subscription.html'", ng-controller='SettingsCtrl') button.btn.btn-secondary(@click='close()') {{ $t('close') }}
div(ng-if='user.purchased.plan.customerId').pull-left
p {{ $t('seeSubscriptionDetails') }}
.text-right
button.btn.btn-default(ng-click='$close()') {{ $t('close') }}
</template> </template>
<script> <script>
import bModal from 'bootstrap-vue/lib/components/modal'; import bModal from 'bootstrap-vue/lib/components/modal';
import { mapState } from 'client/libs/store';
import planGemLimits from '../../../common/script/libs/planGemLimits';
import purchase from '../../../common/script/ops/purchase';
export default { export default {
components: { components: {
bModal, bModal,
}, },
data () {
return {
planGemLimits,
};
},
computed: {
...mapState({user: 'user.data'}),
userReachedGemCap () {
return this.user.purchased.plan.customerId && this.user.purchased.plan.gemsBought >= this.user.purchased.plan.consecutive.gemCapExtra + this.planGemLimits.convCap;
},
},
methods: { methods: {
close () { close () {
this.$root.$emit('hide::modal', 'buy-gems'); this.$root.$emit('hide::modal', 'buy-gems');
}, },
purchase (params) {
try {
purchase(this.user, params);
} catch (e) {
alert(e.message);
}
},
}, },
}; };
</script> </script>

View File

@@ -72,8 +72,8 @@ export default {
}; };
}, },
mounted () { mounted () {
this.restoreValues.stats = this.user.stats; Object.assign(this.restoreValues.stats, this.user.stats);
this.restoreValues.achievements.streak = this.user.achievements.streak; Object.assign(this.restoreValues.achievements.streak, this.user.achievements.streak);
}, },
computed: { computed: {
...mapState({user: 'user.data'}), ...mapState({user: 'user.data'}),

View File

@@ -7,46 +7,25 @@
nav.navbar.navbar-toggleable-md.navbar-light.bg-faded nav.navbar.navbar-toggleable-md.navbar-light.bg-faded
button.navbar-toggler.navbar-toggler-right(type='button', data-toggle='collapse', data-target='#navbarNav', aria-controls='navbarNav', aria-expanded='false', aria-label='Toggle navigation') button.navbar-toggler.navbar-toggler-right(type='button', data-toggle='collapse', data-target='#navbarNav', aria-controls='navbarNav', aria-expanded='false', aria-label='Toggle navigation')
span.navbar-toggler-icon span.navbar-toggler-icon
a.navbar-brand(href='#') Navbar a.navbar-brand(href='#')
.logo.svg-icon(v-html="icons.logo")
#navbarNav.collapse.navbar-collapse #navbarNav.collapse.navbar-collapse
ul.navbar-nav ul.navbar-nav.float-right
li.nav-item.active
a.nav-link(href='#')
| Home
span.sr-only (current)
li.nav-item li.nav-item
a.nav-link(href='#') Features router-link.nav-link(to="/static/features") How it Works
li.nav-item li.nav-item
a.nav-link(href='#') Pricing router-link.nav-link(to="/static/plans") Group Plans
li.nav-item li.nav-item
a.nav-link.disabled(href='#') Disabled a.nav-link(href="https://habitica.wordpress.com/") Blog
li.nav-item
a.nav-link(href="http://blog.habitrpg.com/") Tumblr
li.nav-item
router-link.nav-link(to="/static/press-kit") Press Kit
li.nav-item
router-link.nav-link(to="/static/contact") Contact
li.nav-item
button#play-btn(class="btn btn-primary btn-lg gamifybutton") Enter Habitica
//-
//- nav.navbar.navbar-light.bg-faded
//- .navbar-header
//- button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='#bs-example-navbar-collapse-1')
//- span.sr-only Toggle navigation
//- span.icon-bar
//- span.icon-bar
//- span.icon-bar
//- a.navbar-brand(href='#')
//- img.img-rendering-auto(src='https://d2afqr2xdmyzvu.cloudfront.net/assets/habitica_lockup2_desat.png', alt="$t('altAttrNavLogo')", width='156px')
//- #bs-example-navbar-collapse-1.collapse.navbar-collapse
//- ul.nav.navbar-nav.navbar-right
//- li
//- a(href='/static/features') {{ $t('companyAbout') }}
//- li
//- a(href='/static/plans') {{ $t('groupPlans') }}
//- li
//- a(href='https://habitica.wordpress.com/') {{ $t('companyBlog') }}
//- li
//- a(href='http://blog.habitrpg.com/') {{ $t('tumblr') }}
//- li
//- a(href='/static/press-kit') {{ $t('presskit') }}
//- li
//- a(href='/static/contact') {{ $t('contactUs') }}
//- li
//- button#header-play-button.btn.btn-primary.navbar-btn.navbar-right(@click='playButtonClick()') {{ $t('playButtonFull') }}
#intro.container-fluid #intro.container-fluid
.row .row
h1.col-12.text-center {{ $t('motivate1') }} h1.col-12.text-center {{ $t('motivate1') }}
@@ -556,6 +535,12 @@
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.logo {
width: 128px;
height: 28px;
color: purple;
}
#intro { #intro {
background: #fff; background: #fff;
padding-top: 1em; padding-top: 1em;
@@ -634,9 +619,14 @@
</style> </style>
<script> <script>
import logo from 'assets/svg/logo.svg';
export default { export default {
data () { data () {
return { return {
icons: Object.freeze({
logo,
}),
userCount: 1000000, userCount: 1000000,
}; };
}, },

View File

@@ -49,6 +49,10 @@ div(v-if='user.stats.lvl > 10')
white-space: initial; white-space: initial;
} }
.spell:hover {
cursor: pointer;
}
.spell { .spell {
background: #ffffff; background: #ffffff;
margin-bottom: 1em; margin-bottom: 1em;
@@ -244,16 +248,16 @@ export default {
return; return;
} }
// @TODO: do we need to fetcht the party everytime? We should probably just check store let party = this.$store.state.party.members;
let party = await this.$store.dispatch('guilds:getGroup', {groupId: 'party'});
party = isArray(party) ? party : []; party = isArray(party) ? party : [];
party = party.concat(this.user); party = party.concat(this.user);
this.$store.state.party.data = party;
this.castEnd(party, 'party'); this.castEnd(party, 'party');
} else if (spell.target === 'tasks') { } else if (spell.target === 'tasks') {
let tasks = this.$store.state.tasks.habits.concat(this.user.dailys) let userTasks = this.$store.state.tasks.data;
.concat(this.$store.state.tasks.rewards) let tasks = userTasks.habits
.concat(this.$store.state.tasks.todos); .concat(userTasks.dailys)
.concat(userTasks.rewards)
.concat(userTasks.todos);
// exclude challenge tasks // exclude challenge tasks
tasks = tasks.filter((task) => { tasks = tasks.filter((task) => {
if (!task.challenge) return true; if (!task.challenge) return true;

View File

@@ -49,7 +49,7 @@
// Habits right side control // Habits right side control
.right-control.d-flex.align-items-center.justify-content-center(v-if="task.type === 'habit'", :class="controlClass.down") .right-control.d-flex.align-items-center.justify-content-center(v-if="task.type === 'habit'", :class="controlClass.down")
.task-control.habit-control(:class="controlClass.down + '-control-habit'", @click="isUser ? score('down') : null") .task-control.habit-control(:class="controlClass.down + '-control-habit'", @click="(isUser && controlClass.down !== 'task-habit-disabled') ? score('down') : null")
.svg-icon.negative(v-html="icons.negative") .svg-icon.negative(v-html="icons.negative")
// Rewards right side control // Rewards right side control
.right-control.d-flex.align-items-center.justify-content-center.reward-control(v-if="task.type === 'reward'", :class="controlClass", @click="isUser ? score('down') : null") .right-control.d-flex.align-items-center.justify-content-center.reward-control(v-if="task.type === 'reward'", :class="controlClass", @click="isUser ? score('down') : null")
@@ -62,7 +62,7 @@
@import '~client/assets/scss/colors.scss'; @import '~client/assets/scss/colors.scss';
.task { .task {
margin-bottom: 8px; margin-bottom: 2px;
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12); box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
background: transparent; background: transparent;
border-radius: 2px; border-radius: 2px;
@@ -78,6 +78,10 @@
} }
} }
.task-habit-disabled-control-habit:hover {
cursor: initial;
}
.task-title { .task-title {
padding-bottom: 8px; padding-bottom: 8px;
color: $gray-10; color: $gray-10;
@@ -247,6 +251,7 @@
.small-text { .small-text {
margin-top: 4px; margin-top: 4px;
color: $yellow-10; color: $yellow-10;
font-style: initial;
} }
} }
</style> </style>
@@ -276,6 +281,11 @@
color: $gray-300; color: $gray-300;
white-space: nowrap; white-space: nowrap;
} }
.task-reward {
// @TODO: I'm unsure of where this is defined. Can't find it in search. So, I am using important for now
background-color: rgba(255, 217, 160, 0.28) !important;
}
</style> </style>
<script> <script>

View File

@@ -11,10 +11,14 @@
slot="modal-header", slot="modal-header",
:class="[cssClass]", :class="[cssClass]",
) )
h1 {{ title }} .row
h1.col-8 {{ title }}
.col-4
span.cancel-task-btn(v-once, @click="cancel()") {{ $t('cancel') }}
button.btn.btn-secondary(type="submit", v-once) {{ $t('save') }}
.form-group .form-group
label(v-once) {{ `${$t('title')}*` }} label(v-once) {{ `${$t('title')}*` }}
input.form-control(type='text', :class="[`${cssClass}-modal-input`]", required, v-model="task.text") input.form-control.title-input(type='text', :class="[`${cssClass}-modal-input`]", required, v-model="task.text", autofocus)
.form-group .form-group
label(v-once) {{ $t('notes') }} label(v-once) {{ $t('notes') }}
textarea.form-control(:class="[`${cssClass}-modal-input`]", v-model="task.notes", rows="3") textarea.form-control(:class="[`${cssClass}-modal-input`]", v-model="task.notes", rows="3")
@@ -22,6 +26,7 @@
.option(v-if="task.type === 'reward'") .option(v-if="task.type === 'reward'")
label(v-once) {{ $t('cost') }} label(v-once) {{ $t('cost') }}
input(type="number", v-model="task.value", required, min="0") input(type="number", v-model="task.value", required, min="0")
.svg-icon.gold(v-html="icons.gold")
.option(v-if="['daily', 'todo'].indexOf(task.type) > -1") .option(v-if="['daily', 'todo'].indexOf(task.type) > -1")
label(v-once) {{ $t('checklist') }} label(v-once) {{ $t('checklist') }}
br br
@@ -69,14 +74,16 @@
label(v-once) {{ $t('startDate') }} label(v-once) {{ $t('startDate') }}
datepicker(v-model="task.startDate") datepicker(v-model="task.startDate")
.option(v-if="task.type === 'daily'") .option(v-if="task.type === 'daily'")
label(v-once) {{ $t('repeats') }} .form-group
b-dropdown(:text="$t(task.frequency)") label(v-once) {{ $t('repeats') }}
b-dropdown-item(v-for="frequency in ['daily', 'weekly', 'monthly', 'yearly']", :key="frequency", @click="task.frequency = frequency", :class="{active: task.frequency === frequency}") b-dropdown(:text="$t(task.frequency)")
| {{ $t(frequency) }} b-dropdown-item(v-for="frequency in ['daily', 'weekly', 'monthly', 'yearly']", :key="frequency", @click="task.frequency = frequency", :class="{active: task.frequency === frequency}")
label(v-once) {{ $t('repeatEvery') }} | {{ $t(frequency) }}
input.form-control(type="number", v-model="task.everyX", min="0", required) .form-group
| {{ repeatSuffix }} label(v-once) {{ $t('repeatEvery') }}
br input(type="number", v-model="task.everyX", min="0", required)
| {{ repeatSuffix }}
br
template(v-if="task.frequency === 'weekly'") template(v-if="task.frequency === 'weekly'")
.form-check-inline.weekday-check( .form-check-inline.weekday-check(
v-for="(day, dayNumber) in ['su','m','t','w','th','f','s']", v-for="(day, dayNumber) in ['su','m','t','w','th','f','s']",
@@ -141,7 +148,6 @@
@change="updateRequiresApproval") @change="updateRequiresApproval")
.task-modal-footer(slot="modal-footer") .task-modal-footer(slot="modal-footer")
button.btn.btn-primary(type="submit", v-once) {{ $t('save') }}
span.cancel-task-btn(v-once, v-if="purpose === 'create'", @click="cancel()") {{ $t('cancel') }} span.cancel-task-btn(v-once, v-if="purpose === 'create'", @click="cancel()") {{ $t('cancel') }}
span.delete-task-btn(v-once, v-else, @click="destroy()") {{ $t('delete') }} span.delete-task-btn(v-once, v-else, @click="destroy()") {{ $t('delete') }}
</template> </template>
@@ -311,6 +317,10 @@
} }
} }
.cancel-task-btn {
margin-right: .5em;
}
.task-modal-footer { .task-modal-footer {
margin: 0 auto; margin: 0 auto;
padding-bottom: 24px; padding-bottom: 24px;
@@ -319,7 +329,6 @@
margin-top: 50px; margin-top: 50px;
.delete-task-btn, .cancel-task-btn { .delete-task-btn, .cancel-task-btn {
margin-left: 16px;
cursor: pointer; cursor: pointer;
&:hover, &:focus, &:active { &:hover, &:focus, &:active {
@@ -343,6 +352,14 @@
} }
</style> </style>
<style lang="scss" scoped>
.gold {
width: 24px;
margin-left: 5em;
margin-top: -2.4em;
}
</style>
<script> <script>
import bModal from 'bootstrap-vue/lib/components/modal'; import bModal from 'bootstrap-vue/lib/components/modal';
import { mapGetters, mapActions, mapState } from 'client/libs/store'; import { mapGetters, mapActions, mapState } from 'client/libs/store';
@@ -361,6 +378,7 @@ import difficultyNormalIcon from 'assets/svg/difficulty-normal.svg';
import positiveIcon from 'assets/svg/positive.svg'; import positiveIcon from 'assets/svg/positive.svg';
import negativeIcon from 'assets/svg/negative.svg'; import negativeIcon from 'assets/svg/negative.svg';
import deleteIcon from 'assets/svg/delete.svg'; import deleteIcon from 'assets/svg/delete.svg';
import goldIcon from 'assets/svg/gold.svg';
export default { export default {
components: { components: {
@@ -385,6 +403,7 @@ export default {
negative: negativeIcon, negative: negativeIcon,
positive: positiveIcon, positive: positiveIcon,
destroy: deleteIcon, destroy: deleteIcon,
gold: goldIcon,
}), }),
requiresApproval: false, // We can't set task.group fields so we use this field to toggle requiresApproval: false, // We can't set task.group fields so we use this field to toggle
members: [], members: [],
@@ -527,6 +546,7 @@ export default {
this.$root.$emit('hide::modal', 'task-modal'); this.$root.$emit('hide::modal', 'task-modal');
}, },
destroy () { destroy () {
if (!confirm('Are you sure you want to delete this task?')) return;
this.destroyTask(this.task); this.destroyTask(this.task);
this.$root.$emit('hide::modal', 'task-modal'); this.$root.$emit('hide::modal', 'task-modal');
}, },

View File

@@ -35,7 +35,7 @@
@change="toggleTag(tag)", @change="toggleTag(tag)",
) )
span.custom-control-indicator span.custom-control-indicator
span.custom-control-description {{ tag.name }} span.custom-control-description(v-markdown='tag.name')
.filter-panel-footer.clearfix .filter-panel-footer.clearfix
template(v-if="editingTags === true") template(v-if="editingTags === true")
@@ -117,9 +117,9 @@
</template> </template>
<style lang="scss"> <style lang="scss">
#create-dropdown .dropdown-toggle::after { #create-dropdown .dropdown-toggle::after {
display: none; display: none;
} }
</style> </style>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -142,7 +142,16 @@
} }
.dropdown-icon-item .svg-icon { .dropdown-icon-item .svg-icon {
width: 16px; width: 22px;
color: #C3C0C7;
}
.dropdown-icon-item:hover .svg-icon, .dropdown-item.active .svg-icon {
color: $purple-500;
}
.dropdown-icon-item .text {
font-weight: bold;
} }
button.btn.btn-secondary.filter-button { button.btn.btn-secondary.filter-button {
@@ -278,6 +287,7 @@
import TaskColumn from './column'; import TaskColumn from './column';
import TaskModal from './taskModal'; import TaskModal from './taskModal';
import spells from './spells'; import spells from './spells';
import markdown from 'client/directives/markdown';
import positiveIcon from 'assets/svg/positive.svg'; import positiveIcon from 'assets/svg/positive.svg';
import filterIcon from 'assets/svg/filter.svg'; import filterIcon from 'assets/svg/filter.svg';
@@ -315,6 +325,9 @@ export default {
spells, spells,
SelectMembersModal, SelectMembersModal,
}, },
directives: {
markdown,
},
data () { data () {
return { return {
columns: ['habit', 'daily', 'todo', 'reward'], columns: ['habit', 'daily', 'todo', 'reward'],

View File

@@ -27,7 +27,7 @@
div div
span(:class="userLevelStyle(conversation)") {{conversation.name}} span(:class="userLevelStyle(conversation)") {{conversation.name}}
span.timeago {{conversation.date}} span.timeago {{conversation.date}}
div {{conversation.lastMessageText}} div {{conversation.lastMessageText.substring(0, 30)}}
.col-8.messages .col-8.messages
chat-message.container-fluid(:chat.sync='activeChat') chat-message.container-fluid(:chat.sync='activeChat')

View File

@@ -256,6 +256,7 @@ const router = new VueRouter({
{ name: 'merch', path: 'merch', component: MerchPage, meta: {requiresLogin: false}}, { name: 'merch', path: 'merch', component: MerchPage, meta: {requiresLogin: false}},
// { name: 'newStuff', path: 'newStuff', component: NewStuffPage, meta: {requiresLogin: false}}, // { name: 'newStuff', path: 'newStuff', component: NewStuffPage, meta: {requiresLogin: false}},
{ name: 'overview', path: 'overview', component: OverviewPage, meta: {requiresLogin: false}}, { name: 'overview', path: 'overview', component: OverviewPage, meta: {requiresLogin: false}},
{ name: 'oldNews', path: 'old-news', component: ParentPage, meta: {requiresLogin: false}},
{ name: 'plans', path: 'plans', component: GroupPlansPage, meta: {requiresLogin: false}}, { name: 'plans', path: 'plans', component: GroupPlansPage, meta: {requiresLogin: false}},
{ name: 'pressKit', path: 'press-kit', component: PressKitPage, meta: {requiresLogin: false}}, { name: 'pressKit', path: 'press-kit', component: PressKitPage, meta: {requiresLogin: false}},
{ name: 'privacy', path: 'privacy', component: PrivacyPage, meta: {requiresLogin: false}}, { name: 'privacy', path: 'privacy', component: PrivacyPage, meta: {requiresLogin: false}},

View File

@@ -98,11 +98,12 @@ export async function create (store, createdTask) {
sanitizeChecklist(createdTask); sanitizeChecklist(createdTask);
list.unshift(createdTask);
store.state.user.data.tasksOrder[type].unshift(createdTask._id);
const response = await axios.post('/api/v3/tasks/user', createdTask); const response = await axios.post('/api/v3/tasks/user', createdTask);
Object.assign(list[0], response.data.data); let newTask = response.data.data;
list.unshift(newTask);
store.state.user.data.tasksOrder[type].unshift(newTask._id);
Object.assign(list[0], newTask);
} }
export async function save (store, editedTask) { export async function save (store, editedTask) {

View File

@@ -88,7 +88,11 @@ export default function () {
selectedLanguage, selectedLanguage,
}), }),
hideHeader: false, hideHeader: false,
viewingMembers: [], memberModalOptions: {
viewingMembers: [],
groupId: '',
group: {},
},
openedItemRows: [], openedItemRows: [],
spellOptions: { spellOptions: {
castingSpell: false, castingSpell: false,

View File

@@ -300,5 +300,8 @@
"partyInformationPlaceholder": "Write a message to your Party members here!", "partyInformationPlaceholder": "Write a message to your Party members here!",
"selectPartyMember": "Select a Party Member", "selectPartyMember": "Select a Party Member",
"errorNotInParty": "You are not in a Party", "errorNotInParty": "You are not in a Party",
"health_wellness": "Health & Wellness" "health_wellness": "Health & Wellness",
"self_care": "Self-Care",
"sendLink": "Send Link",
"forgotPassword": "Forgot Password"
} }

View File

@@ -37,6 +37,10 @@ let schema = new Schema({
group: {type: String, ref: 'Group', validate: [validator.isUUID, 'Invalid uuid.'], required: true}, group: {type: String, ref: 'Group', validate: [validator.isUUID, 'Invalid uuid.'], required: true},
memberCount: {type: Number, default: 1}, memberCount: {type: Number, default: 1},
prize: {type: Number, default: 0, min: 0}, prize: {type: Number, default: 0, min: 0},
categories: [{
slug: {type: String},
name: {type: String},
}],
}, { }, {
strict: true, strict: true,
minimize: false, // So empty objects are returned minimize: false, // So empty objects are returned