mirror of
https://github.com/Troplo/Colubrina.git
synced 2024-11-24 20:26:44 +11:00
1.0.30
This commit is contained in:
parent
88012b1930
commit
87976eac86
5 changed files with 243 additions and 114 deletions
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "colubrina",
|
||||
"version": "1.0.29",
|
||||
"version": "1.0.30",
|
||||
"description": "Simple instant communication.",
|
||||
"private": true,
|
||||
"author": "Troplo <troplo@troplo.com>",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
$vuetify.theme.themes[$vuetify.theme.dark ? 'dark' : 'light'].bg
|
||||
"
|
||||
>
|
||||
<DevOverlay v-if="$store.state.site.release === 'dev'"></DevOverlay>
|
||||
<v-overlay :value="$store.state.site.loading">
|
||||
<v-progress-circular indeterminate size="64"></v-progress-circular>
|
||||
</v-overlay>
|
||||
|
@ -454,10 +455,12 @@
|
|||
import AjaxErrorHandler from "@/lib/errorHandler"
|
||||
import { VueFinalModal } from "vue-final-modal"
|
||||
import Header from "@/components/Header"
|
||||
import DevOverlay from "@/components/DevOverlay"
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
DevOverlay,
|
||||
VueFinalModal,
|
||||
Header,
|
||||
editor: require("vue2-ace-editor")
|
||||
|
|
|
@ -1,3 +1,33 @@
|
|||
.colubrina-alert {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.dev-overlay {
|
||||
position: absolute;
|
||||
z-index: 2000;
|
||||
background-color: rgba(0, 0, 0, 0.37);
|
||||
text-align: center;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.dev-header {
|
||||
padding: 10px;
|
||||
cursor: move;
|
||||
z-index: 2001;
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
.chat-col {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
>>>.max-v-list-height {
|
||||
max-height: 10px;
|
||||
overflow-y: auto;
|
||||
|
|
91
frontend/src/components/DevOverlay.vue
Normal file
91
frontend/src/components/DevOverlay.vue
Normal file
|
@ -0,0 +1,91 @@
|
|||
<template>
|
||||
<div class="dev-overlay" id="dev-overlay">
|
||||
<div class="dev-header" id="dev-header">Colubrina DevTools</div>
|
||||
<v-container>
|
||||
<v-btn @click="benchmark"
|
||||
>Benchmark
|
||||
<template v-if="isBenchmarking">({{ multiplier }}x)</template></v-btn
|
||||
><br />
|
||||
<template v-if="isBenchmarking"
|
||||
>Please restart the Colubrina server to stop the benchmark.</template
|
||||
><br />
|
||||
<v-btn @click="deleteBenchmark">Delete benchuser messages</v-btn>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AjaxErrorHandler from "@/lib/errorHandler"
|
||||
export default {
|
||||
name: "DevOverlay",
|
||||
data() {
|
||||
return {
|
||||
isBenchmarking: false,
|
||||
multiplier: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteBenchmark() {
|
||||
this.axios.delete("/api/v1/debug/bench").catch((e) => {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
})
|
||||
},
|
||||
benchmark() {
|
||||
this.axios
|
||||
.get("/api/v1/debug/bench")
|
||||
.then(() => {
|
||||
this.isBenchmarking = true
|
||||
this.multiplier += 1
|
||||
})
|
||||
.catch((e) => {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
})
|
||||
},
|
||||
drag(element) {
|
||||
let pos1 = 0,
|
||||
pos2 = 0,
|
||||
pos3 = 0,
|
||||
pos4 = 0
|
||||
if (document.getElementById("dev-header")) {
|
||||
// if present, the header is where you move the DIV from:
|
||||
document.getElementById("dev-header").onmousedown = dragMouseDown
|
||||
} else {
|
||||
// otherwise, move the DIV from anywhere inside the DIV:
|
||||
element.onmousedown = dragMouseDown
|
||||
}
|
||||
|
||||
function dragMouseDown(e) {
|
||||
e = e || window.event
|
||||
e.preventDefault()
|
||||
// get the mouse cursor position at startup:
|
||||
pos3 = e.clientX
|
||||
pos4 = e.clientY
|
||||
document.onmouseup = closeDragElement
|
||||
// call a function whenever the cursor moves:
|
||||
document.onmousemove = elementDrag
|
||||
}
|
||||
|
||||
function elementDrag(e) {
|
||||
e = e || window.event
|
||||
e.preventDefault()
|
||||
pos1 = pos3 - e.clientX
|
||||
pos2 = pos4 - e.clientY
|
||||
pos3 = e.clientX
|
||||
pos4 = e.clientY
|
||||
element.style.top = element.offsetTop - pos2 + "px"
|
||||
element.style.left = element.offsetLeft - pos1 + "px"
|
||||
}
|
||||
|
||||
function closeDragElement() {
|
||||
document.onmouseup = null
|
||||
document.onmousemove = null
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.drag(document.getElementById("dev-overlay"))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -174,11 +174,110 @@
|
|||
<v-row v-if="!loading" @drop="handleDrag" no-gutters>
|
||||
<v-col class="flex-grow-1 flex-shrink-1 pb-0" id="chat-col">
|
||||
<v-card
|
||||
class="d-flex flex-column fill-height rounded-0 mb-n3"
|
||||
class="d-flex flex-column-reverse fill-height rounded-0 mb-n3 chat-col"
|
||||
style="overflow: auto; height: calc(100vh - 24px - 40px)"
|
||||
color="card"
|
||||
elevation="0"
|
||||
>
|
||||
<v-card-text>
|
||||
<v-toolbar
|
||||
@click="jumpToMessage(replying?.id)"
|
||||
elevation="0"
|
||||
height="35"
|
||||
color="card"
|
||||
v-if="replying"
|
||||
style="cursor: pointer; overflow: hidden"
|
||||
>
|
||||
<v-icon class="mr-2">mdi-reply</v-icon>
|
||||
<v-avatar size="24" class="mr-2">
|
||||
<v-img
|
||||
:src="
|
||||
$store.state.baseURL +
|
||||
'/usercontent/' +
|
||||
replying.user.avatar
|
||||
"
|
||||
v-if="replying.user.avatar"
|
||||
class="elevation-1"
|
||||
/>
|
||||
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
|
||||
</v-avatar>
|
||||
<template v-if="replying.attachments.length">
|
||||
<v-icon class="mr-2">mdi-file-image</v-icon>
|
||||
</template>
|
||||
<template v-if="!replying.content && replying.attachments.length">
|
||||
Click to view attachment
|
||||
</template>
|
||||
{{ replying.content.substring(0, 100) }}
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="replying = null" class="mr-2" small>
|
||||
<v-icon> mdi-close </v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
<v-fade-transition v-model="avoidAutoScroll">
|
||||
<v-toolbar
|
||||
height="22"
|
||||
color="toolbar"
|
||||
elevation="0"
|
||||
style="
|
||||
border-radius: 20px 20px 0 0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: -30px;
|
||||
margin-bottom: -27px;
|
||||
z-index: 20;
|
||||
"
|
||||
width="100%"
|
||||
@click="forceBottom"
|
||||
v-if="avoidAutoScroll"
|
||||
>
|
||||
<div>
|
||||
<v-icon size="16px"> mdi-arrow-down </v-icon>
|
||||
Jump to bottom...
|
||||
</div>
|
||||
</v-toolbar>
|
||||
</v-fade-transition>
|
||||
<v-fade-transition
|
||||
v-model="usersTyping.length"
|
||||
v-if="$vuetify.breakpoint.mobile"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
border-radius: 0 0 20px 20px;
|
||||
position: relative;
|
||||
top: -30px;
|
||||
margin-bottom: -22px;
|
||||
"
|
||||
v-if="usersTyping.length"
|
||||
>
|
||||
{{ usersTyping.map((user) => getName(user)).join(", ") }}
|
||||
{{ usersTyping.length > 1 ? " are" : " is" }} typing...
|
||||
</div>
|
||||
</v-fade-transition>
|
||||
<CommsInput
|
||||
:chat="chat"
|
||||
:replying="replying"
|
||||
:editLastMessage="editLastMessage"
|
||||
:autoScroll="autoScroll"
|
||||
:endSend="endSend"
|
||||
></CommsInput>
|
||||
<v-fade-transition
|
||||
v-model="usersTyping.length"
|
||||
v-if="!$vuetify.breakpoint.mobile"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
border-radius: 0 0 20px 20px;
|
||||
position: absolute;
|
||||
margin-top: -2px;
|
||||
bottom: 1px;
|
||||
"
|
||||
v-if="usersTyping.length"
|
||||
>
|
||||
{{ usersTyping.map((user) => getName(user)).join(", ") }}
|
||||
{{ usersTyping.length > 1 ? " are" : " is" }} typing...
|
||||
</div>
|
||||
</v-fade-transition>
|
||||
</v-card-text>
|
||||
<v-card-text class="flex-grow-1 overflow-y-auto" id="message-list">
|
||||
<v-card-title
|
||||
v-if="
|
||||
|
@ -312,104 +411,6 @@
|
|||
</span>
|
||||
</v-tooltip>
|
||||
</v-card-text>
|
||||
<v-card-text>
|
||||
<v-toolbar
|
||||
@click="jumpToMessage(replying?.id)"
|
||||
elevation="0"
|
||||
height="35"
|
||||
color="card"
|
||||
v-if="replying"
|
||||
style="cursor: pointer; overflow: hidden"
|
||||
>
|
||||
<v-icon class="mr-2">mdi-reply</v-icon>
|
||||
<v-avatar size="24" class="mr-2">
|
||||
<v-img
|
||||
:src="
|
||||
$store.state.baseURL +
|
||||
'/usercontent/' +
|
||||
replying.user.avatar
|
||||
"
|
||||
v-if="replying.user.avatar"
|
||||
class="elevation-1"
|
||||
/>
|
||||
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
|
||||
</v-avatar>
|
||||
<template v-if="replying.attachments.length">
|
||||
<v-icon class="mr-2">mdi-file-image</v-icon>
|
||||
</template>
|
||||
<template v-if="!replying.content && replying.attachments.length">
|
||||
Click to view attachment
|
||||
</template>
|
||||
{{ replying.content.substring(0, 100) }}
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="replying = null" class="mr-2" small>
|
||||
<v-icon> mdi-close </v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
<v-fade-transition v-model="avoidAutoScroll">
|
||||
<v-toolbar
|
||||
height="22"
|
||||
color="toolbar"
|
||||
elevation="0"
|
||||
style="
|
||||
border-radius: 20px 20px 0 0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: -30px;
|
||||
margin-bottom: -27px;
|
||||
"
|
||||
width="100%"
|
||||
@click="forceBottom"
|
||||
v-if="avoidAutoScroll"
|
||||
>
|
||||
<div>
|
||||
<v-icon size="16px"> mdi-arrow-down </v-icon>
|
||||
Jump to bottom...
|
||||
</div>
|
||||
</v-toolbar>
|
||||
</v-fade-transition>
|
||||
<v-fade-transition
|
||||
v-model="usersTyping.length"
|
||||
v-if="$vuetify.breakpoint.mobile"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
border-radius: 0 0 20px 20px;
|
||||
position: relative;
|
||||
top: -30px;
|
||||
margin-bottom: -22px;
|
||||
"
|
||||
v-if="usersTyping.length"
|
||||
>
|
||||
{{ usersTyping.map((user) => getName(user)).join(", ") }}
|
||||
{{ usersTyping.length > 1 ? " are" : " is" }} typing...
|
||||
</div>
|
||||
</v-fade-transition>
|
||||
<CommsInput
|
||||
:chat="chat"
|
||||
:replying="replying"
|
||||
:editLastMessage="editLastMessage"
|
||||
:autoScroll="autoScroll"
|
||||
:endSend="endSend"
|
||||
></CommsInput>
|
||||
<v-fade-transition
|
||||
v-model="usersTyping.length"
|
||||
v-if="!$vuetify.breakpoint.mobile"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
border-radius: 0 0 20px 20px;
|
||||
position: absolute;
|
||||
margin-top: -2px;
|
||||
bottom: 1px;
|
||||
"
|
||||
v-if="usersTyping.length"
|
||||
>
|
||||
{{ usersTyping.map((user) => getName(user)).join(", ") }}
|
||||
{{ usersTyping.length > 1 ? " are" : " is" }} typing...
|
||||
</div>
|
||||
</v-fade-transition>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col
|
||||
|
@ -1156,20 +1157,24 @@ export default {
|
|||
}
|
||||
})
|
||||
this.$socket.on("readReceipt", (data) => {
|
||||
if (
|
||||
data.messageId &&
|
||||
data.chatId === this.chat.chatId &&
|
||||
this.messages?.length
|
||||
) {
|
||||
this.messages.forEach((message) => {
|
||||
message.readReceipts = message.readReceipts.filter(
|
||||
(readReceipt) => readReceipt.id !== data.id
|
||||
)
|
||||
})
|
||||
this.messages
|
||||
.find((message) => message.id === data.messageId)
|
||||
.readReceipts?.push(data)
|
||||
this.autoScroll()
|
||||
try {
|
||||
if (
|
||||
data.messageId &&
|
||||
data.chatId === this.chat.chatId &&
|
||||
this.messages?.length
|
||||
) {
|
||||
this.messages.forEach((message) => {
|
||||
message.readReceipts = message.readReceipts.filter(
|
||||
(readReceipt) => readReceipt.id !== data.id
|
||||
)
|
||||
})
|
||||
this.messages
|
||||
.find((message) => message.id === data.messageId)
|
||||
.readReceipts?.push(data)
|
||||
this.autoScroll()
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Read receipt error", e)
|
||||
}
|
||||
})
|
||||
this.$socket.on("message", (message) => {
|
||||
|
|
Loading…
Reference in a new issue