added load more messages and scroll down button.

This commit is contained in:
supertiger1234 2019-08-16 21:37:53 +01:00
parent 6f65d1291d
commit 53e8fff392
12 changed files with 532 additions and 305 deletions

View file

@ -8,6 +8,7 @@
<script>
import messageFormatter from "@/utils/messageFormatter.js";
import { bus } from "../../main";
export default {
props: ['data'],
@ -20,6 +21,9 @@ export default {
this.close();
}
},
mounted() {
bus.$emit('scrollDown')
},
created() {
document.addEventListener('keydown', this.keyDownEvent)
},
@ -46,7 +50,7 @@ export default {
background: rgba(23, 112, 255, 0.877);
display: flex;
flex-direction: column;
z-index: 99999;
z-index:1;
border-radius: 10px;
margin: 10px;
margin-bottom: 0;

View file

@ -1,15 +1,10 @@
<template>
<div
v-click-outside="closePanel"
class="emoji-panel"
>
<div v-click-outside="closePanel" class="emoji-panel">
<div class="emoji-panel-inner">
<div class="emojis-list">
<!-- Recent Emojis Category -->
<div class="category">
<div class="category-name">
Recent
</div>
<div class="category-name">Recent</div>
<div class="list">
<div
v-for="(recentEmoji, index) in this.recentEmojisList"
@ -21,16 +16,14 @@
v-lazyload
class="panel emoji"
:data-url=" getCustomEmoji(recentEmoji) || emojiShortcodeToPath(':' + recentEmoji + ':')"
>
/>
</div>
</div>
</div>
<!-- Custom Emojis Category -->
<div class="category">
<div class="category-name">
Custom Emojis
</div>
<div class="category-name">Custom Emojis</div>
<div class="list">
<div
v-for="(customEmoji, index) in this.customEmojisList"
@ -38,23 +31,13 @@
class="emoji-item"
@click="customEmojiClickEvent(customEmoji)"
>
<img
v-lazyload
class="panel emoji"
:data-url="customEmojiPath + customEmoji.emojiID"
>
<img v-lazyload class="panel emoji" :data-url="customEmojiPath + customEmoji.emojiID" />
</div>
</div>
</div>
<div
v-for="(group, index) in groups"
:key="group"
class="category"
>
<div class="category-name">
{{ group }}
</div>
<div v-for="(group, index) in groups" :key="group" class="category">
<div class="category-name">{{ group }}</div>
<div class="list">
<div
v-for="emojiSorted in emojiByGroup(index)"
@ -62,33 +45,19 @@
class="emoji-item"
@click="emojiClickEvent(emojiSorted.shortcodes[0])"
>
<img
v-lazyload
class="panel emoji"
:data-url="parseEmojiPath(emojiSorted.unicode)"
>
<img v-lazyload class="panel emoji" :data-url="parseEmojiPath(emojiSorted.unicode)" />
</div>
</div>
</div>
</div>
<div class="tabs">
<div
class="tab"
@click="scrollToCategory(0)"
>
<div class="tab" @click="scrollToCategory(0)">
<i class="material-icons">history</i>
<div class="tooltip">
Recent
</div>
<div class="tooltip">Recent</div>
</div>
<div
class="tab"
@click="scrollToCategory(1)"
>
<div class="tab" @click="scrollToCategory(1)">
<i class="material-icons">face</i>
<div class="tooltip">
Custom Emojis
</div>
<div class="tooltip">Custom Emojis</div>
</div>
<div
v-for="(emoji, index) in groupUnicodes"
@ -97,13 +66,8 @@
@mouseenter="mouseHover(emoji, $event)"
@click="scrollToCategory(index + 2)"
>
<img
class="panel-emoji"
:src="selectRandom(emoji)"
>
<div class="tooltip">
{{ groups[index] }}
</div>
<img class="panel-emoji" :src="selectRandom(emoji)" />
<div class="tooltip">{{ groups[index] }}</div>
</div>
</div>
</div>
@ -115,10 +79,9 @@
import { bus } from "@/main";
import emojiParser from "@/utils/emojiParser.js";
import lazyLoad from "@/directives/LazyLoad.js";
import {mapState} from 'vuex'
import { mapState } from "vuex";
import config from "@/config.js";
export default {
directives: {
lazyload: lazyLoad
@ -377,23 +340,23 @@ export default {
emojis: emojiParser.allEmojis,
groups: emojiParser.allGroups,
recentEmojisList: null,
customEmojisList : null,
customEmojiPath: config.domain + "/files/"
customEmojisList: null,
customEmojiPath: config.domain + "/media/"
};
},
beforeMount() {
this.recentEmojisList = this.recentEmojis
this.customEmojisList = this.customEmojis
this.recentEmojisList = this.recentEmojis;
this.customEmojisList = this.customEmojis;
},
methods: {
getCustomEmoji(shortCode){
const customEmoji = emojiParser.getCustomEmojisByShortCode(shortCode)
return (customEmoji ? this.customEmojiPath + customEmoji.emojiID : undefined)
getCustomEmoji(shortCode) {
const customEmoji = emojiParser.getCustomEmojisByShortCode(shortCode);
return customEmoji
? this.customEmojiPath + customEmoji.emojiID
: undefined;
},
closePanel(event) {
if (!event.target.closest('.emojis-button'))
this.$emit('close')
if (!event.target.closest(".emojis-button")) this.$emit("close");
},
emojiByGroup(index) {
index = parseInt(index);
@ -422,9 +385,9 @@ export default {
const elements = document.querySelectorAll(".category-name");
elements[index].scrollIntoView();
}
},
},
computed: {
...mapState('settingsModule', ['recentEmojis', 'customEmojis'])
...mapState("settingsModule", ["recentEmojis", "customEmojis"])
}
};
</script>
@ -483,6 +446,7 @@ export default {
padding: 2px;
border-radius: 5px;
min-width: 30px;
cursor: pointer;
}
.emoji-item:hover {
background: rgb(59, 59, 59);
@ -529,6 +493,7 @@ export default {
align-items: center;
justify-content: center;
flex-shrink: 0;
cursor: pointer;
}
.tab:hover {
@ -571,12 +536,10 @@ export default {
</style>
<style>
img.panel.emoji {
margin: auto;
padding: 2px;
object-fit: contain;
height: 2em;
width: 2em;
}
</style>

View file

@ -34,7 +34,7 @@ export default {
props: ["emojiArray"],
data(){
return {
customEmojiPath: config.domain + "/files/"
customEmojiPath: config.domain + "/media/"
}
},
computed: {

View file

@ -0,0 +1,259 @@
<template>
<div ref="msg-logs" class="message-logs" @scroll="scrollEvent" @resize="onResize">
<div class="load-more-button" v-if="!noMoreLoadMore && selectedChannelMessages.length >= 50">
<spinner :size="30" v-if="loadingMore" />
<div class="text" v-if="!loadingMore" @click="loadMoreMessages">Load more</div>
</div>
<message
class="message-container"
v-for="(msg, index) in selectedChannelMessages"
:key="index + selectedChannelID"
:date="msg.created"
:admin="msg.creator.admin"
:username="msg.creator.username"
:uniqueID="msg.creator.uniqueID"
:avatar="msg.creator.avatar"
:message="msg.message"
:embed="msg.embed"
:files="msg.files"
:status="msg.status"
:messageID="msg.messageID"
:channelID="msg.channelID"
:type="msg.type"
:timeEdited="msg.timeEdited"
/>
<uploadsQueue v-if="uploadQueue !== undefined" :queue="uploadQueue"/>
</div>
</template>
<script>
import messagesService from "@/services/messagesService";
import { bus } from "../../main";
import Message from "../../components/app/MessageTemplate.vue";
import Spinner from "@/components/Spinner.vue";
import uploadsQueue from "@/components/app/uploadsQueue.vue";
import windowProperties from '@/utils/windowProperties';
export default {
components: {
Message,
Spinner,
uploadsQueue,
},
data() {
return {
scrolledDown: true,
scrolledTop: false,
//load more messages
loadingMore: false,
noMoreLoadMore: false,
selectedChannelID: null,
currentScrollTopPos: null
};
},
methods: {
scrollEvent(event) {
const { currentTarget: { scrollTop, clientHeight, scrollHeight} } = event;
this.scrolledDown = Math.abs(scrollHeight - scrollTop - clientHeight) <= 3.0;
this.scrolledTop = scrollTop === 0;
this.currentScrollTopPos = scrollTop;
},
scrollDown(data) {
const element = this.$refs['msg-logs']
const force = data && data.force ? data.force : false;
const pos = data && data.pos ? data.pos : undefined;
if (!force && !this.scrolledDown) return;
if (!element) return;
element.scrollTop = pos || element.scrollHeight;
},
onResize(dimentions) {
this.scrollDown();
},
async loadMoreMessages() {
const msgLogs = this.$refs['msg-logs'];
const scrollTop = msgLogs.scrollTop;
const scrollHeight = msgLogs.scrollHeight;
const continueMessageID = this.selectedChannelMessages[0].messageID;
this.loadingMore = true
const {ok, result, error} = await messagesService.get(this.selectedChannelID, continueMessageID)
if (ok) {
if (!result.data.messages.length) {
this.loadingMore = false;
this.noMoreLoadMore = true;
return;
}
this.$store.dispatch('addMessages', result.data.messages)
this.$nextTick(_ => {
this.loadingMore = false;
msgLogs.scrollTop = msgLogs.scrollHeight - scrollHeight;
})
}
},
scrolledUpEvent() {
this.loadMoreMessages();
},
scrolledDownEvent(){
this.noMoreLoadMore = false;
this.unloadTopMessages()
},
unloadTopMessages(){
if (this.selectedChannelMessages && this.selectedChannelMessages.length)
this.$store.dispatch('unloadTopMessages', {channelID: this.selectedChannelID});
},
backToBottomEvent() {
this.scrollDown({force: true});
this.unloadTopMessages();
},
},
mounted() {
this.selectedChannelID = this.$store.getters.selectedChannelID;
const pos = this.$store.getters.scrollPosition[this.selectedChannelID];
bus.$on('backToBottom', this.backToBottomEvent)
bus.$on('scrollDown', this.scrollDown)
bus.$emit('scrolledDown',this.scrolledDown);
this.$nextTick( _ => {
this.scrollDown({force: pos, pos: pos});
})
},
beforeDestroy() {
this.$store.dispatch("setEditMessage", null);
this.$store.dispatch('changeScrollPosition',{ channelID: this.selectedChannelID, pos: this.currentScrollTopPos });
bus.$off('backToBottom', this.backToBottomEvent);
bus.$off('scrollDown', this.scrollDown)
},
watch: {
selectedChannelMessages(newMessages, oldMessages){
this.noMoreLoadMore = false;
const msgLogs = this.$refs['msg-logs'];
this.$nextTick(function () {
this.scrollDown();
})
},
uploadQueue() {
this.$nextTick(function () {
this.scrollDown({force: true});
})
},
getWindowWidth(dimentions) {
this.onResize();
},
scrolledTop(scrolledTop) {
if (scrolledTop)
this.scrolledUpEvent();
},
scrolledDown(scrolledDown) {
bus.$emit('scrolledDown',scrolledDown);
if (scrolledDown)
this.scrolledDownEvent();
}
},
computed: {
uploadQueue() {
const allUploads = this.$store.getters.getAllUploads;
const selectedChannelID = this.$store.getters.selectedChannelID;
const filteredArray = [];
for (let upload in allUploads) {
if (allUploads[upload].channelID === selectedChannelID) {
filteredArray.push(allUploads[upload]);
}
}
if (!filteredArray.length) return undefined;
return filteredArray;
},
user() {
return this.$store.getters.user;
},
channel() {
return this.$store.getters.channels[this.selectedChannelID];
},
selectedChannelMessages() {
const selectedChannel = this.$store.getters.selectedChannelID;
return this.$store.getters.messages[selectedChannel];
},
editMessage() {
let editMessage = this.$store.getters.popouts.editMessage;
if (!editMessage) return null;
return editMessage;
},
getWindowWidth() {
return {width: windowProperties.resizeWidth, height: windowProperties.resizeHeight};
},
scrollPosition() {
return this.$store.getters.scrollPosition;
}
}
};
</script>
<style lang="scss" scoped>
.message-logs {
overflow: auto;
flex: 1;
margin-right: 2px;
position: relative;
}
.load-more-button {
background: rgba(0, 0, 0, 0.46);
padding: 5px;
user-select: none;
border-radius: 5px;
margin: 5px;
height: 30px;
display: flex;
flex-direction: column;
justify-content: center;
.text {
text-align: center;
color: white;
cursor: pointer;
align-self: center;
}
}
.back-to-bottom-button {
&:hover {
background: rgb(23, 124, 255);
box-shadow: 0px 0px 15px 0px #0000008a;
}
transition: 0.2s;
background: rgba(23, 124, 255, 0.818);
color: white;
position: absolute;
bottom: 15px;
right: 25px;
border-radius: 10px;
height: 50px;
z-index: 2;
display: flex;
justify-content: center;
flex-shrink: 0;
box-shadow: 0px 0px 7px 0px #0000008a;
align-content: center;
align-items: center;
padding-left: 10px;
user-select: none;
cursor: pointer;
.material-icons {
align-self: center;
flex-shrink: 0;
font-size: 35px;
}
}
</style>

View file

@ -3,48 +3,31 @@
<heading
:uniqueID="recipients && recipients.length ? recipients[0].uniqueID : undefined"
:type="selectedChannelID && channel && !channel.server_id ? 1 : channel && channel.server_id ? 2 : 0"
:status-color="userStatusColor"
:name="selectedChannelID ? channelName : `Welcome back, ${user.username}!` "
/>
<div class="loading" v-if="selectedChannelID && !selectedChannelMessages">
<spinner/>
</div>
<div v-else-if="selectedChannelID" ref="msg-logs" class="message-logs" @scroll="scrollEvent">
<div class="scroll">
<message
class="message-container"
v-for="(msg, index) in selectedChannelMessages"
:key="index + selectedChannelID"
:date="msg.created"
:admin="msg.creator.admin"
:username="msg.creator.username"
:uniqueID="msg.creator.uniqueID"
:avatar="msg.creator.avatar"
:message="msg.message"
:embed="msg.embed"
:files="msg.files"
:status="msg.status"
:messageID="msg.messageID"
:channelID="msg.channelID"
:type="msg.type"
:timeEdited="msg.timeEdited"
/>
<uploadsQueue v-if="uploadQueue !== undefined" :queue="uploadQueue"/>
</div>
<spinner />
</div>
<message-logs v-else-if="selectedChannelID && selectedChannelMessages" :key="selectedChannelID" />
<div class="no-channel-selected" v-if="!selectedChannelID ">
<div class="material-icons">chat</div>
<div class="message">Select a person to message!</div>
</div>
<div class="chat-input-area" v-if="selectedChannelID">
<div style="position: relative;">
<emoji-suggestions v-if="emojiArray" :emojiArray="emojiArray"/>
<emoji-panel v-if="showEmojiPanel" @close="showEmojiPanel = false"/>
<transition name="show-up">
<div class="back-to-bottom-button" @click="backToTopButton" v-if="!scrolledDown && selectedChannelMessages">
Back to bottom
<i class="material-icons">keyboard_arrow_down</i>
</div>
</transition>
<emoji-suggestions v-if="emojiArray" :emojiArray="emojiArray" />
<emoji-panel v-if="showEmojiPanel" @close="showEmojiPanel = false" />
</div>
<edit-panel v-if="editMessage" :data='editMessage' />
<edit-panel v-if="editMessage" :data="editMessage" />
<div class="message-area">
<input type="file" ref="sendFileBrowse" @change="attachmentChange" class="hidden">
<input type="file" ref="sendFileBrowse" @change="attachmentChange" class="hidden" />
<div class="attachment-button" @click="attachmentButton">
<i class="material-icons">attach_file</i>
</div>
@ -64,7 +47,8 @@
<i class="material-icons">face</i>
</button>
<button
:class="{'send-button': true, 'error-send-button': messageLength > 5000}"
class="send-button"
:class="{'error-send-button': messageLength > 5000}"
@click="editMessage ? updateMessage() : sendMessage()"
>
<i class="material-icons">{{editMessage ? 'edit' : 'send'}}</i>
@ -90,30 +74,26 @@ import messagesService from "@/services/messagesService";
import typingService from "@/services/TypingService";
import { bus } from "../../main";
import JQuery from "jquery";
let $ = JQuery;
import Message from "../../components/app/MessageTemplate.vue";
import Spinner from "@/components/Spinner.vue";
import TypingStatus from "@/components/app/TypingStatus.vue";
import uploadsQueue from "@/components/app/uploadsQueue.vue";
import heading from "@/components/app/MessagePanel/Heading.vue";
import emojiSuggestions from "@/components/app/EmojiPanels/emojiSuggestions.vue";
import emojiParser from "@/utils/emojiParser.js";
import statuses from "@/utils/statuses";
import windowProperties from '@/utils/windowProperties';
import MessageLogs from "@/components/app/MessageLogs.vue";
import emojiParser from "@/utils/emojiParser.js";
import windowProperties from "@/utils/windowProperties";
const emojiPanel = () => import("@/components/app/EmojiPanels/emojiPanel.vue");
const EditPanel = () => import("@/components/app/EditPanel.vue");
export default {
components: {
Message,
Spinner,
TypingStatus,
uploadsQueue,
emojiSuggestions,
emojiPanel,
heading,
EditPanel
EditPanel,
MessageLogs
},
data() {
return {
@ -124,8 +104,8 @@ export default {
typing: false,
typingRecipients: {},
showEmojiPanel: false,
scrolledDown: true,
scrolledTop: false,
scrolledDown: true
};
},
methods: {
@ -199,7 +179,7 @@ export default {
const editMessage = this.editMessage;
this.$refs["input-box"].focus();
this.message = this.message.trim();
if (this.message === this.editMessage.message){
if (this.message === this.editMessage.message) {
this.$store.dispatch("setEditMessage", null);
this.message = "";
return;
@ -212,29 +192,33 @@ export default {
this.messageLength = 0;
const msg = emojiParser.replaceShortcode(this.message);
this.$store.dispatch('updateMessage', {
this.$store.dispatch("updateMessage", {
channelID: editMessage.channelID,
messageID: editMessage.messageID,
message: {message: msg, status: 0}
})
message: { message: msg, status: 0 }
});
this.$store.dispatch("setEditMessage", null);
this.message = "";
const {ok, error, result} = await messagesService.update(editMessage.messageID, editMessage.channelID, {
message: msg
})
const { ok, error, result } = await messagesService.update(
editMessage.messageID,
editMessage.channelID,
{
message: msg
}
);
if (ok) {
this.$store.dispatch('updateMessage', {
this.$store.dispatch("updateMessage", {
channelID: editMessage.channelID,
messageID: editMessage.messageID,
message: {status: 1}
})
message: { status: 1 }
});
} else {
this.$store.dispatch('updateMessage', {
this.$store.dispatch("updateMessage", {
channelID: editMessage.channelID,
messageID: editMessage.messageID,
message: {message: msg, status: 2}
})
message: { message: msg, status: 2 }
});
}
},
async postTimer() {
@ -242,14 +226,13 @@ export default {
if (this.message.trim() == "") {
clearInterval(this.postTimerID);
this.postTimerID = null;
}else {
} else {
if (this.selectedChannelID)
await typingService.post(this.selectedChannelID);
if (this.postTimerID)
this.postTimer()
if (this.postTimerID) this.postTimer();
}
}, 2000)
}, 2000);
},
resize() {
let input = this.$refs["input-box"];
@ -259,9 +242,8 @@ export default {
} else {
input.style.height = "auto";
input.style.height = `calc(${input.scrollHeight}px - 1em)`;
this.scrollDown();
}
bus.$emit('scrollDown');
},
emojiSwitchKey(event) {
if (!this.emojiArray) return;
@ -347,11 +329,16 @@ export default {
},
enterEmojiPanel(shortcode) {
const target = this.$refs["input-box"];
if (!target) return;
target.focus();
const isSuccessful = document.execCommand("insertText", false, `:${shortcode}: `);
const isSuccessful = document.execCommand(
"insertText",
false,
`:${shortcode}: `
);
if (!isSuccessful) {
this.message = this.message + `:${shortcode}: `;
this.message = this.message + `:${shortcode}: `;
}
target.blur();
this.$store.dispatch("settingsModule/addRecentEmoji", shortcode);
@ -369,17 +356,20 @@ export default {
return;
}
if (this.editMessage) {
return this.updateMessage()
return this.updateMessage();
} else {
this.sendMessage();
}
}
}
if (event.keyCode === 38){ //38 = up arrow
if (event.keyCode === 38) {
//38 = up arrow
if (this.message !== "") return;
if (this.editMessage) return;
const messagesFiltered = this.selectedChannelMessages.filter(m => m.creator.uniqueID === this.user.uniqueID);
const messagesFiltered = this.selectedChannelMessages.filter(
m => m.creator.uniqueID === this.user.uniqueID
);
if (!messagesFiltered.length) return;
event.preventDefault();
@ -389,14 +379,19 @@ export default {
channelID: lastMessage.channelID,
message: lastMessage.message
});
}
},
hideTypingStatus(data) {
if (this.user.uniqueID === data.message.creator.uniqueID) return;
if (!this.typingRecipients[data.channelID] || !this.typingRecipients[data.channelID][data.message.creator.uniqueID]) return;
clearTimeout( this.typingRecipients[data.channelID][data.message.creator.uniqueID].timer );
if (
!this.typingRecipients[data.channelID] ||
!this.typingRecipients[data.channelID][data.message.creator.uniqueID]
)
return;
clearTimeout(
this.typingRecipients[data.channelID][data.message.creator.uniqueID]
.timer
);
this.$delete(
this.typingRecipients[data.channelID],
data.message.creator.uniqueID
@ -456,38 +451,16 @@ export default {
});
}
},
scrollEvent(event) {
const { currentTarget: { scrollTop, clientHeight, scrollHeight} } = event;
this.scrolledDown = Math.abs(scrollHeight - scrollTop - clientHeight) <= 3.0;
this.scrolledTop = scrollTop === 0;
backToTopButton() {
bus.$emit('backToBottom');
},
scrollDown() {
if (!this.scrolledDown) return;
const element = this.$refs['msg-logs']
if (!element) return;
element.scrollTop = element.scrollHeight;
},
onResize(dimentions) {
this.scrollDown();
},
async loadMoreMessages() {
const msgLogs = this.$refs['msg-logs'];
const scrollTop = msgLogs.scrollTop;
const scrollHeight = msgLogs.scrollHeight;
editMessageEvent(editMessage) {
this.message = editMessage ? emojiParser.emojiToShortcode(editMessage.message) : '';
const continueMessageID = this.selectedChannelMessages[0].messageID;
const {ok, result, error} = await messagesService.get(this.selectedChannelID, continueMessageID)
if (ok) {
if (!result.data.messages.length) return
this.$store.dispatch('addMessages', result.data.messages)
this.$nextTick(_ => {
msgLogs.scrollTop = msgLogs.scrollHeight - scrollHeight;
})
}
},
scrolledUpEvent() {
this.loadMoreMessages();
onBlur() {
clearTimeout(this.postTimerID);
this.postTimerID = null;
}
},
mounted() {
@ -520,59 +493,34 @@ export default {
2500
);
};
this.scrollDown();
bus.$on("newMessage", this.hideTypingStatus);
bus.$on("emojiSuggestions:Selected", this.enterEmojiSuggestion);
bus.$on("emojiPanel:Selected", this.enterEmojiPanel);
window.onblur = () => {
clearTimeout(this.postTimerID);
this.postTimerID = null;
}
window.addEventListener('focus', this.onFocus)
bus.$on('scrolledDown', (scrolledDown) => {
this.scrolledDown = scrolledDown;
})
window.addEventListener('blur', this.onBlur)
window.addEventListener("focus", this.onFocus);
},
beforeDestroy() {
clearTimeout(this.postTimerID);
this.postTimerID = null;
bus.$off("newMessage", this.hideTypingStatus);
bus.$off("emojiSuggestions:Selected", this.enterEmojiSuggestion);
bus.$on("emojiPanel:Selected", this.enterEmojiPanel);
window.removeEventListener('focus', this.onFocus)
window.removeEventListener("focus", this.onFocus);
window.removeEventListener("blur", this.onBlur);
delete this.$options.sockets.typingStatus;
},
watch: {
selectedChannelMessages(newMessages, oldMessages){
this.$nextTick(function () {
this.scrollDown();
})
},
selectedChannelID(){
this.scrolledDown = true;
},
uploadQueue() {
this.$nextTick(function () {
this.scrollDown(true);
})
},
editMessage(editMessage) {
if (!editMessage) {
this.message = ""
} else {
this.message = emojiParser.emojiToShortcode(editMessage.message)
}
this.$nextTick(() => {
this.resize();
this.scrollDown();
})
},
getWindowWidth(dimentions) {
this.onResize();
},
scrolledTop(scrolledTop) {
if (scrolledTop)
this.scrolledUpEvent();
this.editMessageEvent(editMessage);
}
},
computed: {
@ -617,37 +565,30 @@ export default {
const channel = this.$store.getters.channels[selectedChannel];
return channel ? channel.recipients : undefined;
},
userStatusColor() {
const selectedChannel = this.$store.getters.selectedChannelID;
const channel = this.$store.getters.channels[selectedChannel];
const presences = this.$store.getters['members/presences'];
let status = 0;
if (!channel || !channel.recipients || !channel.recipients.length) {
status = 0;
} else if (
this.$store.getters.user.friends[channel.recipients[0].uniqueID]
) {
status = presences[channel.recipients[0].uniqueID] || 0;
}
return statuses[status].color;
},
editMessage() {
let editMessage = this.$store.getters.popouts.editMessage;
if (!editMessage) return null;
return editMessage;
},
getWindowWidth() {
return {width: windowProperties.resizeWidth, height: windowProperties.resizeHeight};
return {
width: windowProperties.resizeWidth,
height: windowProperties.resizeHeight
};
},
}
};
</script>
<style scoped>
<style lang="scss" scoped>
.show-up-enter-active,
.show-up-leave-active {
transition: 0.3s;
}
.show-up-enter, .show-up-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
transform: translateY(10px);
}
.no-channel-selected {
display: flex;
@ -659,19 +600,13 @@ export default {
justify-content: center;
color: white;
font-size: 20px;
}
.no-channel-selected .message {
margin-top: 20px;
text-align: center;
}
.no-channel-selected .material-icons {
font-size: 50px;
}
.channel-selected {
display: flex;
width: 100%;
align-items: center;
height: 100%;
.message {
margin-top: 20px;
text-align: center;
}
.material-icons {
font-size: 50px;
}
}
.hidden {
@ -695,11 +630,8 @@ export default {
.message-logs {
overflow: auto;
flex: 1;
}
.message-logs,
.message-logs .scroll {
/* transform: scale(1, -1) translate3d(0,0,0); */
margin-right: 5px;
margin-right: 2px;
position: relative;
}
.loading {
@ -725,14 +657,16 @@ export default {
user-select: none;
transition: 0.3s;
border-radius: 5px;
&:hover {
background: rgba(0, 0, 0, 0.322);
}
.material-icons {
color: white;
margin: auto;
}
}
.attachment-button:hover {
background: rgba(0, 0, 0, 0.322);
}
.attachment-button .material-icons {
color: white;
margin: auto;
}
.chat-input-area .info {
color: rgba(255, 255, 255, 0.466);
font-size: 12px;
@ -740,15 +674,18 @@ export default {
margin-top: 5px;
display: flex;
}
.typing-outer {
flex: 1;
height: 20px;
}
.message-count {
float: right;
margin-right: 20px;
margin-top: 3px;
}
.message-area {
display: flex;
width: 100%;
@ -772,14 +709,14 @@ export default {
max-height: 30vh;
overflow-y: auto;
border-radius: 5px;
}
.chat-input:hover {
background: rgba(0, 0, 0, 0.288);
}
&:hover {
background: rgba(0, 0, 0, 0.288);
}
.chat-input:focus {
background: rgba(0, 0, 0, 0.466);
&:focus {
background: rgba(0, 0, 0, 0.466);
}
}
.send-button {
@ -797,20 +734,22 @@ export default {
flex-shrink: 0;
border-radius: 5px;
user-select: none;
}
.send-button .material-icons {
margin: auto;
}
.send-button:hover {
background: rgba(0, 0, 0, 0.514);
}
.error-send-button {
background-color: rgba(255, 0, 0, 0.294);
cursor: pointer;
.material-icons {
margin: auto;
}
&:hover {
background: rgba(0, 0, 0, 0.514);
}
}
.error-send-button:hover {
.error-send-button {
background-color: rgba(255, 0, 0, 0.294);
&:hover {
background-color: rgba(255, 0, 0, 0.294);
}
}
.emojis-button {
font-size: 20px;
color: white;
@ -818,7 +757,7 @@ export default {
border: none;
outline: none;
margin-left: 2px;
cursor: pointer;
min-height: 40px;
width: 50px;
transition: 0.3s;
@ -826,12 +765,41 @@ export default {
flex-shrink: 0;
border-radius: 5px;
user-select: none;
.material-icons {
margin: auto;
}
&:hover {
background: rgba(0, 0, 0, 0.514);
}
}
.emojis-button .material-icons {
margin: auto;
}
.emojis-button:hover {
background: rgba(0, 0, 0, 0.514);
.back-to-bottom-button {
&:hover {
background: rgb(23, 124, 255);
box-shadow: 0px 0px 15px 0px #0000008a;
}
transition: 0.2s;
background: rgba(23, 124, 255, 0.818);
color: white;
position: absolute;
bottom: 15px;
right: 25px;
border-radius: 10px;
height: 50px;
z-index: 2;
display: flex;
justify-content: center;
flex-shrink: 0;
box-shadow: 0px 0px 7px 0px #0000008a;
align-content: center;
align-items: center;
padding-left: 10px;
user-select: none;
cursor: pointer;
.material-icons {
align-self: center;
flex-shrink: 0;
font-size: 35px;
}
}
</style>

View file

@ -1,23 +1,15 @@
<template>
<div class="heading">
<div
class="show-menu-button"
@click="toggleLeftMenu"
>
<div class="show-menu-button" @click="toggleLeftMenu">
<i class="material-icons">menu</i>
</div>
<div
v-if="type === 1"
class="user-status"
:style="`box-shadow: 0px 0px 14px 3px ${statusColor}; background-color: ${statusColor};`"
:style="`box-shadow: 0px 0px 14px 3px ${userStatusColor}; background-color: ${userStatusColor};`"
/>
<div class="information">
<div
:class="{name: true, clickable: !!uniqueID }"
@click="openUserInfoPanel"
>
{{ name }}
</div>
<div :class="{name: true, clickable: !!uniqueID }" @click="openUserInfoPanel">{{ name }}</div>
</div>
<div
v-if="type === 2 && selectedServerID"
@ -31,18 +23,14 @@
<script>
import { bus } from "@/main";
import statuses from "@/utils/statuses";
export default {
props: [
"type", // 0: without online status; 1: with online status; 2: server.
"statusColor", // only if type is set to 1;
"name",
"uniqueID"
],
computed: {
selectedServerID() {
return this.$store.getters['servers/selectedServerID'];
}
},
methods: {
openUserInfoPanel() {
if (this.uniqueID)
@ -54,6 +42,26 @@ export default {
toggleMembersPanel() {
bus.$emit("toggleMembersPanel");
}
},
computed: {
selectedServerID() {
return this.$store.getters["servers/selectedServerID"];
},
userStatusColor() {
const selectedChannel = this.$store.getters.selectedChannelID;
const channel = this.$store.getters.channels[selectedChannel];
const presences = this.$store.getters["members/presences"];
let status = 0;
if (!channel || !channel.recipients || !channel.recipients.length) {
status = 0;
} else if (
this.$store.getters.user.friends[channel.recipients[0].uniqueID]
) {
status = presences[channel.recipients[0].uniqueID] || 0;
}
return statuses[status].color;
}
}
};
</script>
@ -75,7 +83,7 @@ export default {
user-select: none;
display: none;
}
.show-members-button{
.show-members-button {
display: inline-block;
margin-right: 5px;
margin-top: 3px;
@ -127,7 +135,6 @@ export default {
text-decoration: underline;
}
@media (max-width: 949px) {
.show-members-button {
display: block;

View file

@ -167,7 +167,7 @@ export default {
if (!files || files.length === 0 || !files[0].dimensions)
return undefined;
const messageLog = document.querySelector('.scroll');
const messageLog = document.querySelector('.message-logs');
const w = messageLog.offsetWidth;
const h = messageLog.offsetHeight;
@ -187,6 +187,9 @@ export default {
const imageTag = this.$refs['image'];
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"
},
@ -425,8 +428,10 @@ export default {
flex-direction: column;
}
.image-content img {
width: 100%;
height: 100%;
width: 100px;
object-fit: contain;
height: 100px;
}
.image-content:hover img {
filter: brightness(70%);

View file

@ -1,7 +1,7 @@
<template>
<div class="content">
<errors-list-template :errors="errors" v-if="errors" />
<div class="content-inner">
<div class="content-inner" :key="key">
<div class="top">
<profile-picture
class="server-avatar"
@ -65,7 +65,8 @@ export default {
changed: false,
errors: null,
avatarDomain: config.domain + "/avatars/",
update: {}
update: {},
key: 1,
};
},
methods: {
@ -93,6 +94,7 @@ export default {
this.requestSent = false;
return;
}
this.key = Math.random();
this.update = {};
this.requestSent = false;
},

View file

@ -3,7 +3,7 @@
<errors-list-template :errors="errors" v-if="errors" />
<div class="inner-content">
<div class="left">
<form>
<form :key="key">
<div class="outer-input">
<div class="title">Username</div>
<div class="user-tag">
@ -18,7 +18,7 @@
</div>
<div class="outer-input">
<div class="title">Current Password</div>
<input type="password" autocomplete="new-password" @input="inputEvent('password', $event)" />
<input type="password" autocomplete="new-password" ref="passwordInput" @input="inputEvent('password', $event)" />
</div>
<div class="link" v-if="!resetPassword" @click="resetPassword = true">Reset Password</div>
<div class="outer-input" v-if="resetPassword">
@ -68,7 +68,8 @@ export default {
requestSent: false,
changed: false,
resetPassword: false,
update: {}
update: {},
key: 0,
};
},
methods: {
@ -121,8 +122,10 @@ export default {
}
this.errors = data.errors;
} else {
this.$refs['passwordInput'].value = "";
this.resetPassword = false
this.update = {};
this.$set(this, 'update', {})
this.key = Math.random();
}
this.requestSent = false;
}

View file

@ -36,7 +36,7 @@ export default {
return undefined;
const embed = this.$refs['embed'];
const messageLog = document.querySelector('.scroll');
const messageLog = document.querySelector('.message-logs');
const w = messageLog.offsetWidth;
const h = messageLog.offsetHeight;
@ -77,7 +77,7 @@ export default {
imageURL() {
const image = this.embed.image;
if (!image) return undefined;
if (typeof image === 'string') return image;
if (typeof image === 'string') return undefined;
if (image.url) return image.url;
return undefined;
}

View file

@ -5,12 +5,16 @@ import channelService from "@/services/channelService";
import messagesService from "@/services/messagesService";
const state = {
messages: {}
messages: {},
scrollPosition: {}
};
const getters = {
messages(state) {
return state.messages;
},
scrollPosition(state) {
return state.scrollPosition
}
};
@ -103,6 +107,14 @@ const actions = {
return true;
}
})
},
changeScrollPosition(context, {channelID, pos}) {
context.commit('changeScrollPosition', {channelID, pos})
},
unloadTopMessages(context, {channelID}) {
const messages = context.state.messages[channelID];
const unloaded = messages.slice(Math.max(messages.length - 50, 0))
context.commit('messages', {channelID, messages: unloaded})
}
};
@ -127,6 +139,9 @@ async function getMessages(context, channelID, isServerChannel) {
const mutations = {
changeScrollPosition(state, {channelID, pos}) {
Vue.set(state.scrollPosition, channelID, pos);
},
deleteMessage(state, {channelID, index}) {
Vue.delete(state.messages[channelID], index)
},

View file

@ -18,11 +18,12 @@ const config = [
version: 6.1,
title: "Download button, bug fixes",
shortTitle: "",
date: "13/08/2019",
headColor: "rgba(205, 80, 87, 0.77)",
date: "16/08/2019",
headColor: "rgba(25, 300, 87, 0.77)",
new: [
"Added download button.",
"Scroll up to load more messages.",
"Scroll to bottom button has been added."
],
fix: [
"Fixed emoji size (2emx2em).",