diff --git a/website/client/components/chat/autoComplete.vue b/website/client/components/chat/autoComplete.vue
index 52f8b664e1..029ab98e57 100644
--- a/website/client/components/chat/autoComplete.vue
+++ b/website/client/components/chat/autoComplete.vue
@@ -14,7 +14,7 @@ div.autocomplete-selection(v-if='searchResults.length > 0', :style='autocomplete
import groupBy from 'lodash/groupBy';
export default {
- props: ['selections', 'text', 'coords', 'groupId', 'chat'],
+ props: ['selections', 'text', 'coords', 'chat'],
data () {
return {
currentSearch: '',
@@ -43,6 +43,9 @@ export default {
});
},
},
+ mounted () {
+ this.grabUserNames();
+ },
watch: {
text (newText) {
if (newText[newText.length - 1] !== '@') return;
@@ -50,22 +53,19 @@ export default {
this.currentSearchPosition = newText.length - 1;
},
chat () {
+ this.grabUserNames();
+ },
+ },
+ methods: {
+ grabUserNames () {
let usersThatMessage = groupBy(this.chat, 'user');
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);
}
}
},
- 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) {
let newText = this.text.slice(0, this.currentSearchPosition + 1) + result;
this.searchActive = false;
diff --git a/website/client/components/chat/chatMessages.vue b/website/client/components/chat/chatMessages.vue
index acab9fd02e..7f3d8fd812 100644
--- a/website/client/components/chat/chatMessages.vue
+++ b/website/client/components/chat/chatMessages.vue
@@ -107,11 +107,11 @@
color: #277eab;
}
- .tier10 {
+ .tier9 {
color: #6133b4;
}
- .tier9 {
+ .tier10 {
color: #77f4c7;
fill: #77f4c7;
stroke: #005737;
diff --git a/website/client/components/creatorIntro.vue b/website/client/components/creatorIntro.vue
index 65ab53c992..32dd6ce660 100644
--- a/website/client/components/creatorIntro.vue
+++ b/website/client/components/creatorIntro.vue
@@ -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)')
#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
.col-3.text-center.sub-menu-item(@click='changeSubPage("2017")', :class='{active: activeSubPage === "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;
}
+ 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 {
cursor: pointer;
}
diff --git a/website/client/components/groups/group.vue b/website/client/components/groups/group.vue
index 232b248258..51f4f46c0d 100644
--- a/website/client/components/groups/group.vue
+++ b/website/client/components/groups/group.vue
@@ -28,7 +28,7 @@
h3(v-once) {{ $t('chat') }}
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.float-left(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }}
.col-12
diff --git a/website/client/components/groups/groupFormModal.vue b/website/client/components/groups/groupFormModal.vue
index bfcab72dc7..6af624718d 100644
--- a/website/client/components/groups/groupFormModal.vue
+++ b/website/client/components/groups/groupFormModal.vue
@@ -11,7 +11,7 @@
select.form-control(v-model="workingGroup.newLeader")
option(v-for='member in members', :value="member._id") {{ member.profile.name }}
- .form-group
+ .form-group(v-if='!this.workingGroup.id')
label
strong(v-once) {{$t('privacySettings')}} *
br
@@ -45,7 +45,7 @@
span.custom-control-description(v-once) {{ $t('allowGuildInvitationsFromNonMembers') }}
// "allowGuildInvitationsFromNonMembers": "Allow Guild invitations from non-members",
- .form-group(v-if='!creatingParty')
+ .form-group(v-if='!isParty')
label
strong(v-once) {{$t('guildSummary')}} *
div.summary-count {{charactersRemaining}} {{ $t('charactersRemaining') }}
@@ -56,7 +56,7 @@
label
strong(v-once) {{$t('groupDescription')}} *
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')
span
@@ -285,10 +285,31 @@ export default {
return data;
},
- mounted () {
- // @TODO: do we need this? Maybe us computed. If we need, then make it on show a specific modal
- this.$root.$on('shown::modal', () => {
- let editingGroup = this.$store.state.editingGroup;
+ computed: {
+ editingGroup () {
+ return 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) {
this.resetWorkingGroup();
return;
@@ -313,24 +334,6 @@ export default {
if (editingGroup._id) this.workingGroup.id = editingGroup._id;
if (editingGroup.leader._id) this.workingGroup.newLeader = editingGroup.leader._id;
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: {
@@ -444,15 +447,18 @@ export default {
},
resetWorkingGroup () {
this.workingGroup = {
+ id: '',
name: '',
type: 'guild',
privacy: 'private',
+ summary: '',
description: '',
categories: [],
onlyLeaderCreatesChallenges: true,
guildLeaderCantBeMessaged: true,
privateGuild: true,
allowGuildInvitationsFromNonMembers: true,
+ newLeader: '',
};
},
},
diff --git a/website/client/components/groups/tavern.vue b/website/client/components/groups/tavern.vue
index 5888103a59..e33626553c 100644
--- a/website/client/components/groups/tavern.vue
+++ b/website/client/components/groups/tavern.vue
@@ -11,7 +11,7 @@
.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')
- 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.float-left(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }}
diff --git a/website/client/components/private-message-modal.vue b/website/client/components/private-message-modal.vue
new file mode 100644
index 0000000000..0bc5e7bab6
--- /dev/null
+++ b/website/client/components/private-message-modal.vue
@@ -0,0 +1,35 @@
+
+ b-modal#private-message(title="Message", size='sm', :hide-footer="true")
+ textarea.form-control(v-model='privateMessage')
+ button.btn.btn-primary(@click='sendPrivateMessage()') Send
+
+
+
diff --git a/website/client/components/tasks/brokenTaskModal.vue b/website/client/components/tasks/brokenTaskModal.vue
new file mode 100644
index 0000000000..5f66358c13
--- /dev/null
+++ b/website/client/components/tasks/brokenTaskModal.vue
@@ -0,0 +1,77 @@
+
+ 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') }}
+
+
+
+
+
diff --git a/website/client/components/tasks/task.vue b/website/client/components/tasks/task.vue
index 7106d089cd..eef0d9d0a2 100644
--- a/website/client/components/tasks/task.vue
+++ b/website/client/components/tasks/task.vue
@@ -1,61 +1,64 @@
-.task(@click='castEnd($event, task)')
- approval-header(:task='task', v-if='this.task.group.id', :group='group')
- .d-flex(:class="{'task-not-scoreable': isUser !== true}")
- // Habits left side control
- .left-control.d-flex.align-items-center.justify-content-center(v-if="task.type === 'habit'", :class="controlClass.up")
- .task-control.habit-control(:class="controlClass.up + '-control-habit'", @click="(isUser && controlClass.up !== 'task-habit-disabled') ? score('up') : null")
- .svg-icon.positive(v-html="icons.positive")
- // Dailies and todos left side control
- .left-control.d-flex.justify-content-center(v-if="task.type === 'daily' || task.type === 'todo'", :class="controlClass")
- .task-control.daily-todo-control(:class="controlClass + '-control-daily-todo'", @click="isUser ? score(task.completed ? 'down' : 'up') : null")
- .svg-icon.check(v-html="icons.check", :class="{'display-check-icon': task.completed}")
- // Task title, description and icons
- .task-content(:class="contentClass")
- .task-clickable-area(@click="edit($event, task)")
- h3.task-title(:class="{ 'has-notes': task.notes }", v-markdown="task.text")
- .task-notes.small-text(v-markdown="task.notes")
- .checklist(v-if="task.checklist && task.checklist.length > 0")
- label.custom-control.custom-checkbox.checklist-item(
- v-for="item in task.checklist", :class="{'checklist-item-done': item.completed}",
- )
- input.custom-control-input(type="checkbox", :checked="item.completed", @change="toggleChecklistItem(item)")
- span.custom-control-indicator
- span.custom-control-description(v-markdown='item.text')
- .icons.small-text.d-flex.align-items-center
- .d-flex.align-items-center(v-if="task.type === 'todo' && task.date", :class="{'due-overdue': isDueOverdue}")
- .svg-icon.calendar(v-html="icons.calendar")
- span {{dueIn}}
- .icons-right.d-flex.justify-content-end
- .d-flex.align-items-center(v-if="showStreak")
- .svg-icon.streak(v-html="icons.streak")
- span(v-if="task.type === 'daily'") {{task.streak}}
- span(v-if="task.type === 'habit'")
- span.m-0(v-if="task.up") +{{task.counterUp}}
- span.m-0(v-if="task.up && task.down") |
- span.m-0(v-if="task.down") -{{task.counterDown}}
- .d-flex.align-items-center(v-if="task.challenge && task.challenge.id")
- .svg-icon.challenge(v-html="icons.challenge")
- b-popover.tags-popover.no-span-margin(
- :triggers="['hover']",
- :placement="'bottom'",
- :popover-style="{'max-width': '1000px'}",
+div
+ broken-task-modal(:brokenChallengeTask='brokenChallengeTask')
+ .task(@click='castEnd($event, task)')
+ approval-header(:task='task', v-if='this.task.group.id', :group='group')
+ .d-flex(:class="{'task-not-scoreable': isUser !== true}")
+ // Habits left side control
+ .left-control.d-flex.align-items-center.justify-content-center(v-if="task.type === 'habit'", :class="controlClass.up")
+ .task-control.habit-control(:class="controlClass.up + '-control-habit'", @click="(isUser && controlClass.up !== 'task-habit-disabled') ? score('up') : null")
+ .svg-icon.positive(v-html="icons.positive")
+ // Dailies and todos left side control
+ .left-control.d-flex.justify-content-center(v-if="task.type === 'daily' || task.type === 'todo'", :class="controlClass")
+ .task-control.daily-todo-control(:class="controlClass + '-control-daily-todo'", @click="isUser ? score(task.completed ? 'down' : 'up') : null")
+ .svg-icon.check(v-html="icons.check", :class="{'display-check-icon': task.completed}")
+ // Task title, description and icons
+ .task-content(:class="contentClass")
+ .task-clickable-area(@click="edit($event, task)")
+ h3.task-title(:class="{ 'has-notes': task.notes }", v-markdown="task.text")
+ .task-notes.small-text(v-markdown="task.notes")
+ .checklist(v-if="task.checklist && task.checklist.length > 0")
+ label.custom-control.custom-checkbox.checklist-item(
+ v-for="item in task.checklist", :class="{'checklist-item-done': item.completed}",
)
- .d-flex.align-items-center(slot="content")
- .tags-popover-title(v-once) {{ `${$t('tags')}:` }}
- .tag-label(v-for="tag in getTagsFor(task)") {{tag}}
- .d-flex.align-items-center(v-if="task.tags && task.tags.length > 0")
- .svg-icon.tags(v-html="icons.tags")
+ input.custom-control-input(type="checkbox", :checked="item.completed", @change="toggleChecklistItem(item)")
+ span.custom-control-indicator
+ span.custom-control-description(v-markdown='item.text')
+ .icons.small-text.d-flex.align-items-center
+ .d-flex.align-items-center(v-if="task.type === 'todo' && task.date", :class="{'due-overdue': isDueOverdue}")
+ .svg-icon.calendar(v-html="icons.calendar")
+ span {{dueIn}}
+ .icons-right.d-flex.justify-content-end
+ .d-flex.align-items-center(v-if="showStreak")
+ .svg-icon.streak(v-html="icons.streak")
+ span(v-if="task.type === 'daily'") {{task.streak}}
+ span(v-if="task.type === 'habit'")
+ span.m-0(v-if="task.up") +{{task.counterUp}}
+ span.m-0(v-if="task.up && task.down") |
+ span.m-0(v-if="task.down") -{{task.counterDown}}
+ .d-flex.align-items-center(v-if="task.challenge && task.challenge.id")
+ .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(
+ :triggers="['hover']",
+ :placement="'bottom'",
+ :popover-style="{'max-width': '1000px'}",
+ )
+ .d-flex.align-items-center(slot="content")
+ .tags-popover-title(v-once) {{ `${$t('tags')}:` }}
+ .tag-label(v-for="tag in getTagsFor(task)") {{tag}}
+ .d-flex.align-items-center(v-if="task.tags && task.tags.length > 0")
+ .svg-icon.tags(v-html="icons.tags")
- // Habits right side control
- .right-control.d-flex.align-items-center.justify-content-center(v-if="task.type === 'habit'", :class="controlClass.down")
- .task-control.habit-control(:class="controlClass.down + '-control-habit'", @click="(isUser && controlClass.down !== 'task-habit-disabled') ? score('down') : null")
- .svg-icon.negative(v-html="icons.negative")
- // Rewards right side control
- .right-control.d-flex.align-items-center.justify-content-center.reward-control(v-if="task.type === 'reward'", :class="controlClass", @click="isUser ? score('down') : null")
- .svg-icon(v-html="icons.gold")
- .small-text {{task.value}}
- approval-footer(:task='task', v-if='this.task.group.id', :group='group')
+ // Habits right side control
+ .right-control.d-flex.align-items-center.justify-content-center(v-if="task.type === 'habit'", :class="controlClass.down")
+ .task-control.habit-control(:class="controlClass.down + '-control-habit'", @click="(isUser && controlClass.down !== 'task-habit-disabled') ? score('down') : null")
+ .svg-icon.negative(v-html="icons.negative")
+ // Rewards right side control
+ .right-control.d-flex.align-items-center.justify-content-center.reward-control(v-if="task.type === 'reward'", :class="controlClass", @click="isUser ? score('down') : null")
+ .svg-icon(v-html="icons.gold")
+ .small-text {{task.value}}
+ approval-footer(:task='task', v-if='this.task.group.id', :group='group')