mirror of
https://github.com/Troplo/Colubrina.git
synced 2024-11-22 19:27:55 +11:00
1.0.12
This commit is contained in:
parent
75df196dae
commit
5ea4d835bd
12 changed files with 351 additions and 428 deletions
34
.github/workflows/desktop.yml
vendored
Normal file
34
.github/workflows/desktop.yml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
name: Build/release
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Install Node.js, NPM and Yarn
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 10
|
||||||
|
|
||||||
|
- name: Build/release Electron app
|
||||||
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
|
with:
|
||||||
|
# GitHub token, automatically provided to the action
|
||||||
|
# (No need to define this secret in the repo settings)
|
||||||
|
github_token: ${{ secrets.github_token }}
|
||||||
|
|
||||||
|
# If the commit is tagged with a version (e.g. "v1.0.0"),
|
||||||
|
# release the app after building
|
||||||
|
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
package_root: "./frontend",
|
||||||
|
use_vue_cli: true,
|
||||||
|
build_script_name: "electron:build"
|
17
backend/migrations/20220802105441-removeOldUserColumns.js
Normal file
17
backend/migrations/20220802105441-removeOldUserColumns.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
async up(queryInterface, Sequelize) {
|
||||||
|
await queryInterface.removeColumn("Users", "guidedWizard")
|
||||||
|
await queryInterface.removeColumn("Users", "emailDirectLogin")
|
||||||
|
},
|
||||||
|
|
||||||
|
async down(queryInterface, Sequelize) {
|
||||||
|
/**
|
||||||
|
* Add reverting commands here.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* await queryInterface.dropTable('users');
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,11 +64,6 @@ module.exports = (sequelize, DataTypes) => {
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
defaultValue: null
|
defaultValue: null
|
||||||
},
|
},
|
||||||
guidedWizard: {
|
|
||||||
type: DataTypes.BOOLEAN,
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: true
|
|
||||||
},
|
|
||||||
privacy: {
|
privacy: {
|
||||||
type: DataTypes.JSON,
|
type: DataTypes.JSON,
|
||||||
defaultValue: {
|
defaultValue: {
|
||||||
|
@ -99,11 +94,6 @@ module.exports = (sequelize, DataTypes) => {
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
defaultValue: false
|
defaultValue: false
|
||||||
},
|
},
|
||||||
emailDirectLogin: {
|
|
||||||
type: DataTypes.BOOLEAN,
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: false
|
|
||||||
},
|
|
||||||
email: {
|
email: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "colubrina",
|
"name": "colubrina",
|
||||||
"version": "1.0.11",
|
"version": "1.0.12",
|
||||||
"private": true,
|
"private": true,
|
||||||
"author": "Troplo <troplo@troplo.com>",
|
"author": "Troplo <troplo@troplo.com>",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
.offset-message {
|
||||||
|
padding-left: 47px;
|
||||||
|
}
|
||||||
|
.message-action-card {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
/* large codeblock */
|
/* large codeblock */
|
||||||
code {
|
code {
|
||||||
background-color: var(--v-bg-base) !important;
|
background-color: var(--v-bg-base) !important;
|
||||||
|
|
|
@ -19,7 +19,11 @@
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
<v-textarea
|
<v-textarea
|
||||||
style="margin-bottom: 5px"
|
:style="
|
||||||
|
!$vuetify.breakpoint.mobile
|
||||||
|
? 'margin-bottom: 3px'
|
||||||
|
: 'margin-bottom: -17px'
|
||||||
|
"
|
||||||
autofocus
|
autofocus
|
||||||
label="Type a message"
|
label="Type a message"
|
||||||
placeholder="Keep it civil"
|
placeholder="Keep it civil"
|
||||||
|
@ -193,12 +197,13 @@ export default {
|
||||||
},
|
},
|
||||||
handlePaste(data) {
|
handlePaste(data) {
|
||||||
if (data.clipboardData.items.length) {
|
if (data.clipboardData.items.length) {
|
||||||
const item = data.clipboardData.items[0]
|
for (const item of data.clipboardData.items) {
|
||||||
if (item.kind === "file") {
|
if (item.kind === "file") {
|
||||||
this.file = item.getAsFile()
|
this.file = item.getAsFile()
|
||||||
this.getURLForImage()
|
this.getURLForImage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
focusInput() {
|
focusInput() {
|
||||||
const input = document.getElementById("message-input")
|
const input = document.getElementById("message-input")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<v-hover v-slot="{ hover }">
|
<v-hover v-slot="{ hover }">
|
||||||
<div>
|
<span>
|
||||||
<template v-if="!message.type">
|
<template v-if="!message.type">
|
||||||
<v-toolbar
|
<v-toolbar
|
||||||
@click="jumpToMessage(message.replyId)"
|
@click="jumpToMessage(message.replyId)"
|
||||||
|
@ -40,8 +40,9 @@
|
||||||
:class="{ 'message-hover': hover }"
|
:class="{ 'message-hover': hover }"
|
||||||
:id="'message-' + index"
|
:id="'message-' + index"
|
||||||
@contextmenu="show($event, 'message', message)"
|
@contextmenu="show($event, 'message', message)"
|
||||||
|
:style="lastMessage ? 'margin-bottom: -5px; margin-top: -5px;' : ''"
|
||||||
>
|
>
|
||||||
<v-avatar size="48" class="mr-2">
|
<v-avatar size="40" class="mr-2" v-if="!lastMessage">
|
||||||
<v-img
|
<v-img
|
||||||
:src="
|
:src="
|
||||||
$store.state.baseURL + '/usercontent/' + message.user.avatar
|
$store.state.baseURL + '/usercontent/' + message.user.avatar
|
||||||
|
@ -51,8 +52,15 @@
|
||||||
/>
|
/>
|
||||||
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
|
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
<v-list-item-content>
|
<small
|
||||||
<v-list-item-subtitle>
|
style="font-size: 9px; position: absolute"
|
||||||
|
class="grey--text"
|
||||||
|
v-if="lastMessage && hover"
|
||||||
|
>
|
||||||
|
{{ $date(message.createdAt).format("hh:mm A") }}
|
||||||
|
</small>
|
||||||
|
<v-list-item-content :class="{ 'offset-message': lastMessage }">
|
||||||
|
<v-list-item-subtitle v-if="!lastMessage">
|
||||||
{{ getName(message.user) }}
|
{{ getName(message.user) }}
|
||||||
<v-chip
|
<v-chip
|
||||||
v-if="message.user.bot"
|
v-if="message.user.bot"
|
||||||
|
@ -87,13 +95,27 @@
|
||||||
</span>
|
</span>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
</v-list-item-subtitle>
|
</v-list-item-subtitle>
|
||||||
<p
|
<span
|
||||||
v-if="edit.id !== message.id"
|
v-if="edit.id !== message.id"
|
||||||
v-markdown
|
|
||||||
style="overflow-wrap: anywhere"
|
style="overflow-wrap: anywhere"
|
||||||
>
|
>
|
||||||
|
<span v-markdown>
|
||||||
{{ message.content }}
|
{{ message.content }}
|
||||||
</p>
|
</span>
|
||||||
|
<v-tooltip top v-if="message.edited && lastMessage">
|
||||||
|
<template v-slot:activator="{ on }">
|
||||||
|
<v-icon color="grey" small v-on="on" style="">
|
||||||
|
mdi-pencil
|
||||||
|
</v-icon>
|
||||||
|
</template>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
$date(message.editedAt).format("DD/MM/YYYY hh:mm:ss A")
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</v-tooltip>
|
||||||
|
</span>
|
||||||
|
|
||||||
<template v-if="edit.id !== message.id">
|
<template v-if="edit.id !== message.id">
|
||||||
<v-row
|
<v-row
|
||||||
v-for="(embed, index) in message.embeds"
|
v-for="(embed, index) in message.embeds"
|
||||||
|
@ -339,7 +361,14 @@
|
||||||
{{ attachment.name }}
|
{{ attachment.name }}
|
||||||
</span>
|
</span>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-actions>
|
<v-card-actions
|
||||||
|
v-if="
|
||||||
|
attachment.extension !== 'jpg' &&
|
||||||
|
attachment.extension !== 'png' &&
|
||||||
|
attachment.extension !== 'jpeg' &&
|
||||||
|
attachment.extension !== 'gif'
|
||||||
|
"
|
||||||
|
>
|
||||||
{{ attachment.name }} -
|
{{ attachment.name }} -
|
||||||
{{ friendlySize(attachment.size) }}
|
{{ friendlySize(attachment.size) }}
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
|
@ -366,17 +395,32 @@
|
||||||
v-if="edit.id === message.id"
|
v-if="edit.id === message.id"
|
||||||
></CommsInput>
|
></CommsInput>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
<v-list-item-action v-if="!$vuetify.breakpoint.mobile && hover">
|
<v-card
|
||||||
<v-list-item-subtitle>
|
elevation="3"
|
||||||
|
class="message-action-card"
|
||||||
|
v-if="!$vuetify.breakpoint.mobile && hover"
|
||||||
|
>
|
||||||
|
<v-tooltip top>
|
||||||
|
<template v-slot:activator="{ on }">
|
||||||
|
<span v-on="on">
|
||||||
<v-btn
|
<v-btn
|
||||||
icon
|
icon
|
||||||
|
v-on="on"
|
||||||
v-if="message.userId === $store.state.user.id"
|
v-if="message.userId === $store.state.user.id"
|
||||||
@click="deleteMessage(message)"
|
@click="deleteMessage(message)"
|
||||||
>
|
>
|
||||||
<v-icon> mdi-delete </v-icon>
|
<v-icon> mdi-delete </v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<span> Delete </span>
|
||||||
|
</v-tooltip>
|
||||||
|
<v-tooltip top>
|
||||||
|
<template v-slot:activator="{ on }">
|
||||||
|
<span v-on="on">
|
||||||
<v-btn
|
<v-btn
|
||||||
icon
|
icon
|
||||||
|
v-on="on"
|
||||||
@click="
|
@click="
|
||||||
edit.content = message.content
|
edit.content = message.content
|
||||||
edit.editing = true
|
edit.editing = true
|
||||||
|
@ -389,7 +433,15 @@
|
||||||
>
|
>
|
||||||
<v-icon> mdi-pencil </v-icon>
|
<v-icon> mdi-pencil </v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<span> Edit </span>
|
||||||
|
</v-tooltip>
|
||||||
|
<v-tooltip top>
|
||||||
|
<template v-slot:activator="{ on }">
|
||||||
|
<span v-on="on">
|
||||||
<v-btn
|
<v-btn
|
||||||
|
v-on="on"
|
||||||
icon
|
icon
|
||||||
@click="
|
@click="
|
||||||
edit.content = ''
|
edit.content = ''
|
||||||
|
@ -403,18 +455,36 @@
|
||||||
>
|
>
|
||||||
<v-icon> mdi-close </v-icon>
|
<v-icon> mdi-close </v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<span> Discard Edits </span>
|
||||||
|
</v-tooltip>
|
||||||
|
<v-tooltip top>
|
||||||
|
<template v-slot:activator="{ on }">
|
||||||
|
<span v-on="on">
|
||||||
<v-btn
|
<v-btn
|
||||||
icon
|
icon
|
||||||
@click="
|
@click="
|
||||||
replying(message)
|
replying(message)
|
||||||
focusInput()
|
focusInput()
|
||||||
"
|
"
|
||||||
|
v-on="on"
|
||||||
>
|
>
|
||||||
<v-icon> mdi-reply </v-icon>
|
<v-icon> mdi-reply </v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<span> Reply </span>
|
||||||
|
</v-tooltip>
|
||||||
|
<v-tooltip top>
|
||||||
|
<template v-slot:activator="{ on }">
|
||||||
|
<span v-on="on">
|
||||||
<v-btn
|
<v-btn
|
||||||
|
v-on="on"
|
||||||
icon
|
icon
|
||||||
v-if="chat.rank === 'admin' || chat.chat.type === 'direct'"
|
v-if="
|
||||||
|
chat.rank === 'admin' || chat.chat.type === 'direct'
|
||||||
|
"
|
||||||
@click="
|
@click="
|
||||||
pinMessage()
|
pinMessage()
|
||||||
focusInput()
|
focusInput()
|
||||||
|
@ -422,8 +492,11 @@
|
||||||
>
|
>
|
||||||
<v-icon> mdi-pin </v-icon>
|
<v-icon> mdi-pin </v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-list-item-subtitle>
|
</span>
|
||||||
</v-list-item-action>
|
</template>
|
||||||
|
<span> Pin to Chat </span>
|
||||||
|
</v-tooltip>
|
||||||
|
</v-card>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="message.type === 'leave'">
|
<template v-else-if="message.type === 'leave'">
|
||||||
|
@ -721,7 +794,7 @@
|
||||||
</v-list-item-action>
|
</v-list-item-action>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</span>
|
||||||
</v-hover>
|
</v-hover>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -764,7 +837,8 @@ export default {
|
||||||
"index",
|
"index",
|
||||||
"show",
|
"show",
|
||||||
"setImagePreview",
|
"setImagePreview",
|
||||||
"deleteMessage"
|
"deleteMessage",
|
||||||
|
"lastMessage"
|
||||||
],
|
],
|
||||||
components: {
|
components: {
|
||||||
CommsInput,
|
CommsInput,
|
||||||
|
|
|
@ -18,26 +18,13 @@ import "highlight.js/styles/github.css"
|
||||||
const md = require("markdown-it")({
|
const md = require("markdown-it")({
|
||||||
html: false, // Enable HTML tags in source
|
html: false, // Enable HTML tags in source
|
||||||
xhtmlOut: false, // Use '/' to close single tags (<br />).
|
xhtmlOut: false, // Use '/' to close single tags (<br />).
|
||||||
// This is only for full CommonMark compatibility.
|
|
||||||
breaks: true, // Convert '\n' in paragraphs into <br>
|
breaks: true, // Convert '\n' in paragraphs into <br>
|
||||||
langPrefix: "language-", // CSS language prefix for fenced blocks. Can be
|
langPrefix: "language-", // CSS language prefix for fenced blocks. Can be
|
||||||
// useful for external highlighters.
|
|
||||||
linkify: true, // Autoconvert URL-like text to links
|
linkify: true, // Autoconvert URL-like text to links
|
||||||
|
|
||||||
// Enable some language-neutral replacement + quotes beautification
|
|
||||||
// For the full list of replacements, see https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/replacements.js
|
|
||||||
typographer: false,
|
typographer: false,
|
||||||
|
|
||||||
// Double + single quotes replacement pairs, when typographer enabled,
|
|
||||||
// and smartquotes on. Could be either a String or an Array.
|
|
||||||
//
|
|
||||||
// For example, you can use '«»„“' for Russian, '„“‚‘' for German,
|
|
||||||
// and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
|
|
||||||
quotes: "“”‘’",
|
quotes: "“”‘’",
|
||||||
|
|
||||||
// Highlighter function. Should return escaped HTML,
|
|
||||||
// or '' if the source string is not changed and should be escaped externally.
|
|
||||||
// If result starts with <pre... internal wrapper is skipped.
|
|
||||||
highlight: function (/*str, lang*/) {
|
highlight: function (/*str, lang*/) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -95,7 +82,11 @@ Vue.use(VueAxios, axios)
|
||||||
Vue.use(Toast)
|
Vue.use(Toast)
|
||||||
Vue.directive("markdown", {
|
Vue.directive("markdown", {
|
||||||
inserted(el) {
|
inserted(el) {
|
||||||
el.innerHTML = md.render(el.innerHTML)
|
el.innerHTML = md
|
||||||
|
.render(el.innerHTML)
|
||||||
|
.replaceAll("&", "&")
|
||||||
|
.replaceAll("<p>", "<span>")
|
||||||
|
.replaceAll("</p>", "</span>")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (process.env.NODE_ENV === "development") {
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="communications-chat" @dragover.prevent @drop.prevent>
|
<div id="communications-chat" @dragover.prevent @drop.prevent="handleDrag">
|
||||||
<v-menu
|
<v-menu
|
||||||
:position-x="$store.state.context.pins.x"
|
:position-x="$store.state.context.pins.x"
|
||||||
:position-y="60"
|
:position-y="60"
|
||||||
|
@ -83,8 +83,7 @@
|
||||||
<v-dialog
|
<v-dialog
|
||||||
v-model="preview.dialog"
|
v-model="preview.dialog"
|
||||||
elevation="0"
|
elevation="0"
|
||||||
:width="preview.width"
|
:min-height="300"
|
||||||
:height="preview.height"
|
|
||||||
:max-width="1000"
|
:max-width="1000"
|
||||||
:max-height="600"
|
:max-height="600"
|
||||||
content-class="rounded-0"
|
content-class="rounded-0"
|
||||||
|
@ -94,12 +93,20 @@
|
||||||
:src="preview.src"
|
:src="preview.src"
|
||||||
:max-width="1000"
|
:max-width="1000"
|
||||||
:max-height="600"
|
:max-height="600"
|
||||||
|
:min-height="300"
|
||||||
|
aspect-ratio="16/9"
|
||||||
contain
|
contain
|
||||||
></v-img>
|
></v-img>
|
||||||
<v-container>
|
<v-container>
|
||||||
<a :href="preview.src" style="text-decoration: none" target="_blank">
|
<a :href="preview.src" style="text-decoration: none" target="_blank">
|
||||||
<small> Open Externally </small>
|
<small> Open Externally </small>
|
||||||
</a>
|
</a>
|
||||||
|
<small
|
||||||
|
class="float-end"
|
||||||
|
style="text-decoration: none; color: inherit"
|
||||||
|
>
|
||||||
|
{{ preview.name }}
|
||||||
|
</small>
|
||||||
</v-container>
|
</v-container>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
|
@ -160,7 +167,7 @@
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
<v-row v-if="!loading" @drop="handleDrag" no-gutters>
|
<v-row v-if="!loading" @drop="handleDrag" no-gutters>
|
||||||
<v-col class="flex-grow-1 flex-shrink-1" id="chat-col">
|
<v-col class="flex-grow-1 flex-shrink-1 pb-0" id="chat-col">
|
||||||
<v-card
|
<v-card
|
||||||
class="d-flex flex-column fill-height rounded-xl"
|
class="d-flex flex-column fill-height rounded-xl"
|
||||||
style="overflow: auto; height: calc(100vh - 24px - 40px - 40px)"
|
style="overflow: auto; height: calc(100vh - 24px - 40px - 40px)"
|
||||||
|
@ -211,6 +218,14 @@
|
||||||
:show="show"
|
:show="show"
|
||||||
:set-image-preview="setImagePreview"
|
:set-image-preview="setImagePreview"
|
||||||
:delete-message="deleteMessage"
|
:delete-message="deleteMessage"
|
||||||
|
:last-message="
|
||||||
|
messages[index - 1]?.userId === message?.userId &&
|
||||||
|
$date(message.createdAt).diff(
|
||||||
|
messages[index - 1]?.createdAt,
|
||||||
|
'minute'
|
||||||
|
) < 10 &&
|
||||||
|
!message.replyId
|
||||||
|
"
|
||||||
></Message>
|
></Message>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -335,7 +350,6 @@
|
||||||
style="
|
style="
|
||||||
border-radius: 20px 20px 0 0;
|
border-radius: 20px 20px 0 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 50;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -30px;
|
top: -30px;
|
||||||
margin-bottom: -27px;
|
margin-bottom: -27px;
|
||||||
|
@ -350,6 +364,30 @@
|
||||||
</div>
|
</div>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
</v-fade-transition>
|
</v-fade-transition>
|
||||||
|
<v-fade-transition
|
||||||
|
v-model="usersTyping.length"
|
||||||
|
v-if="$vuetify.breakpoint.mobile"
|
||||||
|
>
|
||||||
|
<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%"
|
||||||
|
v-if="usersTyping.length"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{{ usersTyping.map((user) => getName(user)).join(", ") }}
|
||||||
|
{{ usersTyping.length > 1 ? " are" : " is" }} typing...
|
||||||
|
</div>
|
||||||
|
</v-toolbar>
|
||||||
|
</v-fade-transition>
|
||||||
<CommsInput
|
<CommsInput
|
||||||
:chat="chat"
|
:chat="chat"
|
||||||
:replying="replying"
|
:replying="replying"
|
||||||
|
@ -357,26 +395,22 @@
|
||||||
:autoScroll="autoScroll"
|
:autoScroll="autoScroll"
|
||||||
:endSend="endSend"
|
:endSend="endSend"
|
||||||
></CommsInput>
|
></CommsInput>
|
||||||
<v-fade-transition v-model="usersTyping.length">
|
<v-fade-transition
|
||||||
<v-toolbar
|
v-model="usersTyping.length"
|
||||||
height="22"
|
v-if="!$vuetify.breakpoint.mobile"
|
||||||
elevation="0"
|
>
|
||||||
|
<div
|
||||||
style="
|
style="
|
||||||
border-radius: 0 0 20px 20px;
|
border-radius: 0 0 20px 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-bottom: -2px;
|
margin-top: -22px;
|
||||||
margin-top: -20px;
|
bottom: -16px;
|
||||||
bottom: -14px;
|
|
||||||
"
|
"
|
||||||
width="100%"
|
|
||||||
color="toolbar"
|
|
||||||
v-if="usersTyping.length"
|
v-if="usersTyping.length"
|
||||||
>
|
>
|
||||||
<div style="overflow: hidden">
|
|
||||||
{{ usersTyping.map((user) => getName(user)).join(", ") }}
|
{{ usersTyping.map((user) => getName(user)).join(", ") }}
|
||||||
{{ usersTyping.length > 1 ? " are" : " is" }} typing...
|
{{ usersTyping.length > 1 ? " are" : " is" }} typing...
|
||||||
</div>
|
</div>
|
||||||
</v-toolbar>
|
|
||||||
</v-fade-transition>
|
</v-fade-transition>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
@ -421,314 +455,28 @@
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<v-list two-line color="card" ref="message-list-search">
|
<v-list two-line color="card" ref="message-list-search">
|
||||||
<template v-for="(message, index) in search.results">
|
<template v-for="(message, index) in search.results">
|
||||||
<v-toolbar
|
<div
|
||||||
@click="jumpToMessage(message.replyId)"
|
|
||||||
:key="message.keyId + '-reply-toolbar'"
|
|
||||||
elevation="0"
|
|
||||||
outlined
|
|
||||||
height="40"
|
|
||||||
color="card"
|
|
||||||
v-if="message.reply"
|
|
||||||
style="cursor: pointer"
|
|
||||||
>
|
|
||||||
<v-icon class="mr-2">mdi-reply</v-icon>
|
|
||||||
<v-avatar size="24" class="mr-2">
|
|
||||||
<v-img
|
|
||||||
:src="
|
|
||||||
$store.state.baseURL +
|
|
||||||
'/usercontent/' +
|
|
||||||
message.reply.user.avatar
|
|
||||||
"
|
|
||||||
v-if="message.reply.user.avatar"
|
|
||||||
class="elevation-1"
|
|
||||||
/>
|
|
||||||
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
|
|
||||||
</v-avatar>
|
|
||||||
<template v-if="message.reply.attachments.length">
|
|
||||||
<v-icon class="mr-2">mdi-file-image</v-icon>
|
|
||||||
</template>
|
|
||||||
<template
|
|
||||||
v-if="
|
|
||||||
!message.reply.content && message.reply.attachments.length
|
|
||||||
"
|
|
||||||
>
|
|
||||||
Click to view attachment
|
|
||||||
</template>
|
|
||||||
{{ message.reply.content.substring(0, 100) }}
|
|
||||||
</v-toolbar>
|
|
||||||
<v-list-item
|
|
||||||
style="cursor: pointer"
|
|
||||||
@click="jumpToMessage(message.id)"
|
@click="jumpToMessage(message.id)"
|
||||||
:key="message.keyId"
|
:key="message.keyId"
|
||||||
:class="{
|
style="cursor: pointer"
|
||||||
'text-xs-right': message.userId === $store.state.user.id,
|
|
||||||
'text-xs-left': message.userId !== $store.state.user.id
|
|
||||||
}"
|
|
||||||
:id="'message-' + index"
|
|
||||||
>
|
>
|
||||||
<v-avatar size="48" class="mr-2">
|
<Message
|
||||||
<v-img
|
:message="message"
|
||||||
:src="
|
:jump-to-message="jumpToMessage"
|
||||||
$store.state.baseURL +
|
:edit="edit"
|
||||||
'/usercontent/' +
|
:focus-input="focusInput"
|
||||||
message.user.avatar
|
:replying="setReply"
|
||||||
"
|
:get-name="getName"
|
||||||
v-if="message.user.avatar"
|
:end-edit="endEdit"
|
||||||
class="elevation-1"
|
:auto-scroll="autoScroll"
|
||||||
/>
|
:chat="chat"
|
||||||
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
|
:index="index"
|
||||||
</v-avatar>
|
:show="show"
|
||||||
<v-list-item-content>
|
:set-image-preview="setImagePreview"
|
||||||
<v-list-item-subtitle>
|
:delete-message="deleteMessage"
|
||||||
{{ getName(message.user) }}
|
:last-message="false"
|
||||||
<small>
|
></Message>
|
||||||
{{
|
|
||||||
$date(message.createdAt).format("DD/MM/YYYY hh:mm A")
|
|
||||||
}}</small
|
|
||||||
>
|
|
||||||
<v-tooltip top v-if="message.edited">
|
|
||||||
<template v-slot:activator="{ on, attrs }">
|
|
||||||
<span v-on="on" v-bind="attrs">
|
|
||||||
<v-icon
|
|
||||||
color="grey"
|
|
||||||
small
|
|
||||||
style="
|
|
||||||
margin-bottom: 2px;
|
|
||||||
margin-left: 4px;
|
|
||||||
position: absolute;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
mdi-pencil
|
|
||||||
</v-icon>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<span>
|
|
||||||
{{
|
|
||||||
$date(message.editedAt).format(
|
|
||||||
"DD/MM/YYYY hh:mm:ss A"
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
</v-tooltip>
|
|
||||||
</v-list-item-subtitle>
|
|
||||||
<p
|
|
||||||
v-if="edit.id !== message.id"
|
|
||||||
v-markdown
|
|
||||||
style="overflow-wrap: anywhere"
|
|
||||||
>
|
|
||||||
{{ message.content }}
|
|
||||||
</p>
|
|
||||||
<template v-if="edit.id !== message.id">
|
|
||||||
<v-row
|
|
||||||
v-for="(embed, index) in message.embeds"
|
|
||||||
:key="index"
|
|
||||||
:id="'embed-' + index"
|
|
||||||
>
|
|
||||||
<v-card
|
|
||||||
elevaion="0"
|
|
||||||
color="card"
|
|
||||||
max-width="25%"
|
|
||||||
width="25%"
|
|
||||||
class="ml-3"
|
|
||||||
>
|
|
||||||
<v-container>
|
|
||||||
<v-row v-if="embed.type === 'openGraph'">
|
|
||||||
<v-col
|
|
||||||
cols="12"
|
|
||||||
class="text-xs-center"
|
|
||||||
v-if="embed.openGraph.ogImage"
|
|
||||||
>
|
|
||||||
<v-img
|
|
||||||
:src="
|
|
||||||
embed.openGraph.ogImage?.url ||
|
|
||||||
embed.openGraph.ogImage[0]?.url
|
|
||||||
"
|
|
||||||
class="elevation-1"
|
|
||||||
contain
|
|
||||||
:aspect-ratio="16 / 9"
|
|
||||||
>
|
|
||||||
<template v-slot:placeholder>
|
|
||||||
<v-row
|
|
||||||
class="fill-height ma-0"
|
|
||||||
align="center"
|
|
||||||
justify="center"
|
|
||||||
>
|
|
||||||
<v-progress-circular
|
|
||||||
indeterminate
|
|
||||||
color="grey lighten-5"
|
|
||||||
></v-progress-circular>
|
|
||||||
</v-row>
|
|
||||||
</template>
|
|
||||||
</v-img>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" class="text-xs-center">
|
|
||||||
<h4>
|
|
||||||
{{ embed.openGraph.ogSiteName }}
|
|
||||||
</h4>
|
|
||||||
<a
|
|
||||||
:href="embed.link"
|
|
||||||
target="_blank"
|
|
||||||
style="text-decoration: none"
|
|
||||||
>
|
|
||||||
<h3>
|
|
||||||
{{ embed.openGraph.ogTitle }}
|
|
||||||
</h3>
|
|
||||||
</a>
|
|
||||||
<p v-if="embed.openGraph.ogDescription">
|
|
||||||
{{ embed.openGraph.ogDescription }}
|
|
||||||
</p>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
<template v-else-if="embed.type === 'image'">
|
|
||||||
<v-hover v-slot="{ hover }">
|
|
||||||
<div>
|
|
||||||
<v-img
|
|
||||||
@click="setImagePreview(embed)"
|
|
||||||
contain
|
|
||||||
:aspect-ratio="16 / 9"
|
|
||||||
:src="embed.mediaProxyLink"
|
|
||||||
>
|
|
||||||
<template v-slot:placeholder>
|
|
||||||
<v-row
|
|
||||||
class="fill-height ma-0"
|
|
||||||
align="center"
|
|
||||||
justify="center"
|
|
||||||
>
|
|
||||||
<v-progress-circular
|
|
||||||
indeterminate
|
|
||||||
color="grey lighten-5"
|
|
||||||
></v-progress-circular>
|
|
||||||
</v-row>
|
|
||||||
</template>
|
|
||||||
<template v-slot:default>
|
|
||||||
<v-fade-transition v-if="hover">
|
|
||||||
<v-overlay absolute>
|
|
||||||
<v-icon large
|
|
||||||
>mdi-arrow-expand-all</v-icon
|
|
||||||
>
|
|
||||||
</v-overlay>
|
|
||||||
</v-fade-transition>
|
|
||||||
</template>
|
|
||||||
</v-img>
|
|
||||||
</div>
|
</div>
|
||||||
</v-hover>
|
|
||||||
<v-card-actions>
|
|
||||||
MediaProxy Image
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
icon
|
|
||||||
:href="embed.url"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<v-icon> mdi-download </v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</template>
|
|
||||||
</v-container>
|
|
||||||
</v-card>
|
|
||||||
</v-row>
|
|
||||||
</template>
|
|
||||||
<template v-if="edit.id !== message.id">
|
|
||||||
<v-card
|
|
||||||
v-for="(attachment, index) in message.attachments"
|
|
||||||
:key="attachment.id"
|
|
||||||
:id="'attachment-' + index"
|
|
||||||
max-width="40%"
|
|
||||||
elevaion="0"
|
|
||||||
color="card"
|
|
||||||
>
|
|
||||||
<v-hover
|
|
||||||
v-slot="{ hover }"
|
|
||||||
v-if="
|
|
||||||
attachment.extension === 'jpg' ||
|
|
||||||
attachment.extension === 'png' ||
|
|
||||||
attachment.extension === 'jpeg' ||
|
|
||||||
attachment.extension === 'gif'
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<v-img
|
|
||||||
@click="setImagePreview(attachment)"
|
|
||||||
contain
|
|
||||||
:aspect-ratio="16 / 9"
|
|
||||||
:src="
|
|
||||||
$store.state.baseURL +
|
|
||||||
'/usercontent/' +
|
|
||||||
attachment.attachment
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<template v-slot:placeholder>
|
|
||||||
<v-row
|
|
||||||
class="fill-height ma-0"
|
|
||||||
align="center"
|
|
||||||
justify="center"
|
|
||||||
>
|
|
||||||
<v-progress-circular
|
|
||||||
indeterminate
|
|
||||||
color="grey lighten-5"
|
|
||||||
></v-progress-circular>
|
|
||||||
</v-row>
|
|
||||||
</template>
|
|
||||||
<template v-slot:default>
|
|
||||||
<v-fade-transition v-if="hover">
|
|
||||||
<v-overlay absolute>
|
|
||||||
<v-icon large>mdi-arrow-expand-all</v-icon>
|
|
||||||
</v-overlay>
|
|
||||||
</v-fade-transition>
|
|
||||||
</template>
|
|
||||||
</v-img>
|
|
||||||
</div>
|
|
||||||
</v-hover>
|
|
||||||
<v-card-text v-else>
|
|
||||||
<v-icon class="mr-2" :size="48">
|
|
||||||
{{ fileTypes[attachment.extension] || "mdi-file" }}
|
|
||||||
</v-icon>
|
|
||||||
<span>
|
|
||||||
{{ attachment.name }}
|
|
||||||
</span>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions>
|
|
||||||
{{ attachment.name }} -
|
|
||||||
{{ friendlySize(attachment.size) }}
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
icon
|
|
||||||
:href="
|
|
||||||
$store.state.baseURL +
|
|
||||||
'/usercontent/' +
|
|
||||||
attachment.attachment
|
|
||||||
"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<v-icon> mdi-download </v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</template>
|
|
||||||
<v-text-field
|
|
||||||
v-model="edit.content"
|
|
||||||
v-if="edit.editing && edit.id === message.id"
|
|
||||||
autofocus
|
|
||||||
:value="message.content"
|
|
||||||
label="Type a message"
|
|
||||||
placeholder="Type a message"
|
|
||||||
type="text"
|
|
||||||
ref="edit-input"
|
|
||||||
outlined
|
|
||||||
append-outer-icon="mdi-send"
|
|
||||||
@keyup.enter="editMessage(message)"
|
|
||||||
@keydown.esc="
|
|
||||||
edit.content = ''
|
|
||||||
edit.editing = false
|
|
||||||
edit.id = null
|
|
||||||
focusInput()
|
|
||||||
"
|
|
||||||
@click:append-outer="editMessage(message)"
|
|
||||||
/>
|
|
||||||
</v-list-item-content>
|
|
||||||
</v-list-item>
|
|
||||||
</template>
|
</template>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
@ -912,7 +660,8 @@ export default {
|
||||||
dialog: false,
|
dialog: false,
|
||||||
src: "",
|
src: "",
|
||||||
height: 0,
|
height: 0,
|
||||||
width: 0
|
width: 0,
|
||||||
|
name: ""
|
||||||
},
|
},
|
||||||
fileTypes: {
|
fileTypes: {
|
||||||
png: "mdi-file-image",
|
png: "mdi-file-image",
|
||||||
|
@ -1207,6 +956,7 @@ export default {
|
||||||
this.preview.height = img.height
|
this.preview.height = img.height
|
||||||
this.preview.width = img.width
|
this.preview.width = img.width
|
||||||
this.preview.dialog = true
|
this.preview.dialog = true
|
||||||
|
this.preview.name = attachment.name
|
||||||
}
|
}
|
||||||
img.src = link
|
img.src = link
|
||||||
},
|
},
|
||||||
|
|
|
@ -159,6 +159,10 @@ export default {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.$socket.disconnect()
|
this.$socket.disconnect()
|
||||||
this.$socket.connect()
|
this.$socket.connect()
|
||||||
|
if (this.isElectron()) {
|
||||||
|
this.axios.defaults.baseURL = this.instance
|
||||||
|
localStorage.setItem("instance", this.instance)
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
this.$store.state.site.emailVerification &&
|
this.$store.state.site.emailVerification &&
|
||||||
!this.$store.state.user.emailVerified
|
!this.$store.state.user.emailVerified
|
||||||
|
@ -191,7 +195,6 @@ export default {
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.instanceString = res.data.name + " v" + res.data.latestVersion
|
this.instanceString = res.data.name + " v" + res.data.latestVersion
|
||||||
this.axios.defaults.baseURL = this.instance
|
this.axios.defaults.baseURL = this.instance
|
||||||
localStorage.setItem("instance", this.instance)
|
|
||||||
this.$store.dispatch("getState")
|
this.$store.dispatch("getState")
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
|
|
@ -7,10 +7,23 @@
|
||||||
<v-container>
|
<v-container>
|
||||||
<v-form ref="form" class="pa-4 pt-6">
|
<v-form ref="form" class="pa-4 pt-6">
|
||||||
<p class="text-center text-h4">
|
<p class="text-center text-h4">
|
||||||
Login to
|
Register to
|
||||||
<span class="troplo-title">{{ $store.state.site.name }}</span
|
<span class="troplo-title">{{ $store.state.site.name }}</span
|
||||||
><small style="font-size: 15px">beta</small>
|
><small style="font-size: 15px">beta</small>
|
||||||
</p>
|
</p>
|
||||||
|
<v-text-field
|
||||||
|
@keyup.enter="doRegister()"
|
||||||
|
class="rounded-xl"
|
||||||
|
v-model="instance"
|
||||||
|
v-if="isElectron()"
|
||||||
|
label="Instance URL"
|
||||||
|
placeholder="https://colubrina.troplo.com"
|
||||||
|
type="email"
|
||||||
|
></v-text-field>
|
||||||
|
<small style="float: right" v-if="isElectron()">{{
|
||||||
|
instanceString
|
||||||
|
}}</small
|
||||||
|
><br v-if="isElectron()" />
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@keyup.enter="doRegister()"
|
@keyup.enter="doRegister()"
|
||||||
class="rounded-xl"
|
class="rounded-xl"
|
||||||
|
@ -82,10 +95,29 @@ export default {
|
||||||
email: "",
|
email: "",
|
||||||
totp: "",
|
totp: "",
|
||||||
totpDialog: false,
|
totpDialog: false,
|
||||||
loading: false
|
loading: false,
|
||||||
|
instance: "https://colubrina.troplo.com",
|
||||||
|
instanceString: ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
isElectron() {
|
||||||
|
return process.env.IS_ELECTRON
|
||||||
|
},
|
||||||
|
testInstance() {
|
||||||
|
if (this.isElectron()) {
|
||||||
|
this.axios
|
||||||
|
.get(this.instance + "/api/v1/state")
|
||||||
|
.then((res) => {
|
||||||
|
this.instanceString = res.data.name + " v" + res.data.latestVersion
|
||||||
|
this.axios.defaults.baseURL = this.instance
|
||||||
|
this.$store.dispatch("getState")
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.instanceString = "Error connecting to instance"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
viewport() {
|
viewport() {
|
||||||
return window.innerHeight
|
return window.innerHeight
|
||||||
},
|
},
|
||||||
|
@ -106,6 +138,10 @@ export default {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.$socket.disconnect()
|
this.$socket.disconnect()
|
||||||
this.$socket.connect()
|
this.$socket.connect()
|
||||||
|
if (this.isElectron()) {
|
||||||
|
this.axios.defaults.baseURL = this.instance
|
||||||
|
localStorage.setItem("instance", this.instance)
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
this.$store.state.site.emailVerification &&
|
this.$store.state.site.emailVerification &&
|
||||||
!this.$store.state.user.emailVerified
|
!this.$store.state.user.emailVerified
|
||||||
|
@ -114,6 +150,9 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.$router.push("/")
|
this.$router.push("/")
|
||||||
}
|
}
|
||||||
|
if (this.isElectron()) {
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
if (
|
if (
|
||||||
|
@ -133,6 +172,12 @@ export default {
|
||||||
if (this.$store.state.user?.id) {
|
if (this.$store.state.user?.id) {
|
||||||
this.$router.push("/")
|
this.$router.push("/")
|
||||||
}
|
}
|
||||||
|
this.testInstance()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
instance() {
|
||||||
|
this.testInstance()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -26,7 +26,11 @@
|
||||||
</v-overlay>
|
</v-overlay>
|
||||||
</v-fade-transition>
|
</v-fade-transition>
|
||||||
<v-img
|
<v-img
|
||||||
:src="'/usercontent/' + $store.state.user.avatar"
|
:src="
|
||||||
|
$store.state.baseURL +
|
||||||
|
'/usercontent/' +
|
||||||
|
$store.state.user.avatar
|
||||||
|
"
|
||||||
v-if="$store.state.user.avatar"
|
v-if="$store.state.user.avatar"
|
||||||
class="elevation-1"
|
class="elevation-1"
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in a new issue