mirror of
https://github.com/Troplo/Colubrina.git
synced 2024-11-22 11:17:56 +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,
|
||||
defaultValue: null
|
||||
},
|
||||
guidedWizard: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: true
|
||||
},
|
||||
privacy: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: {
|
||||
|
@ -99,11 +94,6 @@ module.exports = (sequelize, DataTypes) => {
|
|||
allowNull: false,
|
||||
defaultValue: false
|
||||
},
|
||||
emailDirectLogin: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "colubrina",
|
||||
"version": "1.0.11",
|
||||
"version": "1.0.12",
|
||||
"private": true,
|
||||
"author": "Troplo <troplo@troplo.com>",
|
||||
"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 */
|
||||
code {
|
||||
background-color: var(--v-bg-base) !important;
|
||||
|
|
|
@ -19,7 +19,11 @@
|
|||
</v-btn>
|
||||
</v-toolbar>
|
||||
<v-textarea
|
||||
style="margin-bottom: 5px"
|
||||
:style="
|
||||
!$vuetify.breakpoint.mobile
|
||||
? 'margin-bottom: 3px'
|
||||
: 'margin-bottom: -17px'
|
||||
"
|
||||
autofocus
|
||||
label="Type a message"
|
||||
placeholder="Keep it civil"
|
||||
|
@ -193,10 +197,11 @@ export default {
|
|||
},
|
||||
handlePaste(data) {
|
||||
if (data.clipboardData.items.length) {
|
||||
const item = data.clipboardData.items[0]
|
||||
if (item.kind === "file") {
|
||||
this.file = item.getAsFile()
|
||||
this.getURLForImage()
|
||||
for (const item of data.clipboardData.items) {
|
||||
if (item.kind === "file") {
|
||||
this.file = item.getAsFile()
|
||||
this.getURLForImage()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-hover v-slot="{ hover }">
|
||||
<div>
|
||||
<span>
|
||||
<template v-if="!message.type">
|
||||
<v-toolbar
|
||||
@click="jumpToMessage(message.replyId)"
|
||||
|
@ -40,8 +40,9 @@
|
|||
:class="{ 'message-hover': hover }"
|
||||
:id="'message-' + index"
|
||||
@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
|
||||
:src="
|
||||
$store.state.baseURL + '/usercontent/' + message.user.avatar
|
||||
|
@ -51,8 +52,15 @@
|
|||
/>
|
||||
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
|
||||
</v-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-subtitle>
|
||||
<small
|
||||
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) }}
|
||||
<v-chip
|
||||
v-if="message.user.bot"
|
||||
|
@ -87,13 +95,27 @@
|
|||
</span>
|
||||
</v-tooltip>
|
||||
</v-list-item-subtitle>
|
||||
<p
|
||||
<span
|
||||
v-if="edit.id !== message.id"
|
||||
v-markdown
|
||||
style="overflow-wrap: anywhere"
|
||||
>
|
||||
{{ message.content }}
|
||||
</p>
|
||||
<span v-markdown>
|
||||
{{ message.content }}
|
||||
</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">
|
||||
<v-row
|
||||
v-for="(embed, index) in message.embeds"
|
||||
|
@ -339,7 +361,14 @@
|
|||
{{ attachment.name }}
|
||||
</span>
|
||||
</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 }} -
|
||||
{{ friendlySize(attachment.size) }}
|
||||
<v-spacer />
|
||||
|
@ -366,64 +395,108 @@
|
|||
v-if="edit.id === message.id"
|
||||
></CommsInput>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action v-if="!$vuetify.breakpoint.mobile && hover">
|
||||
<v-list-item-subtitle>
|
||||
<v-btn
|
||||
icon
|
||||
v-if="message.userId === $store.state.user.id"
|
||||
@click="deleteMessage(message)"
|
||||
>
|
||||
<v-icon> mdi-delete </v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
icon
|
||||
@click="
|
||||
edit.content = message.content
|
||||
edit.editing = true
|
||||
edit.id = message.id
|
||||
"
|
||||
v-if="
|
||||
message.userId === $store.state.user.id &&
|
||||
edit.id !== message.id
|
||||
"
|
||||
>
|
||||
<v-icon> mdi-pencil </v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
icon
|
||||
@click="
|
||||
edit.content = ''
|
||||
edit.editing = false
|
||||
edit.id = null
|
||||
"
|
||||
v-if="
|
||||
message.userId === $store.state.user.id &&
|
||||
edit.id === message.id
|
||||
"
|
||||
>
|
||||
<v-icon> mdi-close </v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
icon
|
||||
@click="
|
||||
replying(message)
|
||||
focusInput()
|
||||
"
|
||||
>
|
||||
<v-icon> mdi-reply </v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
icon
|
||||
v-if="chat.rank === 'admin' || chat.chat.type === 'direct'"
|
||||
@click="
|
||||
pinMessage()
|
||||
focusInput()
|
||||
"
|
||||
>
|
||||
<v-icon> mdi-pin </v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-action>
|
||||
<v-card
|
||||
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
|
||||
icon
|
||||
v-on="on"
|
||||
v-if="message.userId === $store.state.user.id"
|
||||
@click="deleteMessage(message)"
|
||||
>
|
||||
<v-icon> mdi-delete </v-icon>
|
||||
</v-btn>
|
||||
</span>
|
||||
</template>
|
||||
<span> Delete </span>
|
||||
</v-tooltip>
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<span v-on="on">
|
||||
<v-btn
|
||||
icon
|
||||
v-on="on"
|
||||
@click="
|
||||
edit.content = message.content
|
||||
edit.editing = true
|
||||
edit.id = message.id
|
||||
"
|
||||
v-if="
|
||||
message.userId === $store.state.user.id &&
|
||||
edit.id !== message.id
|
||||
"
|
||||
>
|
||||
<v-icon> mdi-pencil </v-icon>
|
||||
</v-btn>
|
||||
</span>
|
||||
</template>
|
||||
<span> Edit </span>
|
||||
</v-tooltip>
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<span v-on="on">
|
||||
<v-btn
|
||||
v-on="on"
|
||||
icon
|
||||
@click="
|
||||
edit.content = ''
|
||||
edit.editing = false
|
||||
edit.id = null
|
||||
"
|
||||
v-if="
|
||||
message.userId === $store.state.user.id &&
|
||||
edit.id === message.id
|
||||
"
|
||||
>
|
||||
<v-icon> mdi-close </v-icon>
|
||||
</v-btn>
|
||||
</span>
|
||||
</template>
|
||||
<span> Discard Edits </span>
|
||||
</v-tooltip>
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<span v-on="on">
|
||||
<v-btn
|
||||
icon
|
||||
@click="
|
||||
replying(message)
|
||||
focusInput()
|
||||
"
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon> mdi-reply </v-icon>
|
||||
</v-btn>
|
||||
</span>
|
||||
</template>
|
||||
<span> Reply </span>
|
||||
</v-tooltip>
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<span v-on="on">
|
||||
<v-btn
|
||||
v-on="on"
|
||||
icon
|
||||
v-if="
|
||||
chat.rank === 'admin' || chat.chat.type === 'direct'
|
||||
"
|
||||
@click="
|
||||
pinMessage()
|
||||
focusInput()
|
||||
"
|
||||
>
|
||||
<v-icon> mdi-pin </v-icon>
|
||||
</v-btn>
|
||||
</span>
|
||||
</template>
|
||||
<span> Pin to Chat </span>
|
||||
</v-tooltip>
|
||||
</v-card>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<template v-else-if="message.type === 'leave'">
|
||||
|
@ -721,7 +794,7 @@
|
|||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</template>
|
||||
</div>
|
||||
</span>
|
||||
</v-hover>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -764,7 +837,8 @@ export default {
|
|||
"index",
|
||||
"show",
|
||||
"setImagePreview",
|
||||
"deleteMessage"
|
||||
"deleteMessage",
|
||||
"lastMessage"
|
||||
],
|
||||
components: {
|
||||
CommsInput,
|
||||
|
|
|
@ -18,26 +18,13 @@ import "highlight.js/styles/github.css"
|
|||
const md = require("markdown-it")({
|
||||
html: false, // Enable HTML tags in source
|
||||
xhtmlOut: false, // Use '/' to close single tags (<br />).
|
||||
// This is only for full CommonMark compatibility.
|
||||
breaks: true, // Convert '\n' in paragraphs into <br>
|
||||
langPrefix: "language-", // CSS language prefix for fenced blocks. Can be
|
||||
// useful for external highlighters.
|
||||
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,
|
||||
|
||||
// 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: "“”‘’",
|
||||
|
||||
// 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*/) {
|
||||
return ""
|
||||
}
|
||||
|
@ -95,7 +82,11 @@ Vue.use(VueAxios, axios)
|
|||
Vue.use(Toast)
|
||||
Vue.directive("markdown", {
|
||||
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") {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div id="communications-chat" @dragover.prevent @drop.prevent>
|
||||
<div id="communications-chat" @dragover.prevent @drop.prevent="handleDrag">
|
||||
<v-menu
|
||||
:position-x="$store.state.context.pins.x"
|
||||
:position-y="60"
|
||||
|
@ -83,8 +83,7 @@
|
|||
<v-dialog
|
||||
v-model="preview.dialog"
|
||||
elevation="0"
|
||||
:width="preview.width"
|
||||
:height="preview.height"
|
||||
:min-height="300"
|
||||
:max-width="1000"
|
||||
:max-height="600"
|
||||
content-class="rounded-0"
|
||||
|
@ -94,12 +93,20 @@
|
|||
:src="preview.src"
|
||||
:max-width="1000"
|
||||
:max-height="600"
|
||||
:min-height="300"
|
||||
aspect-ratio="16/9"
|
||||
contain
|
||||
></v-img>
|
||||
<v-container>
|
||||
<a :href="preview.src" style="text-decoration: none" target="_blank">
|
||||
<small> Open Externally </small>
|
||||
</a>
|
||||
<small
|
||||
class="float-end"
|
||||
style="text-decoration: none; color: inherit"
|
||||
>
|
||||
{{ preview.name }}
|
||||
</small>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
@ -160,7 +167,7 @@
|
|||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
<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
|
||||
class="d-flex flex-column fill-height rounded-xl"
|
||||
style="overflow: auto; height: calc(100vh - 24px - 40px - 40px)"
|
||||
|
@ -211,6 +218,14 @@
|
|||
:show="show"
|
||||
:set-image-preview="setImagePreview"
|
||||
:delete-message="deleteMessage"
|
||||
:last-message="
|
||||
messages[index - 1]?.userId === message?.userId &&
|
||||
$date(message.createdAt).diff(
|
||||
messages[index - 1]?.createdAt,
|
||||
'minute'
|
||||
) < 10 &&
|
||||
!message.replyId
|
||||
"
|
||||
></Message>
|
||||
</div>
|
||||
<div
|
||||
|
@ -335,7 +350,6 @@
|
|||
style="
|
||||
border-radius: 20px 20px 0 0;
|
||||
cursor: pointer;
|
||||
z-index: 50;
|
||||
position: relative;
|
||||
top: -30px;
|
||||
margin-bottom: -27px;
|
||||
|
@ -350,6 +364,30 @@
|
|||
</div>
|
||||
</v-toolbar>
|
||||
</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
|
||||
:chat="chat"
|
||||
:replying="replying"
|
||||
|
@ -357,26 +395,22 @@
|
|||
:autoScroll="autoScroll"
|
||||
:endSend="endSend"
|
||||
></CommsInput>
|
||||
<v-fade-transition v-model="usersTyping.length">
|
||||
<v-toolbar
|
||||
height="22"
|
||||
elevation="0"
|
||||
<v-fade-transition
|
||||
v-model="usersTyping.length"
|
||||
v-if="!$vuetify.breakpoint.mobile"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
border-radius: 0 0 20px 20px;
|
||||
position: relative;
|
||||
margin-bottom: -2px;
|
||||
margin-top: -20px;
|
||||
bottom: -14px;
|
||||
margin-top: -22px;
|
||||
bottom: -16px;
|
||||
"
|
||||
width="100%"
|
||||
color="toolbar"
|
||||
v-if="usersTyping.length"
|
||||
>
|
||||
<div style="overflow: hidden">
|
||||
{{ usersTyping.map((user) => getName(user)).join(", ") }}
|
||||
{{ usersTyping.length > 1 ? " are" : " is" }} typing...
|
||||
</div>
|
||||
</v-toolbar>
|
||||
{{ usersTyping.map((user) => getName(user)).join(", ") }}
|
||||
{{ usersTyping.length > 1 ? " are" : " is" }} typing...
|
||||
</div>
|
||||
</v-fade-transition>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
@ -421,314 +455,28 @@
|
|||
></v-text-field>
|
||||
<v-list two-line color="card" ref="message-list-search">
|
||||
<template v-for="(message, index) in search.results">
|
||||
<v-toolbar
|
||||
@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"
|
||||
<div
|
||||
@click="jumpToMessage(message.id)"
|
||||
:key="message.keyId"
|
||||
:class="{
|
||||
'text-xs-right': message.userId === $store.state.user.id,
|
||||
'text-xs-left': message.userId !== $store.state.user.id
|
||||
}"
|
||||
:id="'message-' + index"
|
||||
style="cursor: pointer"
|
||||
>
|
||||
<v-avatar size="48" class="mr-2">
|
||||
<v-img
|
||||
:src="
|
||||
$store.state.baseURL +
|
||||
'/usercontent/' +
|
||||
message.user.avatar
|
||||
"
|
||||
v-if="message.user.avatar"
|
||||
class="elevation-1"
|
||||
/>
|
||||
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
|
||||
</v-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-subtitle>
|
||||
{{ getName(message.user) }}
|
||||
<small>
|
||||
{{
|
||||
$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>
|
||||
</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>
|
||||
<Message
|
||||
:message="message"
|
||||
:jump-to-message="jumpToMessage"
|
||||
:edit="edit"
|
||||
:focus-input="focusInput"
|
||||
:replying="setReply"
|
||||
:get-name="getName"
|
||||
:end-edit="endEdit"
|
||||
:auto-scroll="autoScroll"
|
||||
:chat="chat"
|
||||
:index="index"
|
||||
:show="show"
|
||||
:set-image-preview="setImagePreview"
|
||||
:delete-message="deleteMessage"
|
||||
:last-message="false"
|
||||
></Message>
|
||||
</div>
|
||||
</template>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
|
@ -912,7 +660,8 @@ export default {
|
|||
dialog: false,
|
||||
src: "",
|
||||
height: 0,
|
||||
width: 0
|
||||
width: 0,
|
||||
name: ""
|
||||
},
|
||||
fileTypes: {
|
||||
png: "mdi-file-image",
|
||||
|
@ -1207,6 +956,7 @@ export default {
|
|||
this.preview.height = img.height
|
||||
this.preview.width = img.width
|
||||
this.preview.dialog = true
|
||||
this.preview.name = attachment.name
|
||||
}
|
||||
img.src = link
|
||||
},
|
||||
|
|
|
@ -159,6 +159,10 @@ export default {
|
|||
this.loading = false
|
||||
this.$socket.disconnect()
|
||||
this.$socket.connect()
|
||||
if (this.isElectron()) {
|
||||
this.axios.defaults.baseURL = this.instance
|
||||
localStorage.setItem("instance", this.instance)
|
||||
}
|
||||
if (
|
||||
this.$store.state.site.emailVerification &&
|
||||
!this.$store.state.user.emailVerified
|
||||
|
@ -191,7 +195,6 @@ export default {
|
|||
.then((res) => {
|
||||
this.instanceString = res.data.name + " v" + res.data.latestVersion
|
||||
this.axios.defaults.baseURL = this.instance
|
||||
localStorage.setItem("instance", this.instance)
|
||||
this.$store.dispatch("getState")
|
||||
})
|
||||
.catch(() => {
|
||||
|
|
|
@ -7,10 +7,23 @@
|
|||
<v-container>
|
||||
<v-form ref="form" class="pa-4 pt-6">
|
||||
<p class="text-center text-h4">
|
||||
Login to
|
||||
Register to
|
||||
<span class="troplo-title">{{ $store.state.site.name }}</span
|
||||
><small style="font-size: 15px">beta</small>
|
||||
</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
|
||||
@keyup.enter="doRegister()"
|
||||
class="rounded-xl"
|
||||
|
@ -82,10 +95,29 @@ export default {
|
|||
email: "",
|
||||
totp: "",
|
||||
totpDialog: false,
|
||||
loading: false
|
||||
loading: false,
|
||||
instance: "https://colubrina.troplo.com",
|
||||
instanceString: ""
|
||||
}
|
||||
},
|
||||
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() {
|
||||
return window.innerHeight
|
||||
},
|
||||
|
@ -106,6 +138,10 @@ export default {
|
|||
this.loading = false
|
||||
this.$socket.disconnect()
|
||||
this.$socket.connect()
|
||||
if (this.isElectron()) {
|
||||
this.axios.defaults.baseURL = this.instance
|
||||
localStorage.setItem("instance", this.instance)
|
||||
}
|
||||
if (
|
||||
this.$store.state.site.emailVerification &&
|
||||
!this.$store.state.user.emailVerified
|
||||
|
@ -114,6 +150,9 @@ export default {
|
|||
} else {
|
||||
this.$router.push("/")
|
||||
}
|
||||
if (this.isElectron()) {
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (
|
||||
|
@ -133,6 +172,12 @@ export default {
|
|||
if (this.$store.state.user?.id) {
|
||||
this.$router.push("/")
|
||||
}
|
||||
this.testInstance()
|
||||
},
|
||||
watch: {
|
||||
instance() {
|
||||
this.testInstance()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -26,7 +26,11 @@
|
|||
</v-overlay>
|
||||
</v-fade-transition>
|
||||
<v-img
|
||||
:src="'/usercontent/' + $store.state.user.avatar"
|
||||
:src="
|
||||
$store.state.baseURL +
|
||||
'/usercontent/' +
|
||||
$store.state.user.avatar
|
||||
"
|
||||
v-if="$store.state.user.avatar"
|
||||
class="elevation-1"
|
||||
/>
|
||||
|
|
Loading…
Reference in a new issue