mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 06:37:23 +01:00
Added initial inbox (#8852)
* Added initial inbox * Minor styles * Added more styles, selected conversations, sending messages * Added translations and colors
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="12" viewBox="0 0 16 12">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="12" viewBox="0 0 16 12">
|
||||||
<path fill="#4F2A93" fill-rule="evenodd" d="M14 10H2V2l6 5 6-5v8zm0-10H2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
|
<path fill-rule="evenodd" d="M14 10H2V2l6 5 6-5v8zm0-10H2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 235 B After Width: | Height: | Size: 220 B |
@@ -1,5 +1,7 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-sm
|
div
|
||||||
|
inbox-modal
|
||||||
|
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-sm
|
||||||
.navbar-header
|
.navbar-header
|
||||||
.logo.svg-icon(v-html="icons.logo")
|
.logo.svg-icon(v-html="icons.logo")
|
||||||
.collapse.navbar-collapse
|
.collapse.navbar-collapse
|
||||||
@@ -44,7 +46,7 @@ nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-sm
|
|||||||
router-link.dropdown-item.edit-avatar(:to="{name: 'avatar'}")
|
router-link.dropdown-item.edit-avatar(:to="{name: 'avatar'}")
|
||||||
h3 {{ user.profile.name }}
|
h3 {{ user.profile.name }}
|
||||||
span.small-text {{ $t('editAvatar') }}
|
span.small-text {{ $t('editAvatar') }}
|
||||||
router-link.dropdown-item(:to="{name: 'inbox'}") {{ $t('inbox') }}
|
a.nav-link.dropdown-item(@click.prevent='showInbox()') {{ $t('inbox') }}
|
||||||
router-link.dropdown-item(:to="{name: 'stats'}") {{ $t('stats') }}
|
router-link.dropdown-item(:to="{name: 'stats'}") {{ $t('stats') }}
|
||||||
router-link.dropdown-item(:to="{name: 'achievements'}") {{ $t('achievements') }}
|
router-link.dropdown-item(:to="{name: 'achievements'}") {{ $t('achievements') }}
|
||||||
router-link.dropdown-item(:to="{name: 'settings'}") {{ $t('settings') }}
|
router-link.dropdown-item(:to="{name: 'settings'}") {{ $t('settings') }}
|
||||||
@@ -198,8 +200,12 @@ import goldIcon from 'assets/svg/gold.svg';
|
|||||||
import notificationsIcon from 'assets/svg/notifications.svg';
|
import notificationsIcon from 'assets/svg/notifications.svg';
|
||||||
import userIcon from 'assets/svg/user.svg';
|
import userIcon from 'assets/svg/user.svg';
|
||||||
import logo from 'assets/svg/logo.svg';
|
import logo from 'assets/svg/logo.svg';
|
||||||
|
import InboxModal from './userMenu/inbox.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
InboxModal,
|
||||||
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
icons: Object.freeze({
|
icons: Object.freeze({
|
||||||
@@ -222,6 +228,9 @@ export default {
|
|||||||
localStorage.removeItem('habit-mobile-settings');
|
localStorage.removeItem('habit-mobile-settings');
|
||||||
this.$router.go('/');
|
this.$router.go('/');
|
||||||
},
|
},
|
||||||
|
showInbox () {
|
||||||
|
this.$root.$emit('show::modal', 'inbox-modal');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
218
website/client/components/userMenu/inbox.vue
Normal file
218
website/client/components/userMenu/inbox.vue
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
b-modal#inbox-modal(title="", :hide-footer="true", size='lg')
|
||||||
|
.header-wrap.container(slot="modal-header")
|
||||||
|
.row
|
||||||
|
.col-4
|
||||||
|
.row
|
||||||
|
.col-2
|
||||||
|
.svg-icon.envelope(v-html="icons.messageIcon")
|
||||||
|
.col-6
|
||||||
|
h2.text-center(v-once) {{$t('messages')}}
|
||||||
|
// @TODO: Implement this after we fix username bug
|
||||||
|
// .col-2.offset-1
|
||||||
|
// button.btn.btn-secondary(@click='toggleClick()') +
|
||||||
|
// .col-8.to-form(v-if='displayCreate')
|
||||||
|
// strong To:
|
||||||
|
// b-form-input
|
||||||
|
.row
|
||||||
|
.col-4.sidebar
|
||||||
|
.search-section
|
||||||
|
b-form-input(:placeholder="$t('search')", v-model='search')
|
||||||
|
.empty-messages.text-center(v-if='filtersConversations.length === 0')
|
||||||
|
.svg-icon.envelope(v-html="icons.messageIcon")
|
||||||
|
h4(v-once) {{$t('emptyMessagesLine1')}}
|
||||||
|
p(v-once) {{$t('emptyMessagesLine2')}}
|
||||||
|
.conversations(v-if='filtersConversations.length > 0')
|
||||||
|
.conversation(v-for='conversation in conversations', @click='selectConversation(conversation.key)', :class="{active: selectedConversation === conversation.key}")
|
||||||
|
div
|
||||||
|
span {{conversation.name}}
|
||||||
|
span.timeago {{conversation.date}}
|
||||||
|
div {{conversation.lastMessageText}}
|
||||||
|
.col-8.messages
|
||||||
|
.message(v-for='message in currentMessages') {{message.text}}
|
||||||
|
|
||||||
|
// @TODO: Implement new message header here when we fix the above
|
||||||
|
|
||||||
|
.new-message-row(v-if='selectedConversation')
|
||||||
|
b-form-input(v-model='newMessage')
|
||||||
|
button.btn.btn-secondary(@click='sendPrivateMessage()') Send
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
.envelope {
|
||||||
|
color: $gray-400 !important;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
background-color: $gray-700;
|
||||||
|
min-height: 600px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.search-section {
|
||||||
|
padding: 1em;
|
||||||
|
box-shadow: 0 1px 2px 0 rgba(26, 24, 29, 0.24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.to-form input {
|
||||||
|
width: 60%;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-messages {
|
||||||
|
margin-top: 10em;
|
||||||
|
color: $gray-400;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
color: $gray-400;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.envelope {
|
||||||
|
width: 30px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-message-row {
|
||||||
|
background-color: $gray-700;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
height: 88px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: inline-block;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
box-shadow: none;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation {
|
||||||
|
padding: 1.5em;
|
||||||
|
background: $white;
|
||||||
|
height: 80px;
|
||||||
|
|
||||||
|
.timeago {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation.active {
|
||||||
|
border: 1px solid $purple-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import moment from 'moment';
|
||||||
|
import filter from 'lodash/filter';
|
||||||
|
import { mapState } from 'client/libs/store';
|
||||||
|
|
||||||
|
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||||
|
import bFormInput from 'bootstrap-vue/lib/components/form-input';
|
||||||
|
|
||||||
|
import messageIcon from 'assets/svg/message.svg';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
bModal,
|
||||||
|
bFormInput,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
icons: Object.freeze({
|
||||||
|
messageIcon,
|
||||||
|
}),
|
||||||
|
displayCreate: true,
|
||||||
|
selectedConversation: '',
|
||||||
|
search: '',
|
||||||
|
newMessage: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({user: 'user.data'}),
|
||||||
|
conversations () {
|
||||||
|
let conversations = {};
|
||||||
|
for (let messageId in this.user.inbox.messages) {
|
||||||
|
let message = this.user.inbox.messages[messageId];
|
||||||
|
let userId = message.uuid;
|
||||||
|
|
||||||
|
if (!this.selectedConversation) this.selectedConversation = userId;
|
||||||
|
|
||||||
|
if (!conversations[userId]) {
|
||||||
|
conversations[userId] = {
|
||||||
|
name: message.user,
|
||||||
|
key: userId,
|
||||||
|
messages: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
conversations[userId].messages.push({
|
||||||
|
text: message.text,
|
||||||
|
timestamp: message.timestamp,
|
||||||
|
});
|
||||||
|
conversations[userId].lastMessageText = message.text;
|
||||||
|
conversations[userId].date = moment(new Date(message.timestamp)).fromNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
return conversations;
|
||||||
|
},
|
||||||
|
currentMessages () {
|
||||||
|
if (!this.selectedConversation) return;
|
||||||
|
return this.conversations[this.selectedConversation].messages;
|
||||||
|
},
|
||||||
|
filtersConversations () {
|
||||||
|
if (!this.search) return Object.values(this.conversations);
|
||||||
|
return filter(this.conversations, (conversation) => {
|
||||||
|
return conversation.name.toLowerCase().indexOf(this.search.toLowerCase()) !== -1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleClick () {
|
||||||
|
this.displayCreate = !this.displayCreate;
|
||||||
|
},
|
||||||
|
selectConversation (key) {
|
||||||
|
this.selectedConversation = key;
|
||||||
|
},
|
||||||
|
sendPrivateMessage () {
|
||||||
|
this.$store.dispatch('members:sendPrivateMessage', {
|
||||||
|
toUserId: this.selectedConversation,
|
||||||
|
message: this.newMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.conversations[this.selectedConversation].messages.push({
|
||||||
|
text: this.newMessage,
|
||||||
|
timestamp: new Date(),
|
||||||
|
});
|
||||||
|
this.conversations[this.selectedConversation].lastMessageText = this.newMessage;
|
||||||
|
this.conversations[this.selectedConversation].date = new Date();
|
||||||
|
|
||||||
|
this.newMessage = '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -180,5 +180,8 @@
|
|||||||
"tier7": "Tier 7 (Legendary)",
|
"tier7": "Tier 7 (Legendary)",
|
||||||
"tierModerator": "Moderator (Guardian)",
|
"tierModerator": "Moderator (Guardian)",
|
||||||
"tierStaff": "Staff (Heroic)",
|
"tierStaff": "Staff (Heroic)",
|
||||||
"tierNPC": "NPC"
|
"tierNPC": "NPC",
|
||||||
|
"messages": "Messages",
|
||||||
|
"emptyMessagesLine1": "You don’t have any messages",
|
||||||
|
"emptyMessagesLine2": "Send a message to start a conversation!"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user