This commit is contained in:
Troplo 2022-08-02 21:15:16 +10:00
parent 75df196dae
commit 5ea4d835bd
12 changed files with 351 additions and 428 deletions

34
.github/workflows/desktop.yml vendored Normal file
View 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"

View 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');
*/
}
}

View file

@ -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,

View file

@ -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": {

View file

@ -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;

View file

@ -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")

View file

@ -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,

View file

@ -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("&amp;", "&")
.replaceAll("<p>", "<span>")
.replaceAll("</p>", "</span>")
} }
}) })
if (process.env.NODE_ENV === "development") { if (process.env.NODE_ENV === "development") {

View file

@ -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
}, },

View file

@ -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(() => {

View file

@ -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>

View file

@ -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"
/> />