lots of changes ;o

This commit is contained in:
supertiger 2019-02-17 12:32:00 +00:00
parent fb43e6ea0b
commit 3ce91179b2
24 changed files with 553 additions and 100 deletions

View file

@ -1,4 +1,21 @@
const config = [
{
title: 'Typing Indicator',
shortTitle: 'Typing Indicator',
date: '08/02/2019',
new: [
'See who\'s typing with the new typing indicator!',
'Online and offline friends now have their own category.',
'Timestamps for each message.'
],
fix: [
'Some bugs with the message list scrolling.',
'Added some margin and padding to some places.'
],
next: [
'Message notifications.',
]
},
{
title: 'Avatar',
shortTitle: 'Avatar',

View file

@ -1,11 +1,19 @@
<template>
<div class="left-panel">
<MyMiniInformation />
<div class="list">
<PendingFriends />
<div class="tabs">
<div :class="{selector: true, right: !isFriendsTab}"></div>
<div class="tab" @click="isFriendsTab = true">Friends</div>
<div class="tab" @click="isFriendsTab = false">Recents</div>
</div>
<div class="list" v-if="isFriendsTab">
<pending-friends />
<online-friends />
<offline-friends />
</div>
<div class="list" v-else>
<recent-friends />
</div>
<AddFriendPanel/>
</div>
</template>
@ -17,13 +25,20 @@ import PendingFriends from './relationships/PendingFriends.vue'
import AddFriendPanel from './relationships/AddFriendPanel.vue'
import OnlineFriends from './relationships/OnlineFriends.vue'
import OfflineFriends from './relationships/OfflineFriends.vue'
import RecentFriends from './relationships/RecentFriends.vue'
export default {
components: {
MyMiniInformation,
PendingFriends,
AddFriendPanel,
OnlineFriends,
OfflineFriends
OfflineFriends,
RecentFriends
},
data() {
return {
isFriendsTab: true
}
}
}
</script>
@ -35,7 +50,8 @@ export default {
width: 300px;
flex-shrink: 0;
display: flex;
flex-direction: column
flex-direction: column;
z-index: 1;
}
.list{
margin: 10px;
@ -43,6 +59,39 @@ export default {
overflow: auto;
}
.tabs{
display: flex;
color: white;
flex-shrink: 0;
margin-top: 20px;
position: relative;
}
.tab{
flex: 1;
text-align: center;
margin: auto;
flex-shrink: 0;
user-select: none;
cursor: default;
padding: 10px;
background: rgba(0, 0, 0, 0.171);
margin-left: 1px;
margin-right: 1px;
}
.selector {
background: rgba(255, 255, 255, 0.137);
width: 148px;
height: 39px;
top: 0;
left: 1px;
position: absolute;
z-index: -1;
transition: 0.3s;
}
.right{
left: 151px;
}
/* ------- SCROLL BAR -------*/
/* width */
.list::-webkit-scrollbar {

View file

@ -1,11 +1,14 @@
<template>
<div class="message">
<div :class="{message: true, ownMessage: user.uniqueID === $props.uniqueID}">
<div class="profile-picture" :style="`background-image: url(${userAvatar})`"></div>
<div class="triangle">
<div class="triangle-inner"></div>
</div>
<div class="content">
<div class="username">{{this.$props.username}}</div>
<div class="user-info">
<div class="username">{{this.$props.username}}</div>
<div class="date">{{getDate}}</div>
</div>
<div class="content-message">{{this.$props.message}}</div>
</div>
<div class="sending-status">{{statusMessage}}</div>
@ -15,9 +18,13 @@
<script>
import config from '@/config.js'
import friendlyDate from '@/date'
export default {
props: ['message', 'status', 'username', 'avatar'],
props: ['message', 'status', 'username', 'avatar', 'date', 'uniqueID'],
computed: {
getDate() {
return friendlyDate(this.$props.date);
},
userAvatar() {
return config.domain + "/avatars/" + this.$props.avatar
},
@ -33,7 +40,10 @@ export default {
} else {
return ""
}
}
},
user() {
return this.$store.getters.user
}
}
}
</script>
@ -50,6 +60,13 @@ export default {
animation: showMessage .3s ease-in-out;
}
.ownMessage .triangle-inner{
border-right: 7px solid rgba(184, 184, 184, 0.219);
}
.ownMessage .content{
background: rgba(184, 184, 184, 0.219);
}
@keyframes showMessage {
from {
transform: translate(0px, 9px);
@ -68,7 +85,7 @@ export default {
margin-left: 0;
flex-shrink: 0;
background-position: center;
background-size: 100%;
background-size: cover;
background-repeat: no-repeat;
}
@ -104,16 +121,30 @@ export default {
margin-right: 0;
transition: 1s;
}
.username {
color: rgb(189, 189, 189);
font-size: 14px;
.user-info {
display: flex;
}
.username {
color: rgb(219, 219, 219);
font-size: 14px;
margin: auto;
margin-left: 0;
margin-right: 0;
}
.date{
color: rgb(161, 161, 161);
font-size: 10px;
margin: auto;
margin-left: 5px;
}
.content-message {
word-wrap: break-word;
word-break: break-word;
white-space: pre-wrap;
font-size: 14px;
overflow: hidden;
color: white;
}
.message .sending-status {

View file

@ -111,7 +111,7 @@ export default {
margin-left: 10px;
margin-right: 10px;
background-position: center;
background-size: 100%;
background-size: cover;
background-repeat: no-repeat;
}

View file

@ -8,12 +8,13 @@
</div>
<div class="current-channel"><span v-if="!selectedChannelID">Welcome back!</span><span v-else>{{channelName}}</span></div>
</div>
<typing-status v-if="typing" :username="whosTyping"/>
<div class="loading" v-if="selectedChannelID && !messages[selectedChannelID]">
<spinner />
</div>
<div v-else-if="selectedChannelID" class="message-logs">
<message v-for="(msg, index) in messages[selectedChannelID]" :key="index" :username="msg.creator.username" :avatar="msg.creator.avatar" :message="msg.message" :status="msg.status" />
<div v-else-if="selectedChannelID" class="message-logs" @wheel="invertScroll">
<div class="scroll">
<message v-for="(msg, index) in messages[selectedChannelID]" :key="index" :date="msg.created" :username="msg.creator.username" :uniqueID="msg.creator.uniqueID" :avatar="msg.creator.avatar" :message="msg.message" :status="msg.status" />
</div>
</div>
<news v-else />
<div class="chat-input-area" v-if="selectedChannelID">
@ -22,6 +23,11 @@
<button :class="{'send-button': true, 'error-send-button': messageLength > 5000}" @click="sendMessage">Send</button>
</div>
<div class="info">
<div class="typing-outer">
<transition name="typing-animate">
<typing-status v-if="typing" :username="whosTyping"/>
</transition>
</div>
<div :class="{'message-count': true, 'error-info': messageLength > 5000 }">{{messageLength}}/5000</div>
</div>
</div>
@ -79,6 +85,8 @@ export default {
if(this.message == "")return;
if (this.message.length > 5000) return;
clearInterval(this.postTimerID);
this.postTimerID = null;
const msg = this.message;
const tempID = this.generateNum(25);
@ -89,12 +97,13 @@ export default {
message: {
tempID,
message: this.message,
channelID: this.selectedChannelID
channelID: this.selectedChannelID,
created: new Date()
}
})
this.message = ""
this.$store.dispatch('updateChannelLastMessage', this.selectedChannelID);
const { ok, error, result } = await messagesService.post(this.selectedChannelID, {
message: msg,
socketID: this.$socket.id,
@ -136,15 +145,18 @@ export default {
this.sendMessage();
}
},
scrollDown(speed){
//Scroll to bottom
$(".message-logs").stop(true).animate({
scrollTop: $(".message-logs")[0].scrollHeight
}, speed);
invertScroll(event) {
if(event.deltaY) {
event.preventDefault();
event.currentTarget.scrollTop -= parseFloat(getComputedStyle(event.currentTarget).getPropertyValue('font-size')) * (event.deltaY < 0 ? -1 : 1) * 2;
}
},
hideTypingStatus(data) {
if(this.user.uniqueID === data.message.creator.uniqueID) return;
this.typing = false;
}
},
mounted() {
bus.$on('scrollDown', this.scrollDown);
this.$options.sockets.typingStatus = (data) => {
const {channelID, userID} = data;
if (channelID !== this.selectedChannelID) return;
@ -157,12 +169,16 @@ export default {
this.typing = false;
}, 2500);
}
bus.$on('newMessage', this.hideTypingStatus)
},
beforeDestroy() {
bus.$off('newMessage', this.hideTypingStatus);
delete this.$options.sockets.typingStatus;
bus.$off('scrollDown', this.scrollDown)
},
computed: {
user() {
return this.$store.getters.user;
},
channel() {
return this.$store.getters.channels[this.selectedChannelID];
},
@ -183,6 +199,18 @@ export default {
<style scoped>
.typing-animate-enter-active {
transition: .10s;
}
.typing-animate-enter /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
transform: translateY(3px)
}
.typing-animate-leave-to {
opacity: 0;
transform: translateY(-3px)
}
.error-info {
color: red;
}
@ -227,6 +255,8 @@ export default {
overflow: auto;
flex: 1;
}
.message-logs, .message-logs .scroll {transform: scale(1,-1);}
.loading{
overflow: auto;
flex: 1;
@ -241,11 +271,21 @@ export default {
color: white;
font-size: 12px;
margin-left: 25px;
margin-top: 5px;
display: flex;
}
.typing-outer{
flex: 1;
height: 20px;
}
.message-count {
float: right;
margin-right: 30px;
margin-top: 3px;
}
.message-area{
display: flex;
width: 100%;
display: flex;
width: 100%;
}
.chat-input{
font-family: 'Roboto', sans-serif;

View file

@ -104,7 +104,7 @@ export default {
margin-left: 20px;
flex-shrink: 0;
background-position: center;
background-size: 100%;
background-size: cover;
background-repeat: no-repeat;
}
.information {

View file

@ -1,7 +1,7 @@
<template>
<div class="typing-status">
<object class="animation" type="image/svg+xml" :data="animation"></object>
<div class="text">{{this.$props.username}} is typing...</div>
<div class="text"><strong>{{this.$props.username}}</strong> is typing...</div>
</div>
</template>
@ -19,34 +19,20 @@ export default {
<style scoped>
.typing-status {
color: white;
background: rgba(0, 0, 0, 0.246);
padding: 5px;
margin-bottom: 5px;
display: flex;
transition: 0.3s;
flex-shrink: 0;
flex: 1;
}
.animation {
height: 40px;
width: 40px;
height: 20px;
width: 20px;
display: flex;
}
.text {
margin: auto;
margin-left: 10px;
margin-left: 5px;
font-size: 13px;
}
@keyframes moveBall {
from{
height: 0px;
width: 0px;
opacity: 1;
}
to {
height: 40px;
width: 40px;
opacity: 0;
}
}
</style>

View file

@ -90,7 +90,7 @@ export default {
transition: .3s;
}
.slide-enter, .slide-leave-to /* .fade-leave-active below version 2.1.8 */ {
margin-bottom: -270px;
margin-bottom: -250px;
opacity: 0;
}
@ -104,7 +104,7 @@ export default {
.add-friend{
background: rgba(0, 0, 0, 0.13);
flex: 1;
height: 250px;
height: 230px;
display: flex;
flex-direction: column;
color: white;
@ -150,8 +150,8 @@ form {
background: rgba(0, 0, 0, 0.274);
width: 100%;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
padding-top: 5px;
padding-bottom: 5px;
color: white;
cursor: pointer;
user-select: none;

View file

@ -1,5 +1,5 @@
<template>
<div class="friend" @click="openChat">
<div :class="{friend: true, notifyAnimation: (notificationss && notificationss > 0) }" :style="`background: ${status.bgColor};`" @click="openChat">
<div class="profile-picture" :style="`border-color: ${status.statusColor}; background-image: url(${userAvatar})`">
<div class="status" :style="`background-image: url(${status.statusURL})`" ></div>
</div>
@ -7,6 +7,11 @@
<div class="username">{{$props.username}}</div>
<div class="status-name" :style="`color: ${status.statusColor}`">{{status.statusName}}</div>
</div>
<div class="notification" v-if="notificationss && notificationss >0">
<div class="notification-inner">
{{notificationss}}
</div>
</div>
</div>
</template>
@ -15,9 +20,10 @@ import channelService from '@/services/channelService';
import messagesService from '@/services/messagesService';
import config from '@/config.js'
import statuses from '@/statuses';
import {bus} from '@/main'
export default {
props: ['username', 'tag', 'channelID', 'uniqueID'],
props: ['username', 'tag', 'channelID', 'uniqueID', ],
methods: {
async getMessages() {
const {ok, error, result} = await messagesService.get(this.$props.channelID);
@ -29,9 +35,10 @@ export default {
}
},
async openChat() {
bus.$emit('closeLeftMenu');
this.$store.dispatch('selectedChannelID', this.$props.channelID);
this.$store.dispatch('setName', this.$props.username);
if (this.$store.getters.channels[this.$props.channelID]) return
this.$store.dispatch('setChannelName', this.$props.username);
if (this.$store.getters.channels[this.$props.channelID] && !this.$store.getters.messages[this.$props.channelID]) return this.getMessages();
const {ok, error, result} = await channelService.post(this.$props.channelID);
if ( ok ) {
this.$store.dispatch('channel', result.data.channel);
@ -43,6 +50,14 @@ export default {
}
},
computed: {
notificationss () {
const channelID = this.$props.channelID;
const notifications = this.$store.getters.notifications.find(function(e) {
return e.channelID == channelID
})
if (!notifications) return;
return notifications.count;
},
user() {
return this.$store.getters.user.friends[this.$props.uniqueID].recipient;
},
@ -54,7 +69,8 @@ export default {
return {
statusName: statuses[parseInt(status)].name,
statusURL: statuses[parseInt(status)].url,
statusColor: statuses[parseInt(status)].color
statusColor: statuses[parseInt(status)].color,
bgColor: statuses[parseInt(status)].bgColor
}
}
}
@ -67,10 +83,42 @@ export default {
color: white;
background-color: rgba(0, 0, 0, 0.137);
margin: 5px;
padding: 10px;
padding: 5px;
padding-right: 0;
display: flex;
transition: 0.3s;
border-radius: 3px;
border-radius: 5px;
position: relative;
overflow: hidden;
}
.notifyAnimation:before{
content: '';
position: absolute;
z-index: -1;
top: 0;
left: 0;
right: 0;
bottom: 0;
animation: notifyAnime;
animation-duration: 1s;
animation-iteration-count: infinite;
animation-fill-mode: forwards
}
@keyframes notifyAnime {
0%{
background: rgba(255, 0, 0, 0.198);
}
40%{
background: rgba(255, 0, 0, 0.411);
}
60%{
background: rgba(255, 0, 0, 0.411);
}
100%{
background: rgba(255, 0, 0, 0.198);
}
}
.friend:hover {
@ -78,18 +126,19 @@ export default {
}
.profile-picture{
height: 40px;
width: 40px;
height: 30px;
width: 30px;
background-color: rgba(0, 0, 0, 0.425);
border-radius: 50%;
margin: auto;
margin-left: 2px;
margin-right: 5px;
border: solid 3px;
border: solid 2px;
position: relative;
background-position: center;
background-size: 100%;
background-size: cover;
background-repeat: no-repeat;
transition: 0.3s;
}
.information{
margin: auto;
@ -98,10 +147,25 @@ export default {
flex: 1;
}
.notification{
position: absolute;
display: flex;
background: rgba(134, 134, 134, 0.315);
height: 100%;
right: 0;
top: 0;
bottom: 0;
width: 50px;
border-radius: 1px;
}
.notification-inner{
margin: auto;
}
.status {
position: absolute;
height: 20px;
width: 20px;
height: 15px;
width: 15px;
background-color: black;
border-radius: 50%;
background-size: calc(100% + 2px);
@ -114,7 +178,7 @@ export default {
.friend:hover .status {
opacity: 1;
bottom: -5px;
bottom: -4px;
}
.status-name{
@ -125,7 +189,7 @@ export default {
}
.friend:hover .status-name {
opacity: 0.8;
height: 19px;
height: 13px;
}
</style>

View file

@ -30,7 +30,7 @@ export default {
const result = Object.keys(allFriend).map(function(key) {
return allFriend[key];
});
return result.filter(friend => friend.status == 2 && friend.recipient.status === undefined || friend.recipient.status == 0 );
return result.filter(friend => friend.status == 2 && (friend.recipient.status === undefined || friend.recipient.status == 0 ));
}
}
}

View file

@ -30,7 +30,7 @@ export default {
const result = Object.keys(allFriend).map(function(key) {
return allFriend[key];
});
return result.filter(friend => friend.status == 2 && friend.recipient.status !== undefined || friend.recipient.status > 0 );
return result.filter(friend => friend.status == 2 && (friend.recipient.status !== undefined && friend.recipient.status > 0 ));
}
}
}

View file

@ -70,7 +70,7 @@ export default {
margin-left: 2px;
margin-right: 5px;
background-position: center;
background-size: 100%;
background-size: cover;
background-repeat: no-repeat;
}
.information{

View file

@ -0,0 +1,97 @@
<template>
<div class="recents">
<transition name="list" appear>
<div class="list">
<FriendsTemplate v-for="(channel, key) of channels" :key="key" notifications="1" :channelID="channel.channelID" :uniqueID="channel.recipients[0].uniqueID" :username="channel.recipients[0].username" :tag="channel.recipients[0].tag"/>
</div>
</transition>
</div>
</template>
<script>
import Tab from './Tab.vue'
import FriendsTemplate from './FriendsTemplate.vue'
export default {
components: {
Tab,
FriendsTemplate
},
computed: {
channels() {
const json = this.$store.getters.channels;
const notifications = this.$store.getters.notifications;
const keys = Object.keys(json);
let result = [];
keys.forEach(function(key){
result.push(json[key]);
});
result.sort(function(a, b){
const notificationA = notifications.find(item => {
return item.channelID === a.channelID
})
const notificationB = notifications.find(item => {
return item.channelID === b.channelID
})
// make notifications more prority.
if (notificationA)
return -1
if (notificationB)
return 1
if (a.lastMessaged === undefined)
return 1
if (b.lastMessaged === undefined)
return -1
return b.lastMessaged - a.lastMessaged
});
// gets unopened dms
const notificationsFiltered = notifications.filter(item => {
const find = result.find(resFind => {
return resFind.channelID === item.channelID
})
if (!find) {
return true;
}
})
for (let index in notificationsFiltered){
notificationsFiltered[index].creator = "dummy";
notificationsFiltered[index].recipients = [notificationsFiltered[index].sender];
}
result = notificationsFiltered.concat(result)
return result
}
}
}
</script>
<style scoped>
.list-enter-active, .list-leave-active {
transition: .3s;
}
.list-enter, .list-leave-to /* .fade-leave-active below version 2.1.8 */ {
transform: translateY(-20px);
opacity: 0;
}
.recents{
background-color: rgba(0, 0, 0, 0);
margin: 5px;
user-select: none;
padding-bottom: 3px;
border-radius: 5px;
transition: 0.3s;
}
.tab{
border-radius: 5px;
transition: 0.3s;
}
.tab:hover{
background-color: rgba(0, 0, 0, 0.123);
}
</style>

View file

@ -21,6 +21,9 @@ export default {
display: flex;
color: white;
}
.tab-name {
padding-top: 3px;
}
.material-icons{
transition: 0.3s;
}

View file

@ -33,6 +33,7 @@ export default {
border-radius: 10px;
padding: 5px;
width: 180px;
z-index: 1;
}
.status-list {
padding: 5px;

53
src/date.js Normal file
View file

@ -0,0 +1,53 @@
export default (miliseconds) => {
let friendlyDate = "";
const now = new Date();
const messageDate = new Date(miliseconds);
if (sameDay(now, messageDate)) {
friendlyDate = `Today at ${getFullTime(messageDate)}`
} else if (yesterDay(now, messageDate)) {
friendlyDate = `Yesterday at ${getFullTime(messageDate)}`
} else {
friendlyDate = getFullDateWithTime(messageDate)
}
return friendlyDate;
}
function sameDay(d1, d2) {
return d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate();
}
function yesterDay(d1, d2) {
return d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
(d1.getDate() - d2.getDate()) == 1
}
function getFullTime(date){
let finalTime = ""
let hours = date.getHours();
let minutes = date.getMinutes()
if (hours <= 9) {
finalTime = `0${hours}`;
} else {
finalTime = `${hours}`
}
if (minutes <= 9) {
finalTime += `:0${minutes}`;
} else {
finalTime += `:${minutes}`
}
return finalTime;
}
function getFullDateWithTime(date) {
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const dayName = days[date.getDay()];
const monthName = months[date.getMonth()];
return `${dayName} ${date.getDate()} ${monthName} ${date.getFullYear()} at ${getFullTime(date)}`
}

View file

@ -3,31 +3,37 @@ const config = [
{
name: "Offline",
url: require("@/assets/status/0.svg"),
color: "#919191"
color: "#919191",
bgColor: "rgba(145, 145, 145, 0.19)",
},
//Online
{
name: "Online",
url: require("@/assets/status/1.svg"),
color: "#27eb48"
color: "#27eb48",
bgColor: "rgba(39, 235, 72, 0.19)"
},
//Away
{
name: "Away",
url: require("@/assets/status/2.svg"),
color: "#ffdd1e"
color: "#ffdd1e",
bgColor: "rgb(255, 221, 30, 0.19)",
},
//Busy
{
name: "Busy",
url: require("@/assets/status/3.svg"),
color: "#ea0b1e"
color: "#ea0b1e",
bgColor: "rgb(234, 11, 30, 0.19)"
},
//Looking to play
{
name: "Looking to play",
url: require("@/assets/status/4.svg"),
color: "#9a3dd3"
color: "#9a3dd3",
bgColor: "rgb(154, 61, 211, 0.19)",
}
]

View file

@ -4,12 +4,13 @@ import user from './modules/userModule';
import socketModule from './modules/socketIOModule';
import channelModule from './modules/channelModule';
import messageModule from './modules/messageModule';
import notificationsModule from './modules/notificationsModule';
import {router} from './../router'
Vue.use(Vuex);
export const store = new Vuex.Store({
modules: { user, socketModule, channelModule, messageModule },
modules: { user, channelModule, messageModule, notificationsModule, socketModule },
state: {
},
@ -17,12 +18,7 @@ export const store = new Vuex.Store({
},
mutations: {
sendMessage(state, message) {
if (state.messageLogs[state.channelID] === undefined) {
state.messageLogs[state.channelID] = {};
}
state.messageLogs[state.channelID][Date.now().toString()] = {channelID: state.channelID, message: message, messageID: Date.now(), status: 0};
}
},
actions: {

View file

@ -1,5 +1,6 @@
import {bus} from '../../main'
import {router} from './../../router'
import Vue from 'vue';
const state = {
selectedChannelID: null,
@ -26,19 +27,28 @@ const actions = {
selectedChannelID(context, channelID) {
context.commit('selectedChannelID', channelID)
},
setName(context, name) {
context.commit('setName', name)
setChannelName(context, name) {
context.commit('setChannelName', name)
},
updateChannelLastMessage(context, channelID) {
context.commit('updateChannelLastMessage', channelID)
}
}
const mutations = {
updateChannelLastMessage(state, channelID){
Vue.set(state.channels[channelID], 'lastMessaged', Date.now());
},
addAllChannels(state, channels){
Vue.set(state, 'channels', channels);
},
channel(state, channel) {
state.channels[channel.channelID] = channel;
Vue.set(state.channels, channel.channelID, channel);
},
selectedChannelID(state, channelID) {
state.selectedChannelID = channelID;
},
setName(state, name) {
setChannelName(state, name) {
state.channelName = name;
}
}

View file

@ -18,10 +18,13 @@ const actions = {
context.commit('messages', data)
},
addMessage(context, data) {
// if the message is sent by this client, add additional information.
if (data.sender) {
data.message.creator = context.getters.user
data.message.status = 0;
}
// send notification if message is not ours
context.commit('addMessage', data);
},
replaceMessage(context, data) {
@ -32,17 +35,14 @@ const actions = {
const mutations = {
messages(state, data) {
Vue.set(state.messages, data.channelID, data.messages.reverse())
setTimeout(() => {
bus.$emit('scrollDown', 0);
}, 300);
},
addMessage(state, data) {
bus.$emit('scrollDown', 300);
bus.$emit('newMessage', data);
Vue.set(
state.messages[data.channelID],
state.messages[data.channelID].length,
data.message
);
);
},
replaceMessage (state, data) {

View file

@ -0,0 +1,62 @@
import {bus} from '../../main'
import {router} from './../../router'
import Vue from 'vue';
const state = {
notifications: []
}
const getters = {
notifications(state) {
return state.notifications;
}
}
const actions = {
addAllNotifications(context, notifications) {
context.commit('addAllNotifications', notifications)
},
messageCreatedNotification(context, notification) {
const {guildID, channelID, lastMessageID, sender} = notification;
let find = context.state.notifications.find(item => {
return item.channelID === channelID
})
if (find) {
return context.commit('messageCreatedNotification', {exists: true, notification: {channelID, lastMessageID,sender}});
}
context.commit('messageCreatedNotification', {exists: false, notification: {channelID, lastMessageID, sender, count: 1}});
}
}
const mutations = {
addAllNotifications(state, notifications){
Vue.set(state, 'notifications', notifications);
},
messageCreatedNotification(state, data) {
const {exists, notification} = data;
if (exists) {
for (let i = 0; i < state.notifications.length; i++) {
if (state.notifications[i].channelID === notification.channelID) {
const count = state.notifications[i].count;
Vue.set(state.notifications[i], 'count', count + 1);
Vue.set(state.notifications[i], 'lastMessageID', data.notification.lastMessageID);
Vue.set(state.notifications[i], 'sender', data.notification.sender);
break;
}
}
return;
}
state.notifications.push(notification);
}
}
export default {
namespace: true,
state,
actions,
mutations,
getters
}

View file

@ -14,13 +14,17 @@ const actions = {
}
},
socket_success(context, data) {
const friendsArray = data.user.friends;
const {message, user, dms, notifications, currentFriendStatus} = data;
const friendsArray = user.friends;
const friendObject = {};
// convert array into object and add online status.
if(friendsArray !== undefined && friendsArray.length >=1) {
for (let index = 0; index < friendsArray.length; index++) {
const element = friendsArray[index];
friendObject[element.recipient.uniqueID] = element;
for (let currentFriendStatus of data.currentFriendStatus){
for (let currentFriendStatus of currentFriendStatus){
if(currentFriendStatus[0] == element.recipient.uniqueID){
friendObject[element.recipient.uniqueID].recipient.status = currentFriendStatus[1]
}
@ -30,6 +34,17 @@ const actions = {
}
context.commit('user', data.user)
// convert dms array to object
const channelsObject = {}
if (dms && dms.length >=1) {
for (let channel of dms) {
channelsObject[channel.channelID] = channel;
}
}
context.commit('addAllChannels', channelsObject)
context.dispatch('addAllNotifications', notifications)
},
socket_relationshipAdd(context, friend) {
context.commit('addFriend', friend)
@ -41,11 +56,22 @@ const actions = {
context.commit('removeFriend', uniqueID)
},
socket_receiveMessage(context, data) {
context.dispatch('addMessage', {
message: data.message,
if (context.getters.messages[data.message.channelID]) {
context.dispatch('updateChannelLastMessage', data.message.channelID);
context.dispatch('addMessage', {
message: data.message,
channelID: data.message.channelID,
tempID: data.tempID
})
}
// send notification if other users message the recipient
if (data.message.creator.uniqueID === context.getters.user.uniqueID) return;
const notification = {
channelID: data.message.channelID,
tempID: data.tempID
})
lastMessageID: data.message.messageID,
sender: data.message.creator
}
context.dispatch('messageCreatedNotification', notification);
},
socket_userStatusChange(context, data) {
context.commit('userStatusChange', data)
@ -61,7 +87,12 @@ const actions = {
},
socket_userAvatarChange(context, data) {
context.commit('userAvatarChange', data)
}
},
['socket_channel:created'](context, data){
const {channel} = data;
// rename to 'channel' to setchannel
context.dispatch('channel', channel);
}
}
export default {

View file

@ -53,6 +53,9 @@ export default {
bus.$on('toggleLeftMenu', () => {
this.showLeftPanel = !this.showLeftPanel;
})
bus.$on('closeLeftMenu', () => {
this.showLeftPanel = false;
})
bus.$on('openSettings', () => {
this.showSettings = true;
})
@ -98,6 +101,7 @@ export default {
position: absolute;
top: 47px;
height: calc(100% - 47px);
background-color: rgba(39, 39, 39, 0.97);
}
}
</style>

View file

@ -85,7 +85,9 @@ export default {
color: #383838;
height: 100%;
}
button {
font-family: 'Roboto', sans-serif;
}
.spinner{
margin: auto;
padding: 30px;
@ -163,6 +165,7 @@ export default {
margin-top: 20px;
color: white;
margin-bottom: 50px;
flex-shrink: 0;
}
.change-title {