mirror of
https://github.com/danbulant/Nertivia-Client
synced 2026-06-14 12:01:06 +00:00
New design, a lot of changes.
This commit is contained in:
parent
41db687eaa
commit
154d6147a9
51 changed files with 1599 additions and 1731 deletions
5
package-lock.json
generated
5
package-lock.json
generated
|
|
@ -10296,6 +10296,11 @@
|
|||
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
|
||||
"dev": true
|
||||
},
|
||||
"simple-markdown": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-markdown/-/simple-markdown-0.6.1.tgz",
|
||||
"integrity": "sha512-02HKXvM9J7pJWf74fuWthcgof5jF81Yndt+XcXtWnEtpp8QaX9dUAJpdAA6KWrB/rSGzrOi0PRAVy9/0bJiIZw=="
|
||||
},
|
||||
"simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"markdown-it-chat-formatter": "^0.1.1",
|
||||
"match-sorter": "^2.3.0",
|
||||
"particles.js": "^2.0.0",
|
||||
"simple-markdown": "^0.6.1",
|
||||
"socket.io": "^2.2.0",
|
||||
"socket.io-client": "^2.2.0",
|
||||
"twemoji": "^11.3.0",
|
||||
|
|
|
|||
|
|
@ -1,182 +0,0 @@
|
|||
<template>
|
||||
<div class="change-log">
|
||||
<div class="inner">
|
||||
<div
|
||||
class="close-button"
|
||||
@click="close"
|
||||
>
|
||||
<i class="material-icons">
|
||||
close
|
||||
</i>
|
||||
</div>
|
||||
<div class="change-title">
|
||||
Change Log <div class="changelog-icon">
|
||||
<i class="material-icons ">update</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="change-list">
|
||||
<div
|
||||
v-for="(change, index) in changelog"
|
||||
:key="index"
|
||||
class="change"
|
||||
>
|
||||
<div class="date">
|
||||
{{ change.date }}
|
||||
</div>
|
||||
<div class="changes-title">
|
||||
{{ change.title }}
|
||||
</div>
|
||||
<div class="information">
|
||||
<div v-if="change.new">
|
||||
<strong>What's new?</strong><br>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(wnew, index) in change.new"
|
||||
:key="index"
|
||||
>
|
||||
{{ wnew }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="change.fix">
|
||||
<strong>Issues fixed</strong><br>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(wfix, index) in change.fix"
|
||||
:key="index"
|
||||
>
|
||||
{{ wfix }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="change.next">
|
||||
<strong>Up next</strong><br>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(wnext, index) in change.next"
|
||||
:key="index"
|
||||
>
|
||||
{{ wnext }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="change.msg">
|
||||
{{ change.msg }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {bus} from './../main.js'
|
||||
import changelog from '@/utils/changelog.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
changelog
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
bus.$emit('closeChangeLog')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.change-log{
|
||||
position: absolute;
|
||||
background: rgba(0, 0, 0, 0.342);
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
color: white;
|
||||
}
|
||||
.inner{
|
||||
background-color: rgba(0, 0, 0, 0.664);
|
||||
margin: auto;
|
||||
width: 600px;
|
||||
height: 700px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.close-button{
|
||||
margin: auto;
|
||||
margin-top: 10px;
|
||||
margin-right: 10px;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
color: grey;
|
||||
transition: .3s;
|
||||
}
|
||||
.close-button:hover{
|
||||
color: white;
|
||||
}
|
||||
|
||||
.close-button .material-icons {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.change-title{
|
||||
color: white;
|
||||
font-size: 30px;
|
||||
margin: auto;
|
||||
margin-top: 10px;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
padding-bottom: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.changelog-icon{
|
||||
margin-top: 2px;
|
||||
margin-left: 10px
|
||||
}
|
||||
.changelog-icon .material-icons {
|
||||
font-size: 40px;
|
||||
}
|
||||
.change-list {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
border-top: 1px solid white;
|
||||
}
|
||||
.change{
|
||||
margin: 5px;
|
||||
min-height: 100px;
|
||||
border-bottom: solid 1px white;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.change:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
.date{
|
||||
text-align: right;
|
||||
padding-top: 10px;
|
||||
padding-right: 10px;
|
||||
color: grey;
|
||||
}
|
||||
.changes-title {
|
||||
font-size: 25px;
|
||||
padding-left: 15px;
|
||||
color: rgba(255, 255, 255, 0.795);
|
||||
}
|
||||
.information {
|
||||
margin: 10px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
@media (max-height: 700px) {
|
||||
.inner {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ export default {
|
|||
right: 0;
|
||||
left: 0;
|
||||
z-index: 9999999999999999;
|
||||
height: 39px;
|
||||
height: 25px;
|
||||
-webkit-app-region: drag;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
|
@ -66,8 +66,7 @@ export default {
|
|||
display: flex;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
width: 60px;
|
||||
border-radius: 2px;
|
||||
width: 30px;
|
||||
color: white;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
@ -82,9 +81,9 @@ export default {
|
|||
background: rgba(255, 0, 0, 0.595);
|
||||
}
|
||||
.frame-buttons .minimize:hover {
|
||||
background: rgba(255, 81, 0, 0.595);
|
||||
background: rgba(46, 46, 46, 0.595);
|
||||
}
|
||||
.frame-buttons .res-max:hover {
|
||||
background: rgba(0, 162, 255, 0.595);
|
||||
background: rgba(46, 46, 46, 0.595);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,88 +1,78 @@
|
|||
<template>
|
||||
<div class="left-panel">
|
||||
<MyMiniInformation />
|
||||
<div class="tabs">
|
||||
<div :class="{selector: true, right: currentTab === 1}" />
|
||||
<div
|
||||
class="tab"
|
||||
:class="{notifyAnimation: friendRequestExists}"
|
||||
@click="currentTab = 0"
|
||||
>
|
||||
Friends
|
||||
<navigation />
|
||||
<div class="content">
|
||||
<div class="tabs">
|
||||
<div :class="{selector: true, right: currentTab === 1}" />
|
||||
<div
|
||||
class="tab"
|
||||
:class="{notifyAnimation: friendRequestExists}"
|
||||
@click="currentTab = 0"
|
||||
>Friends</div>
|
||||
<div class="tab" :class="{notifyAnimation: DMNotification}" @click="currentTab = 1">Recents</div>
|
||||
</div>
|
||||
<div
|
||||
class="tab"
|
||||
:class="{notifyAnimation: DMNotification}"
|
||||
@click="currentTab = 1"
|
||||
>
|
||||
Recents
|
||||
<div v-if="currentTab === 0" class="list">
|
||||
<pending-friends />
|
||||
<online-friends />
|
||||
<offline-friends />
|
||||
</div>
|
||||
<div v-else class="list">
|
||||
<recent-friends />
|
||||
</div>
|
||||
<MyMiniInformation />
|
||||
</div>
|
||||
<div
|
||||
v-if="currentTab === 0"
|
||||
class="list"
|
||||
>
|
||||
<pending-friends />
|
||||
<online-friends />
|
||||
<offline-friends />
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="list"
|
||||
>
|
||||
<recent-friends />
|
||||
</div>
|
||||
<AddFriendPanel />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import MyMiniInformation from '../../components/app/MyMiniInformation.vue'
|
||||
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'
|
||||
|
||||
import MyMiniInformation from "../../components/app/MyMiniInformation.vue";
|
||||
import PendingFriends from "./relationships/PendingFriends.vue";
|
||||
import OnlineFriends from "./relationships/OnlineFriends.vue";
|
||||
import OfflineFriends from "./relationships/OfflineFriends.vue";
|
||||
import RecentFriends from "./relationships/RecentFriends.vue";
|
||||
import Navigation from "@/components/app/Navigation";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MyMiniInformation,
|
||||
PendingFriends,
|
||||
AddFriendPanel,
|
||||
OnlineFriends,
|
||||
OfflineFriends,
|
||||
RecentFriends
|
||||
RecentFriends,
|
||||
Navigation
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentTab: 0
|
||||
}
|
||||
currentTab: 0
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
currentTab(tab) {
|
||||
localStorage.setItem('friendsListTab', tab)
|
||||
localStorage.setItem("friendsListTab", tab);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const tab = localStorage.getItem('friendsListTab');
|
||||
const tab = localStorage.getItem("friendsListTab");
|
||||
if (tab) {
|
||||
this.currentTab = parseInt(tab)
|
||||
this.currentTab = parseInt(tab);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
DMNotification() {
|
||||
const notifications = this.$store.getters.notifications;
|
||||
const channels = this.$store.getters.channels
|
||||
const channels = this.$store.getters.channels;
|
||||
const notification = notifications.find(e => {
|
||||
return channels[e.channelID] && !channels[e.channelID].server_id && e.channelID !== this.$store.getters.selectedChannelID
|
||||
})
|
||||
return (
|
||||
channels[e.channelID] &&
|
||||
!channels[e.channelID].server_id &&
|
||||
e.channelID !== this.$store.getters.selectedChannelID
|
||||
);
|
||||
});
|
||||
// unopened dm
|
||||
if (!notification) {
|
||||
return notifications.find(e => {
|
||||
return !channels[e.channelID]
|
||||
})
|
||||
return !channels[e.channelID];
|
||||
});
|
||||
}
|
||||
return notification;
|
||||
},
|
||||
|
|
@ -93,106 +83,112 @@ export default {
|
|||
});
|
||||
return result.find(friend => friend.status === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
.left-panel {
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.671);
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
width: 300px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
z-index: 1;
|
||||
}
|
||||
.list{
|
||||
margin: 2px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
}
|
||||
.list {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tabs{
|
||||
.tabs {
|
||||
display: flex;
|
||||
color: white;
|
||||
flex-shrink: 0;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
}
|
||||
.tab{
|
||||
.tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
flex-shrink: 0;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
padding: 10px;
|
||||
height: 50px;
|
||||
background: rgba(0, 0, 0, 0.171);
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
border-radius: 5px;
|
||||
transition: 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tab:hover{
|
||||
.tab:hover {
|
||||
background: rgba(255, 255, 255, 0.096);
|
||||
}
|
||||
.selector {
|
||||
background: rgba(255, 255, 255, 0.322);
|
||||
width: 148px;
|
||||
height: 39px;
|
||||
width: 118px;
|
||||
height: 50px;
|
||||
top: 0;
|
||||
left: 1px;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
transition: 0.3s;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.right{
|
||||
transform: translateX(150px);
|
||||
.right {
|
||||
transform: translateX(120px);
|
||||
}
|
||||
|
||||
/* ------- SCROLL BAR -------*/
|
||||
/* width */
|
||||
.list::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
width: 3px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
.list::-webkit-scrollbar-track {
|
||||
background: #8080806b;
|
||||
background: #8080806b;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
.list::-webkit-scrollbar-thumb {
|
||||
background: #f5f5f559;
|
||||
background: #f5f5f559;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
.list::-webkit-scrollbar-thumb:hover {
|
||||
background: #f5f5f59e;
|
||||
background: #f5f5f59e;
|
||||
}
|
||||
|
||||
.notifyAnimation{
|
||||
.notifyAnimation {
|
||||
animation: notifyAnime;
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes notifyAnime {
|
||||
0%{
|
||||
0% {
|
||||
background: rgba(121, 3, 3, 0.541);
|
||||
}
|
||||
40%{
|
||||
40% {
|
||||
background: rgba(255, 0, 0, 0.568);
|
||||
}
|
||||
60%{
|
||||
60% {
|
||||
background: rgba(255, 0, 0, 0.568);
|
||||
}
|
||||
100%{
|
||||
100% {
|
||||
background: rgba(121, 3, 3, 0.541);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,18 +71,18 @@ export default {
|
|||
.member {
|
||||
display: flex;
|
||||
padding: 3px;
|
||||
margin: 3px 5px;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
border-radius: 5px;
|
||||
transition: 0.3s;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.member:hover {
|
||||
background: rgba(0, 0, 0, 0.301);
|
||||
}
|
||||
|
||||
.information {
|
||||
margin-left: 5px;
|
||||
overflow: hidden;
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ export default {
|
|||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(0, 0, 0, 0.671);
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
}
|
||||
|
|
@ -105,8 +105,6 @@ export default {
|
|||
.tab {
|
||||
background: rgba(0, 0, 0, 0.308);
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
margin: 5px;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
color: rgb(200, 200, 200);
|
||||
|
|
|
|||
|
|
@ -636,7 +636,7 @@ export default {
|
|||
.right-panel {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.507);
|
||||
background-color: rgba(0, 0, 0, 0.650);
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div class="container" @mouseover="hover = true" @mouseleave="hover = false">
|
||||
<div
|
||||
v-if="!type || type === 0"
|
||||
:class="{message: true, ownMessage: user.uniqueID === $props.uniqueID, halloween: user.uniqueID === $props.uniqueID, ownMessageLeft: user.uniqueID === $props.uniqueID && (apperance && apperance.own_message_right === true)} "
|
||||
:class="{message: true, ownMessage: user.uniqueID === $props.uniqueID, ownMessageLeft: user.uniqueID === $props.uniqueID && (apperance && apperance.own_message_right === true)} "
|
||||
>
|
||||
<div class="avatar">
|
||||
<profile-picture
|
||||
|
|
@ -18,80 +18,71 @@
|
|||
</div>
|
||||
<div class="content" @dblclick="contentDoubleClickEvent">
|
||||
<div class="user-info">
|
||||
<div class="username halloween-color"
|
||||
<div
|
||||
class="username"
|
||||
@click="openUserInformation"
|
||||
>
|
||||
{{ this.$props.username }}
|
||||
</div>
|
||||
<div class="date">
|
||||
{{ getDate }}
|
||||
</div>
|
||||
>{{ this.$props.username }}</div>
|
||||
<div class="date">{{ getDate }}</div>
|
||||
</div>
|
||||
<div class="content-message"
|
||||
v-html="formatMessage"
|
||||
/>
|
||||
<div class="content-message" v-html="formatMessage" />
|
||||
|
||||
<div class="file-content"
|
||||
v-if="getFile"
|
||||
>
|
||||
<div class="file-content" v-if="getFile">
|
||||
<div class="icon">
|
||||
<i class="material-icons">insert_drive_file</i>
|
||||
</div>
|
||||
<div class="information">
|
||||
<div class="info"> {{ getFile.fileName }}</div>
|
||||
<a
|
||||
:href="getFile.url"
|
||||
target="_blank"
|
||||
>
|
||||
<div class="info">{{ getFile.fileName }}</div>
|
||||
<a :href="getFile.url" target="_blank">
|
||||
<div class="download-button">Download</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="image-content" ref="image" v-if="getImage">
|
||||
<img
|
||||
:src="getImage"
|
||||
@click="imageClicked"
|
||||
>
|
||||
<img :src="getImage" @click="imageClicked" />
|
||||
</div>
|
||||
<message-embed-template v-if="embed && Object.keys(embed).length" :embed="embed"/>
|
||||
<message-embed-template v-if="embed && Object.keys(embed).length" :embed="embed" />
|
||||
</div>
|
||||
<div class="other-information">
|
||||
<div class="drop-down-button" ref="drop-down-button" @click="openContextMenu"><i class="material-icons">more_vert</i></div>
|
||||
<div class="sending-status" v-if="timeEdited && (status === undefined || status === 1)" :title="`Edited ${getEditedDate}`"><i class="material-icons">edit</i></div>
|
||||
<div class="sending-status" v-else-if="status === 0"><i class="material-icons">hourglass_full</i></div>
|
||||
<div class="sending-status" v-else-if="status === 1"><i class="material-icons">done</i></div>
|
||||
<div class="sending-status" v-else-if="status === 2"><i class="material-icons">close</i> Failed</div>
|
||||
<div class="drop-down-button" ref="drop-down-button" @click="openContextMenu">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</div>
|
||||
<div
|
||||
class="sending-status"
|
||||
v-if="timeEdited && (status === undefined || status === 1)"
|
||||
:title="`Edited ${getEditedDate}`"
|
||||
>
|
||||
<i class="material-icons">edit</i>
|
||||
</div>
|
||||
<div class="sending-status" v-else-if="status === 0">
|
||||
<i class="material-icons">hourglass_full</i>
|
||||
</div>
|
||||
<div class="sending-status" v-else-if="status === 1">
|
||||
<i class="material-icons">done</i>
|
||||
</div>
|
||||
<div class="sending-status" v-else-if="status === 2">
|
||||
<i class="material-icons">close</i> Failed
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div
|
||||
v-if="type && (type === 1 || type === 2 || type === 3 || type === 4)"
|
||||
class="presence-message"
|
||||
:class="{join: type === 1, leave: type === 2, kick: type === 3, ban: type === 4}"
|
||||
>
|
||||
<span>
|
||||
<span class="username"
|
||||
@click="openUserInformation"
|
||||
>{{ this.$props.username }}</span>
|
||||
<span
|
||||
v-if="type === 1"
|
||||
class="text"
|
||||
>has joined the server!</span>
|
||||
<span
|
||||
v-if="type === 2"
|
||||
class="text"
|
||||
>has left the server.</span>
|
||||
<span
|
||||
v-if="type === 3"
|
||||
class="text"
|
||||
>has been kicked.</span>
|
||||
<span
|
||||
v-if="type === 4"
|
||||
class="text"
|
||||
>has been banned.</span>
|
||||
<span class="date">{{ getDate }}</span>
|
||||
</span>
|
||||
<div class="presense-contain">
|
||||
<span>
|
||||
<span class="username" @click="openUserInformation">{{ this.$props.username }}</span>
|
||||
<span v-if="type === 1" class="text">has joined the server!</span>
|
||||
<span v-if="type === 2" class="text">has left the server.</span>
|
||||
<span v-if="type === 3" class="text">has been kicked.</span>
|
||||
<span v-if="type === 4" class="text">has been banned.</span>
|
||||
<span class="date">{{ getDate }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="drop-down-button" ref="drop-down-button" @click="openContextMenu">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -104,10 +95,10 @@ import messageFormatter from "@/utils/messageFormatter.js";
|
|||
import config from "@/config.js";
|
||||
import friendlyDate from "@/utils/date";
|
||||
import path from "path";
|
||||
import windowProperties from '@/utils/windowProperties';
|
||||
import windowProperties from "@/utils/windowProperties";
|
||||
|
||||
import { mapState } from "vuex";
|
||||
import messagesService from '../../services/messagesService';
|
||||
import messagesService from "../../services/messagesService";
|
||||
|
||||
export default {
|
||||
props: [
|
||||
|
|
@ -132,20 +123,19 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
hover: false
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
openContextMenu(event) {
|
||||
const x = event.clientX;
|
||||
const y = event.clientY;
|
||||
this.$store.dispatch('setMessageContext', {
|
||||
this.$store.dispatch("setMessageContext", {
|
||||
x,
|
||||
y,
|
||||
channelID: this.channelID,
|
||||
messageID: this.messageID,
|
||||
message: this.message,
|
||||
uniqueID: this.uniqueID
|
||||
|
||||
});
|
||||
},
|
||||
openUserInformation() {
|
||||
|
|
@ -157,27 +147,35 @@ export default {
|
|||
editMessage() {
|
||||
if (this.uniqueID !== this.user.uniqueID) return;
|
||||
this.dropDownVisable = false;
|
||||
this.$store.dispatch("setEditMessage", {channelID: this.channelID, messageID: this.messageID, message: this.message});
|
||||
this.$store.dispatch("setEditMessage", {
|
||||
channelID: this.channelID,
|
||||
messageID: this.messageID,
|
||||
message: this.message
|
||||
});
|
||||
},
|
||||
contentDoubleClickEvent(event){
|
||||
if (event.target.classList.contains("content") || event.target.closest('.user-info')) this.editMessage();
|
||||
contentDoubleClickEvent(event) {
|
||||
if (
|
||||
event.target.classList.contains("content") ||
|
||||
event.target.closest(".user-info")
|
||||
)
|
||||
this.editMessage();
|
||||
},
|
||||
clamp(num, min, max) {
|
||||
return num <= min ? min : num >= max ? max : num;
|
||||
},
|
||||
calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) {
|
||||
let ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
|
||||
return { width: srcWidth*ratio, height: srcHeight*ratio };
|
||||
return { width: srcWidth * ratio, height: srcHeight * ratio };
|
||||
},
|
||||
imageSize() {
|
||||
const files = this.$props.files;
|
||||
if (!files || files.length === 0 || !files[0].dimensions)
|
||||
return undefined;
|
||||
|
||||
const messageLog = document.querySelector('.message-logs');
|
||||
|
||||
const messageLog = document.querySelector(".message-logs");
|
||||
const w = messageLog.offsetWidth;
|
||||
const h = messageLog.offsetHeight;
|
||||
|
||||
|
||||
let minWidth = w / 4;
|
||||
let minHeight = h / 4;
|
||||
if (w <= 800) {
|
||||
|
|
@ -185,34 +183,39 @@ export default {
|
|||
minHeight = h / 1.7;
|
||||
}
|
||||
|
||||
|
||||
const dimensions = this.$props.files[0].dimensions
|
||||
const dimensions = this.$props.files[0].dimensions;
|
||||
const srcWidth = dimensions.width;
|
||||
const srcHeight = dimensions.height;
|
||||
|
||||
const newDimentions = this.calculateAspectRatioFit(srcWidth, srcHeight, minWidth, minHeight);
|
||||
const newDimentions = this.calculateAspectRatioFit(
|
||||
srcWidth,
|
||||
srcHeight,
|
||||
minWidth,
|
||||
minHeight
|
||||
);
|
||||
|
||||
const imageTag = this.$refs['image'];
|
||||
const imageTag = this.$refs["image"];
|
||||
|
||||
imageTag.firstChild.style.width = "100%"
|
||||
imageTag.firstChild.style.height = "100%"
|
||||
imageTag.firstChild.style.width = "100%";
|
||||
imageTag.firstChild.style.height = "100%";
|
||||
|
||||
imageTag.style.width = this.clamp(newDimentions.width, 0, srcWidth) + "px"
|
||||
imageTag.style.height = this.clamp(newDimentions.height, 0, srcHeight) + "px"
|
||||
imageTag.style.width =
|
||||
this.clamp(newDimentions.width, 0, srcWidth) + "px";
|
||||
imageTag.style.height =
|
||||
this.clamp(newDimentions.height, 0, srcHeight) + "px";
|
||||
},
|
||||
onResize(dimentions) {
|
||||
this.imageSize();
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
getWindowWidth(dimentions) {
|
||||
this.onResize(dimentions)
|
||||
this.onResize(dimentions);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const files = this.files;
|
||||
if (!files || files.length === 0 || !files[0].dimensions)
|
||||
return undefined;
|
||||
if (!files || files.length === 0 || !files[0].dimensions) return undefined;
|
||||
this.imageSize();
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -254,33 +257,40 @@ export default {
|
|||
return this.$store.getters.user;
|
||||
},
|
||||
getWindowWidth() {
|
||||
return {width: windowProperties.resizeWidth, height: windowProperties.resizeHeight};
|
||||
},
|
||||
return {
|
||||
width: windowProperties.resizeWidth,
|
||||
height: windowProperties.resizeHeight
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.drop-down-button{
|
||||
.drop-down-button {
|
||||
opacity: 0;
|
||||
transition: 0.2s;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.container:hover .drop-down-button{
|
||||
.container:hover .drop-down-button {
|
||||
opacity: 1;
|
||||
}
|
||||
.presence-message {
|
||||
margin: 10px;
|
||||
display: flex;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.presense-contain {
|
||||
padding: 10px;
|
||||
display: table;
|
||||
color: white;
|
||||
|
|
@ -289,24 +299,25 @@ export default {
|
|||
background: rgba(0, 0, 0, 0.356);
|
||||
}
|
||||
|
||||
|
||||
.presence-message .text {
|
||||
margin-left: 5px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.presence-message .username {
|
||||
font-size: 15px;
|
||||
font-weight: bold
|
||||
font-weight: bold;
|
||||
}
|
||||
.presence-message.join {
|
||||
color: #29BF12;
|
||||
.presence-message.join .text {
|
||||
color: #29bf12;
|
||||
}
|
||||
.presence-message.leave {
|
||||
.presence-message.leave .text {
|
||||
color: rgb(150, 139, 139);
|
||||
}
|
||||
.presence-message.kick {
|
||||
color: #FF9914;
|
||||
.presence-message.kick .text {
|
||||
color: #ff9914;
|
||||
}
|
||||
.presence-message.ban {
|
||||
.presence-message.ban .text {
|
||||
color: #d92121;
|
||||
}
|
||||
|
||||
|
|
@ -318,6 +329,8 @@ export default {
|
|||
border-left: 8px solid rgba(184, 184, 184, 0.219);
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
|
||||
.ownMessageLeft .avatar {
|
||||
margin-right: 0px;
|
||||
margin-left: 5px;
|
||||
|
|
@ -338,12 +351,8 @@ export default {
|
|||
.ownMessage .content {
|
||||
background: rgba(184, 184, 184, 0.219);
|
||||
}
|
||||
.ownMessage.halloween .content {
|
||||
background: rgba(255, 135, 31, 0.43);
|
||||
}
|
||||
.ownMessage.halloween .triangle-inner {
|
||||
background: rgba(255, 135, 31, 0.43);
|
||||
}
|
||||
|
||||
|
||||
.ownMessage .date {
|
||||
color: rgb(209, 209, 209);
|
||||
}
|
||||
|
|
@ -397,11 +406,11 @@ export default {
|
|||
}
|
||||
|
||||
.triangle-inner {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 9px solid transparent;
|
||||
border-bottom: 0px solid transparent;
|
||||
border-right: 8px solid rgba(0, 0, 0, 0.301);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 9px solid transparent;
|
||||
border-bottom: 0px solid transparent;
|
||||
border-right: 8px solid rgba(0, 0, 0, 0.301);
|
||||
}
|
||||
|
||||
.content {
|
||||
|
|
@ -433,7 +442,6 @@ export default {
|
|||
width: 100px;
|
||||
object-fit: contain;
|
||||
height: 100px;
|
||||
|
||||
}
|
||||
.image-content:hover img {
|
||||
filter: brightness(70%);
|
||||
|
|
@ -453,9 +461,6 @@ export default {
|
|||
color: rgb(199, 199, 199);
|
||||
text-decoration: underline;
|
||||
}
|
||||
.username.halloween-color {
|
||||
color: orange
|
||||
}
|
||||
.date {
|
||||
color: rgb(177, 177, 177);
|
||||
font-size: 10px;
|
||||
|
|
@ -489,8 +494,6 @@ export default {
|
|||
align-content: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.message .other-information div .material-icons {
|
||||
font-size: 15px;
|
||||
color: rgb(306, 306, 306);
|
||||
|
|
@ -505,14 +508,11 @@ export default {
|
|||
}
|
||||
|
||||
@media (max-width: 468px) {
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style>
|
||||
|
||||
.code-inline{
|
||||
.code-inline {
|
||||
background: rgba(0, 0, 0, 0.322);
|
||||
}
|
||||
.msg-link {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div
|
||||
class="my-mini-information"
|
||||
:style="{backgroundColor: getStatusColor}"
|
||||
|
||||
@mouseover="hover = true" @mouseleave="hover = false"
|
||||
|
||||
>
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
<profile-picture
|
||||
:url="`${avatar}${hover ? '' : '?type=png'}`"
|
||||
:admin="user.admin"
|
||||
size="50px"
|
||||
size="35px"
|
||||
:hover="true"
|
||||
@click.native="openUserInformation"
|
||||
/>
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
class="current-status"
|
||||
:src="getStatus"
|
||||
>
|
||||
<i class="material-icons expand-status-icon">expand_more</i>
|
||||
<i class="material-icons expand-status-icon">expand_less</i>
|
||||
<transition name="show-status-list">
|
||||
<statusList
|
||||
v-if="status.isPoppedOut"
|
||||
|
|
@ -49,12 +49,6 @@
|
|||
<i class="material-icons">error</i>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="setting-icon"
|
||||
@click="openSettings"
|
||||
>
|
||||
<i class="material-icons">settings</i>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -123,12 +117,7 @@ export default {
|
|||
this.$store.dispatch("changeStatus", result.data.set);
|
||||
}
|
||||
},
|
||||
openSettings() {
|
||||
this.$store.dispatch("setPopoutVisibility", {
|
||||
name: "settings",
|
||||
visibility: true
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -139,8 +128,9 @@ export default {
|
|||
.profile-pic-outer{
|
||||
z-index:9999;
|
||||
display: flex;
|
||||
margin-left: 10px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.survay-button {
|
||||
padding: 10px;
|
||||
|
|
@ -160,6 +150,7 @@ export default {
|
|||
width: 24px;
|
||||
font-size: 30px;
|
||||
color: cyan;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.show-status-list-enter-active,
|
||||
|
|
@ -169,7 +160,7 @@ export default {
|
|||
.show-status-list-enter,
|
||||
.show-status-list-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-5px);
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
|
|
@ -181,19 +172,19 @@ export default {
|
|||
}
|
||||
|
||||
.my-mini-information {
|
||||
border-radius: 10px;
|
||||
margin: 5px;
|
||||
height: 80px;
|
||||
position: relative;
|
||||
background: rgba(0, 0, 0, 0.3);;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
transition: 0.3s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
.information {
|
||||
color: white;
|
||||
margin-top: -7px;
|
||||
margin-top: -4px;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
|
@ -205,9 +196,9 @@ export default {
|
|||
margin-right: 15px;
|
||||
padding: 5px;
|
||||
border-radius: 50%;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
transition: 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.setting-icon:hover {
|
||||
background: rgba(0, 0, 0, 0.294);
|
||||
|
|
@ -221,10 +212,11 @@ export default {
|
|||
padding-top: 1px;
|
||||
padding-left: 5px;
|
||||
margin-left: 10px;
|
||||
margin-top: -2px;
|
||||
margin-top: -10px;
|
||||
transition: 0.3s;
|
||||
user-select: none;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.status:hover {
|
||||
|
|
@ -251,14 +243,15 @@ export default {
|
|||
margin-top: 10px;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tag {
|
||||
color: rgb(199, 199, 199);
|
||||
font-size: 13px;
|
||||
font-size: 11px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-top: 5px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
373
src/components/app/Navigation.vue
Normal file
373
src/components/app/Navigation.vue
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
<template>
|
||||
<div class="navigation" ref="navigation">
|
||||
<div
|
||||
class="tool-tip"
|
||||
ref="toolTip"
|
||||
:style="{top: toolTipTopPosition + 'px'}"
|
||||
v-if="toolTipShown"
|
||||
>{{toolTipLocalName || servers[toolTipServerID].name}}</div>
|
||||
<div class="container" @mouseleave="mouseLeaveEvent">
|
||||
<div class="scrollable">
|
||||
<div class="navigation-items">
|
||||
<div
|
||||
class="item material-icons"
|
||||
:class="{selected: currentTab == 0}"
|
||||
@click="switchTab(0)"
|
||||
@mouseenter="localToolTipEvent('Explore', $event)"
|
||||
>explore</div>
|
||||
<div
|
||||
class="item material-icons"
|
||||
:class="{selected: currentTab == 1, notifyAnimation: DMNotification || friendRequestExists}"
|
||||
@click="switchTab(1)"
|
||||
@mouseenter="localToolTipEvent('Direct Message', $event)"
|
||||
>chat</div>
|
||||
<div
|
||||
class="item material-icons"
|
||||
:class="{selected: currentTab == 2, notifyAnimation: serverNotification}"
|
||||
@click="switchTab(2)"
|
||||
@mouseenter="localToolTipEvent('Servers', $event)"
|
||||
>forum</div>
|
||||
<div
|
||||
class="item material-icons"
|
||||
:class="{selected: currentTab == 3}"
|
||||
@click="switchTab(3)"
|
||||
@mouseenter="localToolTipEvent('Changelog', $event)"
|
||||
>list_alt</div>
|
||||
</div>
|
||||
<div class="seperater" />
|
||||
<div class="server-items" v-if="currentTab === 2">
|
||||
<server-template
|
||||
v-for="(data, index) in serversArr"
|
||||
:serverData="data"
|
||||
@click.native="openServer(data.server_id)"
|
||||
:key="index.server_id"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="seperater" />
|
||||
<div
|
||||
class="item material-icons"
|
||||
v-if="currentTab === 1"
|
||||
@click="addFriend"
|
||||
@mouseenter="localToolTipEvent('Add Friend', $event)"
|
||||
>person_add</div>
|
||||
<div
|
||||
class="item material-icons"
|
||||
v-if="currentTab === 2"
|
||||
@click="addServer"
|
||||
@mouseenter="localToolTipEvent('Add Server', $event)"
|
||||
>add</div>
|
||||
<div
|
||||
class="item material-icons"
|
||||
@click="openSettings"
|
||||
@mouseenter="localToolTipEvent('Settings', $event)"
|
||||
>settings</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { bus } from "@/main.js";
|
||||
import config from "@/config.js";
|
||||
import ServerTemplate from "@/components/app/ServerTemplate/ServerTemplate";
|
||||
|
||||
export default {
|
||||
components: { ServerTemplate },
|
||||
data() {
|
||||
return {
|
||||
avatarDomain: config.domain + "/avatars",
|
||||
toolTipShown: false,
|
||||
toolTipTopPosition: 0,
|
||||
toolTipServerID: null,
|
||||
toolTipLocalName: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
dismissNotification(channelID) {
|
||||
const notifications = this.$store.getters.notifications.find(function(e) {
|
||||
return e.channelID === channelID;
|
||||
});
|
||||
|
||||
if (notifications && notifications.count >= 1 && document.hasFocus()) {
|
||||
this.$socket.emit("notification:dismiss", { channelID });
|
||||
}
|
||||
},
|
||||
openServer(serverID) {
|
||||
const server = this.servers[serverID];
|
||||
const lastSelectedChannel = JSON.parse(
|
||||
localStorage.getItem("selectedChannels") || "{}"
|
||||
)[serverID];
|
||||
const defaultChannelID = server.default_channel_id;
|
||||
const channels = this.$store.getters.channels;
|
||||
const channel = channels[lastSelectedChannel || defaultChannelID];
|
||||
|
||||
this.dismissNotification(channel.channelID);
|
||||
this.$store.dispatch("servers/setSelectedServerID", serverID);
|
||||
this.$store.dispatch("openChannel", channel);
|
||||
},
|
||||
switchChannel(isServer) {
|
||||
const serverChannelID = this.$store.state.channelModule.serverChannelID;
|
||||
const DMChannelID = this.$store.state.channelModule.DMChannelID;
|
||||
|
||||
if (isServer) {
|
||||
this.$store.dispatch("selectedChannelID", serverChannelID);
|
||||
const channel = this.$store.state.channelModule.channels[
|
||||
serverChannelID
|
||||
];
|
||||
this.$store.dispatch("setChannelName", channel ? channel.name : "");
|
||||
this.dismissNotification(serverChannelID);
|
||||
} else {
|
||||
const channel = this.$store.state.channelModule.channels[DMChannelID];
|
||||
this.$store.dispatch(
|
||||
"setChannelName",
|
||||
channel ? channel.recipients[0].username : ""
|
||||
);
|
||||
this.$store.dispatch("selectedChannelID", DMChannelID);
|
||||
this.dismissNotification(DMChannelID);
|
||||
}
|
||||
},
|
||||
switchTab(index) {
|
||||
localStorage.setItem("currentTab", index);
|
||||
this.$store.dispatch("setCurrentTab", index);
|
||||
if (index == 1) {
|
||||
//1: direct message tab.
|
||||
this.switchChannel(false);
|
||||
} else if (index === 2) {
|
||||
//2: server tab
|
||||
this.switchChannel(true);
|
||||
}
|
||||
},
|
||||
openSettings() {
|
||||
this.$store.dispatch("setPopoutVisibility", {
|
||||
name: "settings",
|
||||
visibility: true
|
||||
});
|
||||
},
|
||||
localToolTipEvent(name, event) {
|
||||
const rect = event.target.getBoundingClientRect();
|
||||
this.toolTipLocalName = name;
|
||||
this.toolTipTopPosition = rect.top - this.getTopHeight() + 16;
|
||||
this.toolTipShown = true;
|
||||
},
|
||||
serverToolTipEvent({ serverID, top }) {
|
||||
this.toolTipLocalName = null;
|
||||
this.toolTipServerID = serverID;
|
||||
this.toolTipTopPosition = top - this.getTopHeight() + 16;
|
||||
this.toolTipShown = true;
|
||||
},
|
||||
mouseLeaveEvent() {
|
||||
this.toolTipShown = false;
|
||||
this.toolTipServerID = null;
|
||||
this.toolTipLocalName = null;
|
||||
},
|
||||
getTopHeight() {
|
||||
return window.innerHeight - this.$refs["navigation"].offsetHeight;
|
||||
},
|
||||
addServer() {
|
||||
this.$store.dispatch("setPopoutVisibility", {
|
||||
name: "addServer",
|
||||
visibility: true
|
||||
});
|
||||
},
|
||||
addFriend() {
|
||||
this.$store.dispatch('setAllPopout', {
|
||||
show: true,
|
||||
type: "ADD_FRIEND",
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentTab() {
|
||||
return this.$store.getters.currentTab;
|
||||
},
|
||||
servers() {
|
||||
return this.$store.getters["servers/servers"];
|
||||
},
|
||||
serversArr() {
|
||||
const data = this.servers;
|
||||
return Object.keys(data)
|
||||
.map(key => {
|
||||
return data[key];
|
||||
})
|
||||
.slice()
|
||||
.reverse();
|
||||
},
|
||||
selectedServerID() {
|
||||
return this.$store.getters["servers/selectedServerID"];
|
||||
},
|
||||
serverNotification() {
|
||||
const notifications = this.$store.getters.notifications;
|
||||
const channels = this.$store.getters.channels;
|
||||
const notification = notifications.find(e => {
|
||||
return (
|
||||
channels[e.channelID] &&
|
||||
channels[e.channelID].server_id &&
|
||||
e.channelID !== this.$store.getters.selectedChannelID
|
||||
);
|
||||
});
|
||||
return notification;
|
||||
},
|
||||
DMNotification() {
|
||||
const notifications = this.$store.getters.notifications;
|
||||
const channels = this.$store.getters.channels;
|
||||
const notification = notifications.find(e => {
|
||||
return (
|
||||
channels[e.channelID] &&
|
||||
!channels[e.channelID].server_id &&
|
||||
e.channelID !== this.$store.getters.selectedChannelID
|
||||
);
|
||||
});
|
||||
// unopened dm
|
||||
if (!notification) {
|
||||
return notifications.find(e => {
|
||||
return !channels[e.channelID];
|
||||
});
|
||||
}
|
||||
return notification;
|
||||
},
|
||||
friendRequestExists() {
|
||||
const allFriend = this.$store.getters.user.friends;
|
||||
const result = Object.keys(allFriend).map(function(key) {
|
||||
return allFriend[key];
|
||||
});
|
||||
return result.find(friend => friend.status === 1);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
bus.$on("server-tool-tip", this.serverToolTipEvent);
|
||||
},
|
||||
destroyed() {
|
||||
bus.$off("server-tool-tip", this.serverToolTipEvent);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navigation {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
width: 60px;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
width: 60px;
|
||||
}
|
||||
.navigation-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
align-self: flex-start;
|
||||
align-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.scrollable {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
height: 100%;
|
||||
align-self: center;
|
||||
&::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
}
|
||||
}
|
||||
.item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
font-size: 30px;
|
||||
align-self: center;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
opacity: 0.8;
|
||||
transition: 0.2s;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
&.selected {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.server-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.seperater {
|
||||
background-color: #d8d8d8;
|
||||
flex-shrink: 0;
|
||||
align-self: center;
|
||||
width: calc(100% - 10px);
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.notifyAnimation:before {
|
||||
content: "!";
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
font-size: 15px;
|
||||
position: absolute;
|
||||
z-index: 115651;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
animation: notifyAnime;
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: forwards;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 23, 23, 0.753);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@keyframes notifyAnime {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
40% {
|
||||
opacity: 1;
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
.tool-tip {
|
||||
color: white;
|
||||
position: absolute;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(3px);
|
||||
padding: 5px;
|
||||
max-width: 150px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
left: 60px;
|
||||
z-index: 99999;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
transition: 0.2s;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -14,6 +14,8 @@
|
|||
<GenericPopout key="gp" v-if="popouts.genericMessage"/>
|
||||
<message-context-menu key="mcm" v-if="popouts.messageContextMenu.messageID"/>
|
||||
<server-member-context key="smc" v-if="popouts.serverMemberContext.uniqueID"/>
|
||||
<server-context key="sc" v-if="popouts.allPopout.type === 'SERVER' && popouts.allPopout.show"/>
|
||||
<add-friend key="af" v-if="popouts.allPopout.type === 'ADD_FRIEND' && popouts.allPopout.show"/>
|
||||
</transition-group>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -26,9 +28,11 @@
|
|||
// context menus
|
||||
const messageContextMenu = () => import('./Popouts/messageContextMenu');
|
||||
const ServerMemberContext = () => import('./Popouts/ServerMemberContext');
|
||||
const ServerContext = () => import('./Popouts/ServerContextMenu.vue');
|
||||
|
||||
|
||||
const AddServer = () => import('./Popouts/AddServer.vue');
|
||||
const AddFriend = () => import('./Popouts/AddFriend');
|
||||
const Settings = () => import('./Popouts/Settings.vue');
|
||||
const TakeSurveyPopout = () => import('./Popouts/TakeSurveyPopout.vue');
|
||||
const uploadDialog = () => import('./Popouts/uploadDialog.vue');
|
||||
|
|
@ -55,7 +59,10 @@ export default {
|
|||
ServerSettings,
|
||||
GenericPopout,
|
||||
messageContextMenu,
|
||||
ServerMemberContext
|
||||
ServerMemberContext,
|
||||
ServerContext,
|
||||
AddFriend
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
|||
174
src/components/app/Popouts/Popouts/AddFriend.vue
Normal file
174
src/components/app/Popouts/Popouts/AddFriend.vue
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
<template>
|
||||
<div class="dark-background" @mousedown="backgroundClick">
|
||||
<div class="add-friend">
|
||||
<div class="header">
|
||||
<div class="icon material-icons">person_add</div>
|
||||
<div class="name">Add Friend</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="container">
|
||||
<div class="description">Add friends by using their username and tag.</div>
|
||||
<input class="tag" v-model="input" type="text" placeholder="pancake@time" />
|
||||
<input class="button" @click="addButton" type="button" :value="requestSent ? 'Adding...' :'Add Friend'" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="alerts" :class="{success, error}" v-if="error || success">{{error || success || ""}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RelationshipService from '@/services/RelationshipService.js'
|
||||
|
||||
export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
input: "",
|
||||
requestSent: false,
|
||||
error: null,
|
||||
success: null,
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
closeMenu() {
|
||||
this.$store.dispatch("setAllPopout", {
|
||||
show: false,
|
||||
type: "ADD_FRIEND"
|
||||
});
|
||||
},
|
||||
backgroundClick(e) {
|
||||
if (e.target.classList.contains("dark-background")) {
|
||||
this.closeMenu();
|
||||
}
|
||||
},
|
||||
async addButton(e) {
|
||||
if (this.requestSent) return;
|
||||
this.error = null;
|
||||
this.success = null;
|
||||
this.requestSent = true;
|
||||
|
||||
const split = this.input.trim().split("@");
|
||||
// validation
|
||||
if ( split.length <2 || split.length >2 || split[1] === "" || split[1].length !== 4){
|
||||
this.requestSent = false;
|
||||
this.error = "Invalid username or tag.";
|
||||
return;
|
||||
}
|
||||
const username = split[0];
|
||||
const tag = split[1];
|
||||
|
||||
const {ok, error, result} = await RelationshipService.post({username, tag})
|
||||
this.requestSent = false;
|
||||
if ( ok ) {
|
||||
this.success = result.data.message
|
||||
} else {
|
||||
if (error.response === undefined) {
|
||||
this.error = "Can't connect to server.";
|
||||
return
|
||||
}
|
||||
this.error = error.response.data.errors[0].msg;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dark-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: rgba(0, 0, 0, 0.541);
|
||||
z-index: 111111;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.add-friend {
|
||||
height: 300px;
|
||||
width: 400px;
|
||||
background: rgba(24, 24, 24, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
background-color: rgba(41, 41, 41, 0.801);
|
||||
color: white;
|
||||
height: 50px;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
color: rgb(255, 255, 255);
|
||||
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
color: white;
|
||||
background: transparent;
|
||||
border: solid 1px #afafaf;
|
||||
}
|
||||
.description {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.button {
|
||||
align-self: center;
|
||||
color: white;
|
||||
width: initial;
|
||||
background: #1998ff;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #157fd6;
|
||||
}
|
||||
}
|
||||
|
||||
.alerts {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 30px;
|
||||
color: white;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
&.success {
|
||||
background: #008a00;
|
||||
}
|
||||
&.error {
|
||||
background: #8a0000;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
102
src/components/app/Popouts/Popouts/ServerContextMenu.vue
Normal file
102
src/components/app/Popouts/Popouts/ServerContextMenu.vue
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
<template>
|
||||
<div class="drop-down-menu" v-click-outside="outsideClick">
|
||||
<div class="item" @click="createInvite" >Manage Invites</div>
|
||||
<div class="item" v-if="checkServerCreator" @click="showSettings" >Server Settings</div>
|
||||
<div class="item warn" v-if="!checkServerCreator" @click="leaveServer">Leave Server</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import messagesService from '@/services/messagesService';
|
||||
import ServerService from '../../../../services/ServerService';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
closeMenu() {
|
||||
this.$store.dispatch('setAllPopout', {
|
||||
show: false,
|
||||
type: null,
|
||||
})
|
||||
},
|
||||
outsideClick(event) {
|
||||
if (event.target.classList.contains('options-button')) return;
|
||||
this.closeMenu();
|
||||
},
|
||||
setPosition() {
|
||||
const y = this.contextDetails.y;
|
||||
const x = this.contextDetails.x;
|
||||
|
||||
this.$el.style.top = y + "px";
|
||||
this.$el.style.left = x + "px";
|
||||
},
|
||||
showSettings() {
|
||||
this.closeMenu()
|
||||
this.$store.dispatch('setServerSettings', {serverID: this.contextDetails.serverID})
|
||||
},
|
||||
createInvite(serverID) {
|
||||
this.closeMenu()
|
||||
this.$store.dispatch("setPopoutVisibility", {
|
||||
name: "showServerInviteMenu",
|
||||
visibility: true
|
||||
});
|
||||
},
|
||||
async leaveServer() {
|
||||
this.closeMenu()
|
||||
const {ok, error, result} = await ServerService.leaveServer(this.contextDetails.serverID);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setPosition();
|
||||
},
|
||||
watch: {
|
||||
contextDetails() {
|
||||
this.setPosition();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
contextDetails() {
|
||||
return this.$store.getters.popouts.allPopout
|
||||
},
|
||||
user() {
|
||||
return this.$store.getters.user;
|
||||
},
|
||||
checkServerCreator() {
|
||||
return this.contextDetails.creatorUniqueID === this.user.uniqueID
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.drop-down-menu {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(5px);
|
||||
z-index: 99999;
|
||||
user-select: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 10px;
|
||||
transition: 0.3s;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: rgba(46, 46, 46, 0.651);
|
||||
}
|
||||
&.warn {
|
||||
color: rgb(255, 59, 59);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -39,7 +39,6 @@ import config from "@/config.js";
|
|||
import { bus } from "@/main";
|
||||
import Spinner from "@/components/Spinner.vue";
|
||||
import ServerService from "@/services/ServerService";
|
||||
import { mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
components: {Spinner},
|
||||
|
|
@ -84,9 +83,9 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
serverID: state => state.popoutsModule.serverIDContextMenu
|
||||
})
|
||||
serverID() {
|
||||
return this.$store.getters.popouts.allPopout.serverID
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -106,23 +106,21 @@ export default {
|
|||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: rgba(31, 31, 31, 0.995);
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(5px);
|
||||
z-index: 99999;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
user-select: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 5px;
|
||||
margin: 2px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
transition: 0.3s;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: rgb(46, 46, 46);
|
||||
background: rgba(46, 46, 46, 0.651);
|
||||
}
|
||||
&.warn {
|
||||
color: rgb(255, 59, 59);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,13 @@
|
|||
<div class="my-profile-panel">
|
||||
|
||||
<div class="switches">
|
||||
<div class="checkbox"
|
||||
@click="toggleNotificationSounds">
|
||||
<div class="checkbox-box" :class="{selected: !notificationSettings.disableNotificationSound}" />
|
||||
<div class="checkbox-name">
|
||||
Notification Sounds
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkbox"
|
||||
@click="toggleNotification()">
|
||||
<div class="checkbox-box" :class="{selected: !notificationSettings.disableDesktopNotification}" />
|
||||
|
|
@ -23,7 +30,8 @@ import SettingsService from '@/services/settingsService.js';
|
|||
export default {
|
||||
data() {
|
||||
return {
|
||||
isElectron: window && window.process && window.process.type
|
||||
isElectron: window && window.process && window.process.type,
|
||||
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -39,7 +47,7 @@ export default {
|
|||
const setting = this.notificationSettings['disableDesktopNotification'];
|
||||
if (setting && setting === true && !this.isElectron) {
|
||||
if (Notification.permission === "denied") {
|
||||
alert("Notifications blocked. Please enable them in your browser.");
|
||||
alert("Notifications permission is blocked. Allow notifications from this website in your browsers permission settings.");
|
||||
}
|
||||
Notification.requestPermission().then(function(result) {
|
||||
if (result === 'denied' || result === 'default') return;
|
||||
|
|
@ -49,6 +57,12 @@ export default {
|
|||
}
|
||||
this.toggleSetting('disableDesktopNotification');
|
||||
},
|
||||
toggleNotificationSounds(){
|
||||
console.log("test")
|
||||
const setting = this.notificationSettings['disableNotificationSound'];
|
||||
console.log(!!!setting)
|
||||
this.$store.dispatch('settingsModule/updateNotification', {disableNotificationSound: !setting})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (!this.isElectron && this.notificationSettings.disableDesktopNotification === undefined) {
|
||||
|
|
@ -69,18 +83,19 @@ export default {
|
|||
|
||||
.switches {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 20px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.checkbox-box {
|
||||
background: rgba(88, 88, 88, 0.74);
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin: auto;
|
||||
margin-right: 10px;
|
||||
transition: 0.3s;
|
||||
border-radius: 5px;
|
||||
|
|
|
|||
|
|
@ -106,23 +106,20 @@ export default {
|
|||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: rgba(31, 31, 31, 0.895);
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(5px);
|
||||
z-index: 99999;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
user-select: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 5px;
|
||||
margin: 2px;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
transition: 0.3s;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: rgb(46, 46, 46);
|
||||
background: rgba(46, 46, 46, 0.651);
|
||||
}
|
||||
&.warn {
|
||||
color: rgb(255, 59, 59);
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ export default {
|
|||
);
|
||||
},
|
||||
openChat() {
|
||||
bus.$emit('changeTab', 1)
|
||||
this.$store.dispatch('setCurrentTab', 1)
|
||||
this.$store.dispatch("openChat", {
|
||||
uniqueID: this.uniqueID,
|
||||
channelName: this.user.username
|
||||
|
|
|
|||
|
|
@ -1,24 +1,18 @@
|
|||
<template>
|
||||
<div class="left-panel">
|
||||
<MyMiniInformation />
|
||||
<div class="actions">
|
||||
<div class="action" @click="openAddServer">
|
||||
<div class="material-icons">add</div>
|
||||
<div class="text">Add Server</div>
|
||||
<navigation />
|
||||
<div class="right">
|
||||
<div class="server-banner" v-if="selectedServerID">
|
||||
<div class="banner-image"></div>
|
||||
<div class="sub-banner" >
|
||||
<div class="text" :title="servers[selectedServerID].name">{{servers[selectedServerID].name}}</div>
|
||||
<div class="options-button material-icons" @click="openServerContext">more_vert</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action" @click="openExploreTab">
|
||||
<div class="material-icons">explore</div>
|
||||
<div class="text">Explore</div>
|
||||
<div class="channels-list">
|
||||
<channels-list v-if="selectedServerID" :server-i-d="selectedServerID" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="list">
|
||||
<server
|
||||
v-for="(data, index) in servers"
|
||||
:key="index.server_id"
|
||||
:server-data="data"
|
||||
:open-channel="selectedServerID && selectedServerID === data.server_id"
|
||||
@click.native="toggleChannel(data.server_id, $event)"
|
||||
/>
|
||||
<MyMiniInformation />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -26,12 +20,16 @@
|
|||
<script>
|
||||
import MyMiniInformation from "@/components/app/MyMiniInformation.vue";
|
||||
import Server from "@/components/app/ServerTemplate/ServerTemplate.vue";
|
||||
import {bus} from '@/main'
|
||||
import ChannelsList from "@/components/app/ServerTemplate/ChannelsList.vue";
|
||||
import Navigation from "@/components/app/Navigation.vue";
|
||||
import { bus } from "@/main";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MyMiniInformation,
|
||||
Server
|
||||
ChannelsList,
|
||||
Server,
|
||||
Navigation
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -45,98 +43,147 @@ export default {
|
|||
visibility: true
|
||||
});
|
||||
},
|
||||
toggleChannel(serverID, event) {
|
||||
if (!event.target.closest('.small-view') || event.target.closest('.options-context-button') || event.target.closest('.options-context-menu')) return;
|
||||
if (this.openedServer === serverID) {
|
||||
this.openedServer = null;
|
||||
this.$store.dispatch('servers/setSelectedServerID', null)
|
||||
}
|
||||
else{
|
||||
this.openedServer = serverID;
|
||||
this.$store.dispatch('servers/setSelectedServerID', serverID)
|
||||
}
|
||||
clickServer(serverID, event) {
|
||||
this.openedServer = serverID;
|
||||
this.$store.dispatch("servers/setSelectedServerID", serverID);
|
||||
},
|
||||
openExploreTab() {
|
||||
bus.$emit('changeTab', 0)
|
||||
this.$store.dispatch('setCurrentTab', 0)
|
||||
},
|
||||
openServerContext(event) {
|
||||
const rect = event.target.getBoundingClientRect();
|
||||
if (this.checkServerContextOpened) {
|
||||
this.$store.dispatch('setAllPopout', {
|
||||
show: false,
|
||||
type: null
|
||||
})
|
||||
return;
|
||||
}
|
||||
this.$store.dispatch('setAllPopout', {
|
||||
show: true,
|
||||
type: 'SERVER',
|
||||
serverID: this.servers[this.selectedServerID].server_id,
|
||||
creatorUniqueID: this.servers[this.selectedServerID].creator.uniqueID,
|
||||
x: rect.left - 30,
|
||||
y: rect.top + 35,
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
servers() {
|
||||
const data = this.$store.getters['servers/servers'];
|
||||
return Object.keys(data).map(key => {
|
||||
return data[key];
|
||||
}).slice().reverse()
|
||||
return this.$store.getters["servers/servers"];
|
||||
},
|
||||
serversArr() {
|
||||
const data = this.servers;
|
||||
return Object.keys(data)
|
||||
.map(key => {
|
||||
return data[key];
|
||||
})
|
||||
.slice()
|
||||
.reverse();
|
||||
},
|
||||
selectedServerID() {
|
||||
return this.$store.getters['servers/selectedServerID'];
|
||||
return this.$store.getters["servers/selectedServerID"];
|
||||
},
|
||||
checkServerContextOpened() {
|
||||
const contextDetail = this.$store.getters.popouts.allPopout;
|
||||
return contextDetail.show && contextDetail.type === "SERVER";
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss" >
|
||||
.left-panel {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.671);
|
||||
width: 300px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
z-index: 1;
|
||||
}
|
||||
.list {
|
||||
margin: 2px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* ------- SCROLL BAR -------*/
|
||||
/* width */
|
||||
.list::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
.list::-webkit-scrollbar-track {
|
||||
background: #8080806b;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
.list::-webkit-scrollbar-thumb {
|
||||
background: #f5f5f559;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
.list::-webkit-scrollbar-thumb:hover {
|
||||
background: #f5f5f59e;
|
||||
}
|
||||
|
||||
.actions {
|
||||
color: white;
|
||||
.channels-list {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
background: rgba(29, 29, 29, 0.37);
|
||||
width: 70px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.server-banner {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
flex-direction: row;
|
||||
background-color: rgb(32, 32, 32);
|
||||
height: 150px;
|
||||
}
|
||||
.banner-image {
|
||||
position: absolute;
|
||||
background-image: url("../../assets/background.jpg");
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
height: 100%;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
}
|
||||
.sub-banner {
|
||||
display: flex;
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 0.507);
|
||||
align-self: flex-end;
|
||||
height: 35px;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
position: relative;
|
||||
backdrop-filter: blur(15px);
|
||||
z-index: 2;
|
||||
user-select: none;
|
||||
.action {
|
||||
display: flex;
|
||||
padding: 5px;
|
||||
margin: 2px;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
cursor: pointer;
|
||||
color: rgb(223, 223, 223);
|
||||
border-radius: 5px;
|
||||
transition: 0.2s;
|
||||
.material-icons {
|
||||
color: white;
|
||||
margin-right: 5px;
|
||||
}
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
overflow: hidden;
|
||||
cursor: default;
|
||||
.text {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.options-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
transition: 0.2s;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
font-size: 20px;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.322);
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -54,22 +54,26 @@ export default {
|
|||
.channel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 5px;
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
transition: 0.3s;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.channel:hover {
|
||||
background: rgba(139, 139, 139, 0.288);
|
||||
background: rgba(0, 0, 0, 0.192);
|
||||
}
|
||||
.selected {
|
||||
background: rgba(139, 139, 139, 0.288);
|
||||
.channel.selected {
|
||||
background: rgba(0, 0, 0, 0.288);
|
||||
}
|
||||
|
||||
|
||||
.channel-name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-left: 5px;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
openChannel(channel) {
|
||||
// add to local storage
|
||||
const selectedChannels = JSON.parse(localStorage.getItem('selectedChannels') || '{}')
|
||||
selectedChannels[this.serverID] = channel.channelID;
|
||||
localStorage.setItem('selectedChannels', JSON.stringify(selectedChannels));
|
||||
|
||||
const notificationExists = this.$store.getters.notifications.find(n => n.channelID === channel.channelID)
|
||||
|
||||
if (notificationExists && document.hasFocus()) {
|
||||
|
|
@ -79,11 +84,21 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.channel-list {
|
||||
background: rgba(0, 0, 0, 0.288);
|
||||
.channels-list {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* ------- SCROLL BAR -------*/
|
||||
/* width */
|
||||
.channels-list::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,243 +1,126 @@
|
|||
<template>
|
||||
<div :class="{server: true, 'add-server': mode === 'ADD_SERVER'}">
|
||||
<div :class="{'small-view': true, notifyAnimation: notification}">
|
||||
<profile-picture
|
||||
v-if="!mode"
|
||||
size="50px"
|
||||
:url="`${avatarDomain}/${serverData.avatar}`"
|
||||
/>
|
||||
<div
|
||||
v-if="mode === 'ADD_SERVER'"
|
||||
class="add-icon"
|
||||
>
|
||||
<i class="material-icons">add</i>
|
||||
</div>
|
||||
<div class="server-name">
|
||||
{{ mode === 'ADD_SERVER'? 'Create / Join Server' : serverData.name }}
|
||||
</div>
|
||||
<div
|
||||
v-if="mode !== 'ADD_SERVER'"
|
||||
ref="contextMenuButton"
|
||||
class="options-context-button"
|
||||
@click="showContextMenu = !showContextMenu"
|
||||
>
|
||||
<i class="material-icons">more_vert</i>
|
||||
</div>
|
||||
<div
|
||||
v-if="showContextMenu"
|
||||
v-click-outside="closeContextMenu"
|
||||
class="options-context-menu"
|
||||
>
|
||||
<div
|
||||
class="menu-button"
|
||||
@click="createInvite(serverData.server_id)"
|
||||
>
|
||||
Manage Invites
|
||||
</div>
|
||||
<div
|
||||
v-if="serverData.creator.uniqueID !== user.uniqueID"
|
||||
class="menu-button warn"
|
||||
@click="leaveServer(serverData.server_id)"
|
||||
>
|
||||
Leave Server
|
||||
</div>
|
||||
<div
|
||||
v-if="serverData.creator.uniqueID === user.uniqueID"
|
||||
class="menu-button"
|
||||
@click="showSettings()"
|
||||
>
|
||||
Server Settings
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="container">
|
||||
<channels-list
|
||||
v-if="openChannel"
|
||||
:server-i-d="serverData.server_id"
|
||||
/>
|
||||
</div>
|
||||
<div class="server" :class="{selected: selectedServerID === serverData.server_id, notifyAnimation: notification }" @contextmenu.prevent="contextEvent" @mouseenter="hoverEvent" @mouseover="hover = true" @mouseleave="hover = false">
|
||||
<profile-picture size="45px" :url="`${avatarDomain}/${serverData.avatar}${hover ? '' : '?type=png'}`" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {bus} from "../../../main.js"
|
||||
import config from "@/config.js";
|
||||
import ChannelsList from "@/components/app/ServerTemplate/ChannelsList.vue";
|
||||
import ProfilePicture from "@/components/ProfilePictureTemplate.vue";
|
||||
import ServerService from "@/services/ServerService";
|
||||
import smoothReflow from "vue-smooth-reflow";
|
||||
|
||||
export default {
|
||||
components: { ProfilePicture, ChannelsList },
|
||||
mixins: [smoothReflow],
|
||||
props: ["serverData", "openChannel", "mode"],
|
||||
components: { ProfilePicture },
|
||||
props: ["serverData"],
|
||||
data() {
|
||||
return {
|
||||
showContextMenu: false,
|
||||
showChannels: false,
|
||||
avatarDomain: config.domain + "/avatars"
|
||||
avatarDomain: config.domain + "/avatars",
|
||||
hover: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.user;
|
||||
},
|
||||
selectedServerID() {
|
||||
return this.$store.getters["servers/selectedServerID"];
|
||||
},
|
||||
notification() {
|
||||
const notifications = this.$store.getters.notifications;
|
||||
const channels = this.$store.getters.channels
|
||||
const channels = this.$store.getters.channels;
|
||||
const notification = notifications.find(e => {
|
||||
return channels[e.channelID] && channels[e.channelID].server_id && this.serverData && channels[e.channelID].server_id === this.serverData.server_id
|
||||
})
|
||||
return (
|
||||
channels[e.channelID] &&
|
||||
channels[e.channelID].server_id &&
|
||||
this.serverData &&
|
||||
channels[e.channelID].server_id === this.serverData.server_id
|
||||
);
|
||||
});
|
||||
return notification;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$smoothReflow({
|
||||
el: this.$refs.container
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
showSettings() {
|
||||
this.showContextMenu = false;
|
||||
this.$store.dispatch('setServerSettings', {serverID: this.serverData.server_id})
|
||||
hoverEvent(event) {
|
||||
const rect = event.target.getBoundingClientRect();
|
||||
//let centerX = targetNode.offsetLeft + targetNode.offsetWidth / 2;
|
||||
//let centerY = targetNode.offsetTop + targetNode.offsetHeight / 2;
|
||||
bus.$emit('server-tool-tip', {serverID: this.serverData.server_id, top: rect.top})
|
||||
},
|
||||
createInvite(serverID) {
|
||||
this.showContextMenu = false;
|
||||
this.$store.dispatch("setServerIDContextMenu", serverID);
|
||||
this.$store.dispatch("setPopoutVisibility", {
|
||||
name: "showServerInviteMenu",
|
||||
visibility: true
|
||||
});
|
||||
},
|
||||
closeContextMenu(event) {
|
||||
if (
|
||||
event.target.closest(".options-context-button") ===
|
||||
this.$refs.contextMenuButton
|
||||
)
|
||||
return;
|
||||
this.showContextMenu = false;
|
||||
},
|
||||
async leaveServer(serverID) {
|
||||
this.showContextMenu = false;
|
||||
const {ok, error, result} = await ServerService.leaveServer(serverID);
|
||||
contextEvent(event) {
|
||||
this.$store.dispatch('setAllPopout', {
|
||||
show: true,
|
||||
type: 'SERVER',
|
||||
serverID: this.serverData.server_id,
|
||||
creatorUniqueID: this.serverData.creator.uniqueID,
|
||||
x: event.clientX,
|
||||
y: event.clientY
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
|
||||
.server {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-self: center;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
transition: 0.2s;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
&.selected {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.notifyAnimation:before{
|
||||
content: '';
|
||||
.notifyAnimation:before {
|
||||
content: "!";
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 115651;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
animation: notifyAnime;
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: forwards;
|
||||
border-radius: 5px;
|
||||
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 23, 23, 0.753);
|
||||
}
|
||||
@keyframes notifyAnime {
|
||||
0%{
|
||||
background: rgba(255, 0, 0, 0.198);
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
40%{
|
||||
background: rgba(255, 0, 0, 0.411);
|
||||
40% {
|
||||
opacity: 0.9;
|
||||
}
|
||||
60%{
|
||||
background: rgba(255, 0, 0, 0.411);
|
||||
60% {
|
||||
opacity: 1;
|
||||
}
|
||||
100%{
|
||||
background: rgba(255, 0, 0, 0.198);
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.server {
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: rgba(0, 0, 0, 0.137);
|
||||
border-radius: 5px;
|
||||
margin: 5px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.server:hover {
|
||||
background: rgba(0, 0, 0, 0.288);
|
||||
}
|
||||
.material-icons {
|
||||
transition: 0.3s;
|
||||
}
|
||||
.add-server:hover .material-icons {
|
||||
color: rgba(20, 255, 39, 0.726);
|
||||
}
|
||||
|
||||
.small-view {
|
||||
padding-right: 0;
|
||||
display: flex;
|
||||
transition: 0.3s;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.server-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-left: 5px;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.add-icon {
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.add-icon .material-icons {
|
||||
font-size: 40px;
|
||||
}
|
||||
.options-context-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 5px;
|
||||
color: rgba(255, 255, 255, 0.623);
|
||||
border-radius: 50%;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.options-context-button .material-icons:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.options-context-menu {
|
||||
position: absolute;
|
||||
background: rgba(0, 0, 0, 0.692);
|
||||
border-radius: 10px;
|
||||
z-index: 9999;
|
||||
padding: 5px;
|
||||
right: 40px;
|
||||
top: 20px;
|
||||
}
|
||||
.menu-button {
|
||||
padding: 5px;
|
||||
margin: 2px;
|
||||
border-radius: 5px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.menu-button:hover {
|
||||
background: rgb(47, 47, 47);
|
||||
}
|
||||
|
||||
.warn {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -67,10 +67,10 @@ export default {
|
|||
@media (max-width: 600px) {
|
||||
.left-panel {
|
||||
position: absolute;
|
||||
background-color: rgba(39, 39, 39, 0.97);
|
||||
bottom: 0;
|
||||
height: calc(100% - 50px);
|
||||
z-index: 2;
|
||||
backdrop-filter: blur(15px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,74 +1,80 @@
|
|||
<template>
|
||||
<div class="explore-tab">
|
||||
|
||||
<transition name="slidein">
|
||||
<div class="left-panel"
|
||||
<div
|
||||
class="left-panel"
|
||||
v-show="$mq === 'mobile' && showLeftPanel || ($mq !== 'mobile')"
|
||||
v-click-outside="hideLeftPanel">
|
||||
<div class="header">
|
||||
<div class="icon">
|
||||
<i class="material-icons">explore</i>
|
||||
</div>
|
||||
<div class="details">
|
||||
<div class="title">Explore</div>
|
||||
<div class="description">Find new servers, Emojis and more!</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="items">
|
||||
<div class="item halloween"
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
:class="{selected: selectedTab === index}"
|
||||
@click="selectedTab = index">
|
||||
<i class="material-icons">{{tab.icon}}</i>
|
||||
{{tab.name}}
|
||||
v-click-outside="hideLeftPanel"
|
||||
>
|
||||
<navigation />
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
<div class="icon">
|
||||
<i class="material-icons">explore</i>
|
||||
</div>
|
||||
<div class="details">
|
||||
<div class="title">Explore</div>
|
||||
<div class="description">Find new servers, Emojis and more!</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="items">
|
||||
<div
|
||||
class="item"
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
:class="{selected: selectedTab === index}"
|
||||
@click="selectedTab = index"
|
||||
>
|
||||
<i class="material-icons">{{tab.icon}}</i>
|
||||
{{tab.name}}
|
||||
</div>
|
||||
<div class="card self-promo" v-if="nertiviaServerHide !== true && !nertiviaServer">
|
||||
<div class="material-icons close-btn" @click="hideSelfPromo">close</div>
|
||||
<div class="logo" />
|
||||
<div class="title">Join the official Nertivia server</div>
|
||||
<div class="button" @click="joinNertiviaServer">Join</div>
|
||||
</div>
|
||||
|
||||
<div class="card halloween" v-if="halloween">
|
||||
<div class="pumpkin">🎃</div>
|
||||
<div class="title">Happy Halloween!</div>
|
||||
</div>
|
||||
|
||||
<div class="card self-promo" v-if="nertiviaServerHide !== true && !nertiviaServer">
|
||||
<div class="material-icons close-btn" @click="hideSelfPromo">close</div>
|
||||
<div class="logo"/>
|
||||
<div class="title">Join the official Nertivia server</div>
|
||||
<div class="button" @click="joinNertiviaServer">Join</div>
|
||||
</div>
|
||||
|
||||
<div class="card donate-paypal" v-if="donateHide !== true">
|
||||
<div class="material-icons close-btn" @click="hideDonatePaypal">close</div>
|
||||
<div class="material-icons heart">favorite</div>
|
||||
<div class="title">Support Nertivia by donating any amount of money. You will get a supporter badge and more features in the future.</div>
|
||||
<div class="button" @click="donateButton">Donate With PayPal</div>
|
||||
<div class="card donate-paypal" v-if="donateHide !== true">
|
||||
<div class="material-icons close-btn" @click="hideDonatePaypal">close</div>
|
||||
<div class="material-icons heart">favorite</div>
|
||||
<div
|
||||
class="title"
|
||||
>Support Nertivia by donating any amount of money. You will get a supporter badge and more features in the future.</div>
|
||||
<div class="button" @click="donateButton">Donate</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="right-panel">
|
||||
<div class="header"> <div class="material-icons left-menu-show-button" @click="showLeftPanel = !showLeftPanel">view_list</div> <i class="material-icons">{{tabs[selectedTab].icon}}</i>{{tabs[selectedTab].name}}</div>
|
||||
<div class="header">
|
||||
<div
|
||||
class="material-icons left-menu-show-button"
|
||||
@click="showLeftPanel = !showLeftPanel"
|
||||
>view_list</div>
|
||||
<i class="material-icons">{{tabs[selectedTab].icon}}</i>
|
||||
{{tabs[selectedTab].name}}
|
||||
</div>
|
||||
<div class="coming-soon" v-if="selectedTab > 0">
|
||||
<div class="icon">
|
||||
<i class="material-icons">explore</i>
|
||||
</div>
|
||||
<div class="text">Coming soon!</div>
|
||||
</div>
|
||||
<component :is="tabs[selectedTab].component" />
|
||||
<component :is="tabs[selectedTab].component" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { bus } from "@/main";
|
||||
import Servers from './Explore/servers'
|
||||
import ServerService from '@/services/ServerService'
|
||||
import Servers from "./Explore/servers";
|
||||
import ServerService from "@/services/ServerService";
|
||||
import Navigation from '@/components/app/Navigation';
|
||||
export default {
|
||||
components: { Servers },
|
||||
components: { Servers, Navigation },
|
||||
data() {
|
||||
return {
|
||||
showLeftPanel: false,
|
||||
|
|
@ -80,11 +86,10 @@ export default {
|
|||
{ icon: "brush", name: "Themes", component: "" },
|
||||
{ icon: "message", name: "Message Styles", component: "" }
|
||||
],
|
||||
nertiviaServerID: '6572915451527958528',
|
||||
nertiviaServerHide: localStorage.getItem('exploreTabNertiviaServerPromoHide') === 'true',
|
||||
donateHide: localStorage.getItem('exploreTabDonateHide') === 'true',
|
||||
halloween: new Date().getDate() === 31
|
||||
|
||||
nertiviaServerID: "6572915451527958528",
|
||||
nertiviaServerHide:
|
||||
localStorage.getItem("exploreTabNertiviaServerPromoHide") === "true",
|
||||
donateHide: localStorage.getItem("exploreTabDonateHide") === "true",
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -97,27 +102,29 @@ export default {
|
|||
}
|
||||
},
|
||||
hideSelfPromo() {
|
||||
localStorage.setItem('exploreTabNertiviaServerPromoHide', true)
|
||||
localStorage.setItem("exploreTabNertiviaServerPromoHide", true);
|
||||
this.nertiviaServerHide = true;
|
||||
},
|
||||
hideDonatePaypal() {
|
||||
localStorage.setItem('exploreTabDonateHide', true)
|
||||
localStorage.setItem("exploreTabDonateHide", true);
|
||||
this.donateHide = true;
|
||||
},
|
||||
async joinNertiviaServer() {
|
||||
const {ok, error, result} = await ServerService.joinServerById(this.nertiviaServerID, {
|
||||
socketID: this.$socket.id
|
||||
});
|
||||
const { ok, error, result } = await ServerService.joinServerById(
|
||||
this.nertiviaServerID,
|
||||
{
|
||||
socketID: this.$socket.id
|
||||
}
|
||||
);
|
||||
},
|
||||
donateButton() {
|
||||
window.open('https://www.patreon.com/nertivia', '_blank');
|
||||
window.open("https://www.patreon.com/nertivia", "_blank");
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
mounted() {},
|
||||
computed: {
|
||||
nertiviaServer() {
|
||||
return this.$store.getters['servers/servers'][this.nertiviaServerID]
|
||||
return this.$store.getters["servers/servers"][this.nertiviaServerID];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -125,26 +132,32 @@ export default {
|
|||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.explore-tab {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.459);
|
||||
color: white;
|
||||
position: relative;
|
||||
}
|
||||
.left-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(0, 0, 0, 0.274);
|
||||
flex-direction: row;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
width: 300px;
|
||||
flex-shrink: 0;
|
||||
z-index: 2;
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
.items {
|
||||
user-select: none;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
}
|
||||
.item {
|
||||
.material-icons {
|
||||
margin-right: 5px;
|
||||
|
|
@ -152,22 +165,16 @@ export default {
|
|||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.199);
|
||||
margin: 5px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.452);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
&.selected {
|
||||
background: rgba(0, 0, 0, 0.452);
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
.item.halloween.selected {
|
||||
background: rgba(255, 166, 0, 0.692);
|
||||
}
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
|
|
@ -182,19 +189,19 @@ export default {
|
|||
align-content: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 100px;
|
||||
width: 70px;
|
||||
position: relative;
|
||||
.material-icons {
|
||||
font-size: 70px;
|
||||
font-size: 50px;
|
||||
}
|
||||
&::after{
|
||||
content: 'BETA';
|
||||
&::after {
|
||||
content: "BETA";
|
||||
position: absolute;
|
||||
background: #ff3333;
|
||||
border-radius: 5px;
|
||||
font-size: 10px;
|
||||
font-size: 9px;
|
||||
padding: 2px;
|
||||
bottom: 15px;
|
||||
bottom: 20px;
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
|
|
@ -228,28 +235,28 @@ export default {
|
|||
.title {
|
||||
text-align: center;
|
||||
}
|
||||
.button {
|
||||
background-color: rgba(0, 0, 0, 0.200);
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
font-size: 20px;
|
||||
margin-top: 15px;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
color: rgba(255, 255, 255, 0.924);
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.300);
|
||||
}
|
||||
.button {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
font-size: 15px;
|
||||
margin-top: 15px;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
color: rgba(255, 255, 255, 0.924);
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
&.self-promo{
|
||||
&.self-promo {
|
||||
.logo {
|
||||
background-image: url('../../../assets/logo.png');
|
||||
background-image: url("../../../assets/logo.png");
|
||||
background-size: cover;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
|
|
@ -269,15 +276,6 @@ export default {
|
|||
font-size: 60px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
&.halloween {
|
||||
.pumpkin {
|
||||
font-size: 50px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
background: orange;
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
.coming-soon {
|
||||
|
|
@ -295,13 +293,16 @@ export default {
|
|||
}
|
||||
|
||||
.right-panel {
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
.header {
|
||||
background: rgba(0, 0, 0, 0.448);
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
.material-icons {margin-right: 5px;}
|
||||
.material-icons {
|
||||
margin-right: 5px;
|
||||
}
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
|
@ -312,7 +313,6 @@ export default {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
.left-menu-show-button {
|
||||
border-right: solid 1px rgb(158, 158, 158);
|
||||
padding-right: 5px;
|
||||
|
|
@ -320,14 +320,13 @@ export default {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.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(-300px)
|
||||
transform: translateX(-300px);
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
|
|
@ -336,11 +335,10 @@ export default {
|
|||
}
|
||||
.left-panel {
|
||||
position: absolute;
|
||||
background-color: rgba(39, 39, 39, 0.97);
|
||||
bottom: 0;
|
||||
height: calc(100% - 44px);
|
||||
backdrop-filter: blur(15px);
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<div class="title">Filter:</div>
|
||||
<div class="filter-item">
|
||||
<div
|
||||
class="item halloween"
|
||||
class="item"
|
||||
v-for="(filter, index) in filters"
|
||||
:class="{selected: filterSelected === index}"
|
||||
:key="filter.name"
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
<div class="title">Sort By:</div>
|
||||
<div class="filter-item">
|
||||
<div
|
||||
class="item halloween"
|
||||
class="item"
|
||||
v-for="(sortBy, index) in sortBys"
|
||||
:class="{selected: sortSelected === index}"
|
||||
:key="sortBy.name"
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-area">
|
||||
<div class="search-area" v-if="false">
|
||||
<input type="text" :placeholder="`Search for ${name}`" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -136,9 +136,6 @@ input {
|
|||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
.item.halloween.selected {
|
||||
color: orange;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<div class="item">
|
||||
<div class="top">
|
||||
<div class="background-dark"></div>
|
||||
<profile-picture
|
||||
size="90px"
|
||||
:url="`${avatarDomain}/${server.server.avatar}`"
|
||||
|
|
@ -8,7 +9,7 @@
|
|||
<div class="name">
|
||||
<div class="name-container">
|
||||
<span class="inner-name">{{server.server.name}}</span>
|
||||
<span class="material-icons halloween-icons" v-if="server.verified">check</span>
|
||||
<span class="material-icons" v-if="server.verified">check</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -16,7 +17,7 @@
|
|||
<div class="description">{{server.description}}</div>
|
||||
<div class="buttons">
|
||||
<div class="member-count"><div class="material-icons">account_box</div>{{server.total_members}}</div>
|
||||
<div class="button halloween-button" :class="{selected: joined}" @click="joinButton">
|
||||
<div class="button" :class="{selected: joined}" @click="joinButton">
|
||||
<span v-if="joined">Joined</span>
|
||||
<spinner v-else-if="joinClicked" :size="30"/>
|
||||
<span v-else-if="!joinClicked">Join Server</span>
|
||||
|
|
@ -70,6 +71,7 @@ export default {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.item {
|
||||
position: relative;
|
||||
width: 250px;
|
||||
height: 300px;
|
||||
background: rgba(0, 0, 0, 0.479);
|
||||
|
|
@ -94,6 +96,18 @@ export default {
|
|||
align-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
.background-dark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
background-image: url('../../../../assets/background.jpg');
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
.avatar {
|
||||
background: rgb(26, 133, 255);
|
||||
height: 80px;
|
||||
|
|
@ -110,6 +124,7 @@ export default {
|
|||
width: 100%;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
z-index: 999;
|
||||
.name-container {
|
||||
display: flex;
|
||||
margin: auto;
|
||||
|
|
@ -123,18 +138,16 @@ export default {
|
|||
color: #66e0ff;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.halloween-icons {
|
||||
color: orange;
|
||||
}
|
||||
}
|
||||
}
|
||||
.bottom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(0, 0, 0, 0.194);
|
||||
background: rgba(0, 0, 0, 0.541);
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
|
||||
.description {
|
||||
margin: 10px;
|
||||
flex: 1;
|
||||
|
|
@ -146,6 +159,7 @@ export default {
|
|||
overflow-wrap: anywhere;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
|
@ -187,15 +201,7 @@ export default {
|
|||
background: grey;
|
||||
}
|
||||
}
|
||||
.halloween-button {
|
||||
background: rgba(255, 166, 0, 0.8);
|
||||
&:hover {
|
||||
background: rgb(255, 166, 0);
|
||||
}
|
||||
&.selected {
|
||||
background: grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -1,62 +1,42 @@
|
|||
<template>
|
||||
<div class="news">
|
||||
<div class="change-log">
|
||||
<span class="news-title">Changes in this release</span>
|
||||
<navigation />
|
||||
<div class="changelog">
|
||||
<div class="change-log">
|
||||
<span class="news-title">Changes in this release</span>
|
||||
|
||||
<div
|
||||
v-for="(change, index) in changelog"
|
||||
:key="index"
|
||||
class="change"
|
||||
>
|
||||
<div
|
||||
class="heading"
|
||||
:style="change.headColor ? `background-color: ${change.headColor}` : ``"
|
||||
>
|
||||
<div class="date">
|
||||
{{ change.date }}
|
||||
</div>
|
||||
<div class="changes-title">
|
||||
{{ change.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="information">
|
||||
<div v-if="change.new">
|
||||
<strong>What's new?</strong>
|
||||
<br>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(wnew, index) in change.new"
|
||||
:key="index"
|
||||
v-html="wnew"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="change.fix">
|
||||
<strong>Issues fixed</strong>
|
||||
<br>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(wfix, index) in change.fix"
|
||||
:key="index"
|
||||
v-html="wfix"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="change.next">
|
||||
<strong>Up next</strong>
|
||||
<br>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(wnext, index) in change.next"
|
||||
:key="index"
|
||||
v-html="wnext"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-for="(change, index) in changelog" :key="index" class="change">
|
||||
<div
|
||||
v-if="change.msg"
|
||||
v-html="change.msg"
|
||||
/>
|
||||
class="heading"
|
||||
:style="change.headColor ? `background-color: ${change.headColor}` : ``"
|
||||
>
|
||||
<div class="date">{{ change.date }}</div>
|
||||
<div class="changes-title">{{ change.title }}</div>
|
||||
</div>
|
||||
<div class="information">
|
||||
<div v-if="change.new">
|
||||
<strong>What's new?</strong>
|
||||
<br />
|
||||
<ul>
|
||||
<li v-for="(wnew, index) in change.new" :key="index" v-html="wnew" />
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="change.fix">
|
||||
<strong>Issues fixed</strong>
|
||||
<br />
|
||||
<ul>
|
||||
<li v-for="(wfix, index) in change.fix" :key="index" v-html="wfix" />
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="change.next">
|
||||
<strong>Up next</strong>
|
||||
<br />
|
||||
<ul>
|
||||
<li v-for="(wnext, index) in change.next" :key="index" v-html="wnext" />
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="change.msg" v-html="change.msg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -65,12 +45,13 @@
|
|||
|
||||
<script>
|
||||
import Spinner from "@/components/Spinner.vue";
|
||||
import Navigation from "@/components/app/Navigation";
|
||||
import changelog from "@/utils/changelog.js";
|
||||
export default {
|
||||
components: {},
|
||||
components: { Navigation },
|
||||
data() {
|
||||
return {
|
||||
changelog: changelog
|
||||
changelog
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -84,16 +65,19 @@ export default {
|
|||
height: 100%;
|
||||
color: white;
|
||||
overflow: auto;
|
||||
background: rgba(0, 0, 0, 0.486);
|
||||
background: #0000005d;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.news-title {
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
font-size: 20px;
|
||||
margin-left: 10px;
|
||||
margin-top: 20px;
|
||||
font-size: 21px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: solid 1px white;
|
||||
}
|
||||
.todo-list {
|
||||
flex: 1;
|
||||
|
|
@ -102,29 +86,35 @@ export default {
|
|||
padding: 20px;
|
||||
}
|
||||
.change {
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: solid 1px white;
|
||||
}
|
||||
.heading{
|
||||
.heading {
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.555);
|
||||
margin-bottom: 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.information {
|
||||
overflow-wrap: break-word;
|
||||
margin: 10px;
|
||||
}
|
||||
.heading.latest {
|
||||
background: rgba(38, 139, 255, 0.87);
|
||||
}
|
||||
.change-log {
|
||||
background: rgba(0, 0, 0, 0.137);
|
||||
padding: 20px;
|
||||
background: rgba(0, 0, 0, 0.561);
|
||||
overflow-y: auto;
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
}
|
||||
.changelog {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
overflow: auto;
|
||||
|
||||
}
|
||||
.plan-list {
|
||||
color: white;
|
||||
}
|
||||
|
|
@ -142,5 +132,4 @@ export default {
|
|||
margin-top: -5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -103,21 +103,21 @@ export default {
|
|||
@media (max-width: 949px) {
|
||||
.members-panel {
|
||||
position: absolute;
|
||||
background-color: rgba(39, 39, 39, 0.97);
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: calc(100% - 50px);
|
||||
z-index: 1;
|
||||
backdrop-filter: blur(15px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.left-panel {
|
||||
position: absolute;
|
||||
background-color: rgba(39, 39, 39, 0.97);
|
||||
bottom: 0;
|
||||
height: calc(100% - 50px);
|
||||
z-index: 2;
|
||||
backdrop-filter: blur(15px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,205 +0,0 @@
|
|||
<template>
|
||||
<div class="add-friend-panel">
|
||||
<div
|
||||
class="panel-title"
|
||||
@click="expanded = !expanded"
|
||||
>
|
||||
<div>
|
||||
<i
|
||||
v-if="!expanded"
|
||||
class="material-icons"
|
||||
>
|
||||
person_add
|
||||
</i>
|
||||
<span>{{ expanded ? "Hide" : "Add friend" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<transition
|
||||
name="slide"
|
||||
appear
|
||||
>
|
||||
<div
|
||||
v-if="expanded"
|
||||
class="add-friend"
|
||||
>
|
||||
<div class="title">
|
||||
Add friend
|
||||
</div>
|
||||
<div class="info">
|
||||
Type in your friends username and tag. eg: someone@jt4g
|
||||
</div>
|
||||
<div class="infoC">
|
||||
Creators tag: Fishie@azK0
|
||||
</div>
|
||||
<form
|
||||
action="#"
|
||||
@submit.prevent="addFriend"
|
||||
>
|
||||
<input
|
||||
v-model="input"
|
||||
type="text"
|
||||
placeholder="username@tag"
|
||||
>
|
||||
<loadingButton
|
||||
:loading="currentButtonMessage == 1"
|
||||
:message="buttonMessages[currentButtonMessage]"
|
||||
/>
|
||||
</form>
|
||||
<div :class="{message: true, warning: errorMessage.isError}">
|
||||
{{ errorMessage.message }}
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RelationshipService from '@/services/RelationshipService.js'
|
||||
import loadingButton from './../../Button.vue'
|
||||
export default {
|
||||
components: {
|
||||
loadingButton
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expanded: false,
|
||||
buttonMessages: [
|
||||
"Add Friend",
|
||||
"Adding..."
|
||||
],
|
||||
currentButtonMessage: 0,
|
||||
input: "",
|
||||
errorMessage:{
|
||||
message: "",
|
||||
isError: false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async addFriend() {
|
||||
this.$set(this.errorMessage, 'message', "")
|
||||
|
||||
this.currentButtonMessage = 1;
|
||||
const split = this.input.trim().split("@");
|
||||
|
||||
// validation
|
||||
if ( split.length <2 || split.length >2 || split[1] === "" || split[1].length !== 4){
|
||||
this.$set(this.errorMessage, 'message', "Invalid username or tag.")
|
||||
this.$set(this.errorMessage, 'isError', true)
|
||||
this.currentButtonMessage = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const username = split[0];
|
||||
const tag = split[1];
|
||||
|
||||
const {ok, error, result} = await RelationshipService.post({username, tag})
|
||||
this.currentButtonMessage = 0;
|
||||
if ( ok ) {
|
||||
this.$set(this.errorMessage, 'message', result.data.message)
|
||||
this.$set(this.errorMessage, 'isError', false)
|
||||
} else {
|
||||
if (error.response === undefined) {
|
||||
this.$set(this.errorMessage, 'message', "Can't connect to server.")
|
||||
this.$set(this.errorMessage, 'isError', true)
|
||||
return
|
||||
}
|
||||
this.$set(this.errorMessage, 'message', error.response.data.errors[0].msg)
|
||||
this.$set(this.errorMessage, 'isError', true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.slide-enter-active, .slide-leave-active {
|
||||
transition: .3s;
|
||||
}
|
||||
.slide-enter, .slide-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
margin-bottom: -250px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.add-friend-panel{
|
||||
width: 100%;
|
||||
background: rgba(0, 0, 0, 0.123);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.add-friend{
|
||||
background: rgba(0, 0, 0, 0);
|
||||
flex: 1;
|
||||
height: 230px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
}
|
||||
.title{
|
||||
margin: auto;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 0px;
|
||||
font-size: 20px;
|
||||
color: white;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
.info{
|
||||
text-align: center;
|
||||
color: rgb(182, 182, 182);
|
||||
font-size: 15px;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
.infoC{
|
||||
text-align: center;
|
||||
color: rgb(255, 79, 79);
|
||||
font-size: 15px;
|
||||
}
|
||||
form {
|
||||
margin: auto;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.message{
|
||||
margin: auto;
|
||||
margin-top: 2px;
|
||||
font-size: 15px;
|
||||
color: green;
|
||||
}
|
||||
.warning {
|
||||
color: red;
|
||||
}
|
||||
.panel-title{
|
||||
background: rgba(0, 0, 0, 0.274);
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
transition: 0.3s;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.panel-title:hover{
|
||||
background: rgba(0, 0, 0, 0.445);
|
||||
}
|
||||
|
||||
.panel-title .material-icons{
|
||||
vertical-align: top;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.panel-title span{
|
||||
margin-left: 5px;
|
||||
}
|
||||
.panel-title div{
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
<template>
|
||||
<div
|
||||
:class="{friend: true, notifyAnimation: (notifications && notifications > 0) }"
|
||||
class="friend"
|
||||
:style="{background: status.status !== 0 ? status.bgColor : ''}"
|
||||
:class="{selected: uniqueIDSelected, notifyAnimation: (notifications && notifications > 0) }"
|
||||
@click="openChat"
|
||||
@mouseover="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
|
|
@ -80,6 +81,9 @@ export default {
|
|||
statusColor: statuses[parseInt(status)].color,
|
||||
bgColor: statuses[parseInt(status)].bgColor
|
||||
}
|
||||
},
|
||||
uniqueIDSelected() {
|
||||
return this.$store.getters.selectedUserUniqueID === this.recipient.uniqueID
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -108,20 +112,18 @@ export default {
|
|||
|
||||
<style scoped>
|
||||
.username{
|
||||
width: 201px;
|
||||
width: 150px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.friend {
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 0.100);
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
padding-right: 0;
|
||||
padding-left: 10px;
|
||||
padding-left: 10px;
|
||||
display: flex;
|
||||
transition: 0.3s;
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
|
@ -151,9 +153,11 @@ export default {
|
|||
}
|
||||
|
||||
.friend:hover {
|
||||
background-color: rgba(0, 0, 0, 0.246);
|
||||
background: rgba(0, 0, 0, 0.3) !important;
|
||||
}
|
||||
.friend.selected {
|
||||
background: rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
.profile-picture{
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
|
|
|
|||
|
|
@ -63,14 +63,12 @@ export default {
|
|||
|
||||
.friends{
|
||||
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{
|
||||
|
|
|
|||
|
|
@ -79,14 +79,11 @@ export default {
|
|||
|
||||
.friends{
|
||||
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{
|
||||
|
|
|
|||
|
|
@ -84,14 +84,12 @@ export default {
|
|||
|
||||
.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{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export default {
|
|||
.tab{
|
||||
display: flex;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tab-name {
|
||||
padding-top: 3px;
|
||||
|
|
|
|||
|
|
@ -67,17 +67,16 @@ export default {
|
|||
|
||||
.status-popout{
|
||||
position: absolute;
|
||||
background-color: rgba(26, 26, 26, 0.863);
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
bottom: 55px;
|
||||
left: 30px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(5px);
|
||||
width: 180px;
|
||||
z-index: 4;
|
||||
}
|
||||
.status-list {
|
||||
padding: 5px;
|
||||
padding: 10px;
|
||||
transition: 0.3s;
|
||||
border-radius: 5px;
|
||||
margin: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
|
|
@ -86,7 +85,7 @@ export default {
|
|||
}
|
||||
|
||||
.status-list:hover {
|
||||
background: rgba(0, 0, 0, 0.349);
|
||||
background: rgba(46, 46, 46, 0.651);
|
||||
}
|
||||
|
||||
.status-icon{
|
||||
|
|
|
|||
|
|
@ -32,16 +32,22 @@ export const store = new Vuex.Store({
|
|||
members: membersModule
|
||||
},
|
||||
state: {
|
||||
|
||||
currentTab: 0,
|
||||
},
|
||||
getters: {
|
||||
|
||||
},
|
||||
mutations: {
|
||||
|
||||
|
||||
currentTab(state) {
|
||||
return state.currentTab;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
||||
}
|
||||
setCurrentTab({commit}, currentTab) {
|
||||
localStorage.setItem("currentTab", currentTab);
|
||||
commit('SET_CURRENT_TAB', currentTab)
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
SET_CURRENT_TAB(state, currentTab) {
|
||||
state.currentTab = currentTab;
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
@ -4,6 +4,7 @@ import Vue from "vue";
|
|||
|
||||
const state = {
|
||||
selectedChannelID: null,
|
||||
selectedUserUniqueID: null,
|
||||
DMChannelID: null,
|
||||
serverChannelID: null,
|
||||
channelName: null,
|
||||
|
|
@ -14,6 +15,9 @@ const getters = {
|
|||
selectedChannelID(state) {
|
||||
return state.selectedChannelID;
|
||||
},
|
||||
selectedUserUniqueID(state) {
|
||||
return state.selectedUserUniqueID;
|
||||
},
|
||||
channels(state) {
|
||||
return state.channels;
|
||||
},
|
||||
|
|
@ -43,6 +47,9 @@ const actions = {
|
|||
selectedChannelID(context, channelID) {
|
||||
context.commit("selectedChannelID", channelID);
|
||||
},
|
||||
selectedUserUniqueID(context, uniqueID) {
|
||||
context.commit("selectedUserUniqueID", uniqueID);
|
||||
},
|
||||
setChannelName(context, name) {
|
||||
context.commit("setChannelName", name);
|
||||
},
|
||||
|
|
@ -74,6 +81,9 @@ const mutations = {
|
|||
selectedChannelID(state, channelID) {
|
||||
state.selectedChannelID = channelID;
|
||||
},
|
||||
selectedUserUniqueID(state, uniqueID) {
|
||||
state.selectedUserUniqueID = uniqueID;
|
||||
},
|
||||
setDMChannelID(state, channelID) {
|
||||
state.DMChannelID = channelID;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ const getters = {
|
|||
const actions = {
|
||||
// server channel
|
||||
async openChannel(context, channel) {
|
||||
context.commit("selectedChannelID", channel.channelID)
|
||||
context.commit("setChannelName", channel.name);
|
||||
const messages = context.state.messages[channel.channelID];
|
||||
if (messages) {
|
||||
|
|
@ -32,14 +33,12 @@ const actions = {
|
|||
context.commit("setServerChannelID", channel.channelID);
|
||||
return;
|
||||
}
|
||||
context.commit("selectedChannelID", "loading")
|
||||
getMessages(context, channel.channelID, true);
|
||||
|
||||
},
|
||||
//dm channel
|
||||
async openChat(context, { uniqueID, channelID, channelName }) {
|
||||
if (channelName) context.commit("setChannelName", channelName);
|
||||
|
||||
const channels = context.rootState.channelModule.channels;
|
||||
channelID = Object.keys(channels).find(_channelID => {
|
||||
return channels[_channelID].recipients && channels[_channelID].recipients.length && channels[_channelID].recipients[0].uniqueID === uniqueID
|
||||
|
|
@ -47,6 +46,7 @@ const actions = {
|
|||
const messages = context.state.messages[channelID];
|
||||
const channel = channels[channelID];
|
||||
|
||||
context.commit('selectedUserUniqueID', uniqueID);
|
||||
if (channelID) {
|
||||
context.commit("setDMChannelID", channelID);
|
||||
context.commit("selectedChannelID", channelID);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,18 @@ const state = {
|
|||
uniqueID: null,
|
||||
x: null,
|
||||
y: null
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: convert all above into one.
|
||||
allPopout: {
|
||||
show: false,
|
||||
type: null,
|
||||
serverID: null,
|
||||
uniqueID: null,
|
||||
creatorUniqueID: null,
|
||||
x: null,
|
||||
y: null,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -54,6 +65,9 @@ const getters = {
|
|||
}
|
||||
|
||||
const actions = {
|
||||
setAllPopout({commit, state}, data) {
|
||||
commit('setAllPopout', {...state.allPopout, ...data})
|
||||
},
|
||||
setServerSettings({commit}, {serverID, index}){
|
||||
commit('setServerSettings', {serverID, index})
|
||||
},
|
||||
|
|
@ -87,6 +101,9 @@ const actions = {
|
|||
}
|
||||
|
||||
const mutations = {
|
||||
setAllPopout(state, data) {
|
||||
Vue.set(state, 'allPopout', data)
|
||||
},
|
||||
setServerMemberContext(state, data) {
|
||||
Vue.set(state, 'serverMemberContext', data);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ const actions = {
|
|||
},
|
||||
updateNotification(context, data) {
|
||||
let notificationSettings = JSON.parse(localStorage.getItem('notificationSettings')) || {};
|
||||
Object.assign(notificationSettings, data);
|
||||
localStorage.setItem("notificationSettings", JSON.stringify(notificationSettings));
|
||||
context.commit('updateNotification', notificationSettings)
|
||||
const assigned = Object.assign({}, notificationSettings, data);
|
||||
localStorage.setItem("notificationSettings", JSON.stringify(assigned));
|
||||
context.commit('updateNotification', assigned)
|
||||
},
|
||||
addRecentEmoji(context, shortcode) {
|
||||
const recentEmojis = JSON.parse(localStorage.getItem('recentEmojis')) || [];
|
||||
|
|
|
|||
|
|
@ -270,13 +270,18 @@ const actions = {
|
|||
if (!server.socketID) return;
|
||||
if (this._vm.$socket.id !== server.socketID) return;
|
||||
const defaultChannel = channels.find(c => c.channelID === server.default_channel_id)
|
||||
bus.$emit('changeTab', 2)
|
||||
context.dispatch('setCurrentTab', 2, {root: true})
|
||||
context.dispatch('servers/setSelectedServerID', server.server_id, {root: true})
|
||||
context.dispatch('openChannel', defaultChannel, {root: true})
|
||||
|
||||
|
||||
},
|
||||
['socket_server:leave'](context, {server_id}) {
|
||||
const lastSelectedChannel = JSON.parse(localStorage.getItem('selectedChannels') || '{}')
|
||||
if (lastSelectedChannel[server_id]) {
|
||||
delete lastSelectedChannel[server_id];
|
||||
localStorage.setItem('selectedChannels', JSON.stringify(lastSelectedChannel));
|
||||
}
|
||||
// check if server channel selected
|
||||
const serverChannelIDs = context.rootState.servers.channelsIDs[server_id];
|
||||
|
||||
|
|
|
|||
|
|
@ -14,13 +14,25 @@
|
|||
|
||||
const config = [
|
||||
|
||||
{
|
||||
version: 7.8,
|
||||
title: "Redesigns!",
|
||||
shortTitle: "",
|
||||
date: "25/10/2019",
|
||||
headColor: "rgba(25, 130, 255, 0.77)",
|
||||
new: [
|
||||
'Layout has been redesigned.',
|
||||
'Added an option to mute notification sounds.',
|
||||
'Last clicked channels should be remembered for each server.',
|
||||
'You can now delete Join, Left and Ban messages from the chat.',
|
||||
],
|
||||
next: ['Custom server banners.'],
|
||||
},
|
||||
{
|
||||
version: 7.6,
|
||||
title: "👻Spooky bug fixes👻",
|
||||
shortTitle: "",
|
||||
date: "16/10/2019",
|
||||
headColor: "rgb(255, 166, 0)", // halloween
|
||||
//headColor: "rgba(25, 130, 255, 0.77)",
|
||||
new: [
|
||||
'Better handled Google Drive linking.',
|
||||
'👻👻👻',
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ function replace_custom_emoji(state, silent) {
|
|||
// console.log(nameEnd, parseUntil(state,nameStart,58))
|
||||
|
||||
// parser failed to find another ':', so it's not a valid emoji
|
||||
if(nameEnd > max || nameEnd < 0 || nameEnd - nameStart <= 0) { return false; }
|
||||
if((nameEnd+1) > max || nameEnd < 0 || nameEnd - nameStart <= 0) { return false; }
|
||||
|
||||
let emojiName = state.src.slice(nameStart, nameEnd)
|
||||
|
||||
|
|
@ -79,7 +79,9 @@ function replace_custom_emoji(state, silent) {
|
|||
let idStart = pos
|
||||
let idEnd = skipUntil(state, pos, 62);
|
||||
|
||||
if(idEnd > max || idEnd < 0 || idEnd - idStart <= 1) { return false; }
|
||||
if((idEnd+1) > max || idEnd < 0 || idEnd - idStart <= 1) { return false; }
|
||||
|
||||
// console.log(idStart, idEnd)
|
||||
|
||||
let emojiID = state.src.slice(idStart, idEnd)
|
||||
|
||||
|
|
@ -88,7 +90,7 @@ function replace_custom_emoji(state, silent) {
|
|||
state.posMax = idEnd
|
||||
|
||||
let token = state.push('custom_emoji', 'img', 0);
|
||||
token.attrs = [[ 'src', `${config.domain}/media/${emojiID}` ], [ 'alt', emojiName ]]
|
||||
token.attrs = [[ 'src', (`${config.domain}/media/${emojiID}`) ], [ 'alt', (emojiName) ]]
|
||||
}
|
||||
|
||||
state.pos = idEnd + 1
|
||||
|
|
@ -96,14 +98,17 @@ function replace_custom_emoji(state, silent) {
|
|||
return true
|
||||
}
|
||||
|
||||
const emojiRe = /<:[\w\d]+:[\w\d]+>/
|
||||
|
||||
export default function custom_emoji_plugin(md, opts) {
|
||||
md.renderer.rules.custom_emoji = (tokens, idx) => {
|
||||
let token = tokens[idx]
|
||||
|
||||
const srcIdx = token.attrIndex('src');
|
||||
const altIdx = token.attrIndex('alt');
|
||||
|
||||
// todo: better escaping method,
|
||||
// even if this is good and covers most cases, there may be edge cases where DOMPurify may be better
|
||||
let src = md.utils.escapeHtml(token.attrs.find(([name]) => name === 'src')[1])
|
||||
let alt = md.utils.escapeHtml(token.attrs.find(([name]) => name === 'alt')[1])
|
||||
let src = encodeURI(md.utils.escapeHtml(token.attrs[srcIdx][1]))
|
||||
let alt = md.utils.escapeHtml(token.attrs[altIdx][1])
|
||||
|
||||
return `<${md.utils.escapeHtml(token.tag)} class="emoji" title=${alt} alt=${alt} src=${src} />`
|
||||
}
|
||||
|
|
|
|||
83
src/utils/markdown-it-plugins/normalizeFence.js
Normal file
83
src/utils/markdown-it-plugins/normalizeFence.js
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
function fence(state, startLine, endLine, silent) {
|
||||
var marker, len, params, nextLine, mem, token, markup,
|
||||
haveEndMarker = false,
|
||||
pos = state.bMarks[startLine] + state.tShift[startLine],
|
||||
max = state.eMarks[startLine];
|
||||
|
||||
// if it's indented more than 3 spaces, it should be a code block
|
||||
// if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }
|
||||
|
||||
if (pos + 3 > max) { return false; }
|
||||
|
||||
marker = state.src.charCodeAt(pos);
|
||||
|
||||
if (marker !== 0x60 /* ` */) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// scan marker length
|
||||
mem = pos;
|
||||
pos = state.skipChars(pos, marker);
|
||||
|
||||
len = pos - mem;
|
||||
|
||||
if (len < 3) { return false; }
|
||||
|
||||
markup = state.src.slice(mem, pos);
|
||||
params = state.src.slice(pos, max);
|
||||
|
||||
if (marker === 0x60 /* ` */) {
|
||||
if (params.indexOf(String.fromCharCode(marker)) >= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Since start is found, we can report success here in validation mode
|
||||
if (silent) { return true; }
|
||||
|
||||
// search end of block
|
||||
nextLine = startLine;
|
||||
|
||||
for (;;) {
|
||||
nextLine++;
|
||||
if (nextLine >= endLine) {
|
||||
return false
|
||||
}
|
||||
|
||||
pos = mem = state.bMarks[nextLine] + state.tShift[nextLine];
|
||||
max = state.eMarks[nextLine];
|
||||
|
||||
if (state.src.charCodeAt(pos) !== marker) { continue; }
|
||||
|
||||
pos = state.skipChars(pos, marker);
|
||||
|
||||
// closing code fence must be at least as long as the opening one
|
||||
if (pos - mem < len) { continue; }
|
||||
|
||||
// make sure tail has spaces only
|
||||
pos = state.skipSpaces(pos);
|
||||
|
||||
if (pos < max) { continue; }
|
||||
|
||||
haveEndMarker = true;
|
||||
// found!
|
||||
break;
|
||||
}
|
||||
|
||||
// If a fence has heading spaces, they should be removed from its inner block
|
||||
len = state.sCount[startLine];
|
||||
|
||||
state.line = nextLine + (haveEndMarker ? 1 : 0);
|
||||
|
||||
token = state.push('fence', 'code', 0);
|
||||
token.info = params;
|
||||
token.content = state.getLines(startLine + 1, nextLine, len, true);
|
||||
token.markup = markup;
|
||||
token.map = [ startLine, state.line ];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export default function normalizeFence(md, opts) {
|
||||
md.block.ruler.at('fence', fence)
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import config from "@/config.js";
|
|||
import customEmoji from './markdown-it-plugins/customEmoji'
|
||||
import formatLink from './markdown-it-plugins/formatLink'
|
||||
import formatCode from './markdown-it-plugins/formatCode'
|
||||
import normalizeFence from './markdown-it-plugins/normalizeFence'
|
||||
|
||||
import hljs from 'highlight.js'
|
||||
|
||||
|
|
@ -25,7 +26,9 @@ const markdown = new MarkdownIt({
|
|||
|
||||
return '<div class="codeblock"><code>' + markdown.utils.escapeHtml(str) + '</code></div>';
|
||||
}
|
||||
}).use(chatPlugin)
|
||||
})
|
||||
.use(normalizeFence)
|
||||
.use(chatPlugin)
|
||||
.use(customEmoji)
|
||||
.use(formatLink)
|
||||
.use(formatCode);
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ const newFriendAudio = new Audio(newFriendSound);
|
|||
|
||||
export default {
|
||||
notification: () => {
|
||||
if (isBusy()) return;
|
||||
if (isBusy() || isNotificationDisabled()) return;
|
||||
const audio = new Audio(notificationSound);
|
||||
audio.play();
|
||||
},
|
||||
newFriend: () => {
|
||||
if (isBusy()) return;
|
||||
if (isBusy() || isNotificationDisabled()) return;
|
||||
const audio = new Audio(newFriendSound);
|
||||
audio.play();
|
||||
}
|
||||
|
|
@ -25,4 +25,7 @@ export default {
|
|||
|
||||
function isBusy(){
|
||||
return store.getters.user.status == 3
|
||||
}
|
||||
function isNotificationDisabled(){
|
||||
return !!store.getters['settingsModule/settings'].notification.disableNotificationSound;
|
||||
}
|
||||
29
src/utils/simpleMarkdownRules/CustomEmoji.js
Normal file
29
src/utils/simpleMarkdownRules/CustomEmoji.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import SimpleMarkdown from 'simple-markdown';
|
||||
|
||||
export default {
|
||||
// Specify the order in which this rule is to be run
|
||||
// This rule doesn't conflict with much else, so it should be fine to just put it before the
|
||||
// general-case text rule:
|
||||
order: SimpleMarkdown.defaultRules.text.order - 0.5,
|
||||
|
||||
// First we check whether a string matches
|
||||
match: function(source) {
|
||||
// // || followed by any character repeated [\s\S]+? followed by ||
|
||||
// // Also see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions for more info on regexes
|
||||
return /^\<\:([\S]+?)\:([\S]+?)>/.exec(source);
|
||||
},
|
||||
|
||||
// Then parse this string into a syntax node
|
||||
parse: function(capture, parse, state) {
|
||||
return {
|
||||
// capture[1] is the parenthesis group from the regex in `match`:
|
||||
name: parse(capture[1], state),
|
||||
id: parse(capture[2], state)
|
||||
};
|
||||
},
|
||||
|
||||
// Finally transform this syntax node into an html element:
|
||||
html: function(node, output, state) {
|
||||
return '<img class="emoji" title="' + output(node.name, state) + '" alt="' + output(node.name, state) + '" src="https://supertiger.tk/api/media/' + output(node.id, state) + '">'
|
||||
}
|
||||
}
|
||||
29
src/utils/simpleMarkdownRules/linkify.js
Normal file
29
src/utils/simpleMarkdownRules/linkify.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import SimpleMarkdown from 'simple-markdown';
|
||||
import linkify from 'linkify-it'
|
||||
const linkifyInstance = linkify();
|
||||
|
||||
|
||||
export default {
|
||||
// Specify the order in which this rule is to be run
|
||||
// This rule doesn't conflict with much else, so it should be fine to just put it before the
|
||||
// general-case text rule:
|
||||
order: SimpleMarkdown.defaultRules.link - 0.5,
|
||||
|
||||
// First we check whether a string matches
|
||||
match: function(source) {
|
||||
return linkifyInstance.match(source)
|
||||
},
|
||||
|
||||
// Then parse this string into a syntax node
|
||||
parse: function(capture, parse, state) {
|
||||
console.log(capture)
|
||||
return {
|
||||
content: parse(capture[0], state),
|
||||
};
|
||||
},
|
||||
|
||||
// Finally transform this syntax node into an html element:
|
||||
html: function(node, output, state) {
|
||||
return 'owo'
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +1,14 @@
|
|||
<template>
|
||||
<div id="app" ref="app">
|
||||
<vue-headful :title="title" description="Nertivia Chat Client"/>
|
||||
<div class="background-image halloween-background"></div>
|
||||
<div class="background-image"></div>
|
||||
<transition name="fade-between-two" appear>
|
||||
<ConnectingScreen v-if="!loggedIn"/>
|
||||
<div class="box" v-if="loggedIn">
|
||||
<div class="frame">
|
||||
<div class="tabs">
|
||||
|
||||
<div :class="`tab ${currentTab === 0 ? 'selected' : ''}`" @click="switchTab(0)">
|
||||
<i class="material-icons">explore</i>
|
||||
Explore
|
||||
</div>
|
||||
|
||||
<div :class="{tab: true, selected: currentTab === 1, notifyAnimation: DMNotification || friendRequestExists}" @click="switchTab(1)">
|
||||
<i class="material-icons">chat</i>
|
||||
Direct Message
|
||||
</div>
|
||||
|
||||
<div :class="{tab: true, selected: currentTab === 2, notifyAnimation: serverNotification}" @click="switchTab(2)">
|
||||
<i class="material-icons">forum</i>
|
||||
Servers
|
||||
</div>
|
||||
|
||||
<div :class="`tab ${currentTab === 3 ? 'selected' : ''}`" @click="switchTab(3)">
|
||||
<i class="material-icons">list_alt</i>
|
||||
Changelog
|
||||
</div>
|
||||
|
||||
|
||||
<!-- <div :class="`tab ${currentTab === 4 ? 'selected' : ''}`" @click="switchTab(4)">
|
||||
<i class="material-icons">info</i>
|
||||
Ad
|
||||
</div> -->
|
||||
<div class="frame" v-if="isElectron">
|
||||
<div class="window-buttons">
|
||||
<electron-frame-buttons />
|
||||
</div>
|
||||
<div class="window-buttons" v-if="isElectron">
|
||||
<electron-frame-buttons />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="panel-layout">
|
||||
|
|
@ -59,7 +31,6 @@ import changelog from "@/utils/changelog.js";
|
|||
import ConnectingScreen from "./../components/app/ConnectingScreen.vue";
|
||||
import Spinner from "./../components/Spinner.vue";
|
||||
|
||||
|
||||
const ElectronFrameButtons = () =>
|
||||
import("@/components/ElectronJS/FrameButtons.vue");
|
||||
|
||||
|
|
@ -87,7 +58,6 @@ const Explore = () => ({
|
|||
export default {
|
||||
name: "app",
|
||||
components: {
|
||||
|
||||
DirectMessage,
|
||||
Servers,
|
||||
ConnectingScreen,
|
||||
|
|
@ -98,7 +68,6 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
currentTab: 0,
|
||||
title: "Nertivia",
|
||||
isElectron: window && window.process && window.process.type
|
||||
};
|
||||
|
|
@ -133,7 +102,7 @@ export default {
|
|||
},
|
||||
switchTab(index) {
|
||||
localStorage.setItem("currentTab", index);
|
||||
this.currentTab = index;
|
||||
this.$store.dispatch('setCurrentTab', index)
|
||||
if (index == 1) { //1: direct message tab.
|
||||
this.switchChannel(false)
|
||||
} else if (index === 2) { //2: server tab
|
||||
|
|
@ -156,25 +125,25 @@ export default {
|
|||
|
||||
const currentTab = localStorage.getItem("currentTab");
|
||||
if(currentTab) {
|
||||
this.currentTab = parseInt(currentTab);
|
||||
this.$store.dispatch('setCurrentTab', parseInt(currentTab))
|
||||
}
|
||||
// check if changelog is updated
|
||||
const seenVersion = localStorage.getItem("changelog-version-seen");
|
||||
if (seenVersion && seenVersion < changelog[0].version) {
|
||||
localStorage.setItem("currentTab", 3);
|
||||
this.currentTab = 3;
|
||||
this.$store.dispatch('setCurrentTab', 3)
|
||||
}
|
||||
localStorage.setItem("changelog-version-seen", changelog[0].version);
|
||||
bus.$on("title:change", title => {
|
||||
this.title = title;
|
||||
});
|
||||
|
||||
bus.$on('changeTab', this.switchTab)
|
||||
},
|
||||
destroyed() {
|
||||
bus.$off('changeTab', this.switchTab)
|
||||
},
|
||||
|
||||
computed: {
|
||||
currentTab() {
|
||||
return this.$store.getters.currentTab;
|
||||
},
|
||||
loggedIn() {
|
||||
return this.$store.getters.loggedIn;
|
||||
},
|
||||
|
|
@ -277,6 +246,8 @@ export default {
|
|||
display: flex;
|
||||
-webkit-app-region: drag;
|
||||
flex-shrink: 0;
|
||||
height: 25px;
|
||||
background: #1089ff;
|
||||
}
|
||||
|
||||
.window-buttons {
|
||||
|
|
@ -287,6 +258,9 @@ export default {
|
|||
|
||||
.tabs {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
height: 40px;
|
||||
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
max-width: 500px;
|
||||
|
|
@ -381,12 +355,6 @@ textarea {
|
|||
filter: blur(15px);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.halloween-background {
|
||||
background: url(./../assets/halloween_background.jpg);
|
||||
filter: blur(10px);
|
||||
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.panel-layout {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -1,508 +0,0 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<vue-headful
|
||||
title="Nertivia"
|
||||
description="Nertivia Chat Client"
|
||||
/>
|
||||
<div class="background-image" />
|
||||
<div class="layout">
|
||||
<div class="small-view-nav-bar">
|
||||
<div class="small-logo" />
|
||||
<div class="small-title">
|
||||
Nertivia
|
||||
</div>
|
||||
<div
|
||||
class="show-menu-button"
|
||||
@click="showMobileMenu = !showMobileMenu"
|
||||
>
|
||||
<i class="material-icons">
|
||||
menu
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panels">
|
||||
<div class="left-panel">
|
||||
<div class="title">
|
||||
The best chat client that wont restrict you from important and fun features.
|
||||
</div>
|
||||
<img
|
||||
src="../assets/graphics/HomeGraphics.png"
|
||||
class="graphic-app"
|
||||
>
|
||||
<div
|
||||
class="change-log-mini"
|
||||
@click="showChangeLog = true"
|
||||
>
|
||||
<div class="change-title">
|
||||
Change log <span style="font-size: 15px; color: rgba(211, 211, 211, 0.774);">Click for details</span>
|
||||
</div>
|
||||
<div class="change-list">
|
||||
<div
|
||||
v-for="change in changelogFiltered"
|
||||
:key="change.title"
|
||||
class="change"
|
||||
>
|
||||
<div class="notable-changes">
|
||||
{{ change.shortTitle }}
|
||||
</div>
|
||||
<div class="change-date">
|
||||
{{ change.date }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="twitter-outer">
|
||||
<twitter class="twitter">
|
||||
<div slot="loading">
|
||||
loading .....
|
||||
</div>
|
||||
<a
|
||||
class="twitter-timeline"
|
||||
data-height="500"
|
||||
data-theme="dark"
|
||||
href="https://twitter.com/NertiviaApp?ref_src=twsrc%5Etfw"
|
||||
>Tweets by NertiviaApp</a>
|
||||
</twitter>
|
||||
</div>
|
||||
</div>
|
||||
<RightPanel :class="{'show-menu-content': showMobileMenu }" />
|
||||
</div>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<ChangeLog v-if="showChangeLog" />
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { twitter } from 'vue-twitter'
|
||||
import {bus} from '../main';
|
||||
import RightPanel from "./../components/homePage/RightPanel.vue"
|
||||
import ChangeLog from "./../components/ChangeLog.vue"
|
||||
import changelog from '@/utils/changelog.js'
|
||||
export default {
|
||||
components: {
|
||||
RightPanel,
|
||||
ChangeLog,
|
||||
twitter
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loginSelected: true,
|
||||
showMobileMenu: false,
|
||||
showChangeLog: false,
|
||||
changelog
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
changelogFiltered() {
|
||||
return this.changelog.slice(0, 3)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
bus.$on('closeChangeLog', () => {
|
||||
this.showChangeLog = false;
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#app {
|
||||
font-family: "Roboto", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #383838;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.twitter-outer{
|
||||
margin: auto;
|
||||
width: 600px;
|
||||
opacity: 0.8;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.twitter-outer:hover{
|
||||
opacity: 1;
|
||||
}
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity .2s;
|
||||
}
|
||||
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #383838;
|
||||
height: 100%;
|
||||
}
|
||||
button {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
.spinner{
|
||||
margin: auto;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.background-image {
|
||||
background: url(./../assets/background.jpg);
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-position: bottom;
|
||||
background-size: cover;
|
||||
|
||||
}
|
||||
|
||||
.layout{
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width:100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
.panels{
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.left-panel {
|
||||
flex: 1;
|
||||
background: rgba(0, 0, 0, 0.253);
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.loader{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
}
|
||||
.title-panel{
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.graphics-panel{
|
||||
flex: 1;
|
||||
|
||||
}
|
||||
|
||||
.graphic-app{
|
||||
display: table;
|
||||
margin: auto;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
width: 900px;
|
||||
height: auto;
|
||||
user-select: none;
|
||||
}
|
||||
.title{
|
||||
color: white;
|
||||
font-size: 35px;
|
||||
text-align: center;
|
||||
margin-top: 120px;
|
||||
}
|
||||
|
||||
.change-log-mini{
|
||||
background: rgba(0, 0, 0, 0.322);
|
||||
height: 150px;
|
||||
width: 640px;
|
||||
margin: auto;
|
||||
margin-top: 20px;
|
||||
color: white;
|
||||
margin-bottom: 50px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.change-title {
|
||||
font-size: 18px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 10px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.change-list{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.change {
|
||||
background: rgba(0, 0, 0, 0.335);
|
||||
width: 200px;
|
||||
height: 90px;
|
||||
margin-left: 10px;
|
||||
border-radius: 5px;
|
||||
transition: 0.3s;
|
||||
position: relative
|
||||
}
|
||||
.change:hover {
|
||||
background: rgba(0, 0, 0, 0.466);
|
||||
}
|
||||
.notable-changes{
|
||||
margin: 10px;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.change-date{
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
color: rgba(255, 255, 255, 0.753);
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.small-view-nav-bar{
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
background: rgba(0, 0, 0, 0.411);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
}
|
||||
|
||||
.small-logo {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
background: url(./../assets/logo.png);
|
||||
background-size: 105%;
|
||||
background-position: center;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0px 0px 96px -4px rgba(69,212,255,1);
|
||||
margin: auto;
|
||||
margin-left: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.small-title{
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
flex: 1;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.show-menu-button{
|
||||
color: rgba(255, 255, 255, 0.698);
|
||||
margin-right: 20px;
|
||||
margin-top: 7px;
|
||||
user-select: none;
|
||||
transition: 0.3s
|
||||
}
|
||||
|
||||
.show-menu-button:hover {
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
.show-menu-content {
|
||||
display: flex !important;
|
||||
width: 400px !important;
|
||||
opacity: 1 !important;
|
||||
transform: scale(1) !important;
|
||||
}
|
||||
|
||||
@media (max-width: 1051px) {
|
||||
.change:nth-child(3){
|
||||
display: none;
|
||||
}
|
||||
.change-log-mini{
|
||||
width: 430px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 906px) {
|
||||
|
||||
.change:nth-child(3){
|
||||
display: block;
|
||||
}
|
||||
.change-log-mini{
|
||||
width: 640px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 649px) {
|
||||
.twitter-outer{
|
||||
margin-top: 20px;
|
||||
margin-bottom: 50px;
|
||||
width: initial;
|
||||
}
|
||||
.change-list{
|
||||
flex-direction: column;
|
||||
}
|
||||
.change-log-mini{
|
||||
height: initial;
|
||||
width: calc(100% - 20px);
|
||||
padding-bottom: 10px;
|
||||
margin: auto;
|
||||
}
|
||||
.change{
|
||||
margin-bottom: 5px;
|
||||
margin-left: 5px;
|
||||
margin-right: 0;
|
||||
width: calc(100% - 10px);
|
||||
}
|
||||
}
|
||||
@media (max-width: 1380px) {
|
||||
.graphic-app{
|
||||
width: calc(100% - 80px);
|
||||
}
|
||||
.title{
|
||||
font-size: 30px;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
|
||||
}
|
||||
}
|
||||
@media (max-width: 906px) {
|
||||
.right-panel-home {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: 50px;
|
||||
display: flex;
|
||||
margin-right: 0;
|
||||
margin-top: 0;
|
||||
height:calc(100% - 50px);
|
||||
background-color: rgb(34, 34, 34);
|
||||
width: 0;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s ease;
|
||||
transform: scale(0.97);
|
||||
opacity: 0;
|
||||
|
||||
}
|
||||
.right-panel-inner{
|
||||
width: 400px;
|
||||
}
|
||||
.small-view-nav-bar{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
}
|
||||
@media (max-width: 401px) {
|
||||
.show-menu-content {
|
||||
width: 100% !important;
|
||||
}
|
||||
.right-panel-inner{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- Used for forms !-->
|
||||
<style>
|
||||
@media (max-width: 1380px) {
|
||||
.graphic-app{
|
||||
width: calc(100% - 80px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 906px) {
|
||||
|
||||
|
||||
|
||||
.right-panel-home {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: 50px;
|
||||
display: flex;
|
||||
margin-right: 0;
|
||||
margin-top: 0;
|
||||
height:calc(100% - 50px);
|
||||
background-color: rgba(34, 34, 34, 0.877);
|
||||
width: 0;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s ease;
|
||||
transform: scale(0.97);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.right-panel-inner{
|
||||
width: 400px;
|
||||
}
|
||||
.small-view-nav-bar{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
}
|
||||
@media (max-width: 401px) {
|
||||
.show-menu-content {
|
||||
width: 100% !important;
|
||||
}
|
||||
.right-panel-inner{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.form {
|
||||
color: white;
|
||||
margin: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
input{
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.301);
|
||||
outline: none;
|
||||
border: none;
|
||||
color: white;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
width: 200px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
input:hover{
|
||||
background: rgba(0, 0, 0, 0.452);
|
||||
}
|
||||
|
||||
input:focus {
|
||||
background: rgba(0, 0, 0, 0.603);
|
||||
}
|
||||
.input-title{
|
||||
margin-top: 10px;
|
||||
}
|
||||
.form-button{
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.226);
|
||||
display: table;
|
||||
transition: 0.5s;
|
||||
margin: auto;
|
||||
color: white;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-button:hover{
|
||||
background: rgba(0, 0, 0, 0.534)
|
||||
}
|
||||
.alert{
|
||||
color: red;
|
||||
font-size: 15px;
|
||||
width: 220px;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in a new issue