Sept 23 fixes (#9074)

* Discover challenges

* Fixed hero loading

* Moved add task button

* Fixed bailey showing

* Added logs for bad sub data

* Fixed blurb editing

* Added confirmation for deleteing message

* Reset invite modals on invite

* fixed group member sorting

* Fixed chat time styles

* Fixed hover on liked

* Fixed like count

* Added reverse

* Fixed editing party

* Added leader conditions

* Added search

* Added loading

* Reset members when leaving party

* Rounded pending

* Fixed overflow on collecting quests

* Added to invite friends

* Hid summary from party

* Fixed button styles

* Fixed button class

* Removed okay button

* Fixed renav for profile modal

* Added subscription back to menu

* Fixed static link

* Added daily due setting

* Added local auth adding

* Fixed centering of text

* Removed message locally

* Added count for new message

* Added style fix for profile pet

* Fixed achievement popovers

* Fixed white boxes

* Added plain color backgrounds

* fixed challenge mutability

* Fixed challenge editing

* Added notation for large numbers

* Add color text to guild sizes

* Removed membership filters from discover challenges

* Added invites to group

* Cmd + enter send message

* Made leader clickable

* Updated group validation

* Added cancelling autocomplete

* Added mention icon

* Added removing member

* Removed extra string
This commit is contained in:
Keith Holliday
2017-09-25 13:02:12 -05:00
committed by GitHub
parent 4759764e61
commit a317b351be
29 changed files with 495 additions and 184 deletions

View File

@@ -27,7 +27,7 @@ div
span.small-text(v-html="$t('inviteFriendsParty')") span.small-text(v-html="$t('inviteFriendsParty')")
br br
// TODO link to party creation or party page if partying solo // TODO link to party creation or party page if partying solo
button.btn.btn-primary(@click='openPartyModal()') {{ $t('startAParty') }} button.btn.btn-primary(@click='openPartyModal()') {{ partyMembers && partyMembers.length > 1 ? $t('startAParty') : $t('inviteFriends') }}
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -63,6 +63,7 @@ div
span {{Math.floor(user.stats.gp * 100) / 100}} 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
span.message-count.top-count(v-if='user.inbox.newMessages > 0') {{user.inbox.newMessages}}
.svg-icon.user(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()')
@@ -75,7 +76,8 @@ div
a.dropdown-item(@click='showProfile("stats")') {{ $t('stats') }} a.dropdown-item(@click='showProfile("stats")') {{ $t('stats') }}
a.dropdown-item(@click='showProfile("achievements")') {{ $t('achievements') }} a.dropdown-item(@click='showProfile("achievements")') {{ $t('achievements') }}
a.dropdown-item.dropdown-separated(@click='showProfile("profile")') {{ $t('profile') }} a.dropdown-item.dropdown-separated(@click='showProfile("profile")') {{ $t('profile') }}
router-link.dropdown-item.dropdown-separated(:to="{name: 'site'}") {{ $t('settings') }} router-link.dropdown-item(:to="{name: 'site'}") {{ $t('settings') }}
router-link.dropdown-item.dropdown-separated(:to="{name: 'subscription'}") {{ $t('subscription') }}
a.nav-link.dropdown-item.dropdown-separated(to="/", @click.prevent='logout()') {{ $t('logout') }} a.nav-link.dropdown-item.dropdown-separated(to="/", @click.prevent='logout()') {{ $t('logout') }}
li(v-if='!this.user.purchased.plan.customerId', @click='showBuyGemsModal("subscribe")') li(v-if='!this.user.purchased.plan.customerId', @click='showBuyGemsModal("subscribe")')
.dropdown-item.text-center .dropdown-item.text-center
@@ -295,6 +297,13 @@ div
font-weight: bold; font-weight: bold;
font-size: 12px; font-size: 12px;
} }
.message-count.top-count {
position: absolute;
right: 0;
top: .5em;
padding: .2em;
}
</style> </style>
<script> <script>

View File

@@ -1,6 +1,6 @@
<template lang="pug"> <template lang="pug">
.row .row
challenge-modal(:challenge='challenge', :cloning='cloning' v-on:updatedChallenge='updatedChallenge') challenge-modal(:cloning='cloning' v-on:updatedChallenge='updatedChallenge')
close-challenge-modal(:members='members', :challengeId='challenge._id') close-challenge-modal(:members='members', :challengeId='challenge._id')
challenge-member-progress-modal(:memberId='progressMemberId', :challengeId='challenge._id') challenge-member-progress-modal(:memberId='progressMemberId', :challengeId='challenge._id')
@@ -16,7 +16,7 @@
.svg-icon.calendar-icon(v-html="icons.calendarIcon") .svg-icon.calendar-icon(v-html="icons.calendarIcon")
| {{$t('endDate')}} | {{$t('endDate')}}
// "endDate": "End Date: <% endDate %>", // "endDate": "End Date: <% endDate %>",
span {{challenge.endDate}} // span {{challenge.endDate}}
.tags .tags
span.tag(v-for='tag in challenge.tags') {{tag}} span.tag(v-for='tag in challenge.tags') {{tag}}
.col-4 .col-4
@@ -28,13 +28,27 @@
.svg-icon.gem-icon(v-html="icons.gemIcon") .svg-icon.gem-icon(v-html="icons.gemIcon")
| {{challenge.prize}} | {{challenge.prize}}
.details(v-once) {{$t('prize')}} .details(v-once) {{$t('prize')}}
.row(v-if='isLeader') .row.leader-actions(v-if='isLeader')
.col-6.offset-6 .col-7.offset-5
span span.view-progress
strong View Progress Of strong {{ $t('viewProgressOf') }}
b-dropdown.create-dropdown(text="Select a Participant") b-dropdown.create-dropdown(text="Select a Participant")
b-dropdown-item(v-for="member in members", :key="member._id", @click="openMemberProgressModal(member._id)") b-dropdown-item(v-for="member in members", :key="member._id", @click="openMemberProgressModal(member._id)")
| {{ member.profile.name }} | {{ member.profile.name }}
span(v-if='isLeader')
b-dropdown.create-dropdown(:text="$t('create')", :variant="'success'")
b-dropdown-item(v-for="type in columns", :key="type", @click="createTask(type)")
| {{$t(type)}}
task-modal(
:task="workingTask",
:purpose="taskFormPurpose",
@cancel="cancelTaskModal()",
ref="taskModal",
:challengeId="challengeId",
v-on:taskCreated='taskCreated',
v-on:taskEdited='taskEdited',
@taskDestroyed='taskDestroyed'
)
.row .row
task-column.col-6( task-column.col-6(
@@ -50,20 +64,6 @@
button.btn.btn-success(v-once, @click='joinChallenge()') {{$t('joinChallenge')}} button.btn.btn-success(v-once, @click='joinChallenge()') {{$t('joinChallenge')}}
div(v-if='isMember') div(v-if='isMember')
button.btn.btn-danger(v-once, @click='leaveChallenge()') {{$t('leaveChallenge')}} button.btn.btn-danger(v-once, @click='leaveChallenge()') {{$t('leaveChallenge')}}
div(v-if='isLeader')
b-dropdown.create-dropdown(:text="$t('create')")
b-dropdown-item(v-for="type in columns", :key="type", @click="createTask(type)")
| {{$t(type)}}
task-modal(
:task="workingTask",
:purpose="taskFormPurpose",
@cancel="cancelTaskModal()",
ref="taskModal",
:challengeId="challengeId",
v-on:taskCreated='taskCreated',
v-on:taskEdited='taskEdited',
@taskDestroyed='taskDestroyed'
)
div(v-if='isLeader') div(v-if='isLeader')
button.btn.btn-secondary(v-once, @click='edit()') {{$t('editChallenge')}} button.btn.btn-secondary(v-once, @click='edit()') {{$t('editChallenge')}}
div(v-if='isLeader') div(v-if='isLeader')
@@ -155,6 +155,14 @@
.description-section { .description-section {
margin-top: 2em; margin-top: 2em;
} }
.leader-actions {
margin-top: 1em;
.view-progress {
margin-right: .5em;
}
}
</style> </style>
<style> <style>
@@ -375,6 +383,7 @@ export default {
edit () { edit () {
// @TODO: set working challenge // @TODO: set working challenge
this.cloning = false; this.cloning = false;
this.$store.state.challengeOptions.workingChallenge = Object.assign({}, this.$store.state.challengeOptions.workingChallenge, this.challenge);
this.$root.$emit('show::modal', 'challenge-modal'); this.$root.$emit('show::modal', 'challenge-modal');
}, },
// @TODO: view members // @TODO: view members

View File

@@ -130,6 +130,7 @@
</style> </style>
<script> <script>
import clone from 'lodash/clone';
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';
@@ -139,7 +140,7 @@ import { TAVERN_ID, MIN_SHORTNAME_SIZE_FOR_CHALLENGES, MAX_SUMMARY_SIZE_FOR_CHAL
import { mapState } from 'client/libs/store'; import { mapState } from 'client/libs/store';
export default { export default {
props: ['challenge', 'groupId', 'cloning'], props: ['groupId', 'cloning'],
components: { components: {
bModal, bModal,
bDropdown, bDropdown,
@@ -186,19 +187,19 @@ export default {
}, },
{ {
label: 'mental_health', label: 'mental_health',
key: 'mental_health ', key: 'mental_health',
}, },
{ {
label: 'getting_organized', label: 'getting_organized',
key: 'getting_organized ', key: 'getting_organized',
}, },
{ {
label: 'self_improvement', label: 'self_improvement',
key: 'self_improvement ', key: 'self_improvement',
}, },
{ {
label: 'spirituality', label: 'spirituality',
key: 'spirituality ', key: 'spirituality',
}, },
{ {
label: 'time_management', label: 'time_management',
@@ -250,7 +251,6 @@ export default {
_id: TAVERN_ID, _id: TAVERN_ID,
}); });
this.resetWorkingChallenge();
this.setUpWorkingChallenge(); this.setUpWorkingChallenge();
}, },
watch: { watch: {
@@ -321,9 +321,14 @@ export default {
return false; return false;
} }
}, },
challenge () {
return this.$store.state.challengeOptions.workingChallenge;
},
}, },
methods: { methods: {
setUpWorkingChallenge () { setUpWorkingChallenge () {
this.resetWorkingChallenge();
if (!this.challenge) return; if (!this.challenge) return;
this.workingChallenge = Object.assign({}, this.workingChallenge, this.challenge); this.workingChallenge = Object.assign({}, this.workingChallenge, this.challenge);
@@ -357,6 +362,8 @@ export default {
shortName: '', shortName: '',
todos: [], todos: [],
}; };
this.$store.state.workingChallenge = {};
}, },
async createChallenge () { async createChallenge () {
// @TODO: improve error handling, add it to updateChallenge, make errors translatable. Suggestion: `<% fieldName %> is required` where possible, where `fieldName` is inserted as the translatable string that's used for the field header. // @TODO: improve error handling, add it to updateChallenge, make errors translatable. Suggestion: `<% fieldName %> is required` where possible, where `fieldName` is inserted as the translatable string that's used for the field header.
@@ -385,9 +392,11 @@ export default {
name: catName, name: catName,
}); });
}); });
this.workingChallenge.categories = serverCategories;
let challenge = await this.$store.dispatch('challenges:createChallenge', {challenge: this.workingChallenge}); let challengeDetails = clone(this.workingChallenge);
challengeDetails.categories = serverCategories;
let challenge = await this.$store.dispatch('challenges:createChallenge', {challenge: challengeDetails});
// @TODO: When to remove from guild instead? // @TODO: When to remove from guild instead?
this.user.balance -= this.workingChallenge.prize / 4; this.user.balance -= this.workingChallenge.prize / 4;
@@ -403,18 +412,21 @@ export default {
let categoryKeys = this.workingChallenge.categories; let categoryKeys = this.workingChallenge.categories;
let serverCategories = []; let serverCategories = [];
categoryKeys.forEach(key => { categoryKeys.forEach(key => {
let catName = this.categoriesHashByKey[key]; let newKey = key.trim();
let catName = this.categoriesHashByKey[newKey];
serverCategories.push({ serverCategories.push({
slug: key, slug: newKey,
name: catName, name: catName,
}); });
}); });
this.workingChallenge.categories = serverCategories;
let challengeDetails = clone(this.workingChallenge);
challengeDetails.categories = serverCategories;
this.$emit('updatedChallenge', { this.$emit('updatedChallenge', {
challenge: this.workingChallenge, challenge: challengeDetails,
}); });
this.$store.dispatch('challenges:updateChallenge', {challenge: this.workingChallenge}); this.$store.dispatch('challenges:updateChallenge', {challenge: challengeDetails});
this.resetWorkingChallenge(); this.resetWorkingChallenge();
this.$root.$emit('hide::modal', 'challenge-modal'); this.$root.$emit('hide::modal', 'challenge-modal');
}, },

View File

@@ -15,7 +15,7 @@
input.custom-control-input(type="checkbox", :value='group.key' v-model="categoryFilters") input.custom-control-input(type="checkbox", :value='group.key' v-model="categoryFilters")
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) }}
.form-group .form-group(v-if='$route.name !== "findChallenges"')
h3 Membership h3 Membership
.form-check( .form-check(
v-for="group in roleOptions", v-for="group in roleOptions",

View File

@@ -48,6 +48,10 @@ export default {
}, },
watch: { watch: {
text (newText) { text (newText) {
if (!newText[newText.length - 1] || newText[newText.length - 1] === ' ') {
this.searchActive = false;
}
if (newText[newText.length - 1] !== '@') return; if (newText[newText.length - 1] !== '@') return;
this.searchActive = true; this.searchActive = true;
this.currentSearchPosition = newText.length - 1; this.currentSearchPosition = newText.length - 1;

View File

@@ -10,7 +10,7 @@
//.hr(v-if='displayDivider(msg)') //.hr(v-if='displayDivider(msg)')
.hr-middle(v-once) {{ msg.timestamp }} .hr-middle(v-once) {{ msg.timestamp }}
.row(v-if='user._id !== msg.uuid') .row(v-if='user._id !== msg.uuid')
.col-2 div(:class='inbox ? "col-4" : "col-2"')
avatar( avatar(
v-if='cachedProfileData[msg.uuid]', v-if='cachedProfileData[msg.uuid]',
:member="cachedProfileData[msg.uuid]", :member="cachedProfileData[msg.uuid]",
@@ -18,8 +18,9 @@
:hideClassBadge='true', :hideClassBadge='true',
@click.native="showMemberModal(msg.uuid)", @click.native="showMemberModal(msg.uuid)",
) )
.card.col-10 .card(:class='inbox ? "col-8" : "col-10"')
.message-hidden(v-if='msg.flagCount > 0 && user.contributor.admin') Message Hidden .mentioned-icon(v-if='isUserMentioned(msg)')
.message-hidden(v-if='msg.flagCount > 0 && user.contributor.admin') Message Hidden - {{ msg.flagCount }} Flags
.card-block .card-block
h3.leader( h3.leader(
:class='userLevelStyle(cachedProfileData[msg.uuid])' :class='userLevelStyle(cachedProfileData[msg.uuid])'
@@ -27,7 +28,7 @@
) )
| {{msg.user}} | {{msg.user}}
.svg-icon(v-html="icons[`tier${cachedProfileData[msg.uuid].contributor.level}`]", v-if='cachedProfileData[msg.uuid] && cachedProfileData[msg.uuid].contributor && cachedProfileData[msg.uuid].contributor.level') .svg-icon(v-html="icons[`tier${cachedProfileData[msg.uuid].contributor.level}`]", v-if='cachedProfileData[msg.uuid] && cachedProfileData[msg.uuid].contributor && cachedProfileData[msg.uuid].contributor.level')
p {{msg.timestamp | timeAgo}} p.time {{msg.timestamp | timeAgo}}
.text(v-markdown='msg.text') .text(v-markdown='msg.text')
hr hr
.action(@click='like(msg, index)', v-if='msg.likes', :class='{active: msg.likes[user._id]}') .action(@click='like(msg, index)', v-if='msg.likes', :class='{active: msg.likes[user._id]}')
@@ -43,13 +44,15 @@
span.action(v-if='msg.uuid === user._id || inbox', @click='remove(msg, index)') span.action(v-if='msg.uuid === user._id || inbox', @click='remove(msg, index)')
.svg-icon(v-html="icons.delete") .svg-icon(v-html="icons.delete")
| {{$t('delete')}} | {{$t('delete')}}
span.action.float-right(v-if='likeCount(msg) > 0') span.action.float-right.liked(v-if='likeCount(msg) > 0')
.svg-icon(v-html="icons.liked") .svg-icon(v-html="icons.liked")
| + {{ likeCount(msg) }} | + {{ likeCount(msg) }}
// @TODO can we avoid duplicating all this code? Cannot we just push everything // @TODO can we avoid duplicating all this code? Cannot we just push everything
// to the right if the user is the author? // to the right if the user is the author?
// Maybe we just create two sub components instead
.row(v-if='user._id === msg.uuid') .row(v-if='user._id === msg.uuid')
.card.col-10 .card(:class='inbox ? "col-8" : "col-10"')
.mentioned-icon(v-if='isUserMentioned(msg)')
.message-hidden(v-if='msg.flagCount > 0 && user.contributor.admin') Message Hidden - {{ msg.flagCount }} Flags .message-hidden(v-if='msg.flagCount > 0 && user.contributor.admin') Message Hidden - {{ msg.flagCount }} Flags
.card-block .card-block
h3.leader( h3.leader(
@@ -58,7 +61,7 @@
) )
| {{msg.user}} | {{msg.user}}
.svg-icon(v-html="icons[`tier${cachedProfileData[msg.uuid].contributor.level}`]", v-if='cachedProfileData[msg.uuid] && cachedProfileData[msg.uuid].contributor && cachedProfileData[msg.uuid].contributor.level') .svg-icon(v-html="icons[`tier${cachedProfileData[msg.uuid].contributor.level}`]", v-if='cachedProfileData[msg.uuid] && cachedProfileData[msg.uuid].contributor && cachedProfileData[msg.uuid].contributor.level')
p {{msg.timestamp | timeAgo}} p.time {{msg.timestamp | timeAgo}}
.text(v-markdown='msg.text') .text(v-markdown='msg.text')
hr hr
.action(@click='like(msg, index)', v-if='msg.likes', :class='{active: msg.likes[user._id]}') .action(@click='like(msg, index)', v-if='msg.likes', :class='{active: msg.likes[user._id]}')
@@ -74,10 +77,10 @@
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(v-if='likeCount(msg) > 0') span.action.float-right.liked(v-if='likeCount(msg) > 0')
.svg-icon(v-html="icons.liked") .svg-icon(v-html="icons.liked")
| + {{ likeCount(msg) }} | + {{ likeCount(msg) }}
.col-2 div(:class='inbox ? "col-4" : "col-2"')
avatar( avatar(
v-if='cachedProfileData[msg.uuid]', v-if='cachedProfileData[msg.uuid]',
:member="cachedProfileData[msg.uuid]", :member="cachedProfileData[msg.uuid]",
@@ -134,6 +137,26 @@
} }
// End of tier colors // End of tier colors
.leader {
margin-bottom: 0;
}
.time {
font-size: 12px;
color: #878190;
}
.mentioned-icon {
width: 16px;
height: 16px;
border-radius: 50%;
background-color: #bda8ff;
box-shadow: 0 1px 1px 0 rgba(26, 24, 29, 0.12);
position: absolute;
right: -.5em;
top: -.5em;
}
h3 { // this is the user name h3 { // this is the user name
cursor: pointer; cursor: pointer;
@@ -173,6 +196,7 @@
.text { .text {
font-size: 14px; font-size: 14px;
color: #4e4a57; color: #4e4a57;
text-align: left !important;
} }
.action { .action {
@@ -185,6 +209,10 @@
cursor: pointer; cursor: pointer;
} }
.liked:hover {
cursor: default;
}
.action .svg-icon { .action .svg-icon {
margin-right: .2em; margin-right: .2em;
width: 16px; width: 16px;
@@ -302,6 +330,27 @@ export default {
}, },
}, },
methods: { methods: {
isUserMentioned (message) {
let user = this.user;
if (message.hasOwnProperty('highlight')) return message.highlight;
message.highlight = false;
let messagetext = message.text.toLowerCase();
let username = user.profile.name;
let mentioned = messagetext.indexOf(username.toLowerCase());
let pattern = `${username}([^\w]|$){1}`;
if (mentioned === -1) return message.highlight;
let preceedingchar = messagetext.substring(mentioned - 1, mentioned);
if (mentioned === 0 || preceedingchar.trim() === '' || preceedingchar === '@') {
let regex = new RegExp(pattern, 'i');
message.highlight = regex.test(messagetext);
}
return message.highlight;
},
canViewFlag (message) { canViewFlag (message) {
if (message.uuid === this.user._id) return true; if (message.uuid === this.user._id) return true;
if (!message.flagCount || message.flagCount === 0) return true; if (!message.flagCount || message.flagCount === 0) return true;
@@ -345,7 +394,13 @@ export default {
}, },
likeCount (message) { likeCount (message) {
if (!message.likes) return 0; if (!message.likes) return 0;
return Object.keys(message.likes).length;
let likeCount = 0;
for (let key in message.likes) {
let like = message.likes[key];
if (like) likeCount += 1;
}
return likeCount;
}, },
async like (messageToLike, index) { async like (messageToLike, index) {
let message = cloneDeep(messageToLike); let message = cloneDeep(messageToLike);
@@ -374,10 +429,13 @@ export default {
this.$root.$emit('show::modal', 'report-flag'); this.$root.$emit('show::modal', 'report-flag');
}, },
async remove (message, index) { async remove (message, index) {
if (!confirm(this.$t('areYouSureDeleteMessage'))) return;
this.chat.splice(index, 1); this.chat.splice(index, 1);
if (this.inbox) { if (this.inbox) {
axios.delete(`/api/v3/user/messages/${message.id}`); axios.delete(`/api/v3/user/messages/${message.id}`);
this.$delete(this.user.inbox.messages, message.id);
return; return;
} }

View File

@@ -228,8 +228,9 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.row.text-center.set-title .row.text-center.set-title
strong {{backgroundShopSets[0].text}} strong {{backgroundShopSets[0].text}}
.row.incentive-background-row .row.incentive-background-row
.col-12(v-if='showPlainBackgroundBlurb(backgroundShopSets[0].identifier, backgroundShopSets[0].items)') {{ $t('incentiveBackgroundsUnlockedWithCheckins') }}
.col-2(v-for='bg in backgroundShopSets[0].items', .col-2(v-for='bg in backgroundShopSets[0].items',
@click='buy("background." + bg.key)', @click='unlock("background." + bg.key)',
:popover-title='bg.text', :popover-title='bg.text',
:popover='bg.notes', :popover='bg.notes',
popover-trigger='mouseenter') popover-trigger='mouseenter')
@@ -248,7 +249,6 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.row(v-for='set in sets', v-if='activeSubPage === key') .row(v-for='set in sets', v-if='activeSubPage === key')
.col-8.offset-2.text-center.set-title .col-8.offset-2.text-center.set-title
strong {{set.text}} strong {{set.text}}
.col-12(v-if='showPlainBackgroundBlurb(set.identifier, set.items)') {{ $t('incentiveBackgroundsUnlockedWithCheckins') }}
.col-4.text-center.customize-option.background-button(v-for='bg in set.items', .col-4.text-center.customize-option.background-button(v-for='bg in set.items',
@click='!user.purchased.background[bg.key] ? backgroundSelected(bg) : unlock("background." + bg.key)', @click='!user.purchased.background[bg.key] ? backgroundSelected(bg) : unlock("background." + bg.key)',
:popover-title='bg.text', :popover-title='bg.text',

View File

@@ -8,7 +8,7 @@
.col-6.title-details .col-6.title-details
h1 {{group.name}} h1 {{group.name}}
strong.float-left(v-once) {{$t('groupLeader')}} strong.float-left(v-once) {{$t('groupLeader')}}
span.float-left(v-if='group.leader.profile') : {{group.leader.profile.name}} span.leader.float-left(v-if='group.leader.profile', @click='showMemberProfile(group.leader)') : {{group.leader.profile.name}}
.col-6 .col-6
.row.icon-row .row.icon-row
.col-4.offset-4(v-bind:class="{ 'offset-8': isParty }") .col-4.offset-4(v-bind:class="{ 'offset-8': isParty }")
@@ -16,7 +16,7 @@
.svg-icon.shield(v-html="icons.goldGuildBadgeIcon", v-if='group.memberCount > 1000') .svg-icon.shield(v-html="icons.goldGuildBadgeIcon", v-if='group.memberCount > 1000')
.svg-icon.shield(v-html="icons.silverGuildBadgeIcon", v-if='group.memberCount > 100 && group.memberCount < 999') .svg-icon.shield(v-html="icons.silverGuildBadgeIcon", v-if='group.memberCount > 100 && group.memberCount < 999')
.svg-icon.shield(v-html="icons.bronzeGuildBadgeIcon", v-if='group.memberCount < 100') .svg-icon.shield(v-html="icons.bronzeGuildBadgeIcon", v-if='group.memberCount < 100')
span.number {{group.memberCount}} span.number {{ group.memberCount | abbrNum }}
div(v-once) {{ $t('members') }} div(v-once) {{ $t('members') }}
.col-4(v-if='!isParty') .col-4(v-if='!isParty')
.item-with-icon .item-with-icon
@@ -30,8 +30,11 @@
.row.new-message-row .row.new-message-row
textarea(:placeholder="!isParty ? $t('chatPlaceholder') : $t('partyChatPlaceholder')", v-model='newMessage', @keydown='updateCarretPosition') textarea(:placeholder="!isParty ? $t('chatPlaceholder') : $t('partyChatPlaceholder')", v-model='newMessage', @keydown='updateCarretPosition')
autocomplete(:text='newMessage', v-on:select="selectedAutocomplete", :coords='coords', :chat='group.chat') autocomplete(:text='newMessage', v-on:select="selectedAutocomplete", :coords='coords', :chat='group.chat')
button.btn.btn-secondary.send-chat.float-right(v-once, @click='sendMessage()') {{ $t('send') }} button.btn.btn-secondary.send-chat.float-left(v-once, @click='sendMessage()') {{ $t('send') }}
button.btn.btn-secondary.float-left(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }} .row
.col-6
button.btn.btn-secondary.float-left.fetch(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }}
button.btn.btn-secondary.float-left(v-once, @click='reverseChat()') {{ $t('reverseChat') }}
.row.community-guidelines(v-if='!communityGuidelinesAccepted') .row.community-guidelines(v-if='!communityGuidelinesAccepted')
div.col-8(v-once, v-html="$t('communityGuidelinesIntro')") div.col-8(v-once, v-html="$t('communityGuidelinesIntro')")
@@ -75,7 +78,7 @@
.svg-icon(v-html="icons.questIcon") .svg-icon(v-html="icons.questIcon")
h4(v-once) {{ $t('youAreNotOnQuest') }} h4(v-once) {{ $t('youAreNotOnQuest') }}
p(v-once) {{ $t('questDescription') }} p(v-once) {{ $t('questDescription') }}
button.btn.btn-secondary(v-once, @click="openStartQuestModal()") {{ $t('startAQuest') }} button.btn.btn-secondary(v-once, @click="openStartQuestModal()", v-if='isLeader') {{ $t('startAQuest') }}
.row.quest-active-section(v-if='isParty && onPendingQuest && !onActiveQuest') .row.quest-active-section(v-if='isParty && onPendingQuest && !onActiveQuest')
h2 Pending quest h2 Pending quest
button.btn.btn-secondary(v-once, @click="questForceStart()") {{ $t('begin') }} button.btn.btn-secondary(v-once, @click="questForceStart()") {{ $t('begin') }}
@@ -110,7 +113,7 @@
| {{parseFloat(group.quest.progress.hp).toFixed(2)}} / {{parseFloat(questData.boss.hp).toFixed(2)}} | {{parseFloat(group.quest.progress.hp).toFixed(2)}} / {{parseFloat(questData.boss.hp).toFixed(2)}}
.col-6 .col-6
// @TODO: Why do we not sync quset progress on the group doc? Each user could have different progress // @TODO: Why do we not sync quset progress on the group doc? Each user could have different progress
span.float-right {{user.party.quest.progress.up || 0}} pending damage span.float-right {{parseFloat(user.party.quest.progress.up).toFixed(1) || 0}} pending damage
.row.rage-bar-row(v-if='questData.boss.rage') .row.rage-bar-row(v-if='questData.boss.rage')
.col-12 .col-12
.grey-progress-bar .grey-progress-bar
@@ -119,9 +122,9 @@
.col-6 .col-6
span.float-left span.float-left
| Rage {{questData.boss.rage.value}} | Rage {{questData.boss.rage.value}}
button.btn.btn-secondary(v-once, @click="questAbort()") {{ $t('abort') }} button.btn.btn-secondary(v-once, @click="questAbort()", v-if='isLeader') {{ $t('abort') }}
.section-header .section-header(v-if='!isParty')
.row .row
.col-10 .col-10
h3(v-once) {{ $t('guildSummary') }} h3(v-once) {{ $t('guildSummary') }}
@@ -164,7 +167,7 @@
.section(v-if="sections.challenges") .section(v-if="sections.challenges")
group-challenges(:groupId='searchId') group-challenges(:groupId='searchId')
div.text-center div.text-center
button.btn.btn-primary(class='btn-danger', v-if='isMember', @click='clickLeave()') {{ $t('leave') }} button.btn.btn-danger(v-if='isMember', @click='clickLeave()') {{ $t('leave') }}
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -174,6 +177,10 @@
color: $purple-200; color: $purple-200;
} }
.leader:hover {
cursor: pointer;
}
.button-container { .button-container {
margin-bottom: 1em; margin-bottom: 1em;
@@ -294,7 +301,7 @@
z-index: 10; z-index: 10;
position: absolute; position: absolute;
right: 1em; right: 1em;
bottom: 3em; bottom: 1em;
} }
} }
@@ -411,7 +418,7 @@
.collect-progress-bar { .collect-progress-bar {
background-color: #24cc8f; background-color: #24cc8f;
height: 15px; height: 15px;
max-width: 100%;
} }
.hr { .hr {
@@ -649,6 +656,11 @@ export default {
document.body.removeChild(div); document.body.removeChild(div);
}, },
updateCarretPosition (eventUpdate) { updateCarretPosition (eventUpdate) {
if (eventUpdate.metaKey && eventUpdate.keyCode === 13) {
this.sendMessage();
return;
}
let text = eventUpdate.target; let text = eventUpdate.target;
this.getCoord(eventUpdate, text); this.getCoord(eventUpdate, text);
}, },
@@ -661,6 +673,8 @@ export default {
this.$root.$emit('show::modal', 'members-modal'); this.$root.$emit('show::modal', 'members-modal');
}, },
async sendMessage () { async sendMessage () {
if (!this.newMessage) return;
let response = await this.$store.dispatch('chat:postChat', { let response = await this.$store.dispatch('chat:postChat', {
group: this.group, group: this.group,
message: this.newMessage, message: this.newMessage,
@@ -685,6 +699,7 @@ export default {
} }
let group = await this.$store.dispatch('guilds:getGroup', {groupId: this.searchId}); let group = await this.$store.dispatch('guilds:getGroup', {groupId: this.searchId});
if (this.isParty) { if (this.isParty) {
this.$store.state.party.data = group; this.$store.state.party.data = group;
this.group = this.$store.state.party.data; this.group = this.$store.state.party.data;
@@ -749,14 +764,10 @@ export default {
if (this.isParty) { if (this.isParty) {
data.type = 'party'; data.type = 'party';
Analytics.updateUser({partySize: null, partyID: null}); Analytics.updateUser({partySize: null, partyID: null});
this.$store.state.party.members = [];
} }
await this.$store.dispatch('guilds:leave', data); await this.$store.dispatch('guilds:leave', data);
// @TODO: Implement
// User.sync().then(function () {
// $rootScope.hardRedirect('/party');
// });
}, },
upgradeGroup () { upgradeGroup () {
this.$store.state.upgradingGroup = this.group; this.$store.state.upgradingGroup = this.group;
@@ -794,6 +805,12 @@ export default {
} }
// $rootScope.$state.go('options.inventory.quests'); // $rootScope.$state.go('options.inventory.quests');
}, },
async showMemberProfile (leader) {
let heroDetails = await this.$store.dispatch('members:fetchMember', { memberId: leader._id });
this.$store.state.profileUser = heroDetails.data.data;
this.$store.state.profileOptions.startingPage = 'profile';
this.$root.$emit('show::modal', 'profile');
},
async questCancel () { async questCancel () {
if (!confirm(this.$t('sureCancel'))) return; if (!confirm(this.$t('sureCancel'))) return;
let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/cancel'}); let quest = await this.$store.dispatch('quests:sendAction', {groupId: this.group._id, action: 'quests/cancel'});

View File

@@ -373,7 +373,7 @@ export default {
async submit () { async submit () {
if (this.$store.state.user.data.balance < 1 && !this.workingGroup.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(this.$t('notEnoughGems'));
return; return;
// @TODO return $rootScope.openModal('buyGems', {track:"Gems > Gems > Create Group"}); // @TODO return $rootScope.openModal('buyGems', {track:"Gems > Gems > Create Group"});
// @TODO when modal is implemented, enable analytics // @TODO when modal is implemented, enable analytics
@@ -385,27 +385,16 @@ export default {
}); */ }); */
} }
if (!this.workingGroup.name || !this.workingGroup.description) { let errors = [];
// @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');
return;
}
if (!this.workingGroup.summary) { if (!this.workingGroup.name) errors.push(this.$t('nameRequired'));
// @TODO: Add proper notifications. Summary is mandatory for only public guilds (not tavern, private guilds, parties) if (!this.workingGroup.summary) errors.push(this.$t('summaryRequired'));
alert('Enter a summary'); if (this.workingGroup.summary.length > MAX_SUMMARY_SIZE_FOR_GUILDS) errors.push(this.$t('summaryTooLong'));
return; if (!this.workingGroup.description) errors.push(this.$t('descriptionRequired'));
} if (!this.isParty && (!this.workingGroup.categories || this.workingGroup.categories.length === 0)) errors.push(this.$t('categoiresRequired'));
if (this.workingGroup.summary.length > MAX_SUMMARY_SIZE_FOR_GUILDS) { if (errors.length > 0) {
// @TODO: Add proper notifications. Summary is mandatory for only public guilds (not tavern, private guilds, parties) alert(errors.join('\n'));
alert('Summary is too long');
return;
}
if (!this.workingGroup.categories || this.workingGroup.categories.length === 0) {
// @TODO: Add proper notifications
alert('One or more categories must be selected');
return; return;
} }

View File

@@ -13,7 +13,7 @@ b-modal#invite-modal(:title="$t('inviteFriends')", size='lg')
input.form-control(type='text', v-model='user.uuid') input.form-control(type='text', v-model='user.uuid')
tr tr
td td
button.btn.btn-xs.pull-right(@click='addUuid()') button.btn.btn-primary.pull-right(@click='addUuid()')
i.glyphicon.glyphicon-plus i.glyphicon.glyphicon-plus
| + | +
tr tr
@@ -36,7 +36,7 @@ b-modal#invite-modal(:title="$t('inviteFriends')", size='lg')
input.form-control(type='email', v-model='email.email') input.form-control(type='email', v-model='email.email')
tr tr
td(colspan=2) td(colspan=2)
a.btn.btn-xs.pull-right(@click='addEmail()') button.btn.btn-primary.pull-right(@click='addEmail()')
i.glyphicon.glyphicon-plus i.glyphicon.glyphicon-plus
| + | +
tr tr
@@ -132,6 +132,9 @@ export default {
this.text(this.$t(invitationString)); this.text(this.$t(invitationString));
this.invitees = [];
this.emails = [];
// @TODO: This function didn't make it over this.resetInvitees(); // @TODO: This function didn't make it over this.resetInvitees();
// @TODO: Sync group invites? // @TODO: Sync group invites?
@@ -140,7 +143,7 @@ export default {
// } else { // } else {
// this.$router.push(`/groups/guilds/${this.group._id}`); // this.$router.push(`/groups/guilds/${this.group._id}`);
// } // }
this.$root.$emit('hide:modal', 'invite-modal'); this.$root.$emit('hide::modal', 'invite-modal');
// @TODO: error? // @TODO: error?
// _resetInvitees(); // _resetInvitees();
}, },

View File

@@ -1,7 +1,8 @@
<template lang="pug"> <template lang="pug">
// @TODO: Move this to a member directory // @TODO: Move this to a member directory
div div
b-modal#members-modal(:title="$t('createGuild')", size='md') remove-member-modal(:member-to-remove='memberToRemove', :group-id='this.groupId' @member-removed='memberRemoved')
b-modal#members-modal(:title="$t('createGuild')", size='md', :hide-footer='true')
.header-wrap(slot="modal-header") .header-wrap(slot="modal-header")
.row .row
.col-6 .col-6
@@ -16,36 +17,47 @@ div
span.dropdown-label {{ $t('sortBy') }} span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t('sort')", right=true) b-dropdown(:text="$t('sort')", right=true)
b-dropdown-item(v-for='sortOption in sortOptions', @click='sort(sortOption.value)', :key='sortOption.value') {{sortOption.text}} b-dropdown-item(v-for='sortOption in sortOptions', @click='sort(sortOption.value)', :key='sortOption.value') {{sortOption.text}}
.row(v-for='member in sortedMembers') .row(v-if='invites.length > 0')
.col-11.no-padding-left .col-6.offset-3.nav
member-details(:member='member') .nav-item(@click='viewMembers()', :class="{active: selectedPage === 'members'}") {{ $t('members') }}
.col-1.actions .nav-item(@click='viewInvites()', :class="{active: selectedPage === 'invites'}") {{ $t('invites') }}
b-dropdown(right=true) div(v-if='selectedPage === "members"')
.svg-icon.inline.dots(slot='button-content', v-html="icons.dots") .row(v-for='(member, index) in sortedMembers')
b-dropdown-item(@click='sort(option.value)', v-if='isLeader') .col-11.no-padding-left
span.dropdown-icon-item member-details(:member='member')
.svg-icon.inline(v-html="icons.removeIcon", v-if='isLeader') .col-1.actions
span.text {{$t('removeMember')}} b-dropdown(right=true)
b-dropdown-item(@click='sendMessage(member._id)') .svg-icon.inline.dots(slot='button-content', v-html="icons.dots")
span.dropdown-icon-item b-dropdown-item(@click='removeMember(member, index)', v-if='isLeader')
.svg-icon.inline(v-html="icons.messageIcon") span.dropdown-icon-item
span.text {{$t('sendMessage')}} .svg-icon.inline(v-html="icons.removeIcon", v-if='isLeader')
b-dropdown-item(@click='sort(option.value)', v-if='isLeader') span.text {{$t('removeMember')}}
span.dropdown-icon-item b-dropdown-item(@click='sendMessage(member._id)')
.svg-icon.inline(v-html="icons.starIcon") span.dropdown-icon-item
span.text {{$t('promoteToLeader')}} .svg-icon.inline(v-html="icons.messageIcon")
b-dropdown-item(@click='sort(option.value)', v-if='isLeader && groupIsSubscribed') span.text {{$t('sendMessage')}}
span.dropdown-icon-item b-dropdown-item(@click='sort(option.value)', v-if='isLeader')
.svg-icon.inline(v-html="icons.starIcon") span.dropdown-icon-item
span.text {{$t('addManager')}} .svg-icon.inline(v-html="icons.starIcon")
b-dropdown-item(@click='sort(option.value)', v-if='isLeader && groupIsSubscribed') span.text {{$t('promoteToLeader')}}
span.dropdown-icon-item b-dropdown-item(@click='sort(option.value)', v-if='isLeader && groupIsSubscribed')
.svg-icon.inline(v-html="icons.removeIcon") span.dropdown-icon-item
span.text {{$t('removeManager2')}} .svg-icon.inline(v-html="icons.starIcon")
.row(v-if='groupId === "challenge"') span.text {{$t('addManager')}}
.col-12.text-center b-dropdown-item(@click='sort(option.value)', v-if='isLeader && groupIsSubscribed')
button.btn.btn-secondary(@click='loadMoreMembers()') {{ $t('loadMore') }} span.dropdown-icon-item
.row.gradient(v-if='members.length > 3') .svg-icon.inline(v-html="icons.removeIcon")
span.text {{$t('removeManager2')}}
.row(v-if='groupId === "challenge"')
.col-12.text-center
button.btn.btn-secondary(@click='loadMoreMembers()') {{ $t('loadMore') }}
.row.gradient(v-if='members.length > 3')
div(v-if='selectedPage === "invites"')
.row(v-for='member in invites')
.col-11.no-padding-left
member-details(:member='member')
.modal-footer
button.btn.btn-primary(@click='close()') {{ $t('close') }}
</template> </template>
<style lang='scss'> <style lang='scss'>
@@ -130,10 +142,29 @@ div
.dropdown-icon-item .svg-icon { .dropdown-icon-item .svg-icon {
width: 20px; width: 20px;
} }
.nav {
font-weight: bold;
margin-bottom: .5em;
margin-top: .5em;
}
.nav-item {
display: inline-block;
font-size: 16px;
margin: 0 auto;
padding: .5em;
color: #878190;
}
.nav-item:hover, .nav-item.active {
color: #4f2a93;
border-bottom: 2px solid #4f2a93;
cursor: pointer;
}
</style> </style>
<script> <script>
// @TODO: Move this under members directory
import sortBy from 'lodash/sortBy'; 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';
@@ -141,6 +172,7 @@ import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
import { mapState } from 'client/libs/store'; import { mapState } from 'client/libs/store';
import privateMessageModal from 'client/components/private-message-modal'; import privateMessageModal from 'client/components/private-message-modal';
import removeMemberModal from 'client/components/members/removeMemberModal';
import MemberDetails from '../memberDetails'; import MemberDetails from '../memberDetails';
import removeIcon from 'assets/members/remove.svg'; import removeIcon from 'assets/members/remove.svg';
import messageIcon from 'assets/members/message.svg'; import messageIcon from 'assets/members/message.svg';
@@ -155,12 +187,15 @@ export default {
bDropdownItem, bDropdownItem,
MemberDetails, MemberDetails,
privateMessageModal, privateMessageModal,
removeMemberModal,
}, },
data () { data () {
return { return {
sortOption: '', sortOption: '',
selectedPage: 'members',
members: [], members: [],
memberToRemove: '', invites: [],
memberToRemove: {},
sortOptions: [ sortOptions: [
{ {
value: 'level', value: 'level',
@@ -212,6 +247,13 @@ export default {
}, },
sortedMembers () { sortedMembers () {
let sortedMembers = this.members; let sortedMembers = this.members;
if (this.searchTerm) {
sortedMembers = sortedMembers.filter(member => {
return member.profile.name.toLowerCase().indexOf(this.searchTerm.toLowerCase) !== -1;
});
}
if (!this.sortOption) return sortedMembers; if (!this.sortOption) return sortedMembers;
sortedMembers = sortBy(this.members, [(member) => { sortedMembers = sortBy(this.members, [(member) => {
@@ -227,7 +269,7 @@ export default {
} }
}]); }]);
return this.members; return sortedMembers;
}, },
}, },
watch: { watch: {
@@ -252,6 +294,12 @@ export default {
includeAllPublicFields: true, includeAllPublicFields: true,
}); });
this.members = members; this.members = members;
let invites = await this.$store.dispatch('members:getGroupInvites', {
groupId,
includeAllPublicFields: true,
});
this.invites = invites;
} }
if (this.$store.state.memberModalOptions.viewingMembers.length > 0) { if (this.$store.state.memberModalOptions.viewingMembers.length > 0) {
@@ -277,24 +325,15 @@ export default {
this.$root.$emit('show::modal', 'members-modal'); this.$root.$emit('show::modal', 'members-modal');
}, },
async removeMember (member) { async removeMember (member, index) {
this.memberToRemove = member; this.memberToRemove = member;
this.memberToRemove.index = index;
this.$root.$emit('show::modal', 'remove-member'); this.$root.$emit('show::modal', 'remove-member');
}, },
async confirmRemoveMember (confirmation) { memberRemoved () {
if (!confirmation) { this.members.splice(this.memberToRemove.index, 1);
this.memberToRemove = ''; this.group.memberCount -= 1;
return; this.memberToRemove = {};
}
await this.$store.dispatch('members:removeMember', {
memberId: this.memberToRemove._id,
groupId: this.group._id,
message: this.removeMessage,
});
this.memberToRemove = '';
this.removeMessage = '';
}, },
async quickReply (uid) { async quickReply (uid) {
this.memberToReply = uid; this.memberToReply = uid;
@@ -330,6 +369,12 @@ export default {
this.members = this.members.concat(newMembers); this.members = this.members.concat(newMembers);
}, },
viewMembers () {
this.selectedPage = 'members';
},
viewInvites () {
this.selectedPage = 'invites';
},
}, },
}; };
</script> </script>

View File

@@ -4,11 +4,11 @@ router-link.card-link(:to="{ name: 'guild', params: { groupId: guild._id } }")
.card-block .card-block
.row .row
.col-md-2.badge-column .col-md-2.badge-column
.shield-wrap .shield-wrap(:class="{gold: guild.memberCount > 1000, silver: guild.memberCount > 100 && guild.memberCount < 999}")
.svg-icon.shield(v-html="icons.goldGuildBadge", v-if='guild.memberCount > 1000') .svg-icon.shield(v-html="icons.goldGuildBadge", v-if='guild.memberCount > 1000')
.svg-icon.shield(v-html="icons.silverGuildBadgeIcon", v-if='guild.memberCount > 100 && guild.memberCount < 999') .svg-icon.shield(v-html="icons.silverGuildBadgeIcon", v-if='guild.memberCount > 100 && guild.memberCount < 999')
.svg-icon.shield(v-html="icons.bronzeGuildBadgeIcon", v-if='guild.memberCount < 100') .svg-icon.shield(v-html="icons.bronzeGuildBadgeIcon", v-if='guild.memberCount < 100')
.member-count {{guild.memberCount}} .member-count {{ guild.memberCount | abbrNum }}
.col-md-10 .col-md-10
.row .row
.col-md-8 .col-md-8
@@ -76,6 +76,14 @@ router-link.card-link(:to="{ name: 'guild', params: { groupId: guild._id } }")
width: 70px; width: 70px;
} }
.gold {
color: #fdbb5a;
}
.silver {
color: #c2c2c2;
}
.badge-column { .badge-column {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -12,7 +12,7 @@
div(v-if='questData') div(v-if='questData')
questDialogContent(:item="questData") questDialogContent(:item="questData")
div.text-center div.text-center
button.btn.btn-primary(@click='questInit()', :disabled="!Boolean(selectedQuest)") {{$t('inviteToPartyOrQuest')}} button.btn.btn-primary(@click='questInit()', :disabled="!Boolean(selectedQuest) || loading") {{$t('inviteToPartyOrQuest')}}
div.text-center div.text-center
p {{$t('inviteInformation')}} p {{$t('inviteInformation')}}
.side-panel(v-if='questData') .side-panel(v-if='questData')
@@ -122,6 +122,7 @@ export default {
}, },
data () { data () {
return { return {
loading: false,
selectedQuest: {}, selectedQuest: {},
icons: Object.freeze({ icons: Object.freeze({
copy: copyIcon, copy: copyIcon,
@@ -157,6 +158,8 @@ export default {
}, },
async questInit () { async questInit () {
this.loading = true;
Analytics.updateUser({ Analytics.updateUser({
partyID: this.group._id, partyID: this.group._id,
partySize: this.group.memberCount, partySize: this.group.memberCount,
@@ -170,6 +173,8 @@ export default {
if (this.$store.state.party.data) this.$store.state.party.data.quest = quest; if (this.$store.state.party.data) this.$store.state.party.data.quest = quest;
this.loading = false;
this.$root.$emit('hide::modal', 'start-quest-modal'); this.$root.$emit('hide::modal', 'start-quest-modal');
}, },
}, },

View File

@@ -12,8 +12,13 @@
.row .row
textarea(:placeholder="$t('tavernCommunityGuidelinesPlaceholder')", v-model='newMessage', :class='{"user-entry": newMessage}', @keydown='updateCarretPosition') textarea(:placeholder="$t('tavernCommunityGuidelinesPlaceholder')", v-model='newMessage', :class='{"user-entry": newMessage}', @keydown='updateCarretPosition')
autocomplete(:text='newMessage', v-on:select="selectedAutocomplete", :coords='coords', :chat='group.chat') autocomplete(:text='newMessage', v-on:select="selectedAutocomplete", :coords='coords', :chat='group.chat')
button.btn.btn-secondary.send-chat.float-right(v-once, @click='sendMessage()') {{ $t('send') }}
button.btn.btn-secondary.float-left(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }} .row
.col-6
button.btn.btn-secondary.send-chat.float-left(v-once, @click='sendMessage()') {{ $t('send') }}
.col-6
button.btn.btn-secondary.float-right.fetch(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }}
button.btn.btn-secondary.float-right(v-once, @click='reverseChat()') {{ $t('reverseChat') }}
.row.community-guidelines(v-if='!communityGuidelinesAccepted') .row.community-guidelines(v-if='!communityGuidelinesAccepted')
div.col-8(v-once, v-html="$t('communityGuidelinesIntro')") div.col-8(v-once, v-html="$t('communityGuidelinesIntro')")
@@ -523,6 +528,11 @@ export default {
document.body.removeChild(div); document.body.removeChild(div);
}, },
updateCarretPosition (eventUpdate) { updateCarretPosition (eventUpdate) {
if (eventUpdate.metaKey && eventUpdate.keyCode === 13) {
this.sendMessage();
return;
}
let text = eventUpdate.target; let text = eventUpdate.target;
this.getCoord(eventUpdate, text); this.getCoord(eventUpdate, text);
}, },
@@ -551,6 +561,9 @@ export default {
async fetchRecentMessages () { async fetchRecentMessages () {
this.group = await this.$store.dispatch('guilds:getGroup', {groupId: TAVERN_ID}); this.group = await this.$store.dispatch('guilds:getGroup', {groupId: TAVERN_ID});
}, },
reverseChat () {
this.group.chat.reverse();
},
}, },
}; };
</script> </script>

View File

@@ -186,8 +186,9 @@ export default {
window.scrollTo(0, 200); window.scrollTo(0, 200);
this.loadHero(id, index); this.loadHero(id, index);
}, },
clickMember (hero) { async clickMember (hero) {
this.$store.state.profileUser = hero; let heroDetails = await this.$store.dispatch('members:fetchMember', { memberId: hero._id });
this.$store.state.profileUser = heroDetails.data.data;
this.$store.state.profileOptions.startingPage = 'profile'; this.$store.state.profileOptions.startingPage = 'profile';
this.$root.$emit('show::modal', 'profile'); this.$root.$emit('show::modal', 'profile');
}, },

View File

@@ -0,0 +1,53 @@
<template lang="pug">
b-modal#remove-member(:title="$t('removeMember')", size='md', :hide-footer="true")
.text-center
h2.col-12 {{ $t('sureKick') }}
.col-12.removing-member(v-if='memberToRemove.profile') {{memberToRemove.profile.name}}
.modal-body
textarea.form-control(type='text',
rows='5',
:placeholder="$t('optionalMessage')",
v-model='removeMessage')
.modal-footer
button.pull-left.btn.btn-danger(@click='confirmRemoveMember()') {{ $t('yesRemove') }}
button.btn.btn-default(@click='close()') {{ $t('cancel') }}
</template>
<style scoped>
.removing-member {
color: #878190;
margin-bottom: .5em;
}
</style>
<script>
import bModal from 'bootstrap-vue/lib/components/modal';
export default {
props: ['memberToRemove', 'groupId'],
components: {
bModal,
},
data () {
return {
removeMessage: '',
};
},
methods: {
async confirmRemoveMember () {
await this.$store.dispatch('members:removeMember', {
memberId: this.memberToRemove._id,
groupId: this.groupId,
message: this.removeMessage,
});
this.removeMessage = '';
this.$emit('member-removed', this.memberToRemove);
this.close();
},
close () {
this.$root.$emit('hide::modal', 'remove-member');
},
},
};
</script>

View File

@@ -34,7 +34,8 @@
.form-horizontal(v-if='user.flags.classSelected && !user.preferences.disableClasses') .form-horizontal(v-if='user.flags.classSelected && !user.preferences.disableClasses')
h5 {{ $t('characterBuild') }} h5 {{ $t('characterBuild') }}
h6(v-once) {{ $t('class') + ': ' }} h6(v-once) {{ $t('class') + ': ' }}
span {{ classText }}&nbsp; // @TODO: what is classText
span(v-if='classText') {{ classText }}&nbsp;
button.btn.btn-danger.btn-xs(@click='changeClass(null)', v-once) {{ $t('changeClass') }} button.btn.btn-danger.btn-xs(@click='changeClass(null)', v-once) {{ $t('changeClass') }}
small.cost 3 small.cost 3
span.Pet_Currency_Gem1x.inline-gems span.Pet_Currency_Gem1x.inline-gems
@@ -117,21 +118,20 @@
button.btn.btn-primary(v-if='!user.auth[network.key].id', @click='socialLogin(network.key, user)') {{ $t('registerWithSocial', {network: network.name}) }} button.btn.btn-primary(v-if='!user.auth[network.key].id', @click='socialLogin(network.key, user)') {{ $t('registerWithSocial', {network: network.name}) }}
button.btn.btn-primary(disabled='disabled', v-if='!hasBackupAuthOption(network.key) && user.auth[network.key].id') {{ $t('registeredWithSocial', {network: network.name}) }} button.btn.btn-primary(disabled='disabled', v-if='!hasBackupAuthOption(network.key) && user.auth[network.key].id') {{ $t('registeredWithSocial', {network: network.name}) }}
button.btn.btn-danger(@click='deleteSocialAuth(network.key)', v-if='hasBackupAuthOption(network.key) && user.auth[network.key].id') {{ $t('detachSocial', {network: network.name}) }} button.btn.btn-danger(@click='deleteSocialAuth(network.key)', v-if='hasBackupAuthOption(network.key) && user.auth[network.key].id') {{ $t('detachSocial', {network: network.name}) }}
// hr hr
// TODO div(v-if='!user.auth.local.username')
// div(v-if='!user.auth.local.username')
p {{ $t('addLocalAuth') }} p {{ $t('addLocalAuth') }}
form(ng-submit='http("post", "/api/v3/user/auth/local/register", localAuth, "addedLocalAuth")', name='localAuth', novalidate) .form(name='localAuth', novalidate)
//-.alert.alert-danger(ng-messages='changeUsername.$error && changeUsername.submitted') {{ $t('fillAll') }} //-.alert.alert-danger(ng-messages='changeUsername.$error && changeUsername.submitted') {{ $t('fillAll') }}
.form-group .form-group
input.form-control(type='text', placeholder="$t('username')", v-model='localAuth.username', required) input.form-control(type='text', :placeholder="$t('username')", v-model='localAuth.username', required)
.form-group .form-group
input.form-control(type='text', placeholder="$t('email')", v-model='localAuth.email', required) input.form-control(type='text', :placeholder="$t('email')", v-model='localAuth.email', required)
.form-group .form-group
input.form-control(type='password', placeholder="$t('password')", v-model='localAuth.password', required) input.form-control(type='password', :placeholder="$t('password')", v-model='localAuth.password', required)
.form-group .form-group
input.form-control(type='password', placeholder="$t('confirmPass')", v-model='localAuth.confirmPassword', required) input.form-control(type='password', :placeholder="$t('confirmPass')", v-model='localAuth.confirmPassword', required)
button.btn.btn-primary(type='submit', ng-disabled='localAuth.$invalid', value="$t('submit')") button.btn.btn-primary(type='submit', @click='addLocalAuth()') {{ $t('submit') }}
.usersettings(v-if='user.auth.local.username') .usersettings(v-if='user.auth.local.username')
p {{ $t('username') }} p {{ $t('username') }}
@@ -231,6 +231,12 @@ export default {
usernameUpdates: {}, usernameUpdates: {},
emailUpdates: {}, emailUpdates: {},
passwordUpdates: {}, passwordUpdates: {},
localAuth: {
username: '',
email: '',
password: '',
confirmPassword: '',
},
}; };
}, },
mounted () { mounted () {
@@ -290,6 +296,7 @@ export default {
// Guide.goto('intro', 0, true); // Guide.goto('intro', 0, true);
}, },
showBailey () { showBailey () {
this.user.flags.newStuff = true;
this.$root.$emit('show::modal', 'new-stuff'); this.$root.$emit('show::modal', 'new-stuff');
}, },
hasBackupAuthOption (networkKeyToCheck) { hasBackupAuthOption (networkKeyToCheck) {
@@ -380,6 +387,9 @@ export default {
alert(e.message); alert(e.message);
} }
}, },
addLocalAuth () {
axios.post('/api/v3/user/auth/local/register', this.localAuth, 'addedLocalAuth');
},
}, },
}; };
</script> </script>

View File

@@ -163,6 +163,16 @@ export default {
}]); }]);
}, },
purchasedPlanIdInfo () { purchasedPlanIdInfo () {
if (!this.subscriptionBlocks[this.user.purchased.plan.planId]) {
// @TODO: find which subs are in the common
console.log(this.subscriptionBlocks[this.user.purchased.plan.planId]); // eslint-disable-line
return {
price: 0,
months: 0,
plan: '',
};
}
return { return {
price: this.subscriptionBlocks[this.user.purchased.plan.planId].price, price: this.subscriptionBlocks[this.user.purchased.plan.planId].price,
months: this.subscriptionBlocks[this.user.purchased.plan.planId].months, months: this.subscriptionBlocks[this.user.purchased.plan.planId].months,

View File

@@ -1,9 +1,7 @@
<template lang="pug"> <template lang="pug">
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-sm nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-sm
.navbar-header .navbar-header
router-link.nav-item( router-link.nav-item(:to='!isUserLoggedIn ? "/static/home" : "/"')
to='/static/home',
)
.logo.svg-icon(v-html='icons.logo') .logo.svg-icon(v-html='icons.logo')
.collapse.navbar-collapse .collapse.navbar-collapse
ul.navbar-nav.mr-auto(v-if='$route.name !== "home"') ul.navbar-nav.mr-auto(v-if='$route.name !== "home"')

View File

@@ -6,7 +6,7 @@
.filters.d-flex.justify-content-end .filters.d-flex.justify-content-end
.filter.small-text( .filter.small-text(
v-for="filter in types[type].filters", v-for="filter in types[type].filters",
:class="{active: activeFilter.label === filter.label}", :class="{active: activeFilters[type].label === filter.label}",
@click="activateFilter(type, filter)", @click="activateFilter(type, filter)",
) {{ $t(filter.label) }} ) {{ $t(filter.label) }}
.tasks-list(ref="taskList", v-sortable='', @onsort='sorted') .tasks-list(ref="taskList", v-sortable='', @onsort='sorted')
@@ -226,9 +226,14 @@ export default {
reward: rewardIcon, reward: rewardIcon,
}); });
let activeFilters = {};
for (let type in types) {
activeFilters[type] = types[type].filters.find(f => f.default === true);
}
return { return {
types, types,
activeFilter: types[this.type].filters.find(f => f.default === true), activeFilters,
icons, icons,
openedCompletedTodos: false, openedCompletedTodos: false,
@@ -252,7 +257,7 @@ export default {
return inAppRewards(this.user); return inAppRewards(this.user);
}, },
hasRewardsList () { hasRewardsList () {
return this.isUser === true && this.type === 'reward' && this.activeFilter.label !== 'custom'; return this.isUser === true && this.type === 'reward' && this.activeFilters[this.type].label !== 'custom';
}, },
initialColumnDescription () { initialColumnDescription () {
// Show the column description in the middle only if there are no elements (tasks or in app items) // Show the column description in the middle only if there are no elements (tasks or in app items)
@@ -262,6 +267,12 @@ export default {
return this.tasks[`${this.type}s`].length === 0; return this.tasks[`${this.type}s`].length === 0;
}, },
dailyDueDefaultView () {
if (this.user.preferences.dailyDueDefaultView) {
this.activateFilter('daily', this.types.daily.filters[1]);
}
return this.user.preferences.dailyDueDefaultView;
},
}, },
watch: { watch: {
taskList: { taskList: {
@@ -270,6 +281,11 @@ export default {
}, 250), }, 250),
deep: true, deep: true,
}, },
dailyDueDefaultView () {
if (this.user.preferences.dailyDueDefaultView) {
this.activateFilter('daily', this.types.daily.filters[1]);
}
},
}, },
mounted () { mounted () {
this.setColumnBackgroundVisibility(); this.setColumnBackgroundVisibility();
@@ -301,7 +317,7 @@ export default {
if (type === 'todo' && filter.label === 'complete2') { if (type === 'todo' && filter.label === 'complete2') {
this.loadCompletedTodos(); this.loadCompletedTodos();
} }
this.activeFilter = filter; this.activeFilters[type] = filter;
}, },
setColumnBackgroundVisibility () { setColumnBackgroundVisibility () {
this.$nextTick(() => { this.$nextTick(() => {
@@ -330,7 +346,7 @@ export default {
}, },
filterTask (task) { filterTask (task) {
// View // View
if (!this.activeFilter.filter(task)) return false; if (!this.activeFilters[task.type].filter(task)) return false;
// Tags // Tags
const selectedTags = this.selectedTags; const selectedTags = this.selectedTags;

View File

@@ -36,7 +36,7 @@
.svg-icon.envelope(v-html="icons.messageIcon") .svg-icon.envelope(v-html="icons.messageIcon")
h4(v-once) Nothing Here Yet h4(v-once) Nothing Here Yet
p(v-once) Select a conversation on the left p(v-once) Select a conversation on the left
chat-message.container-fluid.message-scroll(:chat.sync='activeChat', :inbox='true', ref="chatscroll") chat-message.message-scroll(:chat.sync='activeChat', :inbox='true', ref="chatscroll")
// @TODO: Implement new message header here when we fix the above // @TODO: Implement new message header here when we fix the above

View File

@@ -20,9 +20,9 @@ div
member-details(:member="user") member-details(:member="user")
.row .row
.col-6.offset-3.text-center.nav .col-6.offset-3.text-center.nav
.nav-item(@click='selectedPage = "profile"', :class="{active: selectedPage === 'profile'}") {{ $t('profile') }} .nav-item(@click='selectPage("profile")', :class="{active: selectedPage === 'profile'}") {{ $t('profile') }}
.nav-item(@click='selectedPage = "stats"', :class="{active: selectedPage === 'stats'}") {{ $t('stats') }} .nav-item(@click='selectPage("stats")', :class="{active: selectedPage === 'stats'}") {{ $t('stats') }}
.nav-item(@click='selectedPage = "achievements"', :class="{active: selectedPage === 'achievements'}") {{ $t('achievements') }} .nav-item(@click='selectPage("achievements")', :class="{active: selectedPage === 'achievements'}") {{ $t('achievements') }}
#userProfile.standard-page(v-show='selectedPage === "profile"', v-if='user.profile') #userProfile.standard-page(v-show='selectedPage === "profile"', v-if='user.profile')
.row .row
.col-8 .col-8
@@ -94,9 +94,9 @@ div
.row(v-for='(category, key) in achievements') .row(v-for='(category, key) in achievements')
h2.col-12.text-center {{ $t(key+'Achievs') }} h2.col-12.text-center {{ $t(key+'Achievs') }}
.col-3.text-center(v-for='(achievement, key) in category.achievements') .col-3.text-center(v-for='(achievement, key) in category.achievements')
.box.achievement-container(:id='key', :class='{"achievement-unearned": !achievement.earned}') .box.achievement-container(:id='key + "-achievement"', :class='{"achievement-unearned": !achievement.earned}')
b-popover( b-popover(
:target="'#' + key", :target="'#' + key + '-achievement'",
triggers="hover", triggers="hover",
placement="top", placement="top",
) )
@@ -121,11 +121,11 @@ div
span {{ value }} span {{ value }}
#stats.standard-page(v-show='selectedPage === "stats"', v-if='user.preferences') #stats.standard-page(v-show='selectedPage === "stats"', v-if='user.preferences')
.row .row
.col-6 {{$t('equipment')}} .col-6
h2.text-center h2.text-center {{$t('equipment')}}
.well .well
.col-4.item-wrapper .col-4.item-wrapper
.box(:class='{white: equippedItems.eyewear}') .box(:class='{white: equippedItems.eyewear && equippedItems.eyewear.indexOf("base_0") === -1}')
div(:class="`shop_${equippedItems.eyewear}`") div(:class="`shop_${equippedItems.eyewear}`")
h3 {{$t('eyewear')}} h3 {{$t('eyewear')}}
.col-4.item-wrapper .col-4.item-wrapper
@@ -133,11 +133,11 @@ div
div(:class="`shop_${equippedItems.head}`") div(:class="`shop_${equippedItems.head}`")
h3 {{$t('headGear')}} h3 {{$t('headGear')}}
.col-4.item-wrapper .col-4.item-wrapper
.box(:class='{white: equippedItems.headAccessory}') .box(:class='{white: equippedItems.headAccessory && equippedItems.headAccessory.indexOf("base_0") === -1}')
div(:class="`shop_${equippedItems.headAccessory}`") div(:class="`shop_${equippedItems.headAccessory}`")
h3 {{$t('headAccess')}} h3 {{$t('headAccess')}}
.col-4.item-wrapper .col-4.item-wrapper
.box(:class='{white: equippedItems.backAccessory}') .box(:class='{white: equippedItems.backAccessory && equippedItems.backAccessory.indexOf("base_0") === -1}')
div(:class="`shop_${equippedItems.backAccessory}`") div(:class="`shop_${equippedItems.backAccessory}`")
h3 {{$t('backAccess')}} h3 {{$t('backAccess')}}
.col-4.item-wrapper .col-4.item-wrapper
@@ -145,7 +145,7 @@ div
div(:class="`shop_${equippedItems.armor}`") div(:class="`shop_${equippedItems.armor}`")
h3 {{$t('armor')}} h3 {{$t('armor')}}
.col-4.item-wrapper .col-4.item-wrapper
.box(:class='{white: equippedItems.bodyAccessory}') .box(:class='{white: equippedItems.bodyAccessory && equippedItems.bodyAccessory.indexOf("base_0") === -1}')
div(:class="`shop_${equippedItems.bodyAccessory}`") div(:class="`shop_${equippedItems.bodyAccessory}`")
h3 {{$t('bodyAccess')}} h3 {{$t('bodyAccess')}}
.col-4.item-wrapper .col-4.item-wrapper
@@ -161,7 +161,7 @@ div
h2.text-center {{$t('costume')}} h2.text-center {{$t('costume')}}
.well .well
.col-4.item-wrapper .col-4.item-wrapper
.box(:class='{white: costumeItems.eyewear}') .box(:class='{white: costumeItems.eyewear && costumeItems.eyewear.indexOf("base_0") === -1}')
div(:class="`shop_${costumeItems.eyewear}`") div(:class="`shop_${costumeItems.eyewear}`")
h3 {{$t('eyewear')}} h3 {{$t('eyewear')}}
.col-4.item-wrapper .col-4.item-wrapper
@@ -169,11 +169,11 @@ div
div(:class="`shop_${costumeItems.head}`") div(:class="`shop_${costumeItems.head}`")
h3 {{$t('headGear')}} h3 {{$t('headGear')}}
.col-4.item-wrapper .col-4.item-wrapper
.box(:class='{white: costumeItems.headAccessory}') .box(:class='{white: costumeItems.headAccessory && costumeItems.headAccessory.indexOf("base_0") === -1}')
div(:class="`shop_${costumeItems.headAccessory}`") div(:class="`shop_${costumeItems.headAccessory}`")
h3 {{$t('headAccess')}} h3 {{$t('headAccess')}}
.col-4.item-wrapper .col-4.item-wrapper
.box(:class='{white: costumeItems.backAccessory}') .box(:class='{white: costumeItems.backAccessory && costumeItems.backAccessory.indexOf("base_0") === -1}')
div(:class="`shop_${costumeItems.backAccessory}`") div(:class="`shop_${costumeItems.backAccessory}`")
h3 {{$t('backAccess')}} h3 {{$t('backAccess')}}
.col-4.item-wrapper .col-4.item-wrapper
@@ -181,7 +181,7 @@ div
div(:class="`shop_${costumeItems.armor}`") div(:class="`shop_${costumeItems.armor}`")
h3 {{$t('armor')}} h3 {{$t('armor')}}
.col-4.item-wrapper .col-4.item-wrapper
.box(:class='{white: costumeItems.bodyAccessory}') .box(:class='{white: costumeItems.bodyAccessory && costumeItems.bodyAccessory.indexOf("base_0") === -1}')
div(:class="`shop_${costumeItems.bodyAccessory}`") div(:class="`shop_${costumeItems.bodyAccessory}`")
h3 {{$t('bodyAccess')}} h3 {{$t('bodyAccess')}}
.col-4.item-wrapper .col-4.item-wrapper
@@ -189,8 +189,8 @@ div
div(:class="`shop_${costumeItems.weapon}`") div(:class="`shop_${costumeItems.weapon}`")
h3 {{$t('mainHand')}} h3 {{$t('mainHand')}}
.col-4.item-wrapper .col-4.item-wrapper
.box(:class='{white: user.preferences.background}') .box(:class='{white: user.preferences.background}', style="overflow:hidden")
div(:class="user.preferences.background") div(:class="'background_' + user.preferences.background")
h3 {{$t('background')}} h3 {{$t('background')}}
.col-4.item-wrapper .col-4.item-wrapper
.box(:class='{white: costumeItems.shield && costumeItems.shield.indexOf("base_0") === -1}') .box(:class='{white: costumeItems.shield && costumeItems.shield.indexOf("base_0") === -1}')
@@ -326,7 +326,7 @@ div
} }
.pet, .mount { .pet, .mount {
margin-top: -1.6em; margin-top: -1.8em !important;
} }
.header { .header {
@@ -660,6 +660,7 @@ export default {
this.editingProfile.name = user.profile.name; this.editingProfile.name = user.profile.name;
this.editingProfile.imageUrl = user.profile.imageUrl; this.editingProfile.imageUrl = user.profile.imageUrl;
this.editingProfile.blurb = user.profile.blurb;
if (!user.achievements.quests) user.achievements.quests = {}; if (!user.achievements.quests) user.achievements.quests = {};
if (!user.achievements.challenges) user.achievements.challenges = {}; if (!user.achievements.challenges) user.achievements.challenges = {};
@@ -700,6 +701,11 @@ export default {
}, },
}, },
methods: { methods: {
selectPage (page) {
this.selectedPage = page;
// @TODO: rename this property?
this.$store.state.profileOptions.startingPage = page;
},
sendMessage () { sendMessage () {
this.$store.state.userIdToMessage = this.user._id; this.$store.state.userIdToMessage = this.user._id;
this.$root.$emit('show::modal', 'private-message'); this.$root.$emit('show::modal', 'private-message');

View File

@@ -1,6 +1,32 @@
import intersection from 'lodash/intersection'; import intersection from 'lodash/intersection';
export default { export default {
filters: {
// https://stackoverflow.com/questions/2685911/is-there-a-way-to-round-numbers-into-a-reader-friendly-format-e-g-1-1k
abbrNum: (number) => {
let decPlaces = 2;
decPlaces = Math.pow(10, decPlaces);
let abbrev = ['k', 'm', 'b', 't'];
for (let i = abbrev.length - 1; i >= 0; i--) {
let size = Math.pow(10, (i + 1) * 3);
if (size <= number) {
number = Math.round(number * decPlaces / size) / decPlaces;
if (number === 1000 && i < abbrev.length - 1) {
number = 1;
i++;
}
number += abbrev[i];
break;
}
}
return number;
},
},
methods: { methods: {
isMemberOfGroup (user, group) { isMemberOfGroup (user, group) {
if (group._id === this.$store.state.constants.TAVERN_ID) return true; if (group._id === this.$store.state.constants.TAVERN_ID) return true;

View File

@@ -21,8 +21,11 @@ export async function fetchMember (store, payload) {
export async function getGroupInvites (store, payload) { export async function getGroupInvites (store, payload) {
let url = `${apiV3Prefix}/groups/${payload.groupId}/invites`; let url = `${apiV3Prefix}/groups/${payload.groupId}/invites`;
if (payload.includeAllPublicFields) {
url += '?includeAllPublicFields=true';
}
let response = await axios.get(url); let response = await axios.get(url);
return response; return response.data.data;
} }
export async function getChallengeMembers (store, payload) { export async function getChallengeMembers (store, payload) {

View File

@@ -95,6 +95,7 @@ export default function () {
challengeOptions: { challengeOptions: {
cloning: false, cloning: false,
tasksToClone: {}, tasksToClone: {},
workingChallenge: {},
}, },
editingGroup: {}, // @TODO move to local state editingGroup: {}, // @TODO move to local state
// content data, frozen to prevent Vue from modifying it since it's static and never changes // content data, frozen to prevent Vue from modifying it since it's static and never changes

View File

@@ -89,7 +89,7 @@
"joinedChallenge": "Joined a Challenge", "joinedChallenge": "Joined a Challenge",
"joinedChallengeText": "This user put themselves to the test by joining a Challenge!", "joinedChallengeText": "This user put themselves to the test by joining a Challenge!",
"myChallenges": "My Challenges", "myChallenges": "My Challenges",
"findChallenges": "Find Challenges", "findChallenges": "Discover Challenges",
"noChallengeTitle": "You don't have any Challenges.", "noChallengeTitle": "You don't have any Challenges.",
"challengeDescription1": "Challenges are community events in which players compete and earn prizes by completing a group of related tasks.", "challengeDescription1": "Challenges are community events in which players compete and earn prizes by completing a group of related tasks.",
"challengeDescription2": "Find recommended Challenges based on your interests, browse Habitica's public Challenges, or create your own Challenges.", "challengeDescription2": "Find recommended Challenges based on your interests, browse Habitica's public Challenges, or create your own Challenges.",
@@ -123,5 +123,6 @@
"summaryTooLong": "Summary is too long", "summaryTooLong": "Summary is too long",
"descriptionRequired": "Description is required", "descriptionRequired": "Description is required",
"locationRequired": "Location of challenge is required ('Add to')", "locationRequired": "Location of challenge is required ('Add to')",
"categoiresRequired": "One or more categories must be selected" "categoiresRequired": "One or more categories must be selected",
"viewProgressOf": "View Progress Of"
} }

View File

@@ -382,5 +382,8 @@
"questOwnerRewards": "Quest Owner Rewards", "questOwnerRewards": "Quest Owner Rewards",
"updateParty": "Update Party", "updateParty": "Update Party",
"upgrade": "Upgrade", "upgrade": "Upgrade",
"selectPartyMember": "Select a Party Member" "selectPartyMember": "Select a Party Member",
"areYouSureDeleteMessage": "Are you sure you want to delete this message?",
"reverseChat": "Reverse Chat",
"invites": "Invites"
} }

View File

@@ -170,6 +170,8 @@ api.getMemberAchievements = {
}; };
// Return a request handler for getMembersForGroup / getInvitesForGroup / getMembersForChallenge // Return a request handler for getMembersForGroup / getInvitesForGroup / getMembersForChallenge
// @TODO: This violates the Liskov substitution principle. We should create factory functions. See Webhooks for a good example
function _getMembersForItem (type) { function _getMembersForItem (type) {
// check for allowed `type` // check for allowed `type`
if (['group-members', 'group-invites', 'challenge-members'].indexOf(type) === -1) { if (['group-members', 'group-invites', 'challenge-members'].indexOf(type) === -1) {
@@ -243,9 +245,18 @@ function _getMembersForItem (type) {
} else if (type === 'group-invites') { } else if (type === 'group-invites') {
if (group.type === 'guild') { // eslint-disable-line no-lonely-if if (group.type === 'guild') { // eslint-disable-line no-lonely-if
query['invitations.guilds.id'] = group._id; query['invitations.guilds.id'] = group._id;
if (req.query.includeAllPublicFields === 'true') {
fields = memberFields;
addComputedStats = true;
}
} else { } else {
query['invitations.party.id'] = group._id; // group._id and not groupId because groupId could be === 'party' query['invitations.party.id'] = group._id; // group._id and not groupId because groupId could be === 'party'
// @TODO invitations are now stored like this: `'invitations.parties': []` Probably need a database index for it. // @TODO invitations are now stored like this: `'invitations.parties': []` Probably need a database index for it.
if (req.query.includeAllPublicFields === 'true') {
fields = memberFields;
addComputedStats = true;
}
} }
} }