merged server and dm tab component

This commit is contained in:
supertiger1234 2020-02-17 17:52:49 +00:00
parent 7f527a8627
commit fabbdc57c0
17 changed files with 419 additions and 555 deletions

View file

@ -1,62 +1,61 @@
<template> <template>
<div class="left-panel"> <div class="friend-left-panel">
<navigation /> <!-- <navigation /> -->
<div class="content"> <MyMiniInformation />
<MyMiniInformation /> <div class="tabs">
<div class="tabs"> <div
<div class="tab"
class="tab" :class="{ notify: friendRequestExists, selected: currentTab === 0 }"
:class="{ notify: friendRequestExists, selected: currentTab === 0 }" @click="currentTab = 0"
@click="currentTab = 0" >
> <div class="material-icons">group</div>
<div class="material-icons">group</div> Friends
Friends
</div>
<div
class="tab"
:class="{ notify: DMNotification, selected: currentTab === 1 }"
@click="currentTab = 1"
>
<div class="material-icons">access_time</div>
Recents
</div>
</div>
<div v-if="currentTab === 0" class="list">
<pending-friends />
<online-friends />
<offline-friends />
</div>
<div v-else class="list">
<recent-friends />
</div> </div>
<div <div
class="button" class="tab"
:class="{ selected: uniqueIDSelected }" :class="{ notify: DMNotification, selected: currentTab === 1 }"
@click="saveNotesBtn" @click="currentTab = 1"
> >
<div class="material-icons">notes</div> <div class="material-icons">access_time</div>
<div class="name">Saved Notes</div> Recents
</div> </div>
</div> </div>
<div v-if="currentTab === 0" class="list">
<!-- <pending-friends />
<online-friends />
<offline-friends /> -->
<friends-tab />
</div>
<div v-else class="list">
<recent-friends-tab />
</div>
<div
class="button"
:class="{ selected: uniqueIDSelected }"
@click="saveNotesBtn"
>
<div class="material-icons">notes</div>
<div class="name">Saved Notes</div>
</div>
</div> </div>
</template> </template>
<script> <script>
import MyMiniInformation from "../../components/app/MyMiniInformation.vue"; import MyMiniInformation from "../../components/app/MyMiniInformation.vue";
import PendingFriends from "./relationships/PendingFriends.vue"; // import PendingFriends from "./relationships/PendingFriends.vue";
import OnlineFriends from "./relationships/OnlineFriends.vue"; // import OnlineFriends from "./relationships/OnlineFriends.vue";
import OfflineFriends from "./relationships/OfflineFriends.vue"; // import OfflineFriends from "./relationships/OfflineFriends.vue";
import RecentFriends from "./relationships/RecentFriends.vue"; import RecentFriendsTab from "./relationships/RecentFriendsTab.vue";
import Navigation from "@/components/app/Navigation"; import FriendsTab from "./relationships/FriendsTab.vue";
export default { export default {
components: { components: {
MyMiniInformation, MyMiniInformation,
PendingFriends, // PendingFriends,
OnlineFriends, // OnlineFriends,
OfflineFriends, // OfflineFriends,
RecentFriends, FriendsTab,
Navigation RecentFriendsTab
}, },
data() { data() {
return { return {
@ -119,23 +118,17 @@ export default {
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.left-panel { .friend-left-panel {
height: 100%; height: 100%;
width: 340px; width: 100%;
max-width: calc(100% - 60px);
flex-shrink: 0; flex-shrink: 0;
display: flex;
flex-direction: row;
z-index: 1;
}
.content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-shrink: 0; z-index: 1;
flex: 1; flex: 1;
overflow: hidden;
background: rgba(0, 0, 0, 0.14); background: rgba(0, 0, 0, 0.14);
border-top-left-radius: 10px; border-top-left-radius: 10px;
overflow: hidden;
} }
.list { .list {
flex: 1; flex: 1;
@ -165,7 +158,7 @@ export default {
cursor: pointer; cursor: pointer;
position: relative; position: relative;
} }
.tab:hover{ .tab:hover {
color: white; color: white;
} }
.tab.selected { .tab.selected {
@ -259,7 +252,7 @@ export default {
} }
@media (max-width: 600px) { @media (max-width: 600px) {
.content { .friend-left-panel {
border-radius: 0; border-radius: 0;
} }
} }

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="navigation" ref="navigation"> <div class="main navigation" ref="navigation">
<div <div
class="tool-tip" class="tool-tip"
ref="toolTip" ref="toolTip"

View file

@ -167,7 +167,6 @@ export default {
width: 300px; width: 300px;
max-width: calc(100% - 60px); max-width: calc(100% - 60px);
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.14);
} }
.header { .header {
height: 50px; height: 50px;

View file

@ -125,16 +125,15 @@ export default {
margin-right: 10px; margin-right: 10px;
margin-left: 5px; margin-left: 5px;
flex-shrink: 0; flex-shrink: 0;
transition: 0.3s; transition: background-color 0.2s, box-shadow 0.2s;
} }
.clickable { .clickable {
color: white; color: rgba(255, 255, 255, 0.9);
transition: 0.3s; transition: 0.2s;
cursor: default; cursor: pointer;
} }
.clickable:hover { .clickable:hover {
color: rgb(219, 219, 219); color: white;
text-decoration: underline;
} }
@media (max-width: 949px) { @media (max-width: 949px) {

View file

@ -1,35 +1,33 @@
<template> <template>
<div class="left-panel"> <div class="server-left-panel">
<navigation /> <!-- <navigation /> -->
<div class="right"> <MyMiniInformation />
<MyMiniInformation /> <div
<div class="server-banner"
class="server-banner" @mouseenter="bannerHover = true"
@mouseenter="bannerHover = true" @mouseleave="bannerHover = false"
@mouseleave="bannerHover = false" :class="{ extendBanner: server && server.banner }"
:class="{ extendBanner: server && server.banner }" v-if="selectedServerID"
v-if="selectedServerID" >
> <img
<img class="banner-image"
class="banner-image" @click="bannerImageClicked"
@click="bannerImageClicked" v-if="server && server.banner"
v-if="server && server.banner" :src="
:src=" `${bannerDomain}${server.banner}${bannerHover ? '' : '?type=webp'}`
`${bannerDomain}${server.banner}${bannerHover ? '' : '?type=webp'}` "
" />
/> <div class="sub-banner">
<div class="sub-banner"> <div class="text" :title="servers[selectedServerID].name">
<div class="text" :title="servers[selectedServerID].name"> {{ servers[selectedServerID].name }}
{{ servers[selectedServerID].name }} </div>
</div> <div class="options-button material-icons" @click="openServerContext">
<div class="options-button material-icons" @click="openServerContext"> more_vert
more_vert
</div>
</div> </div>
</div> </div>
<div class="channels-list"> </div>
<channels-list v-if="selectedServerID" :server-i-d="selectedServerID" /> <div class="channels-list">
</div> <channels-list v-if="selectedServerID" :server-i-d="selectedServerID" />
</div> </div>
</div> </div>
</template> </template>
@ -37,14 +35,12 @@
<script> <script>
import MyMiniInformation from "@/components/app/MyMiniInformation.vue"; import MyMiniInformation from "@/components/app/MyMiniInformation.vue";
import ChannelsList from "@/components/app/ServerTemplate/ChannelsList.vue"; import ChannelsList from "@/components/app/ServerTemplate/ChannelsList.vue";
import Navigation from "@/components/app/Navigation.vue";
import config from "@/config"; import config from "@/config";
export default { export default {
components: { components: {
MyMiniInformation, MyMiniInformation,
ChannelsList, ChannelsList
Navigation
}, },
data() { data() {
return { return {
@ -120,22 +116,17 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.left-panel { .server-left-panel {
height: 100%; height: 100%;
width: 340px; width: 100%;
max-width: calc(100% - 60px);
flex-shrink: 0; flex-shrink: 0;
display: flex; display: flex;
flex-direction: row; flex-direction: column;
z-index: 1; z-index: 1;
} flex: 1;
background: rgba(0, 0, 0, 0.14);
.seperater { overflow: hidden;
height: 1px; border-top-left-radius: 10px;
width: calc(100% - 10px);
align-self: center;
background-color: #a0c8d5;
flex-shrink: 0;
} }
.channels-list { .channels-list {
@ -144,17 +135,6 @@ export default {
height: 100%; height: 100%;
overflow: auto; overflow: auto;
} }
.right {
display: flex;
flex-direction: column;
flex: 1;
width: 100%;
height: 100%;
overflow: hidden;
background: rgba(0, 0, 0, 0.14);
border-top-left-radius: 10px;
}
.server-banner { .server-banner {
display: flex; display: flex;
overflow: hidden; overflow: hidden;
@ -228,7 +208,7 @@ export default {
} }
@media (max-width: 600px) { @media (max-width: 600px) {
.right { .server-left-panel {
border-radius: 0; border-radius: 0;
} }
} }

View file

@ -1,85 +0,0 @@
<template>
<div class="direct-message-tab" :class="{ darken: showLeftPanel }">
<transition name="slidein">
<friends-list
v-show="($mq === 'mobile' && showLeftPanel) || $mq !== 'mobile'"
v-click-outside="hideLeftPanel"
class="left-panel"
/>
</transition>
<message-panel :type="0" />
</div>
</template>
<script>
import { bus } from "@/main";
import FriendsList from "@/components/app/FriendsList.vue";
import MessagePanel from "@/components/app/MessagePanel.vue";
export default {
components: {
FriendsList,
MessagePanel
},
data() {
return {
showLeftPanel: false
};
},
mounted() {
bus.$on("toggleLeftMenu", () => {
this.showLeftPanel = !this.showLeftPanel;
});
bus.$on("closeLeftMenu", () => {
this.showLeftPanel = false;
});
},
methods: {
hideLeftPanel(event) {
if (this.showLeftPanel) {
if (event.target.closest(".show-menu-button") == null) {
this.showLeftPanel = false;
}
}
}
}
};
</script>
<style scoped>
.direct-message-tab {
overflow: hidden;
height: 100%;
width: 100%;
}
.left-panel {
z-index: 2;
}
.slidein-enter-active,
.slidein-leave-active {
transition: 0.5s;
}
.slidein-enter, .slidein-leave-to /* .fade-leave-active below version 2.1.8 */ {
/* margin-left: -300px; */
transform: translateX(-340px);
}
@media (max-width: 600px) {
.left-panel {
position: absolute;
bottom: 0;
z-index: 2;
background: linear-gradient(to bottom, #00477e 0, #016dc0);
}
.darken::after {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1;
background: rgba(0, 0, 0, 0.4);
}
}
</style>

View file

@ -371,6 +371,9 @@ export default {
background: linear-gradient(to bottom, #00477e 0, #016dc0); background: linear-gradient(to bottom, #00477e 0, #016dc0);
height: 100%; height: 100%;
} }
.left-panel .content {
border-radius: 0;
}
.darken::after { .darken::after {
content: ""; content: "";
position: absolute; position: absolute;
@ -382,4 +385,5 @@ export default {
background: rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.4);
} }
} }
</style> </style>

View file

@ -3,18 +3,23 @@
class="direct-message-tab" class="direct-message-tab"
:class="{ darken: showLeftPanel || showMembersPanel }" :class="{ darken: showLeftPanel || showMembersPanel }"
> >
<transition name="slide-left"> <transition :name="$mq === 'mobile' ? 'slide-left' : null">
<server-list <div
class="left-panel"
v-show="($mq === 'mobile' && showLeftPanel) || $mq !== 'mobile'" v-show="($mq === 'mobile' && showLeftPanel) || $mq !== 'mobile'"
v-click-outside="hideLeftPanel" v-click-outside="hideLeftPanel"
class="left-panel" >
/> <navigation />
<server-list v-if="currentTab === 2" />
<friends-list v-if="currentTab === 1" />
</div>
</transition> </transition>
<message-panel :type="1" /> <message-panel :type="currentTab === 1 ? 0 : currentTab === 2 ? 1 : null" />
<transition :name="$mq !== 'desktop' ? 'slide-right' : 'none'"> <transition :name="$mq !== 'desktop' ? 'slide-right' : 'none'">
<members-list <members-list
v-if=" v-if="
selectedServerID && selectedServerID &&
currentTab === 2 &&
((($mq === 'members_panel' || $mq === 'mobile') && ((($mq === 'members_panel' || $mq === 'mobile') &&
showMembersPanel) || showMembersPanel) ||
$mq === 'desktop') $mq === 'desktop')
@ -28,16 +33,20 @@
<script> <script>
import { bus } from "@/main"; import { bus } from "@/main";
import ServerList from "@/components/app/ServerList.vue";
import MessagePanel from "@/components/app/MessagePanel.vue"; import MessagePanel from "@/components/app/MessagePanel.vue";
import MembersList from "@/components/app/MembersList.vue"; import Navigation from "@/components/app/Navigation.vue";
const FriendsList = () => import("@/components/app/FriendsList.vue");
const MembersList = () => import("@/components/app/MembersList.vue");
const ServerList = () => import("@/components/app/ServerList.vue");
export default { export default {
components: { components: {
ServerList, ServerList,
FriendsList,
MessagePanel, MessagePanel,
MembersList MembersList,
Navigation
}, },
data() { data() {
return { return {
@ -59,7 +68,9 @@ export default {
methods: { methods: {
hideLeftPanel(event) { hideLeftPanel(event) {
if (this.showLeftPanel) { if (this.showLeftPanel) {
if (event.target.closest(".show-menu-button") == null) { const closestMenuBtn = event.target.closest(".show-menu-button");
const closestNavBtn = event.target.closest(".main.navigation");
if (closestMenuBtn === null && closestNavBtn === null) {
this.showLeftPanel = false; this.showLeftPanel = false;
} }
} }
@ -73,6 +84,9 @@ export default {
} }
}, },
computed: { computed: {
currentTab() {
return this.$store.getters.currentTab;
},
selectedServerID() { selectedServerID() {
return this.$store.getters["servers/selectedServerID"]; return this.$store.getters["servers/selectedServerID"];
} }
@ -86,6 +100,13 @@ export default {
} }
.left-panel { .left-panel {
z-index: 2; z-index: 2;
height: 100%;
width: 340px;
max-width: calc(100% - 60px);
flex-shrink: 0;
display: flex;
flex-direction: row;
z-index: 1;
} }
.slide-left-enter-active, .slide-left-enter-active,

View file

@ -0,0 +1,181 @@
<template>
<div class="friends">
<virtual-list :size="50" :remain="15" v-if="loaded" :variable="true">
<div class="tab" v-if="friends[0].length" :style="{ height: '25px' }">
Pending
</div>
<pending-template
v-for="friend of friends[0]"
:key="friend.recipient.uniqueID"
:friend="friend"
:style="{ height: '55px' }"
/>
<div class="tab" v-if="friends[1].length || friends[2].length">
Online
</div>
<div
class="none-online"
:style="{ height: '29px' }"
v-if="!friends[1].length && friends[2].length"
>
All of your friends are offline.
</div>
<friends-template
v-for="friend of friends[1]"
:key="friend.recipient.uniqueID"
:friend="friend"
:recipient="friend.recipient"
:style="{ height: '50px' }"
/>
<div class="tab">Offline</div>
<div
class="none-online"
:style="{ height: '29px' }"
v-if="!friends[2].length && !friends[1].length"
>
Add some friends.
</div>
<friends-template
v-for="friend of friends[2]"
:key="friend.recipient.uniqueID"
:friend="friend"
:recipient="friend.recipient"
:style="{ height: '50px' }"
/>
</virtual-list>
</div>
</template>
<script>
import VirtualList from "vue-virtual-scroll-list";
import FriendsTemplate from "./FriendsTemplate.vue";
import PendingTemplate from "./PendingTemplate.vue";
export default {
components: {
FriendsTemplate,
PendingTemplate,
VirtualList
},
data() {
return {
loaded: false
};
},
mounted() {
setTimeout(() => (this.loaded = true));
},
computed: {
user() {
return this.$store.getters.user;
},
channels() {
const json = this.$store.getters.channels;
const notifications = this.$store.getters.notifications;
const keys = Object.keys(json);
let result = [];
keys.forEach(key => {
if (
json[key].recipients &&
json[key].recipients.length > 0 &&
!json[key].server_id &&
json[key].recipients[0].uniqueID !== this.user.uniqueID
)
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 => {
if (json[item.channelID] && json[item.channelID].server_id) return;
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;
},
friends() {
const allFriends = Object.values(this.$store.getters.user.friends);
const members = this.$store.getters["members/members"];
const presences = this.$store.getters["members/presences"];
let pendingFriends = [];
let onlineFriends = [];
let offlineFriends = [];
for (let index = 0; index < allFriends.length; index++) {
let friend = allFriends[index];
const presence = presences[friend.uniqueID];
friend.recipient = members[friend.uniqueID];
if (friend.status < 2) {
pendingFriends.push(friend);
continue;
}
if (!presence || presence === 0) {
offlineFriends.push(friend);
continue;
}
onlineFriends.push(friend);
}
return [pendingFriends, onlineFriends, offlineFriends];
}
}
};
</script>
<style scoped>
.friends {
background-color: rgba(0, 0, 0, 0);
user-select: none;
transition: 0.3s;
display: flex;
flex-direction: column;
height: 100%;
flex-shrink: 0;
overflow: hidden;
}
.tab {
display: flex;
align-items: center;
height: 25px;
color: rgba(255, 255, 255, 0.9);
padding-left: 10px;
}
.none-online {
color: rgba(255, 255, 255, 0.7);
align-self: center;
padding: 5px;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.spacer {
display: flex;
height: 5px;
}
</style>

View file

@ -1,36 +1,38 @@
<template> <template>
<div <div class="friend">
class="friend"
:class="{ selected: uniqueIDSelected, tree }"
@click="openChat"
@mouseover="mouseOverEvent"
@mouseleave="hover = false"
>
<div <div
class="profile-picture" class="wrapper"
:style="`border-color: ${status.statusColor};`" :class="{ selected: uniqueIDSelected }"
@click="openUserInformation" @click="openChat"
@mouseover="mouseOverEvent"
@mouseleave="hover = false"
> >
<img
class="avatar"
:src="`${userAvatar}${hover || !isGif ? '' : '?type=webp'}`"
/>
<div <div
class="status" class="profile-picture"
:style="`background-image: url(${status.statusURL})`" :style="`border-color: ${status.statusColor};`"
/> @click="openUserInformation"
</div> >
<div class="information"> <img
<div class="username">{{ recipient.username }}</div> class="avatar"
<div class="status-name">{{ status.statusName }}</div> :src="`${userAvatar}${hover || !isGif ? '' : '?type=webp'}`"
</div> />
<div v-if="notifications && notifications > 0" class="notification"> <div
<div class="notification-inner">{{ notifications }}</div> class="status"
</div> :style="`background-image: url(${status.statusURL})`"
<!-- doesnt work properly. if both channels closed, the chat gets wiped. --> />
<!-- <div v-else-if="recents" class="material-icons close-button" @click="closeChannel"> </div>
<div class="information">
<div class="username">{{ recipient.username }}</div>
<div class="status-name">{{ status.statusName }}</div>
</div>
<div v-if="notifications && notifications > 0" class="notification">
<div class="notification-inner">{{ notifications }}</div>
</div>
<!-- doesnt work properly. if both channels closed, the chat gets wiped. -->
<!-- <div v-else-if="recents" class="material-icons close-button" @click="closeChannel">
close close
</div> --> </div> -->
</div>
</div> </div>
</template> </template>
@ -41,16 +43,7 @@ import statuses from "@/utils/statuses";
import { bus } from "@/main"; import { bus } from "@/main";
export default { export default {
// tree will add padding to the left. props: ["friend", "recents", "recipient"],
props: [
"username",
"tag",
"channelID",
"uniqueID",
"recipient",
"tree",
"recents"
],
data() { data() {
return { return {
hover: false, hover: false,
@ -62,15 +55,18 @@ export default {
}, },
computed: { computed: {
notifications() { notifications() {
const channelID = this.$props.channelID;
const channels = this.$store.getters.channels; const channels = this.$store.getters.channels;
const recipient = this.recipient;
const notifications = this.$store.getters.notifications.find(function(e) { const notifications = this.$store.getters.notifications.find(function(e) {
if (channels[e.channelID] && channels[e.channelID].server_id) return; const channel = channels[e.channelID];
return e.channelID == channelID; if (channel && channel.server_id) return;
return e.sender.uniqueID === recipient.uniqueID;
}); });
if ( if (
!notifications || !notifications ||
(this.$props.channelID === this.$store.getters.selectedChannelID && (this.friend.channelID === this.$store.getters.selectedChannelID &&
document.hasFocus()) document.hasFocus())
) )
return; return;
@ -107,8 +103,7 @@ export default {
} }
}, },
async closeChannel() { async closeChannel() {
this.channelID; await channelService.delete(this.friend.channelID);
await channelService.delete(this.channelID);
}, },
async openChat(event) { async openChat(event) {
if ( if (
@ -125,12 +120,12 @@ export default {
document.hasFocus() document.hasFocus()
) { ) {
this.$socket.client.emit("notification:dismiss", { this.$socket.client.emit("notification:dismiss", {
channelID: this.channelID channelID: this.friend.channelID
}); });
} }
this.$store.dispatch("openChat", { this.$store.dispatch("openChat", {
uniqueID: this.recipient.uniqueID, uniqueID: this.recipient.uniqueID,
channelID: this.channelID, channelID: this.friend.channelID,
channelName: this.recipient.username channelName: this.recipient.username
}); });
}, },
@ -149,26 +144,32 @@ export default {
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.friend { .friend {
color: rgba(255, 255, 255, 0.7); height: 50px;
padding: 5px; flex-shrink: 0;
display: flex; display: flex;
height: 34px; align-items: center;
width: 100%;
}
.wrapper {
color: rgba(255, 255, 255, 0.7);
display: flex;
height: 45px;
width: 100%;
transition: 0.3s; transition: 0.3s;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
margin: 5px;
border-radius: 4px; border-radius: 4px;
} padding-left: 10px;
.tree { margin-left: 3px;
padding-left: 22px; margin-right: 3px;
} }
.friend:hover { .wrapper:hover {
background-color: rgba(0, 0, 0, 0.2); background-color: rgba(0, 0, 0, 0.2);
color: white; color: white;
} }
.friend.selected { .wrapper.selected {
background-color: rgba(0, 0, 0, 0.4); background-color: rgba(0, 0, 0, 0.4);
color: white; color: white;
} }
@ -227,7 +228,7 @@ export default {
transition: 0.3s; transition: 0.3s;
} }
.friend:hover .status { .wrapper:hover .status {
opacity: 1; opacity: 1;
bottom: -4px; bottom: -4px;
} }
@ -239,7 +240,7 @@ export default {
color: #b7cbce; color: #b7cbce;
height: 0; height: 0;
} }
.friend:hover .status-name { .wrapper:hover .status-name {
opacity: 1; opacity: 1;
height: 13px; height: 13px;
} }
@ -256,7 +257,7 @@ export default {
.close-button:hover { .close-button:hover {
color: white; color: white;
} }
.friend:hover .close-button { .wrapper:hover .close-button {
display: flex; display: flex;
} }
</style> </style>

View file

@ -1,78 +0,0 @@
<template>
<div class="friends">
<Tab
@click.native="expanded = !expanded"
:expanded="expanded"
tabname="Offline"
/>
<transition name="list">
<div v-if="expanded" class="list">
<FriendsTemplate
v-for="(friend, key) of friends"
:key="key"
:channel-i-d="friend.channelID"
:recipient="friends[key].recipient"
:tree="true"
/>
</div>
</transition>
</div>
</template>
<script>
import Tab from "./Tab.vue";
import FriendsTemplate from "./FriendsTemplate.vue";
export default {
components: {
Tab,
FriendsTemplate
},
data() {
return {
expanded: true
};
},
computed: {
friends() {
const allFriend = this.$store.getters.user.friends;
const members = this.$store.getters["members/members"];
const presences = this.$store.getters["members/presences"];
const result = Object.keys(allFriend).map(function(key) {
allFriend[key].recipient = members[allFriend[key].uniqueID];
return allFriend[key];
});
return result.filter(
friend =>
friend.status == 2 &&
(!presences[friend.uniqueID] || presences[friend.uniqueID] == 0)
);
}
}
};
</script>
<style scoped>
.list-enter-active,
.list-leave-active {
transition: 0.3s;
}
.list-enter, .list-leave-to /* .fade-leave-active below version 2.1.8 */ {
transform: translateY(-20px);
opacity: 0;
}
.friends {
background-color: rgba(0, 0, 0, 0);
user-select: none;
padding-bottom: 3px;
border-radius: 5px;
transition: 0.3s;
}
.tab {
transition: 0.3s;
margin: 4px;
border-radius: 4px;
}
.tab:hover {
background-color: rgba(0, 0, 0, 0.2);
}
</style>

View file

@ -1,95 +0,0 @@
<template>
<div class="friends">
<Tab
@click.native="expanded = !expanded"
:expanded="expanded"
tabname="Online"
/>
<transition name="list">
<div v-if="expanded" class="list">
<FriendsTemplate
v-for="(friend, key) of friends"
:key="key"
:channel-i-d="friend.channelID"
:recipient="friends[key].recipient"
:tree="true"
/>
</div>
</transition>
</div>
</template>
<script>
import Tab from "./Tab.vue";
import FriendsTemplate from "./FriendsTemplate.vue";
export default {
components: {
Tab,
FriendsTemplate
},
data() {
return {
expanded: true
};
},
computed: {
friends() {
const allFriend = this.$store.getters.user.friends;
const members = this.$store.getters["members/members"];
const presences = this.$store.getters["members/presences"];
const notifications = this.$store.getters.notifications;
const channels = this.$store.getters.channels;
const result = Object.keys(allFriend).map(function(key) {
const friend = allFriend[key];
friend.recipient = members[friend.uniqueID];
const findNotification = notifications.find(e => {
return (
e.sender.uniqueID === friend.recipient.uniqueID &&
!channels[e.channelID].server_id
);
});
if (findNotification) {
friend.channelID = findNotification.channelID;
}
return friend;
});
return result.filter(
friend =>
friend.status == 2 &&
presences[friend.uniqueID] &&
presences[friend.uniqueID] > 0
);
}
},
methods: {}
};
</script>
<style scoped>
.list-enter-active,
.list-leave-active {
transition: 0.3s;
}
.list-enter, .list-leave-to /* .fade-leave-active below version 2.1.8 */ {
transform: translateY(-20px);
opacity: 0;
}
.friends {
background-color: rgba(0, 0, 0, 0);
user-select: none;
padding-bottom: 3px;
transition: 0.3s;
}
.tab {
transition: 0.3s;
margin: 4px;
border-radius: 4px;
}
.tab:hover {
background-color: rgba(0, 0, 0, 0.2);
}
</style>

View file

@ -1,73 +0,0 @@
<template>
<div class="pending-friends" v-if="friends && friends.length">
<Tab
@click.native="expanded = !expanded"
:expanded="expanded"
tabname="Pending requests"
/>
<transition name="list">
<div v-if="expanded" class="list">
<PendingTemplate
v-for="(friend, key) of friends"
:key="key"
:unique-i-d="friend.recipient.uniqueID"
:status="friend.status"
:username="friend.recipient.username"
:tag="friend.recipient.tag"
/>
</div>
</transition>
</div>
</template>
<script>
import Tab from "./Tab.vue";
import PendingTemplate from "./PendingTemplate.vue";
export default {
components: {
Tab,
PendingTemplate
},
data() {
return {
expanded: true
};
},
computed: {
friends() {
const allFriend = this.$store.getters.user.friends;
const members = this.$store.getters["members/members"];
const result = Object.keys(allFriend).map(function(key) {
allFriend[key].recipient = members[allFriend[key].uniqueID];
return allFriend[key];
});
return result.filter(friend => friend.status < 2);
}
}
};
</script>
<style scoped>
.list-enter-active,
.list-leave-active {
transition: 0.3s;
}
.list-enter, .list-leave-to /* .fade-leave-active below version 2.1.8 */ {
transform: translateY(-20px);
opacity: 0;
}
.pending-friends {
background-color: rgba(0, 0, 0, 0);
user-select: none;
padding-bottom: 3px;
transition: 0.3s;
}
.tab {
transition: 0.3s;
margin: 4px;
border-radius: 4px;
}
.tab:hover {
background-color: rgba(0, 0, 0, 0.2);
}
</style>

View file

@ -7,13 +7,13 @@
/> />
<div class="information"> <div class="information">
<div class="username"> <div class="username">
{{ $props.username }} {{ friend.recipient.username }}
</div> </div>
<div class="tag">@{{ $props.tag }}</div> <div class="tag">@{{ friend.recipient.tag }}</div>
</div> </div>
<div class="buttons"> <div class="buttons">
<div <div
:class="{ button: true, accept: true, hide: $props.status == 0 }" :class="{ button: true, accept: true, hide: friend.status == 0 }"
@click="accept" @click="accept"
> >
<i class="material-icons"> <i class="material-icons">
@ -34,10 +34,11 @@ import RelationshipService from "@/services/RelationshipService.js";
import config from "@/config.js"; import config from "@/config.js";
export default { export default {
props: ["username", "tag", "status", "uniqueID"], props: ["friend"],
computed: { computed: {
user() { user() {
return this.$store.getters.user.friends[this.$props.uniqueID].recipient; return this.$store.getters.user.friends[this.friend.recipient.uniqueID]
.recipient;
}, },
userAvatar() { userAvatar() {
return config.domain + "/avatars/" + this.user.avatar; return config.domain + "/avatars/" + this.user.avatar;
@ -45,13 +46,16 @@ export default {
}, },
methods: { methods: {
deny() { deny() {
RelationshipService.delete(this.$props.uniqueID); RelationshipService.delete(this.friend.recipient.uniqueID);
}, },
accept() { accept() {
RelationshipService.put(this.$props.uniqueID); RelationshipService.put(this.friend.recipient.uniqueID);
}, },
openUserInformation() { openUserInformation() {
this.$store.dispatch("setUserInformationPopout", this.uniqueID); this.$store.dispatch(
"setUserInformationPopout",
this.friend.recipient.uniqueID
);
} }
} }
}; };
@ -59,7 +63,7 @@ export default {
<style scoped> <style scoped>
.username { .username {
width: 80px; width: 119px;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -67,16 +71,11 @@ export default {
} }
.pending-friend { .pending-friend {
color: white; color: white;
padding: 5px; padding-left: 15px;
padding-left: 22px;
display: flex; display: flex;
transition: 0.3s; transition: 0.3s;
} }
.pending-friend:hover {
background-color: rgba(0, 0, 0, 0.246);
}
.profile-picture { .profile-picture {
height: 30px; height: 30px;
width: 30px; width: 30px;
@ -88,6 +87,7 @@ export default {
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;
flex-shrink: 0; flex-shrink: 0;
cursor: pointer;
} }
.information { .information {
margin: auto; margin: auto;
@ -96,18 +96,17 @@ export default {
flex: 1; flex: 1;
} }
.tag { .tag {
color: rgb(173, 173, 173); color: rgba(173, 173, 173, 0.8);
font-size: 15px; font-size: 15px;
} }
.buttons { .buttons {
display: flex; display: flex;
margin: auto; margin: auto;
margin-right: 5px; margin-right: 10px;
} }
.button { .button {
background-color: rgba(0, 0, 0, 0.2);
border-radius: 4px; border-radius: 4px;
width: 30px; width: 30px;
height: 30px; height: 30px;
@ -119,14 +118,15 @@ export default {
.hide { .hide {
display: none; display: none;
} }
.button:hover {
background-color: rgba(0, 255, 0, 0.281);
}
.button .material-icons { .button .material-icons {
margin: auto; margin: auto;
color: rgba(255, 255, 255, 0.747); color: rgba(255, 255, 255, 0.7);
transition: 0.2s;
} }
.button.decline:hover { .button:hover .material-icons {
background-color: rgba(255, 0, 0, 0.281); color: white;
}
.button.decline:hover .material-icons {
color: rgb(255, 80, 80);
} }
</style> </style>

View file

@ -1,11 +1,11 @@
<template> <template>
<div class="recents"> <div class="recents">
<virtual-list :size="49" :remain="20"> <virtual-list :size="50" :remain="20" v-if="loaded" >
<FriendsTemplate <FriendsTemplate
v-for="(channel, key) of channels" v-for="(channel, key) of channels"
:key="key" :key="key"
:recents="true" :recents="true"
:channel-i-d="channel.channelID" :friend="channel.channelID"
:recipient="channel.recipients[0]" :recipient="channel.recipients[0]"
/> />
</virtual-list> </virtual-list>
@ -20,6 +20,14 @@ export default {
FriendsTemplate, FriendsTemplate,
VirtualList VirtualList
}, },
data() {
return {
loaded: false
};
},
mounted() {
setTimeout(() => (this.loaded = true));
},
computed: { computed: {
user() { user() {
return this.$store.getters.user; return this.$store.getters.user;
@ -78,8 +86,6 @@ export default {
}; };
</script> </script>
<style scoped> <style scoped>
.recents { .recents {
background-color: rgba(0, 0, 0, 0); background-color: rgba(0, 0, 0, 0);
user-select: none; user-select: none;
@ -97,4 +103,8 @@ export default {
.tab:hover { .tab:hover {
background-color: rgba(0, 0, 0, 0.123); background-color: rgba(0, 0, 0, 0.123);
} }
.spacer {
display: flex;
height: 5px;
}
</style> </style>

View file

@ -1,4 +1,11 @@
const config = [ const config = [
{
version: "1.0.2",
title: "More performance improvements!",
shortTitle: "",
date: "17/02/2020",
fix: ["Improved performance by making code more efficient."]
},
{ {
version: "1.0.1", version: "1.0.1",
title: "Grouped messages + message performance", title: "Grouped messages + message performance",

View file

@ -13,8 +13,8 @@
<main-nav /> <main-nav />
<div class="panel-layout"> <div class="panel-layout">
<news v-if="currentTab == 3" /> <news v-if="currentTab == 3" />
<direct-message v-if="currentTab == 1" /> <servers v-if="currentTab == 1 || currentTab == 2" />
<servers v-if="currentTab == 2" /> <!-- <servers v-if="currentTab == 2" /> -->
<explore v-if="currentTab == 0" /> <explore v-if="currentTab == 0" />
<admin-panel v-if="currentTab == 4" /> <admin-panel v-if="currentTab == 4" />
</div> </div>
@ -43,14 +43,14 @@ const ElectronFrameButtons = () =>
const News = () => const News = () =>
import(/* webpackChunkName: "News" */ "./../components/app/Tabs/News.vue"); import(/* webpackChunkName: "News" */ "./../components/app/Tabs/News.vue");
const DirectMessage = () => ({ // const DirectMessage = () => ({
component: import("./../components/app/Tabs/DirectMessage.vue"), // component: import("./../components/app/Tabs/DirectMessage.vue"),
loading: Spinner, // loading: Spinner,
delay: 0 // delay: 0
}); // });
const Servers = () => ({ const Servers = () => ({
component: import("./../components/app/Tabs/Servers.vue"), component: import("./../components/app/Tabs/ServerAndDMTab"),
loading: Spinner, loading: Spinner,
delay: 0 delay: 0
}); });
@ -69,7 +69,7 @@ const AdminPanel = () => ({
export default { export default {
name: "app", name: "app",
components: { components: {
DirectMessage, // DirectMessage,
Servers, Servers,
ConnectingScreen, ConnectingScreen,
Popouts, Popouts,