New client misc for days (#8924)

* Removed sticky header

* Fixed group desc and information

* Add flag modal

* Fixed chat sync errors

* Fixed balance display

* Fixed key and close issue

* Updated tavern placeholder

* Added and fixed links

* Removed open user modal from clicking menu

* Added better app loading check

* Removed banner from party

* Allowed for nav when clicking the card

* Fixed member display

* Updated create challenge modal to populate list and to create party/public

* Display members modal

* Added fetch recent messages
This commit is contained in:
Keith Holliday
2017-08-03 14:04:03 -06:00
committed by GitHub
parent e61884ed08
commit 75913842bc
13 changed files with 223 additions and 96 deletions

View File

@@ -1,5 +1,5 @@
<template lang="pug">
#app-header.row(:class='{sticky: user.preferences.stickyHeader}', v-if='showHeader')
#app-header.row
create-party-modal
members-modal(v-if="user.party._id", :group='user.party', :hide-badge="true")
member-details(:member="user", @click="$router.push({name: 'avatar'})")
@@ -120,6 +120,11 @@ export default {
}
},
openPartyModal () {
if (this.user.party._id) {
this.$store.state.groupId = this.user.party._id;
this.$root.$emit('show::modal', 'members-modal');
return;
}
this.$root.$emit('show::modal', 'create-party-modal');
},
},

View File

@@ -39,8 +39,8 @@ div
.dropdown-menu
router-link.dropdown-item(:to="{name: 'faq'}") {{ $t('faq') }}
router-link.dropdown-item(:to="{name: 'overview'}") {{ $t('overview') }}
router-link.dropdown-item(to="/groups/a29da26b-37de-4a71-b0c6-48e72a900dac") {{ $t('reportBug') }}
router-link.dropdown-item(to="/groups/5481ccf3-5d2d-48a9-a871-70a7380cee5a") {{ $t('askAQuestion') }}
router-link.dropdown-item(to="/groups/guild/a29da26b-37de-4a71-b0c6-48e72a900dac") {{ $t('reportBug') }}
router-link.dropdown-item(to="/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a") {{ $t('askAQuestion') }}
a.dropdown-item(href="https://trello.com/c/odmhIqyW/440-read-first-table-of-contents", target='_blank') {{ $t('requestAF') }}
a.dropdown-item(href="http://habitica.wikia.com/wiki/Contributing_to_Habitica", target='_blank') {{ $t('contributing') }}
a.dropdown-item(href="http://habitica.wikia.com/wiki", target='_blank') {{ $t('wiki') }}
@@ -51,7 +51,7 @@ div
.svg-icon(v-html="icons.gold")
span {{user.stats.gp | roundBigNumber}}
notification-menu
a.dropdown.item-with-icon.item-user(@click='showAvatar()')
a.dropdown.item-with-icon.item-user
.svg-icon(v-html="icons.user")
.dropdown-menu.dropdown-menu-right.user-dropdown
a.dropdown-item.edit-avatar(@click='showAvatar()')

View File

@@ -84,7 +84,6 @@
}
label {
width: 130px;
margin-right: .5em;
}
@@ -126,7 +125,7 @@ import { TAVERN_ID } from '../../../common/script/constants';
import { mapState } from 'client/libs/store';
export default {
props: ['challenge'],
props: ['challenge', 'groupId'],
components: {
bModal,
bDropdown,
@@ -206,6 +205,17 @@ export default {
});
this.groups = await this.$store.dispatch('guilds:getMyGuilds');
let party = await this.$store.dispatch('guilds:getGroup', {groupId: 'party'});
this.groups.push({
name: party.name,
_id: party._id,
});
this.groups.push({
name: 'Public',
_id: TAVERN_ID,
});
this.ressetWorkingChallenge();
},
watch: {
@@ -258,14 +268,14 @@ export default {
todos: [],
};
},
createChallenge () {
async createChallenge () {
if (!this.workingChallenge.name) alert('Name is required');
if (!this.workingChallenge.description) alert('Description is required');
this.workingChallenge.timestamp = new Date().getTime();
this.$store.dispatch('challenges:createChallenge', {challenge: this.workingChallenge});
let challenge = await this.$store.dispatch('challenges:createChallenge', {challenge: this.workingChallenge});
this.$emit('createChallenge', challenge);
this.ressetWorkingChallenge();
this.$root.$emit('hide::modal', 'challenge-modal');
},

View File

@@ -1,26 +1,29 @@
<template lang="pug">
div
challenge-modal
challenge-modal(:groupId='groupId', v-on:createChallenge='challengeCreated')
.row.no-quest-section(v-if='challenges.length === 0')
.col-12.text-center
.svg-icon.challenge-icon(v-html="icons.challengeIcon")
h4(v-once) {{ $t('haveNoChallenges') }}
p(v-once) {{ $t('challengeDescription') }}
button.btn.btn-secondary(v-once, @click='createChallenge()') {{ $t('createChallenge') }}
.col-12.challenge-item(v-for='challenge in challenges')
.row
.col-9
router-link.title(:to="{ name: 'challenge', params: { challengeId: challenge._id } }")
strong {{challenge.name}}
p {{challenge.description}}
div
.svg-icon.member-icon(v-html="icons.memberIcon")
.member-count {{challenge.memberCount}}
.col-3
div
span.svg-icon.gem(v-html="icons.gemIcon")
span.prize {{challenge.prize}}
div.prize-title Prize
router-link.title(:to="{ name: 'challenge', params: { challengeId: challenge._id } }", v-for='challenge in challenges')
.col-12.challenge-item
.row
.col-9
router-link.title(:to="{ name: 'challenge', params: { challengeId: challenge._id } }")
strong {{challenge.name}}
p {{challenge.description}}
div
.svg-icon.member-icon(v-html="icons.memberIcon")
.member-count {{challenge.memberCount}}
.col-3
div
span.svg-icon.gem(v-html="icons.gemIcon")
span.prize {{challenge.prize}}
div.prize-title Prize
.col-12.text-center
button.btn.btn-secondary(@click='createChallenge()') {{ $t('createChallenge') }}
</template>
<style>
@@ -79,6 +82,7 @@ div
<script>
import challengeModal from './challengeModal';
import { mapState } from 'client/libs/store';
import gemIcon from 'assets/svg/gem.svg';
import memberIcon from 'assets/svg/member-icon.svg';
@@ -89,8 +93,13 @@ export default {
components: {
challengeModal,
},
computed: {
...mapState({user: 'user.data'}),
},
async mounted () {
this.challenges = await this.$store.dispatch('challenges:getGroupChallenges', {groupId: this.groupId});
this.groupIdForChallenges = this.groupId;
if (this.user.party._id) this.groupIdForChallenges = this.user.party._id;
this.challenges = await this.$store.dispatch('challenges:getGroupChallenges', {groupId: this.groupIdForChallenges});
},
data () {
return {
@@ -100,12 +109,17 @@ export default {
memberIcon,
gemIcon,
}),
groupIdForChallenges: '',
};
},
methods: {
createChallenge () {
this.$root.$emit('show::modal', 'challenge-modal');
},
challengeCreated (challenge) {
if (challenge.group._id !== this.groupIdForChallenges) return;
this.challenges.push(challenge);
},
},
};
</script>

View File

@@ -1,13 +1,14 @@
<template lang="pug">
div
copy-as-todo-modal(:copying-message='copyingMessage', :group-name='groupName', :group-id='groupId')
report-flag-modal
.row
// .col-md-2
// @TODO: Implement when we pull avatars .svg-icon(v-html="icons.like")
.hr
.col-md-12(v-for="(msg, index) in chat", :key="msg.id", v-if='chat')
.col-md-12(v-for="(msg, index) in chat", v-if='chat')
// @TODO: is there a different way to do these conditionals? This creates an infinite loop
//.hr(v-if='displayDivider(msg)')
.hr-middle(v-once) {{ msg.timestamp }}
@@ -18,17 +19,17 @@ div
p {{msg.timestamp | timeAgo}}
.text {{msg.text}}
hr
.action(v-once, @click='like(msg)', v-if='msg.likes', :class='{active: msg.likes[user._id]}')
.action(@click='like(msg, index)', v-if='msg.likes', :class='{active: msg.likes[user._id]}')
.svg-icon(v-html="icons.like")
span(v-if='!msg.likes[user._id]') {{ $t('like') }}
span(v-if='msg.likes[user._id]') {{ $t('liked') }}
span.action(v-once, @click='copyAsTodo(msg)')
span.action( @click='copyAsTodo(msg)')
.svg-icon(v-html="icons.copy")
| {{$t('copyAsTodo')}}
span.action(v-once, v-if='user.contributor.admin || (!msg.sent && user.flags.communityGuidelinesAccepted)', @click='report(msg)')
span.action(v-if='user.contributor.admin || (!msg.sent && user.flags.communityGuidelinesAccepted)', @click='report(msg)')
.svg-icon(v-html="icons.report")
| {{$t('report')}}
span.action(v-once, v-if='msg.uuid === user._id', @click='remove(msg, index)')
span.action(v-if='msg.uuid === user._id', @click='remove(msg, index)')
.svg-icon(v-html="icons.delete")
| {{$t('delete')}}
span.action.float-right
@@ -94,9 +95,11 @@ div
<script>
import moment from 'moment';
import cloneDeep from 'lodash/cloneDeep';
import { mapState } from 'client/libs/store';
import copyAsTodoModal from './copyAsTodoModal';
import reportFlagModal from './reportFlagModal';
import deleteIcon from 'assets/svg/delete.svg';
import copyIcon from 'assets/svg/copy.svg';
@@ -108,6 +111,7 @@ export default {
props: ['chat', 'groupId', 'groupName'],
components: {
copyAsTodoModal,
reportFlagModal,
},
data () {
return {
@@ -119,7 +123,6 @@ export default {
liked: likedIcon,
}),
copyingMessage: {},
messages: [],
currentDayDividerDisplay: moment().day(),
};
},
@@ -134,6 +137,9 @@ export default {
},
computed: {
...mapState({user: 'user.data'}),
messages () {
return this.chat;
},
},
methods: {
displayDivider (message) {
@@ -148,27 +154,31 @@ export default {
if (!message.likes) return 0;
return Object.keys(message.likes).length;
},
async like (message) {
async like (messageToLike, index) {
let message = cloneDeep(messageToLike);
await this.$store.dispatch('chat:like', {
groupId: this.groupId,
chatId: message.id,
});
if (!message.likes[this.user._id]) {
message.likes[this.user._id] = true;
} else {
message.likes[this.user._id] = !message.likes[this.user._id];
}
await this.$store.dispatch('chat:like', {
groupId: this.groupId,
chatId: message.id,
});
this.chat.splice(index, 1, message);
},
copyAsTodo (message) {
this.copyingMessage = message;
this.$root.$emit('show::modal', 'copyAsTodo');
},
async report (message) {
await this.$store.dispatch('chat:flag', {
groupId: this.groupId,
chatId: message.id,
});
this.$store.state.flagChatOptions.message = message;
this.$store.state.flagChatOptions.groupId = this.groupId;
this.$root.$emit('show::modal', 'report-flag');
},
async remove (message, index) {
this.chat.splice(index, 1);

View File

@@ -0,0 +1,73 @@
<template lang="pug">
b-modal#report-flag(:title='$t("abuseFlagModalHeading")', size='lg', :hide-footer='true')
.modal-header
h4(v-html="$t('abuseFlagModalHeading', reportData)")
.modal-body
blockquote
// @TODO: markdown(text='abuseObject.text')
p(v-html="$t('abuseFlagModalBody', abuseFlagModalBody)")
.modal-footer
button.pull-left.btn.btn-danger(@click='clearFlagCount()', v-if='user.contributor.admin && abuseObject.flagCount > 0')
| Reset Flag Count
button.btn.btn-primary(@click='close()') {{ $t('cancel') }}
button.btn.btn-danger(@click='reportAbuse()') {{ $t('abuseFlagModalButton') }}
</template>
<script>
import bModal from 'bootstrap-vue/lib/components/modal';
import { mapState } from 'client/libs/store';
import notifications from 'client/mixins/notifications';
export default {
mixins: [notifications],
components: {
bModal,
},
computed: {
...mapState({user: 'user.data'}),
reportData () {
let reportMessage = this.abuseObject.user;
let isSystemMessage = this.abuseObject.uuid === 'system';
if (isSystemMessage) reportMessage = this.$t('systemMessage');
return {
name: `<span class='text-danger'>${reportMessage}</span>`,
};
},
abuseObject () {
return this.$store.state.flagChatOptions.message;
},
groupId () {
return this.$store.state.flagChatOptions.groupId;
},
},
data () {
let abuseFlagModalBody = {
firstLinkStart: '<a href="/static/community-guidelines" target="_blank">',
secondLinkStart: '<a href="/static/terms" target="_blank">',
linkEnd: '</a>',
};
return {
abuseFlagModalBody,
};
},
methods: {
close () {
this.$root.$emit('hide::modal', 'report-flag');
},
async reportAbuse () {
await this.$store.dispatch('chat:flag', {
groupId: this.groupId,
chatId: this.abuseObject.id,
});
this.notify('Thank you for reporting this violation. The moderators have been notified.');
},
async clearFlagCount () {
await this.$store.dispatch('chat:clearFlagCount', {
groupId: this.groupId,
chatId: this.abuseObject.id,
});
},
},
};
</script>

View File

@@ -19,7 +19,7 @@
.col-4(v-if='!isParty')
.item-with-icon
.svg-icon.gem(v-html="icons.gem")
span.number {{group.memberCount}}
span.number {{group.balance}}
div(v-once) {{ $t('guildBank') }}
.row.chat-row
.col-12
@@ -27,14 +27,13 @@
textarea(:placeholder="$t('chatPlaceHolder')", v-model='newMessage')
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') }}
chat-message(:chat.sync='group.chat', :group-id='group._id', group-name='group.name')
.col-4.sidebar
.guild-background.row
.row(:class='{"guild-background": !isParty}')
.col-6
p(v-if='!isParty')
.col-6
.button-container
button.btn.btn-success(class='btn-success', v-if='isLeader') {{ $t('upgrade') }}
@@ -112,7 +111,6 @@
.svg-icon(v-html="icons.downIcon")
.section(v-if="sections.description")
p(v-once) {{ group.description }}
p Life hacks are tricks, shortcuts, or methods that help increase productivity, efficiency, health, and so on. Generally, they get you to a better state of life. Life hacking is the process of utilizing and implementing these secrets. And, in this guild, we want to help everyone discover these improved ways of doing things.
.section-header
.row
@@ -124,8 +122,7 @@
.toggle-down(@click="sections.information = !sections.information", v-if="!sections.information")
.svg-icon(v-html="icons.downIcon")
.section(v-if="sections.information")
h4 Welcome
p Below are some resources that some members might find useful. Consider checking them out before posting any questions, as they just might help answer some of them! Feel free to share your life hacks in the guild chat, or ask any questions that you might have. Please peruse at your leisure, and remember: this guild is meant to help guide you in the right direction. Only you will know what works best for you.
p(v-once) {{ group.information }}
.section-header.challenge
.row
@@ -530,6 +527,9 @@ export default {
this.group.chat.unshift(response.message);
this.newMessage = '';
},
fetchRecentMessages () {
this.fetchGuild();
},
updateGuild () {
this.$store.state.editingGroup = this.group;
this.$root.$emit('show::modal', 'guild-form');

View File

@@ -6,7 +6,7 @@ div
.col-6
h1(v-once) {{$t('members')}}
.col-6
button(type="button" aria-label="Close" class="close")
button(type="button" aria-label="Close" class="close", @click='close()')
span(aria-hidden="true") ×
.row
.form-group.col-6
@@ -15,7 +15,7 @@ div
span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t('sort')", right=true)
b-dropdown-item(v-for='sortOption in sortOptions', @click='sort(sortOption.value)', :key='sortOption.value') {{sortOption.text}}
.row(v-for='member in members', :key='member', )
.row(v-for='member in members')
.col-8.offset-1
member-details(:member='member')
.col-3.actions
@@ -159,16 +159,16 @@ export default {
},
methods: {
async getMembers () {
let groupId = this.group._id || this.$store.state.groupId;
let groupId = this.$store.state.groupId || this.group._id;
if (groupId) {
let members = await this.$store.dispatch('members:getGroupMembers', {
groupId,
includeAllPublicFields: true,
});
this.members = members;
} else if (this.$store.state.viewingMembers.length > 1) {
this.members = this.$store.state.viewingMembers;
}
if (this.$store.state.viewingMembers.length > 1) this.members = this.$store.state.viewingMembers;
},
async clickMember (uid, forceShow) {
let user = this.$store.state.user.data;
@@ -225,6 +225,9 @@ export default {
memberId,
});
},
close () {
this.$root.$emit('hide::modal', 'members-modal');
},
},
};
</script>

View File

@@ -1,33 +1,42 @@
<template lang="pug">
.card
.card-block
.row
.col-md-2
.svg-icon.shield(v-html="icons.goldGuildBadge")
.member-count {{guild.memberCount}}
.col-md-10
.row
.col-md-8
router-link(:to="{ name: 'guild', params: { groupId: guild._id } }")
h3 {{ guild.name }}
p {{ guild.description }}
.col-md-2.cta-container
button.btn.btn-danger(v-if='isMember && displayLeave' @click='leave()', v-once) {{ $t('leave') }}
button.btn.btn-success(v-if='!isMember' @click='join()', v-once) {{ $t('join') }}
div.item-with-icon.gem-bank(v-if='displayGemBank')
.svg-icon.gem(v-html="icons.gem")
span.count {{ guild.balance }}
div.guild-bank(v-if='displayGemBank', v-once) {{$t('guildBank')}}
.row
.col-md-12
.category-label(v-for="category in guild.categories")
| {{category}}
span.recommend-text(v-if='showSuggested(guild._id)') Suggested because youre new to Habitica.
router-link.card-link(:to="{ name: 'guild', params: { groupId: guild._id } }")
.card
.card-block
.row
.col-md-2
.svg-icon.shield(v-html="icons.goldGuildBadge")
.member-count {{guild.memberCount}}
.col-md-10
.row
.col-md-8
router-link(:to="{ name: 'guild', params: { groupId: guild._id } }")
h3 {{ guild.name }}
p {{ guild.description }}
.col-md-2.cta-container
button.btn.btn-danger(v-if='isMember && displayLeave' @click='leave()', v-once) {{ $t('leave') }}
button.btn.btn-success(v-if='!isMember' @click='join()', v-once) {{ $t('join') }}
div.item-with-icon.gem-bank(v-if='displayGemBank')
.svg-icon.gem(v-html="icons.gem")
span.count {{ guild.balance }}
div.guild-bank(v-if='displayGemBank', v-once) {{$t('guildBank')}}
.row
.col-md-12
.category-label(v-for="category in guild.categories")
| {{category}}
span.recommend-text(v-if='showSuggested(guild._id)') Suggested because youre new to Habitica.
</template>
<style lang="scss" scoped>
@import '~client/assets/scss/colors.scss';
.card-link {
color: #4E4A57 !important;
}
.card-link:hover {
text-decoration: none !important;
}
.card {
height: 160px;
border-radius: 4px;

View File

@@ -10,7 +10,7 @@
h3(v-once) {{ $t('welcomeToTavern') }}
.row
textarea(:placeholder="$t('chatPlaceHolder')", v-model='newMessage')
textarea(placeholder="Type a message to Habiticans here", v-model='newMessage')
autocomplete(:text='newMessage', v-on:select="selectedAutocomplete")
button.btn.btn-secondary.send-chat.float-right(v-once, @click='sendMessage()') {{ $t('send') }}
@@ -63,25 +63,25 @@
.section.row(v-if="sections.helpfulLinks")
ul
li
a(herf='', v-once) {{ $t('communityGuidelinesLink') }}
a(href='/static/community-guidelines', v-once) {{ $t('communityGuidelinesLink') }}
li
a(herf='', v-once) {{ $t('lookingForGroup') }}
a(href='/groups/guilds/f2db2a7f-13c5-454d-b3ee-ea1f5089e601', v-once) {{ $t('lookingForGroup') }}
li
a(herf='', v-once) {{ $t('faq') }}
a(href='/static/faq', v-once) {{ $t('faq') }}
li
a(herf='', v-html="$t('glossary')")
a(href='', v-html="$t('glossary')")
li
a(herf='', v-once) {{ $t('wiki') }}
a(href='http://habitica.wikia.com/wiki/Wiki', v-once) {{ $t('wiki') }}
li
a(herf='', v-once) {{ $t('dataDisplayTool') }}
a(href='https://oldgods.net/habitrpg/habitrpg_user_data_display.html', v-once) {{ $t('dataDisplayTool') }}
li
a(herf='', v-once) {{ $t('reportProblem') }}
a(href='/groups/guilds/a29da26b-37de-4a71-b0c6-48e72a900dac', v-once) {{ $t('reportProblem') }}
li
a(herf='', v-once) {{ $t('requestFeature') }}
a(href='https://trello.com/c/odmhIqyW/440-read-first-table-of-contents', v-once) {{ $t('requestFeature') }}
li
a(herf='', v-html="$t('communityForum')")
a(href='', v-html="$t('communityForum')")
li
a(herf='', v-once) {{ $t('askQuestionGuild') }}
a(href='/groups/guilds/5481ccf3-5d2d-48a9-a871-70a7380cee5a', v-once) {{ $t('askQuestionGuild') }}
.section-header
.row

View File

@@ -133,9 +133,6 @@ export default {
invitedToQuest () {
return this.user.party.quest.RSVPNeeded && !this.user.party.quest.completed;
},
userTasks () {
return this.$store.state.tasks.data;
},
},
watch: {
baileyShouldShow () {
@@ -224,16 +221,17 @@ export default {
if (after !== true) return;
this.$root.$emit('show::modal', 'quest-invitation');
},
userTasks () {
// @TODO: Is this the best way to check for loaded?
this.runYesterDailies();
},
},
async mounted () {
if (!this.user.flags.welcomed) {
this.$root.$emit('show::modal', 'welcome');
}
Promise.all(['user.fetch', 'tasks.fetchUserTasks'])
.then(() => {
this.runYesterDailies();
});
window.setTimeout(() => {
this.initTour();
if (this.user.flags.tour.intro === this.TOUR_END) return;

View File

@@ -60,6 +60,10 @@ export default function () {
avatarEditorOptions: {
editingUser: false,
},
flagChatOptions: {
message: {},
groupId: '',
},
editingGroup: {}, // TODO move to local state
// content data, frozen to prevent Vue from modifying it since it's static and never changes
// TODO apply freezing to the entire codebase (the server) and not only to the client side?

View File

@@ -248,11 +248,12 @@
"owned": "Owned",
"not_owned": "Not Owned",
"participantsTitle": "Participants",
"shortName": "Short Name",
"shortName": "What short tag should be used to identify your Challenge?",
"classArmor": "Class Armor",
"backCapitalized": "Back Accessory",
"bodyCapitalized": "Body Accessory",
"eyewearCapitalized": "Eyewear",
"featuredset": "Featured Set <%= name %>",
"mysterySets": "Mystery Sets"
"mysterySets": "Mystery Sets",
"fetchRecentMessages": "Fetch Recent Messages"
}