New client random catchup (#8891)

* Added initial challenge pages

* Added challenge item and find guilds page

* Added challenge detail

* Added challenge modals

* Ported over challenge service code

* Ported over challenge ctrl code

* Added styles and column

* Minor modal updates

* Removed duplicate keys

* Fixed casing

* Added initial chat component

* Added copy as todo modal

* Added sync

* Added chat to groups

* Fixed lint

* Added notification service

* Added tag services

* Added notifications

* Added hall

* Added analytics

* Added http interceptor

* Added initial autocomplete

* Added initial footer component

* Began coding and designing footer

* Added inital hall

* Ported over inital group plan ctrl code

* Added initial invite modal

* Added initial member detail modal

* Added initial notification menu

* Ported over inital notification code

* Fixed import line

* Fixed autocomplete import casing
This commit is contained in:
Keith Holliday
2017-07-25 08:24:40 -06:00
committed by GitHub
parent 86a07a4949
commit 16b244d5c6
49 changed files with 2126 additions and 54 deletions

View File

@@ -0,0 +1,223 @@
<template lang="pug">
.row
footer.container-fluid
.row
.col-2
h3 iOS App
h3 Android App
.col-2
h3 Company
ul
li How it Works
li Blog
li Tumblr
li FAQ
li News
li Merchandis
li Press Kit
li Contact Us
.col-2
h3 Community
ul
li Community Guidelines
li Submit a Bug
li Request a Feature
li Add-Ons & Extensions
li Forum
li Kickstarter
li Facebook
li Reddit
.col-6
.row
.col-6
h3 Developers
ul
li APIv3
li Data Display Tool
li Guidance for Blacksmiths
li The Forge - Developer Blog
.col-6
h3 Social
.social-circle Twitter
.social-circle Instagram
.social-circle Facebook
.row
.col-10
| Were an open source project that depends on our users for support. The money you donate helps us keep the servers running, maintain a small staff, develop new features, and provide incentives for our volunteers.
.col-2
button.btn.btn-primary Donate
.row
hr.col-12
.row
.col-4
| © 2017 Habitica. All rights reserved.
.col-4.text-center
.logo.svg-icon(v-html='icons.gryphon')
.col-4.text-right
span Privacy Policy
span Terms of Use
</template>
<style scoped>
footer {
background-color: #e1e0e3;
height: 376px;
padding-left: 6em;
padding-right: 6em;
padding-top: 3em;
margin: 0;
color: #878190;
}
h3 {
color: #878190;
}
ul {
padding-left: 0;
list-style-type: none;
}
li {
margin-bottom: .5em;
}
.social-circle {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #c3c0c7;
display: inline-block;
margin-right: 1em;
}
.logo.svg-icon {
width: 24px;
margin: 0 auto;
color: #c3c0c7;
}
</style>
<script>
import gryphon from 'assets/svg/gryphon.svg';
// const IS_PRODUCTION = process.env.NODE_ENV === 'production'; // eslint-disable-line no-process-env
export default {
data () {
return {
icons: Object.freeze({
gryphon,
}),
};
},
methods: {
// @TODO: add https://github.com/HabitRPG/habitica/blob/develop/website/client-old/js/controllers/footerCtrl.js$scope.setHealthLow = function(){
// $scope.setHealthLow = function(){
// User.set({
// 'stats.hp': 1
// });
// };
//
// $scope.addMissedDay = function(numberOfDays){
// if (!confirm("Are you sure you want to reset the day by " + numberOfDays + " day(s)?")) return;
//
// User.setCron(numberOfDays);
// };
//
// $scope.addTenGems = function(){
// User.addTenGems();
// };
//
// $scope.addHourglass = function(){
// User.addHourglass();
// };
//
// $scope.addGold = function(){
// User.set({
// 'stats.gp': User.user.stats.gp + 500,
// });
// };
//
// $scope.addMana = function(){
// User.set({
// 'stats.mp': User.user.stats.mp + 500,
// });
// };
//
// $scope.addLevelsAndGold = function(){
// User.set({
// 'stats.exp': User.user.stats.exp + 10000,
// 'stats.gp': User.user.stats.gp + 10000,
// 'stats.mp': User.user.stats.mp + 10000
// });
// };
//
// $scope.addOneLevel = function(){
// User.set({
// 'stats.exp': User.user.stats.exp + (Math.round(((Math.pow(User.user.stats.lvl, 2) * 0.25) + (10 * User.user.stats.lvl) + 139.75) / 10) * 10)
// });
// };
//
// $scope.addQuestProgress = function(){
// $http({
// method: "POST",
// url: 'api/v3/debug/quest-progress'
// })
// .then(function (response) {
// Notification.text('Quest progress increased');
// User.sync();
// })
// };
//
// $scope.makeAdmin = function () {
// User.makeAdmin();
// };
//
// $scope.openModifyInventoryModal = function () {
// $rootScope.openModal('modify-inventory', {controller: 'FooterCtrl', scope: $scope });
// $scope.showInv = { };
// $scope.inv = {
// gear: {},
// special: {},
// pets: {},
// mounts: {},
// eggs: {},
// hatchingPotions: {},
// food: {},
// quests: {},
// };
// $scope.setAllItems = function (type, value) {
// var set = $scope.inv[type];
//
// for (var item in set) {
// if (set.hasOwnProperty(item)) {
// set[item] = value;
// }
// }
// };
// };
//
// $scope.modifyInventory = function () {
// $http({
// method: "POST",
// url: 'api/v3/debug/modify-inventory',
// data: {
// gear: $scope.showInv.gear ? $scope.inv.gear : null,
// special: $scope.showInv.special ? $scope.inv.special : null,
// pets: $scope.showInv.pets ? $scope.inv.pets : null,
// mounts: $scope.showInv.mounts ? $scope.inv.mounts : null,
// eggs: $scope.showInv.eggs ? $scope.inv.eggs : null,
// hatchingPotions: $scope.showInv.hatchingPotions ? $scope.inv.hatchingPotions : null,
// food: $scope.showInv.food ? $scope.inv.food : null,
// quests: $scope.showInv.quests ? $scope.inv.quests : null,
// }
// })
// .then(function (response) {
// Notification.text('Inventory updated. Refresh or sync.');
// })
// };
// }
},
};
</script>

View File

@@ -24,59 +24,59 @@
</template>
<style lang="scss" scoped>
@import '~client/assets/scss/colors.scss';
@import '~client/assets/scss/colors.scss';
#app-header {
padding-left: 14px;
margin-top: 56px;
background: $purple-50;
height: 204px;
color: $header-color;
flex-wrap: nowrap;
position: relative;
}
.no-party, .party-members {
flex-grow: 1;
}
.party-members {
}
.view-party {
position: absolute;
z-index: 10;
right: 0;
padding-right: 40px;
height: 100%;
background-image: linear-gradient(to right, rgba($purple-50, 0), $purple-50);
.btn {
margin-top: 75%;
}
}
.no-party {
.small-text {
#app-header {
padding-left: 14px;
margin-top: 56px;
background: $purple-50;
height: 204px;
color: $header-color;
flex-wrap: nowrap;
position: relative;
}
h3 {
color: $white;
margin-bottom: 4px;
.no-party, .party-members {
flex-grow: 1;
}
.btn {
margin-top: 16px;
.party-members {
}
.view-party {
position: absolute;
z-index: 10;
right: 0;
padding-right: 40px;
height: 100%;
background-image: linear-gradient(to right, rgba($purple-50, 0), $purple-50);
.btn {
margin-top: 75%;
}
}
.no-party {
.small-text {
color: $header-color;
flex-wrap: nowrap;
}
h3 {
color: $white;
margin-bottom: 4px;
}
.btn {
margin-top: 16px;
}
}
}
</style>
<script>
import { mapGetters, mapActions } from 'client/libs/store';
import MemberDetails from './memberDetails';
import createPartyModal from './guilds/createPartyModal';
import createPartyModal from './groups/createPartyModal';
export default {
components: {

View File

@@ -24,6 +24,8 @@ div
router-link.dropdown-item(:to="{name: 'tavern'}") {{ $t('tavern') }}
router-link.dropdown-item(:to="{name: 'myGuilds'}") {{ $t('myGuilds') }}
router-link.dropdown-item(:to="{name: 'guildsDiscovery'}") {{ $t('guildsDiscovery') }}
router-link.nav-item.dropdown(tag="li", :to="{name: 'groupPlan'}", :class="{'active': $route.path.startsWith('/group-plan')}")
a.nav-link(v-once) {{ $t('group') }}
router-link.nav-item(tag="li", :to="{name: 'myChallenges'}", exact)
a.nav-link(v-once) {{ $t('challenges') }}
router-link.nav-item.dropdown(tag="li", to="/help", :class="{'active': $route.path.startsWith('/help')}")
@@ -42,8 +44,7 @@ div
.item-with-icon
.svg-icon(v-html="icons.gold")
span {{user.stats.gp | roundBigNumber}}
.item-with-icon.item-notifications
.svg-icon(v-html="icons.notifications")
notification-menu
router-link.dropdown.item-with-icon.item-user(:to="{name: 'avatar'}")
.svg-icon(v-html="icons.user")
.dropdown-menu.dropdown-menu-right.user-dropdown
@@ -203,21 +204,21 @@ div
import { mapState, mapGetters } from 'client/libs/store';
import gemIcon from 'assets/svg/gem.svg';
import goldIcon from 'assets/svg/gold.svg';
import notificationsIcon from 'assets/svg/notifications.svg';
import userIcon from 'assets/svg/user.svg';
import logo from 'assets/svg/logo.svg';
import InboxModal from './userMenu/inbox.vue';
import notificationMenu from './notificationMenu';
export default {
components: {
InboxModal,
notificationMenu,
},
data () {
return {
icons: Object.freeze({
gem: gemIcon,
gold: goldIcon,
notifications: notificationsIcon,
user: userIcon,
logo,
}),

View File

@@ -0,0 +1,66 @@
<template lang="pug">
div.autocomplete-selection
div(v-for='result in searchResults', @click='select(result)') {{ result }}
</template>
<style scoped>
</style>
<script>
export default {
props: ['selections', 'text'],
data () {
return {
currentSearch: '',
searchActive: false,
currentSearchPosition: 0,
// @TODO: HAve this passed
tmpSelections: [
'TheHollidayInn',
'Paglias',
],
};
},
computed: {
searchResults () {
if (!this.searchActive) return [];
let currentSearch = this.text.substring(this.currentSearchPosition + 1, this.text.length);
return this.tmpSelections.filter((option) => {
return option.toLowerCase().indexOf(currentSearch.toLowerCase()) !== -1;
});
},
},
watch: {
text (newText) {
if (newText[newText.length - 1] !== '@') return;
this.searchActive = true;
this.currentSearchPosition = newText.length - 1;
},
// @TODO: implement position
// caretChanged = function(newCaretPos) {
// var relativeelement = $('.chat-form div:first');
// var textarea = $('.chat-form textarea');
// var userlist = $('.list-at-user');
// var offset = {
// x: textarea.offset().left - relativeelement.offset().left,
// y: textarea.offset().top - relativeelement.offset().top,
// };
// if(relativeelement) {
// var caretOffset = InputCaret.getPosition(textarea);
// userlist.css({
// left: caretOffset.left + offset.x,
// top: caretOffset.top + offset.y + 16
// });
// }
// }
},
methods: {
select (result) {
let newText = this.text.slice(0, this.currentSearchPosition + 1) + result;
this.searchActive = false;
this.$emit('select', newText);
},
},
};
</script>

View File

@@ -0,0 +1,192 @@
<template lang="pug">
.standard-page
div(v-if='activePage === PAGES.CREATE_GROUP')
h2.text-center {{ $t('createAGroup') }}
.col-xs-12
.col-md-12.form-horizontal
.form-group
label.control-label(for='new-group-name') {{ $t('newGroupName', {groupType: 'text'}) }}
input.form-control#new-group-name.input-medium.option-content(required, type='text', :placeholder="$t('newGroupName', {groupType: 'text'})", v-model='newGroup.name')
.form-group
label(for='new-group-description') {{ $t('description') }}
textarea.form-control#new-group-description.option-content(cols='3', :placeholder="$t('description')", v-model='newGroup.description')
.form-group(v-if='type === "guild"')
.radio
label
input(type='radio', name='new-group-privacy', value='public', v-model='newGroup.privacy')
| {{ $t('public') }}
.radio
label
input(type='radio', name='new-group-privacy', value='private', v-model='newGroup.privacy')
| {{ $t('inviteOnly') }}
br
input.btn.btn-default(type='submit', :disabled='!newGroup.privacy && !newGroup.name', :value="$t('create')")
span.gem-cost {{ '4 ' + $t('gems') }}
p
small {{ $t('gemCost') }}
.form-group
.checkbox
label
input(type='checkbox', v-model='newGroup.leaderOnly.challenges')
| {{ $t('leaderOnlyChallenges') }}
.form-group(v-if='type === "party"')
input.btn.btn-default.form-control(type='submit', :value="$t('create')")
br
br
.row
.col-sm-6.col-sm-offset-3
a.btn.btn-primary.btn-lg.btn-block(@click="createGroup()", :disabled="!newGroupIsReady") {{ $t('create') }}
div(v-if='activePage === PAGES.UPGRADE_GROUP')
h2.text-center {{ $t('upgradeTitle') }}
.row.text-center
.col-6.col-offset-3
a.purchase.btn.btn-primary(@click='upgradeGroup(PAYMENTS.STRIPE)') {{ $t('card') }}
a.purchase(@click='upgradeGroup(PAYMENTS.AMAZON)')
img(src='https://payments.amazon.com/gp/cba/button', :alt="$t('amazonPayments')")
// @TODO: Add paypal
.row
.col-md-6.col-md-offset-3
br
.text-center {{ $t('groupSubscriptionPrice') }}
div(v-if='activePage === PAGES.BENEFITS')
h2.text-center {{ $t('groupBenefitsTitle') }}
.row(style="font-size: 2rem;")
.col-md-6.col-md-offset-3.text-center {{ $t('groupBenefitsDescription') }}
.row.row-margin
.col-md-4
h2 {{ $t('teamBasedTasks') }}
div
// shared tasks
h3
span.glyphicon.glyphicon-ok-circle(style='margin-right: 1.5rem;')
| {{ $t('groupBenefitOneTitle') }}
span {{ $t('groupBenefitOneDescription') }}
div
// assign tasks
h3
span.glyphicon.glyphicon-ok-circle(style='margin-right: 1.5rem;')
| {{ $t('groupBenefitTwoTitle') }}
span {{ $t('groupBenefitTwoDescription') }}
div
// claim tasks
h3
span.glyphicon.glyphicon-ok-circle(style='margin-right: 1.5rem;')
| {{ $t('groupBenefitThreeTitle') }}
span {{ $t('groupBenefitThreeDescription') }}
div
// mark tasks
h3
span.glyphicon.glyphicon-ok-circle(style='margin-right: 1.5rem;')
| {{ $t('groupBenefitFourTitle') }}
span {{ $t('groupBenefitFourDescription') }}
div
// group managers
h3
span.glyphicon.glyphicon-ok-circle(style='margin-right: 1.5rem;')
| {{ $t('groupBenefitEightTitle') }}
span {{ $t('groupBenefitEightDescription') }}
.col-md-4
h2 {{ $t('specializedCommunication') }}
div
// chat privately
h3
span.glyphicon.glyphicon-ok-circle(style='margin-right: 1.5rem;')
| {{ $t('groupBenefitFiveTitle') }}
span {{ $t('groupBenefitFiveDescription') }}
div
h3
span.glyphicon.glyphicon-ok-circle(style='margin-right: 1.5rem;')
| {{ $t('groupBenefitMessageLimitTitle') }}
span {{ $t('groupBenefitMessageLimitDescription') }}
.col-md-4
h2 {{ $t('funExtras') }}
div
// free subscription
h3
span.glyphicon.glyphicon-ok-circle(style='margin-right: 1.5rem;')
| {{ $t('groupBenefitSixTitle') }}
span {{ $t('groupBenefitSixDescription') }}
div
// exclusive mount
h3
span.glyphicon.glyphicon-ok-circle(style='margin-right: 1.5rem;')
| {{ $t('groupBenefitSevenTitle') }}
br
br
.row
.col-sm-6.col-sm-offset-3
a.btn.btn-primary.btn-lg.btn-block(ui-sref="options.social.newGroup") {{ $t('createAGroup') }}
.row
.col-md-6.col-md-offset-3
br
.text-center {{ $t('groupSubscriptionPrice') }}
</template>
<script>
export default {
data () {
return {
PAGES: {
CREATE_GROUP: 'create-group',
UPGRADE_GROUP: 'upgrade-group',
},
// @TODO: Import from payment library?
PAYMENTS: {
AMAZON: 'amazon',
STRIPE: 'stripe',
},
newGroup: {
type: 'guild',
privacy: 'private',
name: '',
leaderOnly: {
challenges: false,
},
},
activePage: '',
type: 'guild', // Guild or Party @TODO enum this
};
},
mounted () {
this.activePage = this.PAGES.CREATE_GROUP;
},
computed: {
newGroupIsReady () {
return Boolean(this.newGroup.name);
},
},
methods: {
changePage (page) {
this.activePage = page;
window.scrollTo(0, 0);
},
createGroup () {
this.changePage(this.PAGES.UPGRADE_GROUP);
},
upgradeGroup (paymentType) {
// let subscriptionKey = 'group_monthly'; // @TODO: Get from content API?
if (paymentType === this.PAYMENTS.STRIPE) {
// Payments.showStripe({
// subscription: subscriptionKey,
// coupon: null,
// groupToCreate: this.newGroup
// });
} else if (paymentType === this.PAYMENTS.AMAZON) {
// Payments.amazonPayments.init({
// type: 'subscription',
// subscription: subscriptionKey,
// coupon: null,
// groupToCreate: this.newGroup
// });
}
},
},
};
</script>

View File

@@ -1,6 +1,7 @@
<template lang="pug">
.row(v-if="group")
group-form-modal
invite-modal
.clearfix.col-8
.row
.col-6.title-details
@@ -40,7 +41,7 @@
.button-container
button.btn.btn-success(class='btn-success', v-if='!isMember') {{ $t('join') }}
.button-container
button.btn.btn-primary(v-once) {{$t('invite')}}
button.btn.btn-primary(v-once, @click='showInviteModal()') {{$t('invite')}}
.button-container
button.btn.btn-primary(v-once, v-if='!isLeader') {{$t('messageGuildLeader')}}
.button-container
@@ -355,6 +356,8 @@ import ownedQuestsModal from './ownedQuestsModal';
import quests from 'common/script/content/quests';
import percent from 'common/script/libs/percent';
import groupFormModal from './groupFormModal';
import inviteModal from './inviteModal';
import memberModal from '../members/memberModal';
import chatMessage from '../chat/chatMessages';
import bCollapse from 'bootstrap-vue/lib/components/collapse';
@@ -380,12 +383,14 @@ export default {
props: ['groupId'],
components: {
membersModal,
memberModal,
ownedQuestsModal,
bCollapse,
bCard,
bTooltip,
groupFormModal,
chatMessage,
inviteModal,
},
directives: {
bToggle,
@@ -507,6 +512,9 @@ export default {
this.$store.state.editingGroup = this.group;
this.$root.$emit('show::modal', 'guild-form');
},
showInviteModal () {
this.$root.$emit('show::modal', 'invite-modal');
},
async fetchGuild () {
let group = await this.$store.dispatch('guilds:getGroup', {groupId: this.groupId});
if (this.isParty) {

View File

@@ -0,0 +1,147 @@
<template lang="pug">
b-modal#invite-modal(:title="$t('inviteFriends')", size='lg')
.modal-body
p.alert.alert-info(v-html="$t('inviteAlertInfo')")
.form-horizontal
table.table.table-striped
thead
tr
th {{ $t('userId') }}
tbody
tr(v-for='user in invitees')
td
input.form-control(type='text', v-model='user.uuid')
tr
td
button.btn.btn-xs.pull-right(@click='addUuid()')
i.glyphicon.glyphicon-plus
| +
tr
td
.col-6.col-offset-6
button.btn.btn-primary.btn-block(@click='inviteNewUsers("uuid")') {{sendInviteText}}
hr
p.alert.alert-info {{ $t('inviteByEmail') }}
.form-horizontal
table.table.table-striped
thead
tr
th {{ $t('name') }}
th {{ $t('email') }}
tbody
tr(v-for='email in emails')
td
input.form-control(type='text', v-model='email.name')
td
input.form-control(type='email', v-model='email.email')
tr
td(colspan=2)
a.btn.btn-xs.pull-right(@click='addEmail()')
i.glyphicon.glyphicon-plus
| +
tr
td.form-group(colspan=2)
label.col-sm-1.control-label {{ $t('byColon') }}
.col-sm-5
input.form-control(type='text', v-model='inviter')
.col-sm-6
button.btn.btn-primary.btn-block(@click='inviteNewUsers("email")') {{sendInviteText}}
</template>
<script>
import { mapState } from 'client/libs/store';
import filter from 'lodash/filter';
import map from 'lodash/map';
import bModal from 'bootstrap-vue/lib/components/modal';
export default {
props: ['group'],
data () {
return {
invitees: [],
emails: [],
};
},
components: {
bModal,
},
computed: {
...mapState({user: 'user.data'}),
inviter () {
return this.user.profile.name;
},
sendInviteText () {
if (!this.group) return 'Send Invites';
return this.group.sendInviteText;
},
},
methods: {
addUuid () {
this.invitees.push({uuid: ''});
},
addEmail () {
this.emails.push({name: '', email: ''});
},
inviteNewUsers (inviteMethod) {
if (!this.group._id) {
if (!this.group.name) this.group.name = this.$t('possessiveParty', {name: this.user.profile.name});
// @TODO: Add dispatch
// return Groups.Group.create(this.group)
// .then(function(response) {
// this.group = response.data.data;
// _inviteByMethod(inviteMethod);
// });
}
this.inviteByMethod(inviteMethod);
},
// inviteByMethod (inviteMethod) {
// let invitationDetails;
//
// if (inviteMethod === 'email') {
// let emails = this.getEmails();
// invitationDetails = { inviter: this.inviter, emails };
// } else if (inviteMethod === 'uuid') {
// let uuids = this.getOnlyUuids();
// invitationDetails = { uuids };
// } else {
// return alert('Invalid invite method.');
// }
// @TODO: Add dispatch
// Groups.Group.invite(this.group._id, invitationDetails)
// .then(function () {
// let invitesSent = invitationDetails.emails || invitationDetails.uuids;
// let invitationString = invitesSent.length > 1 ? 'invitationsSent' : 'invitationSent';
//
// Notification.text(window.env.t(invitationString));
//
// _resetInvitees();
//
// if (this.group.type === 'party') {
// $rootScope.hardRedirect('/#/options/groups/party');
// } else {
// $rootScope.hardRedirect('/#/options/groups/guilds/' + this.group._id);
// }
// }, function(){
// _resetInvitees();
// });
// },
getOnlyUuids () {
let uuids = map(this.invitees, 'uuid');
let filteredUuids = filter(uuids, (id) => {
return id !== '';
});
return filteredUuids;
},
getEmails () {
let emails = filter(this.emails, (obj) => {
return obj.email !== '';
});
return emails;
},
},
};
</script>

View File

@@ -10,6 +10,7 @@
h3(v-once) {{ $t('welcomeToTavern') }}
textarea(:placeholder="$t('chatPlaceHolder')", v-model='newMessage')
autocomplete(:text='newMessage', v-on:select="selectedAutocomplete")
button.btn.btn-secondary.send-chat.float-right(v-once, @click='sendMessage()') {{ $t('send') }}
.container.community-guidelines(v-if='communityGuidelinesAccepted')
@@ -282,6 +283,7 @@ import { mapState } from 'client/libs/store';
import { TAVERN_ID } from '../../../common/script/constants';
import chatMessage from '../chat/chatMessages';
import autocomplete from '../chat/autoComplete';
import gemIcon from 'assets/svg/gem.svg';
import questIcon from 'assets/svg/quest.svg';
@@ -294,6 +296,7 @@ import downIcon from 'assets/svg/down.svg';
export default {
components: {
chatMessage,
autocomplete,
},
data () {
return {
@@ -399,6 +402,9 @@ export default {
this.group = await this.$store.dispatch('guilds:getGroup', {groupId: TAVERN_ID});
},
methods: {
selectedAutocomplete (newText) {
this.newMessage = newText;
},
aggreeToGuideLines () {
// @TODO:
},

View File

@@ -0,0 +1,175 @@
<template lang="pug">
.row
small.muted(v-html="$t('blurbHallContributors')")
.well(v-if='user.contributor.admin')
h2 {{ $t('rewardUser') }}
form(v-submit='loadHero(_heroID)')
.form-group
input.form-control(type='text', v-model='_heroID', placeholder {{ $t('UUID') }})
.form-group
input.btn.btn-default(type='submit')
| {{ $t('loadUser') }}
form(v-show='hero', v-submit='saveHero(hero)')
a(v-click='clickMember(hero._id, true)')
h3 {{hero.profile.name}}
.form-group
input.form-control(type='text', v-model='hero.contributor.text', placeholder {{ $t('contribTitle') }})
.form-group
label {{ $t('contribLevel') }}
input.form-control(type='number', v-model='hero.contributor.level')
small {{ $t('contribHallText') }}
|&nbsp;
a(target='_blank', href='https://trello.com/c/wkFzONhE/277-contributor-gear') {{ $t('moreDetails') }}
|,&nbsp;
a(target='_blank', href='https://github.com/HabitRPG/habitica/issues/3801') {{ $t('moreDetails2') }}
.form-group
textarea.form-control(cols=5, placeholder {{ $t('contributions') }}, v-model='hero.contributor.contributions')
//include ../../shared/formattiv-help
hr
.form-group
label {{ $t('balance') }}
input.form-control(type='number', step="any", v-model='hero.balance')
small {{ '`user.balance`' + this.$t('notGems') }}
accordion
accordion-group(heading='Items')
h4 Update Item
.form-group.well
input.form-control(type='text',placeholder='Path (eg, items.pets.BearCub-Base)',v-model='hero.itemPath')
small.muted Enter the <strong>item path</strong>. E.g., <code>items.pets.BearCub-Zombie</code> or <code>items.gear.owned.head_special_0</code> or <code>items.gear.equipped.head</code>. You can find all the item paths below.
br
input.form-control(type='text',placeholder='Value (eg, 5)',v-model='hero.itemVal')
small.muted Enter the <strong>item value</strong>. E.g., <code>5</code> or <code>false</code> or <code>head_warrior_3</code>. All values are listed in the All Item Paths section below.
accordion
accordion-group(heading='All Item Paths')
pre {{allItemPaths}}
accordion-group(heading='Current Items')
pre {{toJson(hero.items, true)}}
accordion-group(heading='Auth')
h4 Auth
pre {{toJson(hero.auth)}}
.form-group
.checkbox
label
input(type='checkbox', v-model='hero.flags.chatRevoked')
| Chat Privileges Revoked
.form-group
.checkbox
label
input(type='checkbox', v-model='hero.auth.blocked')
| Blocked
// h4 Backer Status
// Add backer stuff like tier, disable adds, etcs
.form-group
input.form-control.btn.btn-primary(type='submit')
| {{ $t('save') }}
.table-responsive
table.table.table-striped
thead
tr
th {{ $t('name') }}
th(v-if='user.contributor.admin') {{ $t('UUID') }}
th {{ $t('contribLevel') }}
th {{ $t('title') }}
th {{ $t('contributions') }}
tbody
tr(v-repeat='hero in heroes')
td
span(v-if='hero.contributor.admin', :popover="$t('gamemaster')", popover-trigger='mouseenter', popover-placement='right')
a.label.label-default(v-class='userLevelStyle(hero)', v-click='clickMember(hero._id, true)')
| {{hero.profile.name}}&nbsp;
span(v-class='userAdminGlyphiconStyle(hero)')
span(v-if='!hero.contributor.admin')
a.label.label-default(v-class='userLevelStyle(hero)', v-click='clickMember(hero._id, true)') {{hero.profile.name}}
td(v-if='user.contributor.admin', v-click='populateContributorInput(hero._id, $index)').btn-link {{hero._id}}
td {{hero.contributor.level}}
td {{hero.contributor.text}}
td
markdown(text='hero.contributor.contributions', target='_blank')
</template>
<script>
import keys from 'lodash/keys';
import each from 'lodash/each';
import { mapState } from 'client/libs/store';
import quests from 'common/script/content/quests';
import { mountInfo, petInfo } from 'common/script/content/stable';
import { food, hatchingPotions, special } from 'common/script/content';
import gear from 'common/script/content/gear';
export default {
data () {
return {
heroes: [],
hero: {},
currentHeroIndex: -1,
allItemPaths: this.getAllItemPaths(),
quests,
mountInfo,
petInfo,
food,
hatchingPotions,
special,
gear,
};
},
async mounted () {
this.heroes = await this.$store.dispatch('hall:getHeroes');
},
computed: {
...mapState({user: 'user.data'}),
},
methods: {
getAllItemPaths () {
let questsFormat = this.getFormattedItemReference('items.quests', keys(this.quests), 'Numeric Quantity');
let mountsFormat = this.getFormattedItemReference('items.mounts', keys(this.mountInfo), 'Boolean');
let foodFormat = this.getFormattedItemReference('items.food', keys(this.food), 'Numeric Quantity');
let eggsFormat = this.getFormattedItemReference('items.eggs', keys(this.eggs), 'Numeric Quantity');
let hatchingPotionsFormat = this.getFormattedItemReference('items.hatchingPotions', keys(this.hatchingPotions), 'Numeric Quantity');
let petsFormat = this.getFormattedItemReference('items.pets', keys(this.petInfo), '-1: Owns Mount, 0: Not Owned, 1-49: Progress to mount');
let specialFormat = this.getFormattedItemReference('items.special', keys(this.special), 'Numeric Quantity');
let gearFormat = this.getFormattedItemReference('items.gear.owned', keys(this.gear.flat), 'Boolean');
let equippedGearFormat = '\nEquipped Gear:\n\titems.gear.{equipped/costume}.{head/headAccessory/eyewear/armor/body/back/shield/weapon}.{gearKey}\n';
let equippedPetFormat = '\nEquipped Pet:\n\titems.currentPet.{petKey}\n';
let equippedMountFormat = '\nEquipped Mount:\n\titems.currentMount.{mountKey}\n';
let data = questsFormat.concat(mountsFormat, foodFormat, eggsFormat, hatchingPotionsFormat, petsFormat, specialFormat, gearFormat, equippedGearFormat, equippedPetFormat, equippedMountFormat);
return data;
},
getFormattedItemReference (pathPrefix, itemKeys, values) {
let finishedString = '\n'.concat('path: ', pathPrefix, ', ', 'value: {', values, '}\n');
each(itemKeys, (key) => {
finishedString = finishedString.concat('\t', pathPrefix, '.', key, '\n');
});
return finishedString;
},
async loadHero (uuid, heroIndex) {
this.currentHeroIndex = heroIndex;
let hero = await this.$store.dispatch('hall:getHero', { uuid });
this.hero = hero;
},
async saveHero (hero) {
this.hero.contributor.admin = this.hero.contributor.level > 7 ? true : false;
let heroUpdated = await this.$store.dispatch('hall:updateHero', { heroDetails: hero });
// @TODO: Import
// Notification.text("User updated");
this.hero = {};
this._heroID = -1;
this.heroes[this.currentHeroIndex] = heroUpdated;
this.currentHeroIndex = -1;
},
populateContributorInput (id, index) {
this._heroID = id;
window.scrollTo(0, 200);
this.loadHero(id, index);
},
},
};
</script>

View File

@@ -0,0 +1,19 @@
<template lang="pug">
.row
secondary-menu.col-12
router-link.nav-link(:to="{name: 'contributors'}", exact, :class="{'active': $route.name === 'contributors'}") {{ $t('hallContributors') }}
router-link.nav-link(:to="{name: 'patrons'}", :class="{'active': $route.name === 'patrons'}") {{ $t('hallPatrons') }}
.col-12
router-view
</template>
<script>
import SecondaryMenu from 'client/components/secondaryMenu';
export default {
components: {
SecondaryMenu,
},
};
</script>

View File

@@ -0,0 +1,53 @@
<template lang="pug">
.row
small.muted {{ $t('blurbHallPatrons') }}
.table-responsive
table.table.table-striped(infinite-scroll="loadMore()")
thead
tr
th {{ $t('name') }}
th(v-if='user.contributor.admin') {{ $t('UUID') }}
th {{ $t('backerTier') }}
tbody
tr(v-for='patron in patrons')
td
a.label.label-default(v-class='userLevelStyle(patron)', @click='clickMember(patron._id, true)')
| {{patron.profile.name}}
td(v-if='user.contributor.admin') {{patron._id}}
td {{patron.backer.tier}}
</template>
<script>
import { mapState } from 'client/libs/store';
export default {
data () {
return {
patrons: [],
};
},
async mounted () {
this.patrons = await this.$store.dispatch('hall:getPatrons', { page: 0 });
},
computed: {
...mapState({user: 'user.data'}),
},
methods: {
// @TODO: This is used to style usernames. WE should abstract this to helper mixer
userLevelStyle (user, style) {
style = style || '';
let npc = user && user.backer && user.backer.npc ? user.backer.npc : '';
let level = user && user.contributor && user.contributor.level ? user.contributor.level : '';
style += this.userLevelStyleFromLevel(level, npc, style);
return style;
},
userLevelStyleFromLevel (level, npc, style) {
style = style || '';
if (npc) style += ' label-npc';
if (level) style += ` label-contributor-${level}`;
return style;
},
//@TODO: Import member modal - clickMember()
},
};
</script>

View File

@@ -0,0 +1,143 @@
<template lang="pug">
b-modal#member-detail-modal(title="Empty", size='lg')
.modal-header
h4
span {{profile.profile.name}}
span(v-if='contribText && profile.contributor.level') - {{contribText(profile.contributor, profile.backer)}}
.modal-body
.container-fluid
.row
.col-md-6
img.img-renderiv-auto(v-if='profile.profile.imageUrl', :src='profile.profile.imageUrl')
markdown(v-if='profile.profile.blurb', text='profile.profile.blurb')
ul.muted.list-unstyled(v-if='profile.auth.timestamps')
li {{profile._id}}
li(v-if='profile.auth.timestamps.created')
|&nbsp;
| {{ $t('memberSince') }}
|&nbsp;
| {{profile.auth.timestamps.created | date:user.preferences.dateFormat}} -
li(v-if='profile.auth.timestamps.loggedin')
|&nbsp;
| {{ $t('lastLoggedIn') }}
|&nbsp;
| {{profile.auth.timestamps.loggedin | date:user.preferences.dateFormat}} -
h3 {{ $t('stats') }}
// @TODO: Figure out why this isn't showing up in front page
.label.label-info {{ {warrior:env.t("warrior"), wizard:env.t("mage"), rogue:env.t("rogue"), healer:env.t("healer")}[profile.stats.class] }}
// include ../profiles/stats_all
.col-md-6
.row
//@TODO: +herobox()
.row
h3 {{ $t('achievements') }}
//include ../profiles/achievements
.modal-footer
.btn-group.pull-left(v-if='user')
button.btn.btn-md.btn-default(v-if='user.inbox.blocks.indexOf(profile._id) !== -1', :tooltip="$t('unblock')", @click="User.blockUser({params:{uuid:profile._id}})", tooltip-placement='right')
span.glyphicon.glyphicon-plus
button.btn.btn-md.btn-default(v-if='profile._id != user._id && !profile.contributor.admin && !(user.inbox.blocks | contains:profile._id)', tooltip {{ $t('block') }}, @click="User.blockUser({params:{uuid:profile._id}})", tooltip-placement='right')
span.glyphicon.glyphicon-ban-circle
button.btn.btn-md.btn-default(:tooltip="$t('sendPM')", @click="openModal('private-message',{controller:'MemberModalCtrl'})", tooltip-placement='right')
span.glyphicon.glyphicon-envelope
button.btn.btn-md.btn-default(:tooltip="$t('sendGift')", @click="openModal('send-gift',{controller:'MemberModalCtrl'})", tooltip-placement='right')
span.glyphicon.glyphicon-gift
button.btn.btn-default(@click='close()') {{ $t('close') }}
</template>
<script>
import { mapState } from 'client/libs/store';
import moment from 'moment';
import bModal from 'bootstrap-vue/lib/components/modal';
export default {
// @TODO: We should probably use a store. Only view one member at a time?
props: ['profile'],
data () {
return {
// @TODO: We don't send subscriptions so the structure has changed in the back. Update this when we update the views.
gift: {
type: 'gems',
gems: {amount: 0, fromBalance: true},
subscription: {key: ''},
message: '',
},
};
},
components: {
bModal,
},
computed: {
...mapState({user: 'user.data'}),
},
mounted () {
// @TODO: This.$store.selectmember
// if (member) {
// this.profile = member;
//
// this.achievements = Shared.achievements.getAchievementsForProfile(this.profile);
// this.achievPopoverPlacement = 'left';
// this.achievAppendToBody = 'false'; // append-to-body breaks popovers in modal windows
// }
},
methods: {
timestamp (timestamp) {
return moment(timestamp).format(this.user.preferences.dateFormat.toUpperCase());
},
// @TODO: create mixin for stats: this.statCalc = Stats;
// @TODO: create mixin or library for constume functions this.costume = Costume;
keyDownListener (e) {
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
this.sendPrivateMessage(this.profile._id, this._message);
}
},
// @TODO: Inbox?
sendPrivateMessage (uuid, message) {
if (!message) return;
// Members.sendPrivateMessage(message, uuid)
// .then(function (response) {
// Notification.text(window.env.t('messageSentAlert'));
// $rootScope.User.sync();
// this.$close();
// });
},
async sendGift (uuid) {
await this.$store.dispatch('members:transferGems', {
message: this.gift.message,
toUserId: uuid,
gemAmount: this.gift.gems.amount,
});
// @TODO: Notification.text(this.$t('sentGems'));
// @TODO: What needs to be synced? $rootScope.User.sync();
this.close();
},
async reportAbuse (reporter, message, groupId) {
let response = await this.$store.dispatch('chat:flag', {
groupId,
chatId: message.id,
});
message.flags = response.flags;
message.flagCount = response.flagCount;
// @TODO: Notification.text(this.$t('abuseReported'));
this.close();
},
async clearFlagCount (message, groupId) {
await this.$store.dispatch('chat:clearFlagCount', {
groupId,
chatId: message.id,
});
message.flagCount = 0;
// @TODO: Notification.text("Flags cleared");
this.close();
},
close () {
this.$root.$emit('hide::modal', 'member-detail-modal');
},
},
};
</script>

View File

@@ -0,0 +1,198 @@
<template lang="pug">
.item-with-icon.item-notifications.dropdown
.svg-icon(v-html="icons.notifications")
// span.glyphicon(:class='iconClasses()')
// span.notification-counter(v-if='getNotificationsCount()') {{getNotificationsCount()}}
.dropdown-menu.dropdown-menu-right.user-dropdown
a.dropdown-item test
h4 {{ $t('notifications') }}
div
ul.toolbar-notifs-notifs
li.toolbar-notifs-no-messages(v-if='hasNoNotifications()') {{ $t('noNotifications') }}
li(v-if='user.purchased.plan.mysteryItems.length')
a(@click='$state.go("options.inventory.drops"); ')
span.glyphicon.glyphicon-gift
span {{ $t('newSubscriberItem') }}
li(v-for='party in user.invitations.parties')
a(ui-sref='options.social.party')
span.glyphicon.glyphicon-user
span {{ $t('invitedTo', {name: party.name}) }}
li(v-if='user.flags.cardReceived')
a(@click='$state.go("options.inventory.drops"); ')
span.glyphicon.glyphicon-envelope
span {{ $t('cardReceived') }}
a(@click='clearCards()', :popover="$t('clear')", popover-placement='right', popover-trigger='mouseenter',popover-append-to-body='true')
span.glyphicon.glyphicon-remove-circle
li(v-for='guild in user.invitations.guilds')
a(ui-sref='options.social.guilds.public')
span.glyphicon.glyphicon-user
span {{ $t('invitedTo', {name: guild.name}) }}
li(v-if='user.flags.classSelected && !user.preferences.disableClasses && user.stats.points')
a(ui-sref='options.profile.stats')
span.glyphicon.glyphicon-plus-sign
span {{ $t('haveUnallocated', {points: user.stats.points}) }}
li(v-for='(k,v) in user.newMessages', v-if='v.value')
a(@click='(k === party._id || k === user.party._id) ? $state.go("options.social.party") : $state.go("options.social.guilds.detail",{gid:k}); ')
span.glyphicon.glyphicon-comment
span {{v.name}}
a(@click='clearMessages(k)', :popover="$t('clear')", popover-placement='right', popover-trigger='mouseenter',popover-append-to-body='true')
span.glyphicon.glyphicon-remove-circle
li(v-for='notification in user.groupNotifications')
a(@click='viewGroupApprovalNotification(notification, $index, true)')
span(:class="groupApprovalNotificationIcon(notification)")
span
| {{notification.data.message}}
a(@click='viewGroupApprovalNotification(notification, $index)',
:popover="$t('clear')",
popover-placement='right',
popover-trigger='mouseenter',
popover-append-to-body='true')
span.glyphicon.glyphicon-remove-circle
</template>
<script>
import isEmpty from 'lodash/isEmpty';
import map from 'lodash/map';
import { mapState } from 'client/libs/store';
import quests from 'common/script/content/quests';
import notificationsIcon from 'assets/svg/notifications.svg';
export default {
data () {
return {
icons: Object.freeze({
notifications: notificationsIcon,
}),
};
},
computed: {
...mapState({user: 'user.data'}),
party () {
return {name: ''};
// return this.user.party;
},
},
methods: {
// @TODO: I hate this function, we can do better with a hashmap
selectNotificationValue (mysteryValue, invitationValue, cardValue,
unallocatedValue, messageValue, noneValue, groupApprovalRequested, groupApproved) {
let user = this.user;
if (user.purchased && user.purchased.plan && user.purchased.plan.mysteryItems && user.purchased.plan.mysteryItems.length) {
return mysteryValue;
} else if (user.invitations.parties && user.invitations.parties.length > 0 || user.invitations.guilds && user.invitations.guilds.length > 0) {
return invitationValue;
} else if (user.flags.cardReceived) {
return cardValue;
} else if (user.flags.classSelected && !(user.preferences && user.preferences.disableClasses) && user.stats.points) {
return unallocatedValue;
} else if (!isEmpty(user.newMessages)) {
return messageValue;
} else if (!isEmpty(user.groupNotifications)) {
let groupNotificationTypes = map(user.groupNotifications, 'type');
if (groupNotificationTypes.indexOf('GROUP_TASK_APPROVAL') !== -1) {
return groupApprovalRequested;
} else if (groupNotificationTypes.indexOf('GROUP_TASK_APPROVED') !== -1) {
return groupApproved;
}
return noneValue;
} else {
return noneValue;
}
},
hasQuestProgress () {
let user = this.user;
if (user.party.quest) {
let userQuest = quests[user.party.quest.key];
if (!userQuest) {
return false;
}
if (userQuest.boss && user.party.quest.progress.up > 0) {
return true;
}
if (userQuest.collect && user.party.quest.progress.collectedItems > 0) {
return true;
}
}
return false;
},
getQuestInfo () {
let user = this.user;
let questInfo = {};
if (user.party.quest) {
let userQuest = quests[user.party.quest.key];
questInfo.title = userQuest.text();
if (userQuest.boss) {
questInfo.body = this.$t('questTaskDamage', { damage: user.party.quest.progress.up.toFixed(1) });
} else if (userQuest.collect) {
questInfo.body = this.$t('questTaskCollection', { items: user.party.quest.progress.collectedItems });
}
}
return questInfo;
},
clearMessages () {
this.$store.dispatch('chat:markChatSeen');
},
clearCards () {
this.$store.dispatch('chat:clearCards');
},
getNotificationsCount () {
let count = 0;
if (this.user.invitations.parties) {
count += this.user.invitations.parties.length;
}
if (this.user.purchased.plan && this.user.purchased.plan.mysteryItems.length) {
count++;
}
if (this.user.invitations.guilds) {
count += this.user.invitations.guilds.length;
}
if (this.user.flags.classSelected && !this.user.preferences.disableClasses && this.user.stats.points) {
count += this.user.stats.points > 0 ? 1 : 0;
}
if (this.user.newMessages) {
count += Object.keys(this.user.newMessages).length;
}
return count;
},
iconClasses () {
return this.selectNotificationValue(
'glyphicon-gift',
'glyphicon-user',
'glyphicon-envelope',
'glyphicon-plus-sign',
'glyphicon-comment',
'glyphicon-comment inactive',
'glyphicon-question-sign',
'glyphicon-ok-sign'
);
},
hasNoNotifications () {
return this.selectNotificationValue(false, false, false, false, false, true, false, false);
},
viewGroupApprovalNotification (notification, index, navigate) {
// @TODO: USe notifications: User.readNotification(notification.id);
this.user.groupNotifications.splice(index, 1);
return navigate; // @TODO: remove
// @TODO: this.$route.go if (navigate) $state.go('options.social.guilds.detail', {gid: notification.data.groupId});
},
groupApprovalNotificationIcon (notification) {
if (notification.type === 'GROUP_TASK_APPROVAL') {
return 'glyphicon glyphicon-question-sign';
} else if (notification.type === 'GROUP_TASK_APPROVED') {
return 'glyphicon glyphicon-ok-sign';
}
},
},
};
</script>

View File

@@ -0,0 +1,401 @@
<template lang="pug">
</template>
<script>
// import moment from 'moment';
import { mapState } from 'client/libs/store';
export default {
data () {
// Levels that already display modals and should not trigger generic Level Up
let unlockLevels = {
3: 'drop system',
10: 'class system',
50: 'Orb of Rebirth',
};
// Avoid showing the same notiication more than once
let lastShownNotifications = [];
let alreadyReadNotification = [];
return {
unlockLevels,
lastShownNotifications,
alreadyReadNotification,
isRunningYesterdailies: false,
};
},
computed: {
...mapState({user: 'user.data'}),
// https://stackoverflow.com/questions/42133894/vue-js-how-to-properly-watch-for-nested-properties/42134176#42134176
baileyShouldShow () {
return this.user.flags.newStuff;
},
userHp () {
return this.user.stats.hp;
},
userExp () {
return this.user.stats.exp;
},
userGp () {
return this.user.stats.gp;
},
userMp () {
return this.user.stats.mp;
},
userLvl () {
return this.user.stats.lvl;
},
userClassSelect () {
return !this.user.flags.classSelected && this.user.stats.lvl >= 10;
},
userNotifications () {
return this.user.notifications;
},
userAchievements () {
// @TODO: does this watch deeply?
return this.user.achievements;
},
armoireEmpty () {
return this.user.flags.armoireEmpty;
},
questCompleted () {
return this.user.party.quest.completed;
},
invitedToQuest () {
return this.user.party.quest.RSVPNeeded && !this.user.party.quest.completed;
},
},
watch: {
baileyShouldShow () {
// @TODO: this.openModal('newStuff', {size:'lg'});
},
userHp (after, before) {
if (after <= 0) {
this.playSound('Death');
// @TODO: this.openModal('death', {keyboard:false, backdrop:'static'});
} else if (after <= 30 && !this.user.flags.warnedLowHealth) {
// @TODO: this.openModal('lowHealth', {keyboard:false, backdrop:'static', controller:'UserCtrl', track:'Health Warning'});
}
if (after === before) return;
if (this.user.stats.lvl === 0) return;
// @TODO: Notification.hp(after - before, 'hp');
// @TODO: I am pretty sure we no long need this with $store
// this.$broadcast('syncPartyRequest', {
// type: 'user_update',
// user: this.user,
// });
if (after < 0) this.playSound('Minus_Habit');
},
userExp (after, before) {
if (after === before) return;
if (this.user.stats.lvl === 0) return;
// @TODO: Notification.exp(after - before);
},
userGp (after, before) {
if (after === before) return;
if (this.user.stats.lvl === 0) return;
let money = after - before;
let bonus;
if (this.user._tmp) {
bonus = this.user._tmp.streakBonus || 0;
}
// @TODO: Notification.gp(money, bonus || 0);
// Append Bonus
if (money > 0 && Boolean(bonus)) {
if (bonus < 0.01) bonus = 0.01;
// @TODO: Notification.text("+ " + Notification.coins(bonus) + ' ' + window.env.t('streakCoins'));
delete this.user._tmp.streakBonus;
}
},
userMp (after, before) {
if (after === before) return;
if (!this.user.flags.classSelected || this.user.preferences.disableClasses) return;
// let mana = after - before;
// @TODO: Notification.mp(mana);
},
userLvl (after, before) {
if (after <= before) return;
// @TODO: Notification.lvl();
this.playSound('Level_Up');
if (this.user._tmp && this.user._tmp.drop && this.user._tmp.drop.type === 'Quest') return;
if (this.unlockLevels[`${after}`]) return;
// @TODO: if (!this.user.preferences.suppressModals.levelUp) this.openModal('levelUp', {controller:'UserCtrl', size:'sm'});
},
userClassSelect (after) {
if (!after) return;
// @TODO: this.openModal('chooseClass', {controller:'UserCtrl', keyboard:false, backdrop:'static'});
},
userNotifications (after) {
if (!this.user._wrapped) return;
if (this.user.needsCron) return;
this.handleUserNotifications(after);
},
userAchievements () {
this.playSound('Achievement_Unlocked');
},
armoireEmpty (after, before) {
if (after === before || after === false) return;
// @TODO: this.openModal('armoireEmpty');
},
questCompleted (after) {
if (!after) return;
// @TODO: this.openModal('questCompleted', {controller:'InventoryCtrl'});
},
invitedToQuest (after) {
if (after !== true) return;
// @TODO: this.openModal('questInvitation', {controller:'PartyCtrl'});
},
},
async mounted () {
},
methods: {
playSound () {
// @TODO:
},
runYesterDailies () {
// @TODO: Hopefully we don't need this even we load correctly
if (this.isRunningYesterdailies) return;
// let userLastCron = moment(this.user.lastCron).local();
// let userDayStart = moment().startOf('day').add({ hours: this.user.preferences.dayStart });
if (!this.user.needsCron) return;
let dailys = this.user.dailys;
if (!this.appLoaded) return;
this.isRunningYesterdailies = true;
// let yesterDay = moment().subtract('1', 'day').startOf('day').add({ hours: this.user.preferences.dayStart });
let yesterDailies = [];
dailys.forEach((task) => {
if (task && task.group.approval && task.group.approval.requested) return;
if (task.completed) return;
// @TODO: let shouldDo = Shared.shouldDo(yesterDay, task);
let shouldDo = false;
if (task.yesterDaily && shouldDo) yesterDailies.push(task);
});
if (yesterDailies.length === 0) {
// @TODO:
// User.runCron().then(function () {
// isRunningYesterdailies = false;
// handleUserNotifications(this.user);
// });
return;
}
// @TODO:
// let modalScope = this.$new();
// modalScope.obj = this.user;
// modalScope.taskList = yesterDailies;
// modalScope.list = {
// showCompleted: false,
// type: 'daily',
// };
// modalScope.processingYesterdailies = true;
//
// $scope.yesterDailiesModalOpen = true;
// $modal.open({
// templateUrl: 'modals/yesterDailies.html',
// scope: modalScope,
// backdrop: 'static',
// controller: ['$scope', 'Tasks', 'User', '$rootScope', function ($scope, Tasks, User, $rootScope) {
// this.$on('task:scored', function (event, data) {
// let task = data.task;
// let indexOfTask = _.findIndex($scope.taskList, function (taskInList) {
// return taskInList._id === task._id;
// });
// if (!$scope.taskList[indexOfTask]) return;
// $scope.taskList[indexOfTask].group.approval.requested = task.group.approval.requested;
// if ($scope.taskList[indexOfTask].group.approval.requested) return;
// $scope.taskList[indexOfTask].completed = task.completed;
// });
//
// $scope.ageDailies = function () {
// User.runCron()
// .then(function () {
// isRunningYesterdailies = false;
// handleUserNotifications(this.user);
// });
// };
// }],
// });
},
transferGroupNotification (notification) {
if (!this.user.groupNotifications) this.user.groupNotifications = [];
this.user.groupNotifications.push(notification);
},
handleUserNotifications (after) {
if (!after || after.length === 0) return;
let notificationsToRead = [];
let scoreTaskNotification = [];
this.user.groupNotifications = []; // Flush group notifictions
after.forEach((notification) => {
if (this.lastShownNotifications.indexOf(notification.id) !== -1) {
return;
}
// Some notifications are not marked read here, so we need to fix this system
// to handle notifications differently
if (['GROUP_TASK_APPROVED', 'GROUP_TASK_APPROVAL'].indexOf(notification.type) === -1) {
this.lastShownNotifications.push(notification.id);
if (this.lastShownNotifications.length > 10) {
this.lastShownNotifications.splice(0, 9);
}
}
let markAsRead = true;
// @TODO: Use factory function instead
switch (notification.type) {
case 'GUILD_PROMPT':
if (notification.data.textletiant === -1) {
// @TODO: this.openModal('testing');
} else {
// @TODO: this.openModal('testingletiant');
}
break;
case 'DROPS_ENABLED':
// @TODO: this.openModal('dropsEnabled');
break;
case 'REBIRTH_ENABLED':
// @TODO: this.openModal('rebirthEnabled');
break;
case 'WON_CHALLENGE':
// @TODO:
// User.sync().then( function() {
// Achievement.displayAchievement('wonChallenge');
// });
break;
case 'STREAK_ACHIEVEMENT':
// @TODO: Notification.streak(this.user.achievements.streak);
this.playSound('Achievement_Unlocked');
if (!this.user.preferences.suppressModals.streak) {
// @TODO: Achievement.displayAchievement('streak', {size: 'md'});
}
break;
case 'ULTIMATE_GEAR_ACHIEVEMENT':
this.playSound('Achievement_Unlocked');
// @TODO: Achievement.displayAchievement('ultimateGear', {size: 'md'});
break;
case 'REBIRTH_ACHIEVEMENT':
this.playSound('Achievement_Unlocked');
// @TODO: Achievement.displayAchievement('rebirth');
break;
case 'GUILD_JOINED_ACHIEVEMENT':
this.playSound('Achievement_Unlocked');
// @TODO: Achievement.displayAchievement('joinedGuild', {size: 'md'});
break;
case 'CHALLENGE_JOINED_ACHIEVEMENT':
this.playSound('Achievement_Unlocked');
// @TODO: Achievement.displayAchievement('joinedChallenge', {size: 'md'});
break;
case 'INVITED_FRIEND_ACHIEVEMENT':
this.playSound('Achievement_Unlocked');
// @TODO: Achievement.displayAchievement('invitedFriend', {size: 'md'});
break;
case 'NEW_CONTRIBUTOR_LEVEL':
this.playSound('Achievement_Unlocked');
// @TODO: Achievement.displayAchievement('contributor', {size: 'md'});
break;
case 'CRON':
if (notification.data) {
// @TODO: if (notification.data.hp) Notification.hp(notification.data.hp, 'hp');
// @TODO: if (notification.data.mp) Notification.mp(notification.data.mp);
}
break;
case 'GROUP_TASK_APPROVAL':
this.transferGroupNotification(notification);
markAsRead = false;
break;
case 'GROUP_TASK_APPROVED':
this.transferGroupNotification(notification);
markAsRead = false;
break;
case 'SCORED_TASK':
// Search if it is a read notification
for (let i = 0; i < this.alreadyReadNotification.length; i++) {
if (this.alreadyReadNotification[i] === notification.id) {
markAsRead = false; // Do not let it be read again
break;
}
}
// Only process the notification if it is an unread notification
if (markAsRead) {
scoreTaskNotification.push(notification);
// Add to array of read notifications
this.alreadyReadNotification.push(notification.id);
}
break;
case 'LOGIN_INCENTIVE':
// @TODO: Notification.showLoginIncentive(this.user, notification.data, Social.loadWidgets);
break;
default:
if (notification.data.headerText && notification.data.bodyText) {
// @TODO:
// let modalScope = this.$new();
// modalScope.data = notification.data;
// this.openModal('generic', {scope: modalScope});
} else {
markAsRead = false; // If the notification is not implemented, skip it
}
break;
}
if (markAsRead) notificationsToRead.push(notification.id);
});
let userReadNotifsPromise = false;
// @TODO: User.readNotifications(notificationsToRead);
if (userReadNotifsPromise) {
userReadNotifsPromise.then(() => {
// Only run this code for scoring approved tasks
if (scoreTaskNotification.length > 0) {
let approvedTasks = [];
for (let i = 0; i < scoreTaskNotification.length; i++) {
// Array with all approved tasks
approvedTasks.push({
params: {
task: scoreTaskNotification[i].data.scoreTask,
direction: 'up',
},
});
// Show notification of task approved
// @TODO: Notification.markdown(scoreTaskNotification[i].data.message);
}
// Score approved tasks
// TODO: User.bulkScore(approvedTasks);
}
});
}
this.user.notifications = []; // reset the notifications
},
// @TODO: I think I have these handled in the http interceptor
// this.$on('responseError500', function(ev, error){
// Notification.error(error);
// });
// this.$on('responseError', function(ev, error){
// Notification.error(error, true);
// });
// this.$on('responseText', function(ev, error){
// Notification.text(error);
// });
},
};
</script>