mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 15:17:25 +01:00
Merge branch 'develop' into release
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
b-modal#rebirth(:title="$t('modalAchievement')", size='md', :hide-footer="true", @hidden="reloadPage()")
|
||||
b-modal#rebirth(:title="$t('modalAchievement')", size='md', :hide-footer="true")
|
||||
.modal-body
|
||||
.col-12
|
||||
// @TODO: +achievementAvatar('sun',0)
|
||||
@@ -41,9 +41,6 @@
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'rebirth');
|
||||
},
|
||||
reloadPage () {
|
||||
window.location.reload(true);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -376,6 +376,8 @@ export default {
|
||||
openMemberProgressModal (member) {
|
||||
this.$root.$emit('habitica:challenge:member-progress', {
|
||||
progressMemberId: member._id,
|
||||
isLeader: this.isLeader,
|
||||
isAdmin: this.isAdmin,
|
||||
});
|
||||
},
|
||||
async exportChallengeCsv () {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template lang="pug">
|
||||
b-modal#challenge-member-modal(title="User Progress", size='lg')
|
||||
.row.award-row
|
||||
.row.award-row(v-if='isLeader || isAdmin')
|
||||
.col-12.text-center
|
||||
button.btn.btn-primary(v-once, @click='closeChallenge()') {{ $t('awardWinners') }}
|
||||
.row
|
||||
@@ -37,12 +37,16 @@ export default {
|
||||
reward: [],
|
||||
},
|
||||
memberId: '',
|
||||
isLeader: false,
|
||||
isAdmin: false,
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica:challenge:member-progress', (data) => {
|
||||
if (!data.progressMemberId) return;
|
||||
this.memberId = data.progressMemberId;
|
||||
this.isLeader = data.isLeader;
|
||||
this.isAdmin = data.isAdmin;
|
||||
this.$root.$emit('bv::show::modal', 'challenge-member-modal');
|
||||
});
|
||||
},
|
||||
|
||||
@@ -16,54 +16,7 @@
|
||||
h1 {{ $t('groupTasksTitle') }}
|
||||
// @TODO: Abstract to component!
|
||||
.col-12.col-md-4
|
||||
.input-group
|
||||
input.form-control.input-search(type="text", :placeholder="$t('search')", v-model="searchText")
|
||||
.filter-panel(v-if="isFilterPanelOpen")
|
||||
.tags-category(v-for="tagsType in tagsByType", v-if="tagsType.tags.length > 0", :key="tagsType.key")
|
||||
.tags-header.col-12
|
||||
strong(v-once) {{ $t(tagsType.key) }}
|
||||
a.d-block(v-if="tagsType.key === 'tags' && !editingTags", @click="editTags()") {{ $t('editTags2') }}
|
||||
.tags-list.container.col-12
|
||||
.row(:class="{'no-gutters': !editingTags}")
|
||||
template(v-if="editingTags && tagsType.key === 'tags'")
|
||||
.col-12.col-md-6(v-for="(tag, tagIndex) in tagsSnap")
|
||||
.inline-edit-input-group.tag-edit-item.input-group
|
||||
input.tag-edit-input.inline-edit-input.form-control(type="text", :value="tag.name")
|
||||
.input-group-append(@click="removeTag(tagIndex)")
|
||||
.svg-icon.destroy-icon(v-html="icons.destroy")
|
||||
.col-12.col-md-6
|
||||
input.new-tag-item.edit-tag-item.inline-edit-input.form-control(type="text", :placeholder="$t('newTag')", @keydown.enter="addTag($event)", v-model="newTag")
|
||||
template(v-else)
|
||||
.col-12.col-md-6(v-for="(tag, tagIndex) in tagsType.tags")
|
||||
.custom-control.custom-checkbox
|
||||
input.custom-control-input(
|
||||
type="checkbox",
|
||||
:checked="isTagSelected(tag)",
|
||||
@change="toggleTag(tag)",
|
||||
:id="`tag-${tagsType.key}-${tagIndex}`",
|
||||
)
|
||||
label.custom-control-label(:for="`tag-${tagsType.key}-${tagIndex}`") {{ tag.name }}
|
||||
|
||||
.filter-panel-footer.clearfix
|
||||
template(v-if="editingTags === true")
|
||||
.text-center
|
||||
a.mr-3.btn-filters-primary(@click="saveTags()", v-once) {{ $t('saveEdits') }}
|
||||
a.btn-filters-secondary(@click="cancelTagsEditing()", v-once) {{ $t('cancel') }}
|
||||
template(v-else)
|
||||
.float-left
|
||||
a.btn-filters-danger(@click="resetFilters()", v-once) {{ $t('resetFilters') }}
|
||||
.float-right
|
||||
a.mr-3.btn-filters-primary(@click="applyFilters()", v-once) {{ $t('applyFilters') }}
|
||||
a.btn-filters-secondary(@click="closeFilterPanel()", v-once) {{ $t('cancel') }}
|
||||
span.input-group-append
|
||||
button.btn.btn-secondary.filter-button(
|
||||
type="button",
|
||||
@click="toggleFilterPanel()",
|
||||
:class="{'filter-button-open': selectedTags.length > 0}",
|
||||
)
|
||||
.d-flex.align-items-center
|
||||
span(v-once) {{ $t('filter') }}
|
||||
.svg-icon.filter-icon(v-html="icons.filter")
|
||||
input.form-control.input-search(type="text", :placeholder="$t('search')", v-model="searchText")
|
||||
.create-task-area.d-flex(v-if='canCreateTasks')
|
||||
transition(name="slide-tasks-btns")
|
||||
.d-flex(v-if="openCreateBtn")
|
||||
@@ -99,10 +52,6 @@
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
@import '~client/assets/scss/create-task.scss';
|
||||
|
||||
.user-tasks-page {
|
||||
padding-top: 31px;
|
||||
}
|
||||
|
||||
.tasks-navigation {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
@@ -114,133 +63,6 @@
|
||||
margin-right: 8px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.dropdown-icon-item .svg-icon {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
button.btn.btn-secondary.filter-button {
|
||||
box-shadow: none;
|
||||
border-radius: 2px;
|
||||
border: 1px solid $gray-400 !important;
|
||||
|
||||
&:hover, &:active, &:focus, &.open {
|
||||
box-shadow: none;
|
||||
border-color: $purple-500 !important;
|
||||
color: $gray-50 !important;
|
||||
}
|
||||
|
||||
&.filter-button-open {
|
||||
color: $purple-200 !important;
|
||||
|
||||
.filter-icon {
|
||||
color: $purple-200 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-icon {
|
||||
height: 10px;
|
||||
width: 12px;
|
||||
color: $gray-50;
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-panel {
|
||||
position: absolute;
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
max-width: 40vw;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
background: $white;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
|
||||
top: 44px;
|
||||
left: 20vw;
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.tags-category {
|
||||
border-bottom: 1px solid $gray-600;
|
||||
padding-bottom: 24px;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.tags-header {
|
||||
flex-basis: 96px;
|
||||
flex-shrink: 0;
|
||||
|
||||
a {
|
||||
font-size: 12px;
|
||||
line-height: 1.33;
|
||||
color: $blue-10;
|
||||
margin-top: 4px;
|
||||
|
||||
&:focus, &:hover, &:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tag-edit-input {
|
||||
border-bottom: 1px solid $gray-500 !important;
|
||||
|
||||
&:focus, &:focus ~ .input-group-append {
|
||||
border-color: $purple-500 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.new-tag-item {
|
||||
width: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center left 10px;
|
||||
border-bottom: 1px solid $gray-500 !important;
|
||||
background-size: 10px 10px;
|
||||
padding-left: 40px;
|
||||
background-image: url(~client/assets/svg/for-css/positive.svg);
|
||||
}
|
||||
|
||||
.tag-edit-item .input-group-append {
|
||||
border-bottom: 1px solid $gray-500 !important;
|
||||
|
||||
&:focus {
|
||||
border-color: $purple-500;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-control-label {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.filter-panel-footer {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
|
||||
a {
|
||||
&:focus, &:hover, &:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-filters-danger {
|
||||
color: $red-50;
|
||||
}
|
||||
|
||||
.btn-filters-primary {
|
||||
color: $blue-10;
|
||||
}
|
||||
|
||||
.btn-filters-secondary {
|
||||
color: $gray-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-label {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<template lang="pug">
|
||||
base-notification(
|
||||
:can-remove="canRemove",
|
||||
:has-icon="false",
|
||||
:notification="notification",
|
||||
:read-after-click="true",
|
||||
@click="action",
|
||||
)
|
||||
div(slot="content", v-html="notification.data.message")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseNotification from './base';
|
||||
|
||||
export default {
|
||||
props: ['notification', 'canRemove'],
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
methods: {
|
||||
action () {
|
||||
this.$router.push({ name: 'tasks'});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -87,6 +87,7 @@ import CHALLENGE_INVITATION from './notifications/challengeInvitation';
|
||||
import QUEST_INVITATION from './notifications/questInvitation';
|
||||
import GROUP_TASK_APPROVAL from './notifications/groupTaskApproval';
|
||||
import GROUP_TASK_APPROVED from './notifications/groupTaskApproved';
|
||||
import GROUP_TASK_ASSIGNED from './notifications/groupTaskAssigned';
|
||||
import UNALLOCATED_STATS_POINTS from './notifications/unallocatedStatsPoints';
|
||||
import NEW_MYSTERY_ITEMS from './notifications/newMysteryItems';
|
||||
import CARD_RECEIVED from './notifications/cardReceived';
|
||||
@@ -102,7 +103,7 @@ export default {
|
||||
// One component for each type
|
||||
NEW_STUFF, GROUP_TASK_NEEDS_WORK,
|
||||
GUILD_INVITATION, PARTY_INVITATION, CHALLENGE_INVITATION,
|
||||
QUEST_INVITATION, GROUP_TASK_APPROVAL, GROUP_TASK_APPROVED,
|
||||
QUEST_INVITATION, GROUP_TASK_APPROVAL, GROUP_TASK_APPROVED, GROUP_TASK_ASSIGNED,
|
||||
UNALLOCATED_STATS_POINTS, NEW_MYSTERY_ITEMS, CARD_RECEIVED,
|
||||
NEW_INBOX_MESSAGE, NEW_CHAT_MESSAGE,
|
||||
WorldBoss: WORLD_BOSS,
|
||||
@@ -118,7 +119,7 @@ export default {
|
||||
openStatus: undefined,
|
||||
actionableNotifications: [
|
||||
'GUILD_INVITATION', 'PARTY_INVITATION', 'CHALLENGE_INVITATION',
|
||||
'QUEST_INVITATION', 'GROUP_TASK_NEEDS_WORK',
|
||||
'QUEST_INVITATION', 'GROUP_TASK_NEEDS_WORK', 'GROUP_TASK_APPROVAL',
|
||||
],
|
||||
// A list of notifications handled by this component,
|
||||
// listed in the order they should appear in the notifications panel.
|
||||
@@ -126,7 +127,7 @@ export default {
|
||||
handledNotifications: [
|
||||
'NEW_STUFF', 'GROUP_TASK_NEEDS_WORK',
|
||||
'GUILD_INVITATION', 'PARTY_INVITATION', 'CHALLENGE_INVITATION',
|
||||
'QUEST_INVITATION', 'GROUP_TASK_APPROVAL', 'GROUP_TASK_APPROVED',
|
||||
'QUEST_INVITATION', 'GROUP_TASK_ASSIGNED', 'GROUP_TASK_APPROVAL', 'GROUP_TASK_APPROVED',
|
||||
'NEW_MYSTERY_ITEMS', 'CARD_RECEIVED',
|
||||
'NEW_INBOX_MESSAGE', 'NEW_CHAT_MESSAGE', 'UNALLOCATED_STATS_POINTS',
|
||||
'VERIFY_USERNAME',
|
||||
|
||||
@@ -132,11 +132,6 @@ const NOTIFICATIONS = {
|
||||
label: ($t) => `${$t('achievement')}: ${$t('gearAchievementNotification')}`,
|
||||
modalId: 'ultimate-gear',
|
||||
},
|
||||
REBIRTH_ACHIEVEMENT: {
|
||||
label: ($t) => `${$t('achievement')}: ${$t('rebirthBegan')}`,
|
||||
achievement: true,
|
||||
modalId: 'rebirth',
|
||||
},
|
||||
GUILD_JOINED_ACHIEVEMENT: {
|
||||
label: ($t) => `${$t('achievement')}: ${$t('joinedGuild')}`,
|
||||
achievement: true,
|
||||
@@ -360,18 +355,7 @@ export default {
|
||||
this.playSound(config.sound);
|
||||
}
|
||||
|
||||
if (type === 'REBIRTH_ACHIEVEMENT') {
|
||||
// reload if the user hasn't clicked on the notification
|
||||
const timeOut = setTimeout(() => {
|
||||
window.location.reload(true);
|
||||
}, 60000);
|
||||
|
||||
this.text(config.label(this.$t), () => {
|
||||
// prevent the current reload timeout
|
||||
clearTimeout(timeOut);
|
||||
this.$root.$emit('bv::show::modal', config.modalId);
|
||||
}, false);
|
||||
} else if (forceToModal) {
|
||||
if (forceToModal) {
|
||||
this.$root.$emit('bv::show::modal', config.modalId);
|
||||
} else {
|
||||
this.text(config.label(this.$t), () => {
|
||||
@@ -573,8 +557,11 @@ export default {
|
||||
}, this.user.preferences.suppressModals.streak);
|
||||
this.playSound('Achievement_Unlocked');
|
||||
break;
|
||||
case 'ULTIMATE_GEAR_ACHIEVEMENT':
|
||||
case 'REBIRTH_ACHIEVEMENT':
|
||||
this.playSound('Achievement_Unlocked');
|
||||
this.$root.$emit('bv::show::modal', 'rebirth');
|
||||
break;
|
||||
case 'ULTIMATE_GEAR_ACHIEVEMENT':
|
||||
case 'GUILD_JOINED_ACHIEVEMENT':
|
||||
case 'CHALLENGE_JOINED_ACHIEVEMENT':
|
||||
case 'INVITED_FRIEND_ACHIEVEMENT':
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
:class="{'notEnough': !preventHealthPotion || !this.enoughCurrency(getPriceClass(), item.value * selectedAmountToBuy)}"
|
||||
) {{ $t('buyNow') }}
|
||||
|
||||
div.limitedTime(v-if="item.event")
|
||||
div.limitedTime(v-if="item.event && item.owned == null")
|
||||
span.svg-icon.inline.icon-16(v-html="icons.clock")
|
||||
span.limitedString {{ limitedString }}
|
||||
|
||||
@@ -397,6 +397,10 @@
|
||||
|
||||
this.$emit('buyPressed', this.item);
|
||||
this.hideDialog();
|
||||
|
||||
if (this.item.key === 'rebirth_orb') {
|
||||
window.location.reload(true);
|
||||
}
|
||||
},
|
||||
purchaseGems () {
|
||||
if (this.item.key === 'rebirth_orb') {
|
||||
|
||||
@@ -5,7 +5,7 @@ div
|
||||
slot(name="itemBadge", :item="item", :emptyItem="emptyItem")
|
||||
|
||||
span.badge.badge-pill.badge-item.badge-clock(
|
||||
v-if="item.event && showEventBadge",
|
||||
v-if="item.event && item.owned == null && showEventBadge",
|
||||
)
|
||||
span.svg-icon.inline.clock(v-html="icons.clock")
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<template lang="pug">
|
||||
.tags-popup
|
||||
.tags-category.d-flex
|
||||
.tags-category.d-flex(
|
||||
v-for="tagsType in tagsByType",
|
||||
v-if="tagsType.tags.length > 0 || tagsType.key === 'tags'",
|
||||
:key="tagsType.key"
|
||||
)
|
||||
.tags-header
|
||||
strong(v-once) {{ $t('tags') }}
|
||||
strong(v-once) {{ $t(tagsType.key) }}
|
||||
.tags-list.container
|
||||
.row
|
||||
.col-4(v-for="tag in tags")
|
||||
.col-4(v-for="(tag, tagIndex) in tagsType.tags")
|
||||
.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value="tag.id", v-model="selectedTags", :id="`tag-${tag.id}`")
|
||||
label.custom-control-label(:title="tag.name", :for="`tag-${tag.id}`", v-markdown="tag.name")
|
||||
@@ -103,6 +107,34 @@ export default {
|
||||
selectedTags: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
tagsByType () {
|
||||
const tagsByType = {
|
||||
challenges: {
|
||||
key: 'challenges',
|
||||
tags: [],
|
||||
},
|
||||
groups: {
|
||||
key: 'groups',
|
||||
tags: [],
|
||||
},
|
||||
user: {
|
||||
key: 'tags',
|
||||
tags: [],
|
||||
},
|
||||
};
|
||||
this.$props.tags.forEach(t => {
|
||||
if (t.group) {
|
||||
tagsByType.groups.tags.push(t);
|
||||
} else if (t.challenge) {
|
||||
tagsByType.challenges.tags.push(t);
|
||||
} else {
|
||||
tagsByType.user.tags.push(t);
|
||||
}
|
||||
});
|
||||
return tagsByType;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedTags () {
|
||||
this.$emit('input', this.selectedTags);
|
||||
|
||||
@@ -159,6 +159,15 @@
|
||||
| {{ $t(frequency) }}
|
||||
|
||||
.option.group-options(v-if='groupId')
|
||||
.form-group(v-if="task.type === 'todo'")
|
||||
label(v-once) {{ $t('sharedCompletion') }}
|
||||
b-dropdown.inline-dropdown(:text="$t(sharedCompletion)")
|
||||
b-dropdown-item(
|
||||
v-for="completionOption in ['recurringCompletion', 'singleCompletion', 'allAssignedCompletion']",
|
||||
:key="completionOption",
|
||||
@click="sharedCompletion = completionOption",
|
||||
:class="{active: sharedCompletion === completionOption}"
|
||||
) {{ $t(completionOption) }}
|
||||
.form-group.row
|
||||
label.col-12(v-once) {{ $t('assignedTo') }}
|
||||
.col-12.mt-2
|
||||
@@ -179,23 +188,12 @@
|
||||
|
||||
.row
|
||||
button.btn.btn-primary(@click.stop.prevent="showAssignedSelect = !showAssignedSelect") {{$t('close')}}
|
||||
|
||||
.option.group-options(v-if='groupId')
|
||||
.form-group
|
||||
label(v-once) {{ $t('approvalRequired') }}
|
||||
toggle-switch.d-inline-block(
|
||||
:checked="requiresApproval",
|
||||
@change="updateRequiresApproval"
|
||||
)
|
||||
.form-group(v-if="task.type === 'todo'")
|
||||
label(v-once) {{ $t('sharedCompletion') }}
|
||||
b-dropdown.inline-dropdown(:text="$t(sharedCompletion)")
|
||||
b-dropdown-item(
|
||||
v-for="completionOption in ['recurringCompletion', 'singleCompletion', 'allAssignedCompletion']",
|
||||
:key="completionOption",
|
||||
@click="sharedCompletion = completionOption",
|
||||
:class="{active: sharedCompletion === completionOption}"
|
||||
) {{ $t(completionOption) }}
|
||||
|
||||
.advanced-settings(v-if="task.type !== 'reward'")
|
||||
.advanced-settings-toggle.d-flex.justify-content-between.align-items-center(@click = "showAdvancedOptions = !showAdvancedOptions")
|
||||
@@ -360,8 +358,8 @@
|
||||
margin-top: 12px;
|
||||
position: relative;
|
||||
|
||||
label {
|
||||
max-height: 30px;
|
||||
.custom-control-label p {
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,7 +709,7 @@ export default {
|
||||
calendar: calendarIcon,
|
||||
}),
|
||||
requiresApproval: false, // We can't set task.group fields so we use this field to toggle
|
||||
sharedCompletion: 'recurringCompletion',
|
||||
sharedCompletion: 'singleCompletion',
|
||||
members: [],
|
||||
memberNamesById: {},
|
||||
assignedMembers: [],
|
||||
@@ -842,7 +840,7 @@ export default {
|
||||
});
|
||||
this.assignedMembers = [];
|
||||
if (this.task.group && this.task.group.assignedUsers) this.assignedMembers = this.task.group.assignedUsers;
|
||||
if (this.task.group) this.sharedCompletion = this.task.group.sharedCompletion || 'recurringCompletion';
|
||||
if (this.task.group) this.sharedCompletion = this.task.group.sharedCompletion || 'singleCompletion';
|
||||
}
|
||||
|
||||
// @TODO: This whole component is mutating a prop and that causes issues. We need to not copy the prop similar to group modals
|
||||
@@ -926,7 +924,6 @@ export default {
|
||||
|
||||
// TODO Fix up permissions on task.group so we don't have to keep doing these hacks
|
||||
if (this.groupId) {
|
||||
this.task.group.assignedUsers = this.assignedMembers;
|
||||
this.task.requiresApproval = this.requiresApproval;
|
||||
this.task.group.approval.required = this.requiresApproval;
|
||||
this.task.sharedCompletion = this.sharedCompletion;
|
||||
@@ -954,6 +951,7 @@ export default {
|
||||
});
|
||||
});
|
||||
Promise.all(promises);
|
||||
this.task.group.assignedUsers = this.assignedMembers;
|
||||
this.$emit('taskCreated', this.task);
|
||||
} else {
|
||||
this.createTask(this.task);
|
||||
|
||||
@@ -201,6 +201,7 @@
|
||||
|
||||
.custom-control-label {
|
||||
margin-left: 10px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.filter-panel-footer {
|
||||
|
||||
Reference in New Issue
Block a user