mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 06:37:23 +01:00
Client fixes aug 31 (#9010)
* Separated private message model * Added markdown to profile * Add color backgrounds * Added broken challenge flow * Added summary field to getgroups * Fixed group form information loading * Updated autocomplete to use chat * Fixed npc styles * Fixed onload mentions
This commit is contained in:
@@ -14,7 +14,7 @@ div.autocomplete-selection(v-if='searchResults.length > 0', :style='autocomplete
|
|||||||
import groupBy from 'lodash/groupBy';
|
import groupBy from 'lodash/groupBy';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['selections', 'text', 'coords', 'groupId', 'chat'],
|
props: ['selections', 'text', 'coords', 'chat'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
currentSearch: '',
|
currentSearch: '',
|
||||||
@@ -43,6 +43,9 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted () {
|
||||||
|
this.grabUserNames();
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
text (newText) {
|
text (newText) {
|
||||||
if (newText[newText.length - 1] !== '@') return;
|
if (newText[newText.length - 1] !== '@') return;
|
||||||
@@ -50,22 +53,19 @@ export default {
|
|||||||
this.currentSearchPosition = newText.length - 1;
|
this.currentSearchPosition = newText.length - 1;
|
||||||
},
|
},
|
||||||
chat () {
|
chat () {
|
||||||
|
this.grabUserNames();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
grabUserNames () {
|
||||||
let usersThatMessage = groupBy(this.chat, 'user');
|
let usersThatMessage = groupBy(this.chat, 'user');
|
||||||
for (let userName in usersThatMessage) {
|
for (let userName in usersThatMessage) {
|
||||||
if (this.tmpSelections.indexOf(userName) === -1) {
|
let systemMessage = userName === 'undefined';
|
||||||
|
if (!systemMessage && this.tmpSelections.indexOf(userName) === -1) {
|
||||||
this.tmpSelections.push(userName);
|
this.tmpSelections.push(userName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async groupId () {
|
|
||||||
if (!this.groupId) return;
|
|
||||||
let members = await this.$store.dispatch('members:getGroupMembers', {groupId: this.groupId});
|
|
||||||
this.tmpSelections = members.map((member) => {
|
|
||||||
return member.profile.name;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
select (result) {
|
select (result) {
|
||||||
let newText = this.text.slice(0, this.currentSearchPosition + 1) + result;
|
let newText = this.text.slice(0, this.currentSearchPosition + 1) + result;
|
||||||
this.searchActive = false;
|
this.searchActive = false;
|
||||||
|
|||||||
@@ -107,11 +107,11 @@
|
|||||||
color: #277eab;
|
color: #277eab;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier10 {
|
.tier9 {
|
||||||
color: #6133b4;
|
color: #6133b4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier9 {
|
.tier10 {
|
||||||
color: #77f4c7;
|
color: #77f4c7;
|
||||||
fill: #77f4c7;
|
fill: #77f4c7;
|
||||||
stroke: #005737;
|
stroke: #005737;
|
||||||
|
|||||||
@@ -172,6 +172,16 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
|
|||||||
ng-click='user.items.gear.owned[item.key] ? equip(item.key) : purchase(item.type,item)')
|
ng-click='user.items.gear.owned[item.key] ? equip(item.key) : purchase(item.type,item)')
|
||||||
|
|
||||||
#backgrounds.section.container.customize-section(v-if='activeTopPage === "backgrounds"')
|
#backgrounds.section.container.customize-section(v-if='activeTopPage === "backgrounds"')
|
||||||
|
.row.col-12.text-center.set-title
|
||||||
|
strong {{backgroundShopSets[0].text}}
|
||||||
|
.row.incentive-background-row
|
||||||
|
.col-2(v-for='bg in backgroundShopSets[0].items',
|
||||||
|
@click='unlock("background." + bg.key)',
|
||||||
|
:popover-title='bg.text',
|
||||||
|
:popover='bg.notes',
|
||||||
|
popover-trigger='mouseenter')
|
||||||
|
.incentive-background(:class='[`background_${bg.key}`]')
|
||||||
|
.small-rectangle
|
||||||
.row.sub-menu.col-6.offset-3
|
.row.sub-menu.col-6.offset-3
|
||||||
.col-3.text-center.sub-menu-item(@click='changeSubPage("2017")', :class='{active: activeSubPage === "2017"}')
|
.col-3.text-center.sub-menu-item(@click='changeSubPage("2017")', :class='{active: activeSubPage === "2017"}')
|
||||||
strong(v-once) 2017
|
strong(v-once) 2017
|
||||||
@@ -520,6 +530,61 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.incentive-background-row {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.incentive-background {
|
||||||
|
background-image: none;
|
||||||
|
width: 68px;
|
||||||
|
height: 68px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #92b6bd;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding-top: .3em;
|
||||||
|
|
||||||
|
.small-rectangle {
|
||||||
|
width: 60px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0 auto;
|
||||||
|
opacity: .6;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.background_violet {
|
||||||
|
background-color: #a993ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background_blue {
|
||||||
|
background-color: #92b6bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background_green {
|
||||||
|
background-color: #92bd94;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background_purple {
|
||||||
|
background-color: #9397bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background_red {
|
||||||
|
background-color: #b77e80;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background_yellow {
|
||||||
|
background-color: #bcbb91;
|
||||||
|
}
|
||||||
|
|
||||||
|
.incentive-background:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.background:hover {
|
.background:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
h3(v-once) {{ $t('chat') }}
|
h3(v-once) {{ $t('chat') }}
|
||||||
|
|
||||||
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', :groupId='groupId')
|
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-right(v-once, @click='sendMessage()') {{ $t('send') }}
|
||||||
button.btn.btn-secondary.float-left(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }}
|
button.btn.btn-secondary.float-left(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }}
|
||||||
.col-12
|
.col-12
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
select.form-control(v-model="workingGroup.newLeader")
|
select.form-control(v-model="workingGroup.newLeader")
|
||||||
option(v-for='member in members', :value="member._id") {{ member.profile.name }}
|
option(v-for='member in members', :value="member._id") {{ member.profile.name }}
|
||||||
|
|
||||||
.form-group
|
.form-group(v-if='!this.workingGroup.id')
|
||||||
label
|
label
|
||||||
strong(v-once) {{$t('privacySettings')}} *
|
strong(v-once) {{$t('privacySettings')}} *
|
||||||
br
|
br
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
span.custom-control-description(v-once) {{ $t('allowGuildInvitationsFromNonMembers') }}
|
span.custom-control-description(v-once) {{ $t('allowGuildInvitationsFromNonMembers') }}
|
||||||
// "allowGuildInvitationsFromNonMembers": "Allow Guild invitations from non-members",
|
// "allowGuildInvitationsFromNonMembers": "Allow Guild invitations from non-members",
|
||||||
|
|
||||||
.form-group(v-if='!creatingParty')
|
.form-group(v-if='!isParty')
|
||||||
label
|
label
|
||||||
strong(v-once) {{$t('guildSummary')}} *
|
strong(v-once) {{$t('guildSummary')}} *
|
||||||
div.summary-count {{charactersRemaining}} {{ $t('charactersRemaining') }}
|
div.summary-count {{charactersRemaining}} {{ $t('charactersRemaining') }}
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
label
|
label
|
||||||
strong(v-once) {{$t('groupDescription')}} *
|
strong(v-once) {{$t('groupDescription')}} *
|
||||||
a.float-right {{ $t('markdownFormattingHelp') }}
|
a.float-right {{ $t('markdownFormattingHelp') }}
|
||||||
b-form-input.description-textarea(type="text", textarea, :placeholder="isParty ? $t('partyDescriptionPlaceholder') : $t('guildDescriptionPlaceholder')", v-model="workingGroup.description")
|
textarea.form-control.description-textarea(type="text", textarea, :placeholder="isParty ? $t('partyDescriptionPlaceholder') : $t('guildDescriptionPlaceholder')", v-model="workingGroup.description")
|
||||||
|
|
||||||
.form-group(v-if='creatingParty && !workingGroup.id')
|
.form-group(v-if='creatingParty && !workingGroup.id')
|
||||||
span
|
span
|
||||||
@@ -285,10 +285,31 @@ export default {
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
mounted () {
|
computed: {
|
||||||
// @TODO: do we need this? Maybe us computed. If we need, then make it on show a specific modal
|
editingGroup () {
|
||||||
this.$root.$on('shown::modal', () => {
|
return this.$store.state.editingGroup;
|
||||||
let editingGroup = this.$store.state.editingGroup;
|
},
|
||||||
|
charactersRemaining () {
|
||||||
|
let currentLength = this.workingGroup.summary ? this.workingGroup.summary.length : 0;
|
||||||
|
return MAX_SUMMARY_SIZE_FOR_GUILDS - currentLength;
|
||||||
|
},
|
||||||
|
title () {
|
||||||
|
if (this.creatingParty) return this.$t('createParty');
|
||||||
|
if (!this.workingGroup._id && !this.workingGroup.id) return this.$t('createGuild');
|
||||||
|
if (this.isParty) return this.$t('updateParty');
|
||||||
|
return this.$t('updateGuild');
|
||||||
|
},
|
||||||
|
creatingParty () {
|
||||||
|
return this.$store.state.groupFormOptions.createParty;
|
||||||
|
},
|
||||||
|
isParty () {
|
||||||
|
return this.workingGroup.type === 'party';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
editingGroup () {
|
||||||
|
let editingGroup = this.editingGroup;
|
||||||
|
|
||||||
if (!editingGroup._id) {
|
if (!editingGroup._id) {
|
||||||
this.resetWorkingGroup();
|
this.resetWorkingGroup();
|
||||||
return;
|
return;
|
||||||
@@ -313,24 +334,6 @@ export default {
|
|||||||
if (editingGroup._id) this.workingGroup.id = editingGroup._id;
|
if (editingGroup._id) this.workingGroup.id = editingGroup._id;
|
||||||
if (editingGroup.leader._id) this.workingGroup.newLeader = editingGroup.leader._id;
|
if (editingGroup.leader._id) this.workingGroup.newLeader = editingGroup.leader._id;
|
||||||
if (editingGroup._id) this.getMembers();
|
if (editingGroup._id) this.getMembers();
|
||||||
});
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
charactersRemaining () {
|
|
||||||
let currentLength = this.workingGroup.summary ? this.workingGroup.summary.length : 0;
|
|
||||||
return MAX_SUMMARY_SIZE_FOR_GUILDS - currentLength;
|
|
||||||
},
|
|
||||||
title () {
|
|
||||||
if (this.creatingParty) return this.$t('createParty');
|
|
||||||
if (!this.workingGroup.id) return this.$t('createGuild');
|
|
||||||
if (this.isParty) return this.$t('updateParty');
|
|
||||||
return this.$t('updateGuild');
|
|
||||||
},
|
|
||||||
creatingParty () {
|
|
||||||
return this.$store.state.groupFormOptions.createParty;
|
|
||||||
},
|
|
||||||
isParty () {
|
|
||||||
return this.workingGroup.type === 'party';
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -444,15 +447,18 @@ export default {
|
|||||||
},
|
},
|
||||||
resetWorkingGroup () {
|
resetWorkingGroup () {
|
||||||
this.workingGroup = {
|
this.workingGroup = {
|
||||||
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
type: 'guild',
|
type: 'guild',
|
||||||
privacy: 'private',
|
privacy: 'private',
|
||||||
|
summary: '',
|
||||||
description: '',
|
description: '',
|
||||||
categories: [],
|
categories: [],
|
||||||
onlyLeaderCreatesChallenges: true,
|
onlyLeaderCreatesChallenges: true,
|
||||||
guildLeaderCantBeMessaged: true,
|
guildLeaderCantBeMessaged: true,
|
||||||
privateGuild: true,
|
privateGuild: true,
|
||||||
allowGuildInvitationsFromNonMembers: true,
|
allowGuildInvitationsFromNonMembers: true,
|
||||||
|
newLeader: '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
.row
|
.row
|
||||||
textarea(placeholder="Friendly reminder: this is an all-ages chat, so please keep content and language appropriate! Consult the Community Guidelines in the sidebar if you have questions.", v-model='newMessage', @keydown='updateCarretPosition')
|
textarea(placeholder="Friendly reminder: this is an all-ages chat, so please keep content and language appropriate! Consult the Community Guidelines in the sidebar if you have questions.", v-model='newMessage', @keydown='updateCarretPosition')
|
||||||
autocomplete(:text='newMessage', v-on:select="selectedAutocomplete", :coords='coords', :groupId='groupId', :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-right(v-once, @click='sendMessage()') {{ $t('send') }}
|
||||||
button.btn.btn-secondary.float-left(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }}
|
button.btn.btn-secondary.float-left(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }}
|
||||||
|
|
||||||
|
|||||||
35
website/client/components/private-message-modal.vue
Normal file
35
website/client/components/private-message-modal.vue
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
b-modal#private-message(title="Message", size='sm', :hide-footer="true")
|
||||||
|
textarea.form-control(v-model='privateMessage')
|
||||||
|
button.btn.btn-primary(@click='sendPrivateMessage()') Send
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||||
|
import notifications from 'client/mixins/notifications';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [notifications],
|
||||||
|
props: ['userIdToMessage'],
|
||||||
|
components: {
|
||||||
|
bModal,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
privateMessage: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async sendPrivateMessage () {
|
||||||
|
if (!this.privateMessage || !this.userIdToMessage) return;
|
||||||
|
|
||||||
|
await this.$store.dispatch('members:sendPrivateMessage', {
|
||||||
|
message: this.privateMessage,
|
||||||
|
toUserId: this.userIdToMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.text(this.$t('messageSentAlert'));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
77
website/client/components/tasks/brokenTaskModal.vue
Normal file
77
website/client/components/tasks/brokenTaskModal.vue
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<template lang='pug'>
|
||||||
|
b-modal#broken-task-modal(title="Broken Challenge", size='sm', :hide-footer="true", v-if='brokenChallengeTask.challenge')
|
||||||
|
.modal-body
|
||||||
|
div(v-if='brokenChallengeTask.challenge.broken === "TASK_DELETED" || brokenChallengeTask.challenge.broken === "CHALLENGE_TASK_NOT_FOUND"')
|
||||||
|
h2 {{ $t('brokenTask') }}
|
||||||
|
div
|
||||||
|
button.btn.btn-primary(@click='unlink("keep")') {{ $t('keepIt') }}
|
||||||
|
button.btn.btn-danger(@click='removeTask(obj)') {{ $t('removeIt') }}
|
||||||
|
div(v-if='brokenChallengeTask.challenge.broken === "CHALLENGE_DELETED"')
|
||||||
|
h2 {{ $t('brokenChallenge') }}
|
||||||
|
div
|
||||||
|
button.btn.btn-primary(@click='unlink("keep-all")') {{ $t('keepThem') }}
|
||||||
|
button.btn.btn-danger(@click='unlink("remove-all")') {{ $t('removeThem') }}
|
||||||
|
div(v-if='brokenChallengeTask.challenge.broken === "CHALLENGE_CLOSED"')
|
||||||
|
h2(v-html="$t('challengeCompleted', {user: brokenChallengeTask.challenge.winner})")
|
||||||
|
div
|
||||||
|
button.btn.btn-primary(@click='unlink("keep-all")') {{ $t('keepThem') }}
|
||||||
|
button.btn.btn-danger(@click='unlink("remove-all")') {{ $t('removeThem') }}
|
||||||
|
// @TODO: I ported this over, but do we use it anymore?
|
||||||
|
//div(v-if='brokenChallengeTask.challenge.broken === "UNSUBSCRIBED"')
|
||||||
|
p {{ $t('unsubChallenge') }}
|
||||||
|
p
|
||||||
|
a(@click="unlink('keep-all')") {{ $t('keepThem') }}
|
||||||
|
| |
|
||||||
|
a(@click="unlink('remove-all')") {{ $t('removeThem') }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.modal-body {
|
||||||
|
padding-bottom: 2em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions } from 'client/libs/store';
|
||||||
|
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||||
|
import notifications from 'client/mixins/notifications';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [notifications],
|
||||||
|
props: ['brokenChallengeTask'],
|
||||||
|
components: {
|
||||||
|
bModal,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
destroyTask: 'tasks:destroy',
|
||||||
|
unlinkOneTask: 'tasks:unlinkOneTask',
|
||||||
|
unlinkAllTasks: 'tasks:unlinkAllTasks',
|
||||||
|
}),
|
||||||
|
async unlink (keepOption) {
|
||||||
|
if (keepOption.indexOf('-all') !== -1) {
|
||||||
|
this.unlinkAllTasks({
|
||||||
|
challengeId: this.brokenChallengeTask.challenge.id,
|
||||||
|
keep: keepOption,
|
||||||
|
});
|
||||||
|
await this.$store.dispatch('tasks:fetchUserTasks', true);
|
||||||
|
this.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.unlinkOneTask({
|
||||||
|
task: this.brokenChallengeTask,
|
||||||
|
keep: keepOption,
|
||||||
|
});
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
removeTask () {
|
||||||
|
if (!confirm('Are you sure you want to delete this task?')) return;
|
||||||
|
this.destroyTask(this.brokenChallengeTask);
|
||||||
|
},
|
||||||
|
close () {
|
||||||
|
this.$root.$emit('hide::modal', 'broken-task-modal');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
.task(@click='castEnd($event, task)')
|
div
|
||||||
|
broken-task-modal(:brokenChallengeTask='brokenChallengeTask')
|
||||||
|
.task(@click='castEnd($event, task)')
|
||||||
approval-header(:task='task', v-if='this.task.group.id', :group='group')
|
approval-header(:task='task', v-if='this.task.group.id', :group='group')
|
||||||
.d-flex(:class="{'task-not-scoreable': isUser !== true}")
|
.d-flex(:class="{'task-not-scoreable': isUser !== true}")
|
||||||
// Habits left side control
|
// Habits left side control
|
||||||
@@ -35,7 +37,8 @@
|
|||||||
span.m-0(v-if="task.up && task.down") |
|
span.m-0(v-if="task.up && task.down") |
|
||||||
span.m-0(v-if="task.down") -{{task.counterDown}}
|
span.m-0(v-if="task.down") -{{task.counterDown}}
|
||||||
.d-flex.align-items-center(v-if="task.challenge && task.challenge.id")
|
.d-flex.align-items-center(v-if="task.challenge && task.challenge.id")
|
||||||
.svg-icon.challenge(v-html="icons.challenge")
|
.svg-icon.challenge(v-html="icons.challenge", v-if='!task.challenge.broken')
|
||||||
|
.svg-icon.challenge.broken(v-html="icons.challenge", v-if='task.challenge.broken', @click='handleBrokenTask(task)')
|
||||||
b-popover.tags-popover.no-span-margin(
|
b-popover.tags-popover.no-span-margin(
|
||||||
:triggers="['hover']",
|
:triggers="['hover']",
|
||||||
:placement="'bottom'",
|
:placement="'bottom'",
|
||||||
@@ -194,6 +197,10 @@
|
|||||||
margin: 9px 8px;
|
margin: 9px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.challenge.broken {
|
||||||
|
color: $red-50;
|
||||||
|
}
|
||||||
|
|
||||||
.left-control, .right-control {
|
.left-control, .right-control {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -301,6 +308,7 @@ import checkIcon from 'assets/svg/check.svg';
|
|||||||
import bPopover from 'bootstrap-vue/lib/components/popover';
|
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||||
import markdownDirective from 'client/directives/markdown';
|
import markdownDirective from 'client/directives/markdown';
|
||||||
import notifications from 'client/mixins/notifications';
|
import notifications from 'client/mixins/notifications';
|
||||||
|
import brokenTaskModal from './brokenTaskModal';
|
||||||
import approvalHeader from './approvalHeader';
|
import approvalHeader from './approvalHeader';
|
||||||
import approvalFooter from './approvalFooter';
|
import approvalFooter from './approvalFooter';
|
||||||
|
|
||||||
@@ -310,6 +318,7 @@ export default {
|
|||||||
bPopover,
|
bPopover,
|
||||||
approvalFooter,
|
approvalFooter,
|
||||||
approvalHeader,
|
approvalHeader,
|
||||||
|
brokenTaskModal,
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
markdown: markdownDirective,
|
markdown: markdownDirective,
|
||||||
@@ -327,6 +336,7 @@ export default {
|
|||||||
tags: tagsIcon,
|
tags: tagsIcon,
|
||||||
check: checkIcon,
|
check: checkIcon,
|
||||||
}),
|
}),
|
||||||
|
brokenChallengeTask: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -468,6 +478,10 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleBrokenTask (task) {
|
||||||
|
this.brokenChallengeTask = task;
|
||||||
|
this.$root.$emit('show::modal', 'broken-task-modal');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ div
|
|||||||
.col-8
|
.col-8
|
||||||
.about
|
.about
|
||||||
h2 About
|
h2 About
|
||||||
p {{user.profile.blurb}}
|
p(v-markdown='user.profile.blurb')
|
||||||
.photo
|
.photo
|
||||||
h2 Photo
|
h2 Photo
|
||||||
img.img-rendering-auto(v-if='user.profile.imageUrl', :src='user.profile.imageUrl')
|
img.img-rendering-auto(v-if='user.profile.imageUrl', :src='user.profile.imageUrl')
|
||||||
@@ -254,11 +254,7 @@ div
|
|||||||
.col-4(v-if='user.stats.points', @click='allocate(stat)')
|
.col-4(v-if='user.stats.points', @click='allocate(stat)')
|
||||||
button.btn.btn-primary(popover-trigger='mouseenter', popover-placement='right',
|
button.btn.btn-primary(popover-trigger='mouseenter', popover-placement='right',
|
||||||
:popover='$t(statInfo.allocatepop)') +
|
:popover='$t(statInfo.allocatepop)') +
|
||||||
|
private-message-modal(:userIdToMessage='userIdToMessage')
|
||||||
// @TODO: Make separate componenet
|
|
||||||
b-modal#private-message(title="Message", size='sm', :hide-footer="true")
|
|
||||||
textarea.form-control(v-model='privateMessage')
|
|
||||||
button.btn.btn-primary(@click='sendPrivateMessage()') Send
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -335,8 +331,9 @@ import { beastMasterProgress, mountMasterProgress } from '../../../common/script
|
|||||||
import statsComputed from '../../../common/script/libs/statsComputed';
|
import statsComputed from '../../../common/script/libs/statsComputed';
|
||||||
import autoAllocate from '../../../common/script/fns/autoAllocate';
|
import autoAllocate from '../../../common/script/fns/autoAllocate';
|
||||||
import allocate from '../../../common/script/ops/allocate';
|
import allocate from '../../../common/script/ops/allocate';
|
||||||
import notifications from 'client/mixins/notifications';
|
|
||||||
|
|
||||||
|
import privateMessageModal from 'client/components/private-message-modal';
|
||||||
|
import markdown from 'client/directives/markdown';
|
||||||
import achievementsLib from '../../../common/script/libs/achievements';
|
import achievementsLib from '../../../common/script/libs/achievements';
|
||||||
// @TODO: EMAILS.COMMUNITY_MANAGER_EMAIL
|
// @TODO: EMAILS.COMMUNITY_MANAGER_EMAIL
|
||||||
const COMMUNITY_MANAGER_EMAIL = 'admin@habitica.com';
|
const COMMUNITY_MANAGER_EMAIL = 'admin@habitica.com';
|
||||||
@@ -345,14 +342,16 @@ const DROP_ANIMALS = keys(Content.pets);
|
|||||||
const TOTAL_NUMBER_OF_DROP_ANIMALS = DROP_ANIMALS.length;
|
const TOTAL_NUMBER_OF_DROP_ANIMALS = DROP_ANIMALS.length;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
directives: {
|
||||||
|
markdown,
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
bModal,
|
bModal,
|
||||||
|
privateMessageModal,
|
||||||
},
|
},
|
||||||
mixins: [notifications],
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
userIdToMessage: '',
|
userIdToMessage: '',
|
||||||
privateMessage: '',
|
|
||||||
editing: false,
|
editing: false,
|
||||||
editingProfile: {
|
editingProfile: {
|
||||||
name: '',
|
name: '',
|
||||||
@@ -447,15 +446,6 @@ export default {
|
|||||||
this.userIdToMessage = this.user._id;
|
this.userIdToMessage = this.user._id;
|
||||||
this.$root.$emit('show::modal', 'private-message');
|
this.$root.$emit('show::modal', 'private-message');
|
||||||
},
|
},
|
||||||
async sendPrivateMessage () {
|
|
||||||
if (!this.privateMessage || !this.userIdToMessage) return;
|
|
||||||
|
|
||||||
await this.$store.dispatch('members:sendPrivateMessage', {
|
|
||||||
message: this.privateMessage,
|
|
||||||
toUserId: this.userIdToMessage,
|
|
||||||
});
|
|
||||||
this.text(this.$t('messageSentAlert'));
|
|
||||||
},
|
|
||||||
getProgressDisplay () {
|
getProgressDisplay () {
|
||||||
// let currentLoginDay = Content.loginIncentives[this.user.loginIncentives];
|
// let currentLoginDay = Content.loginIncentives[this.user.loginIncentives];
|
||||||
// if (!currentLoginDay) return this.$t('checkinReceivedAllRewardsMessage');
|
// if (!currentLoginDay) return this.$t('checkinReceivedAllRewardsMessage');
|
||||||
|
|||||||
@@ -174,3 +174,24 @@ export async function approve (store, payload) {
|
|||||||
let response = await axios.post(`/api/v3/tasks/${payload.taskId}/approve/${payload.userId}`);
|
let response = await axios.post(`/api/v3/tasks/${payload.taskId}/approve/${payload.userId}`);
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function unlinkOneTask (store, payload) {
|
||||||
|
if (!payload.keep) payload.keep = 'keep';
|
||||||
|
|
||||||
|
let task = payload.task;
|
||||||
|
const list = store.state.tasks.data[`${task.type}s`];
|
||||||
|
const taskIndex = list.findIndex(t => t._id === task._id);
|
||||||
|
|
||||||
|
if (taskIndex > -1) {
|
||||||
|
list.splice(taskIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = await axios.post(`/api/v3/tasks/unlink-one/${payload.task._id}?keep=${payload.keep}`);
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unlinkAllTasks (store, payload) {
|
||||||
|
if (!payload.keep) payload.keep = 'keep-all';
|
||||||
|
let response = await axios.post(`/api/v3/tasks/unlink-all/${payload.challengeId}?keep=${payload.keep}`);
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ schema.statics.sanitizeUpdate = function sanitizeUpdate (updateObj) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Basic fields to fetch for populating a group info
|
// Basic fields to fetch for populating a group info
|
||||||
export let basicFields = 'name type privacy leader';
|
export let basicFields = 'name type privacy leader summary';
|
||||||
|
|
||||||
schema.pre('remove', true, async function preRemoveGroup (next, done) {
|
schema.pre('remove', true, async function preRemoveGroup (next, done) {
|
||||||
next();
|
next();
|
||||||
|
|||||||
Reference in New Issue
Block a user