Subject line supprt

Mute chats
Custom notification sounds
Use system sounds option
Slidable chats for options
This commit is contained in:
Aziz Hasanain 2021-02-26 14:46:34 +03:00
parent 062204a0de
commit 6ad3095400
6 changed files with 464 additions and 140 deletions

View File

@ -199,6 +199,7 @@ export default {
this.chats = []
this.$store.commit('resetMessages')
this.$router.push('/').catch(()=>{})
this.notifSound = new Audio(this.$store.state.notifSound)
const baseURI = this.$store.getters.baseURI
this.$connect(baseURI, {
@ -300,7 +301,7 @@ export default {
this.win.on('move', this.onMove)
})
this.notifSound = new Audio('wm-audio://receivedText.mp3')
this.notifSound = new Audio(this.$store.state.notifSound)
if (!(this.$store.state.macstyle || process.platform === 'darwin')) {
document.body.style.border = 'none'
@ -351,17 +352,19 @@ export default {
}
if (messageData.sender != 1 && remote.Notification.isSupported()) {
if (this.$store.state.mutedChats.includes(messageData.personId)) return
const notification = {
title: messageData.name,
body: messageData.text,
silent: this.$store.state.playsound
silent: !this.$store.state.systemSound
}
if (process.platform === 'win32') {
notification.icon = __static + '/icon.png'
}
if (this.$store.state.playsound) this.notifSound.play()
if (!this.$store.state.systemSound) this.notifSound.play()
let notif = new remote.Notification(notification)
notif.on('click', (event, arg) => {
if (chatData && chatData.id) {
@ -535,7 +538,7 @@ export default {
}
}
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@200;300;500;700&display=swap');
@import url('https://fonts.googleapis.com/css?family=Roboto:light,regular,medium,thin,italic,mediumitalic,bold');
html {
height: 100%;
@ -637,6 +640,26 @@ body {
color: #42b983;
}
}
input:not([type="range"]):not([type="color"]):not(.message-input) {
height: auto;
height: inherit;
font-size: 13px;
height: 6px;
padding: 10px 6px 10px 6px;
outline: none;
border: 1px solid rgb(213, 213, 213);
margin: 5px;
cursor: text;
-webkit-app-region: no-drag;
}
input:not([type="range"]):not([type="color"]):not(.message-input):focus {
border-radius: 1px;
box-shadow: 0px 0px 0px 3.5px rgba(23, 101, 144, 1);
animation: showFocus .3s;
border-color: rgb(122, 167, 221) !important;
}
}
.titlebar {
@ -736,26 +759,6 @@ body {
overflow: hidden;
}
input:not([type="range"]):not([type="color"]):not(.message-input) {
height: auto;
height: inherit;
font-size: 13px;
height: 6px;
padding: 10px 6px 10px 6px;
outline: none;
border: 1px solid rgb(213, 213, 213);
margin: 5px;
cursor: text;
-webkit-app-region: no-drag;
}
input:not([type="range"]):not([type="color"]):not(.message-input):focus {
border-radius: 1px;
box-shadow: 0px 0px 0px 3.5px rgba(23, 101, 144, 1);
animation: showFocus .3s;
border-color: rgb(122, 167, 221) !important;
}
@keyframes showFocus {
0% {
border-color: #dbdbdb;

View File

@ -295,6 +295,19 @@ function registerLocalAudioProtocol () {
)
}
})
protocol.registerFileProtocol('local-file', (request, callback) => {
const url = request.url.replace(/^local-file:\/\//, '')
// Decode URL to prevent errors when loading filenames with UTF-8 chars or chars like "#"
const decodedUrl = decodeURI(url) // Needed in case URL contains spaces
try {
return callback(decodedUrl)
} catch (error) {
console.error(
'ERROR: registerLocalAudioProtocol: Could not get file path:',
error
)
}
})
}
function registerShortcuts () {

View File

@ -1,26 +1,29 @@
<template>
<div class="chatContainer" :class="this.$route.path == '/message/'+this.chatid ? 'active' : ''" :id="'id'+chatid" @click="navigate"
@mouseover="showDelete = true" @mouseout="showDelete = false">
<div class="unread" :style="read ? 'background-color: transparent;' : ''"></div>
<div class="avatarContainer">
<img v-if='(docid && docid != 0)' class="avatar" :src="`${$store.getters.httpURI}/contactimg?docid=${encodeURIComponent(docid)}&auth=${encodeURIComponent($store.state.password)}`" />
<img v-else class="avatar" src="../assets/profile.jpg" />
</div>
<div class="chatContent">
<div class="title">
<span class="author">
<span class="name" v-html="$options.filters.twemoji(author)"></span>
<span v-if="showNum" class="number"> ({{ this.chatid }})</span>
</span>
<span class="date">{{ date/1000 | moment }}</span>
<div class="chatContainer" :class="this.$route.path == '/message/'+this.chatid ? 'active' : ''" :id="'id'+chatid" @click="navigate">
<div class="chatWrapper" @mousedown="dragMouseDown" :style="{ left: '-'+slideX+'px' }">
<div class="unread" :style="read ? 'background-color: transparent;' : ''"></div>
<div class="avatarContainer">
<img v-if='(docid && docid != 0)' class="avatar" :src="`${$store.getters.httpURI}/contactimg?docid=${encodeURIComponent(docid)}&auth=${encodeURIComponent($store.state.password)}`" />
<img v-else class="avatar" src="../assets/profile.jpg" />
</div>
<div class="text">
<span v-html="trimmedText"></span>
</div>
<div class="delete" v-show="showDelete" @click="deleteChat">
<feather type="x-circle" stroke="rgba(255,0,0,0.7)" :fill="this.$route.path == '/message/'+this.chatid ? '#2284FF' : 'rgba(45,45,45,0.8)'" size="15"></feather>
<div class="chatContent">
<div class="title">
<span class="author">
<span class="name" v-html="$options.filters.twemoji(author)"></span>
<span v-if="showNum" class="number"> ({{ this.chatid }})</span>
<feather type="bell-off" stroke="rgb(85,85,85)" size="13" v-if="$store.state.mutedChats.includes(chatid)"></feather>
</span>
<span class="date">{{ date/1000 | moment }}</span>
</div>
<div class="text">
<span v-html="trimmedText"></span>
</div>
</div>
</div>
<div class="slidableDiv" :style="{ left: 'calc(100% - '+slideX+'px)' }">
<div class="hideAlerts" @click="hideAlerts">{{ $store.state.mutedChats.includes(chatid) ? 'Unhide Alerts' : 'Hide Alerts' }}</div>
<div class="deleteChat" @click="deleteChat">Delete</div>
</div>
</div>
</template>
@ -40,7 +43,10 @@ export default {
},
data () {
return {
showDelete: false
initialX: 0,
slideX: 0,
closingSlider: false,
sliderWidth: 120,
}
},
computed: {
@ -80,7 +86,62 @@ export default {
this.$router.push('/message/'+this.chatid)
}
},
dragMouseDown (e) {
e.preventDefault()
if (this.closingSlider) return
this.initialX = e.clientX
if (this.slideX > 0) {
this.closeDragElement()
}
document.onmousemove = this.elementDrag
document.onmouseup = this.finishDragElement
},
elementDrag (e) {
e.preventDefault()
if (this.closingSlider) return
if (this.initialX - e.clientX < 0 || this.initialX - e.clientX > this.sliderWidth) return
this.slideX = this.initialX - e.clientX
},
closeDragElement () {
this.closingSlider = true
let interval = setInterval(() => {
if (this.slideX <= 0) {
this.closingSlider = false
this.slideX = 0
this.initialX = 0
clearInterval(interval)
return
}
this.slideX -= 3
}, 6)
document.onmouseup = null
document.onmousemove = null
},
finishDragElement () {
if (this.closingSlider) return
if (this.slideX >= (this.sliderWidth/2.2)) {
let interval = setInterval(() => {
if (this.closingSlider) return
if (this.slideX >= this.sliderWidth) {
this.slideX = this.sliderWidth
clearInterval(interval)
return
}
this.slideX += 3
}, 6)
} else {
this.closeDragElement()
}
document.onmouseup = null
document.onmousemove = null
},
deleteChat () {
this.closeDragElement()
this.$confirm({
title: 'Warning',
message: `Are you sure you want to delete your messages from ${this.author}? This cannot be undone.`,
@ -95,6 +156,10 @@ export default {
}
}
})
},
hideAlerts () {
this.closeDragElement()
this.$store.commit(this.$store.state.mutedChats.includes(this.chatid) ? 'unmuteChat' : 'muteChat', this.chatid)
}
}
}
@ -112,23 +177,6 @@ export default {
margin-top: 26px;
}
.delete {
position: absolute;
top: 25px;
right: 4px;
padding: 0px 3px;
cursor: pointer;
display: inherit !important; /* override v-show display: none */
transition: opacity 0.2s;
&[style*="display: none;"] {
opacity: 0;
pointer-events: none; /* disable user interaction */
user-select: none; /* disable user selection */
}
}
.chatContent {
float: right;
// padding: 0px;
@ -168,12 +216,17 @@ export default {
.number {
font-weight: 200;
font-size: 12px;
font-size: 11px;
}
i {
padding-left: 4px;
vertical-align: middle;
}
}
.date {
font-weight: 200;
font-weight: 300;
padding-right: 10px;
float: right;
color: rgb(157,157,157);
@ -197,6 +250,8 @@ export default {
width: 300px;
height: 64px;
border-radius: 5px;
position: relative;
overflow: hidden;
&.active {
background-color: #2284FF;
@ -213,5 +268,41 @@ export default {
}
}
}
.chatWrapper {
height: inherit;
position: absolute;
width: inherit;
}
.slidableDiv {
position: absolute;
left: 100%;
display: flex;
width: 120px;
height: 100%;
div {
display: flex;
flex: 1 1 0px;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 400;
cursor: pointer;
&:hover {
filter: brightness(85%);
}
}
.deleteChat {
background: #FF3B2F;
}
.hideAlerts {
background: #5959D1;
}
}
}
</style>

View File

@ -53,7 +53,9 @@
:key="'msg'+msg.id+'-text'+ii"
:class="(msg.texts.length-1 == ii ? 'last ' : '') + (isEmojis(text.text) ? 'jumbo' : '')"
:style="msg.sender == 1 && text.showStamp && (text.read > 0 || text.delivered > 0) ? 'margin-bottom: 0px;' : ''">
<span style="white-space: pre-wrap;" v-html="$options.filters.twemoji(text.text)" v-if="!text.undisplayable" v-linkified></span>
<div class="subject" v-if="text.subject && text.subject != ''" v-html="$options.filters.twemoji(text.subject)"></div>
<span style="white-space: pre-wrap;" v-if="!text.undisplayable" v-html="$options.filters.twemoji(text.text)" v-linkified></span>
<div style="white-space: pre-wrap;text-align:center;" v-else>
<div style="font-weight:500;font-size:14px;">Unsupported Type</div>
<div style="font-size:11px;margin-top:4px;">
@ -82,18 +84,24 @@
{{ attachment.name }}
</div>
</div>
<twemoji-textarea @contentChanged="autoResize" placeholder="Send a message..."
:emojiData="emojiDataAll"
:emojiGroups="emojiGroups"
:initialContent="messageText[$route.params.id]"
:class="hasAttachments ? 'withAttachments' : ''"
@enterKey="sendText">
<template v-slot:twemoji-picker-button>
<feather type="smile" fill="rgb(152,152,152)" stroke="rgb(29,29,29)" size="26"></feather>
</template>
</twemoji-textarea>
<img src="@/assets/loading.webp" style="height:22px;" v-if="!canSend" />
<div class="msgTextboxWrapper">
<input type="text" v-if="$store.state.subjectLine" class="subjectLine" ref="subjectLine" placeholder="Subject" @keyup.enter.exact="sendText" :class="{ noTopBorder: hasAttachments }">
<twemoji-textarea @contentChanged="autoResize" :placeholder="this.messages[0].type.replace('SMS', 'Text Message')"
:emojiData="emojiDataAll"
:emojiGroups="emojiGroups"
:initialContent="messageText[$route.params.id]"
:class="(hasAttachments || $store.state.subjectLine) ? 'noTopBorder' : ''"
@enterKey="sendText">
<template v-slot:twemoji-picker-button>
<feather type="smile" fill="rgb(152,152,152)" stroke="rgb(29,29,29)" size="26"></feather>
</template>
</twemoji-textarea>
</div>
<img src="@/assets/loading.webp" style="height:26px;float:right;" v-if="!canSend" />
<upload-button v-show="canSend" ref="uploadButton" :enableiMessageAttachments="this.messages[0] && this.messages[0].type == 'iMessage'" @filesChanged="previewFiles" />
<div class="sendBtn" :class="{ cantSend: !canSend }" @click="sendText">
<feather type="arrow-up" size="20"></feather>
</div>
</div>
</template>
</div>
@ -173,7 +181,7 @@ export default {
const groupDates = (date1, date2) => (date2 - date1 < 3600000)
const groupAuthor = (author1, author2) => (author1 == author2)
const groupedMessages = messages.reduce((r, { text, dateRead, dateDelivered, guid, reactions, payload, ...rest }, i, arr) => {
const groupedMessages = messages.reduce((r, { text, subject, dateRead, dateDelivered, guid, reactions, payload, ...rest }, i, arr) => {
const prev = arr[i +-1]
let extras = { }
@ -200,9 +208,9 @@ export default {
}
if (prev && groupAuthor(rest.author, prev.author) && groupAuthor(rest.sender, prev.sender) && groupDates(rest.date, prev.date))
r[r.length - 1].texts.unshift({ text: text, date: rest.date, attachments: rest.attachments, read: dateRead, delivered: dateDelivered, guid: guid, reactions: reactions, showStamp: rest.sender == 1 && !lastSentMessageFound, ...extras })
r[r.length - 1].texts.unshift({ text: text, subject: subject, date: rest.date, attachments: rest.attachments, read: dateRead, delivered: dateDelivered, guid: guid, reactions: reactions, showStamp: rest.sender == 1 && !lastSentMessageFound, ...extras })
else
r.push({ ...rest, texts: [{ text: text, date: rest.date, attachments: rest.attachments, read: dateRead, delivered: dateDelivered, guid: guid, reactions: reactions, showStamp: rest.sender == 1 && !lastSentMessageFound, ...extras }] })
r.push({ ...rest, texts: [{ text: text, subject: subject, date: rest.date, attachments: rest.attachments, read: dateRead, delivered: dateDelivered, guid: guid, reactions: reactions, showStamp: rest.sender == 1 && !lastSentMessageFound, ...extras }] })
if (rest.sender == 1 && !lastSentMessageFound) lastSentMessageFound = true
@ -228,6 +236,9 @@ export default {
if (this.$refs.uploadButton) {
this.$refs.uploadButton.clear()
}
if (this.$refs.subjectLine) {
this.$refs.subjectLine.value = ''
}
this.autoCompleteHooks()
this.fetchMessages()
@ -312,10 +323,14 @@ export default {
this.$nextTick(this.autoCompleteHooks)
},
isEmojis(msgText) {
const regex = /<% RGI_Emoji %>|\p{Emoji_Presentation}|\p{Emoji}\uFE0F|\p{Emoji_Modifier_Base}|[\u180B-\u180D\uFE00-\uFE0F]/gu
const regex = /<% RGI_Emoji %>|\p{Emoji_Presentation}|\p{Emoji}\uFE0F|\p{Emoji_Modifier_Base}/gu
const variationSelector = /[\u180B-\u180D\uFE00-\uFE0F]|\uDB40[\uDD00-\uDDEF]/gu
msgText = msgText.replace(/\u{fffc}/gu, "")
return msgText.replace(' ', '').replace(regex, '').length == 0 && msgText.replace(' ', '').length <= 8 && msgText.replace(' ', '').length != 0
return msgText.replace(' ', '').replace(regex, '').replace(variationSelector, '').length == 0
&& msgText.replace(' ', '').replace(variationSelector, '').length <= 8
&& msgText.replace(' ', '').length != 0
},
humanReadableDay (date) {
let ts = date
@ -415,20 +430,29 @@ export default {
}})
},
sendText () {
let messageText = this.messageText[this.$route.params.id]
if (!messageText) messageText = ''
if (messageText == '' && (!this.$refs.uploadButton.attachments || this.$refs.uploadButton.attachments.length == 0)) return
if (!this.canSend) return
let messageText = this.messageText[this.$route.params.id]
let subjectText = this.$refs.subjectLine ? this.$refs.subjectLine.value : ''
if (messageText == '' && subjectText != '') {
messageText = subjectText
subjectText = ''
}
if (messageText == '' && (!this.$refs.uploadButton.attachments || this.$refs.uploadButton.attachments.length == 0)) return
this.canSend = false
let textObj = {
text: messageText,
attachments: this.$refs.uploadButton.attachments,
address: this.messages[0] ? this.messages[0].chatId : this.receiver
address: this.messages[0] ? this.messages[0].chatId : this.receiver,
subject: subjectText
}
document.getElementById("twemoji-textarea").innerHTML = ""
this.messageText[this.$route.params.id] = ""
if (this.$refs.subjectLine) this.$refs.subjectLine.value = ""
axios.post(this.$store.getters.httpURI+'/sendText', textObj)
.then(response => {
@ -669,7 +693,7 @@ export default {
.attachmentPreview {
border: 1px solid #545454;
margin-left: 32px;
margin-right: 32px;
margin-right: 64px;
margin-bottom: -1px;
border-top-left-radius: 14px;
border-top-right-radius: 14px;
@ -742,9 +766,41 @@ export default {
}
}
.msgTextboxWrapper {
.subjectLine {
border:none;
background-image: none;
background-color: transparent;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
outline: none;
font-family: 'Roboto', -apple-system, BlinkMacSystemFont, Avenir, Helvetica, Arial, sans-serif;
margin-left: 32px;
width: calc(100% - 118px);
background: #1d1d1d !important;
border: 1px solid #545454 !important;
border-top-left-radius: 14px;
border-top-right-radius: 14px;
margin-bottom: -1px;
color: white;
padding-left: 10px;
padding-right: 10px;
line-height: 20px;
font-weight: 500;
letter-spacing: 0.25px;
&.noTopBorder {
border-top-left-radius: 0px;
border-top-right-radius: 0px;
}
}
}
#twemoji-textarea-outer {
background-color: transparent !important;
width: calc(100% - 26px);
width: calc(100% - 58px);
float: left;
#twemoji-textarea {
@ -765,6 +821,7 @@ export default {
flex-grow: 90;
border-radius: 14px !important;
padding-left: 10px !important;
padding-right: 10px !important;
background: rgba(29,29,29, 1) !important;
border: 1px solid #545454 !important;
line-height: 21px !important;
@ -780,7 +837,7 @@ export default {
}
}
&.withAttachments {
&.noTopBorder {
#twemoji-textarea {
border-top-right-radius: 0px !important;
border-top-left-radius: 0px !important;
@ -920,8 +977,40 @@ export default {
.feather {
&:hover {
fill: lighten(rgb(152,152,152), 20%);
cursor: pointer;
filter: brightness(115%);
}
cursor: pointer;
}
.sendBtn {
width: 24px;
height: 24px;
border-radius: 50%;
background: #2284FF;
float: right;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
margin-right: 6px;
svg {
stroke: rgb(255,255,255);
cursor: initial;
}
&.cantSend {
filter: brightness(40%);
cursor: initial;
}
&:not(.cantSend) {
.feather {
cursor: pointer;
}
&:hover {
filter: brightness(115%);
}
}
}
}
@ -1071,6 +1160,12 @@ export default {
display: inline-block;
text-align: start;
unicode-bidi: plaintext;
.subject {
white-space: pre-wrap;
font-weight: 500;
letter-spacing: 0.25px;
}
}
.bubbleWrapper {

View File

@ -6,48 +6,69 @@
<div class="modal__dialog">
<h3>Settings</h3>
<input type="password" placeholder="Password" class="textinput" v-model="password" />
<input type="text" placeholder="IP Address" class="textinput" v-model="ipAddress" :disabled="this.enableTunnel"/>
<div class="tunnelToggle">
<feather type="circle" size="20" @click="toggleTunnel" :fill="relayColor" v-popover:tunnel.bottom></feather>
<div class="settingsWrapper">
<div class="settingsColumn">
<h4>Tweak</h4>
<input type="password" placeholder="Password" class="textinput" v-model="password" />
<input type="text" placeholder="IP Address" class="textinput" v-model="ipAddress" :disabled="this.enableTunnel"/>
<div class="tunnelToggle">
<feather type="circle" size="20" @click="toggleTunnel" :fill="relayColor" v-popover:tunnel.bottom></feather>
</div>
<input ref="portField" type="number" placeholder="Port" class="textinput" min="1" max="65535" @keyup="enforceConstraints" v-model="port" />
<label class="switch">
<input type="checkbox" v-model="ssl">
<i></i>
<div>Enable SSL</div>
</label>
</div>
<div class="settingsColumn">
<h4>Client</h4>
<label class="switch">
<input type="checkbox" v-model="subjectLine">
<i></i>
<div>Enable subject line</div>
</label>
<label class="switch">
<input type="checkbox" v-model="systemSound">
<i></i>
<div>Use system notification sound</div>
</label>
<label class="switch">
<input type="checkbox" v-model="cacheMessages">
<i></i>
<div>Precache messages <span style="color: rgba(255,0,0,0.8);font-size: 12px;">More battery drain</span></div>
</label>
<label class="switch">
<input type="checkbox" v-model="startup">
<i></i>
<div>Launch on startup</div>
</label>
<label class="switch">
<input type="checkbox" v-model="minimize">
<i></i>
<div>Keep in tray</div>
</label>
<label class="switch" v-if="process.platform !== 'darwin'">
<input type="checkbox" v-model="macstyle">
<i></i>
<div>Use macOS style</div>
</label>
<label class="switch">
<input type="checkbox" v-model="acceleration">
<i></i>
<div>Enable hardware acceleration</div>
</label>
<label class="file">
<div>Select custom notification file:</div>
<input type="file" name="soundFile" ref="soundFile" style="display: none;" @change="notifSoundChanged" accept="audio/*">
<div class="fileBtn" @click.prevent="$refs.soundFile.click">
Browse ({{ this.notifSound.includes('wm-audio') ? 'Default' : this.notifSound.split('/').pop() }})
</div>
<div class="fileBtn" @click.prevent="notifSound = 'wm-audio://receivedText.mp3'" style="margin-left: 8px;">Reset</div>
</label>
</div>
</div>
<input ref="portField" type="number" placeholder="Port" class="textinput" min="1" max="65535" @keyup="enforceConstraints" v-model="port" />
<label class="switch">
<input type="checkbox" v-model="ssl">
<i></i>
<div>Enable SSL</div>
</label>
<hr>
<label class="switch">
<input type="checkbox" v-model="playsound">
<i></i>
<div>Custom notification sound</div>
</label>
<label class="switch">
<input type="checkbox" v-model="cacheMessages">
<i></i>
<div>Precache messages <span style="color: rgba(255,0,0,0.8);font-size: 12px;">More battery drain</span></div>
</label>
<label class="switch">
<input type="checkbox" v-model="startup">
<i></i>
<div>Launch on startup</div>
</label>
<label class="switch">
<input type="checkbox" v-model="minimize">
<i></i>
<div>Keep in tray</div>
</label>
<label class="switch" v-if="process.platform !== 'darwin'">
<input type="checkbox" v-model="macstyle">
<i></i>
<div>Use macOS style</div>
</label>
<label class="switch">
<input type="checkbox" v-model="acceleration">
<i></i>
<div>Enable hardware acceleration</div>
</label>
<a class="btn" v-on:click="saveModal">Save</a>
<a v-on:click="closeModal" class="btn destructive">Cancel</a>
@ -71,7 +92,8 @@ export default {
ipAddress: '',
port: 8180,
ssl: false,
playsound: false,
subjectLine: false,
systemSound: false,
launchOnStartup: false,
minimize: true,
macstyle: true,
@ -83,7 +105,8 @@ export default {
relayMessage: "Tunneling is currently disabled. Click the circle and ensure your device is attached to enable it.",
relayColor: 'rgba(152,152,152,0.5)',
enableTunnel: false,
cacheMessages: false
cacheMessages: false,
notifSound: 'wm-audio://receivedText.mp3'
}
},
beforeDestroy () {
@ -93,6 +116,14 @@ export default {
}
},
methods: {
notifSoundChanged(e) {
if (e.target.files && e.target.files[0]) {
const file = e.target.files[0]
const path = 'local-file://'+file.path
this.notifSound = path
}
},
toggleTunnel () {
this.enableTunnel = !this.enableTunnel
this.$store.commit('setTunnel', this.enableTunnel)
@ -154,12 +185,14 @@ export default {
this.$store.commit('setFallbackIPAddress', this.ipAddress)
this.$store.commit('setPort', this.port)
this.$store.commit('setSSL', this.ssl)
this.$store.commit('setPlaySound', this.playsound)
this.$store.commit('setSubjectLine', this.subjectLine)
this.$store.commit('setSystemSound', this.systemSound)
this.$store.commit('setStartup', this.startup)
this.$store.commit('setMinimize', this.minimize)
this.$store.commit('setMacStyle', this.macstyle)
this.$store.commit('setAcceleration', this.acceleration)
this.$store.commit('setCacheMessages', this.cacheMessages)
this.$store.commit('setNotifSound', this.notifSound)
this.show = false
if (this.enableTunnel) {
this.initTunnel()
@ -181,13 +214,15 @@ export default {
this.ipAddress = this.$store.state.fallbackIpAddress != '' ? this.$store.state.fallbackIpAddress : this.ipAddress
this.port = this.$store.state.port
this.ssl = this.$store.state.ssl
this.playsound = this.$store.state.playsound
this.subjectLine = this.$store.state.subjectLine
this.systemSound = this.$store.state.systemSound
this.startup = this.$store.state.startup
this.minimize = this.$store.state.minimize
this.macstyle = this.$store.state.macstyle
this.acceleration = this.$store.state.acceleration
this.enableTunnel = this.$store.state.enableTunnel
this.cacheMessages = this.$store.state.cacheMessages
this.notifSound = this.$store.state.notifSound
if (this.enableTunnel) {
this.initTunnel()
}
@ -285,13 +320,31 @@ export default {
position: relative;
top: 50%;
transform: translateY(-50%);
max-width: 300px;
max-width: 650px;
margin: auto auto;
display: flex;
flex-direction: column;
border-radius: 10px;
z-index: 2;
.settingsWrapper {
display: flex;
justify-content: space-around;
.settingsColumn {
display: flex;
flex: 1 1 0px;
flex-direction: column;
max-width: 300px;
h4 {
padding-left: 8px;
margin: 0;
margin-bottom: 10px;
}
}
}
.textinput {
width: auto;
margin: 0px 5px !important;
@ -363,6 +416,26 @@ export default {
@media screen and (max-width: 992px) {
width: 90%;
}
input:not([type="range"]):not([type="file"]):not([type="color"]):not(.message-input) {
height: auto;
height: inherit;
font-size: 13px;
height: 6px;
padding: 10px 6px 10px 6px;
outline: none;
border: 1px solid rgb(213, 213, 213);
margin: 5px;
cursor: text;
-webkit-app-region: no-drag;
}
input:not([type="range"]):not([type="file"]):not([type="color"]):not(.message-input):focus {
border-radius: 1px;
box-shadow: 0px 0px 0px 3.5px rgba(23, 101, 144, 1);
animation: showFocus .3s;
border-color: rgb(122, 167, 221) !important;
}
}
&__close {
@ -390,6 +463,31 @@ export default {
}
}
label.file {
text-align: left;
font-size: 15px;
margin-bottom: 10px;
margin-left: 8px;
.fileBtn {
background: #646462;
padding: 4px 8px;
border-radius: 6px;
font-size: 13px;
width: fit-content;
cursor: pointer;
display: inline-block;
border: 2px solid rgba(0,0,0,0.6);
border-top: 0px;
border-left: 0px;
max-width: 215px;
&:hover {
filter: brightness(85%);
}
}
}
.switch {
padding-left: 8px;
text-align: left;

View File

@ -15,14 +15,17 @@ export default new Vuex.Store({
fallbackIpAddress: persistentStore.get('fallbackIpAddress', ''),
port: persistentStore.get('port', 8180),
ssl: persistentStore.get('ssl', true),
playsound: persistentStore.get('playsound', true),
subjectLine: persistentStore.get('subjectLine', false),
systemSound: persistentStore.get('systemSound', false),
startup: persistentStore.get('startup', false),
minimize: persistentStore.get('minimize', true),
macstyle: persistentStore.get('macstyle', true),
acceleration: persistentStore.get('acceleration', true),
messagesCache: [],
enableTunnel: persistentStore.get('enableTunnel', false),
cacheMessages: persistentStore.get('cacheMessages', false)
cacheMessages: persistentStore.get('cacheMessages', false),
mutedChats: persistentStore.get('mutedChats', []),
notifSound: persistentStore.get('notifSound', 'wm-audio://receivedText.mp3')
},
mutations: {
setPassword(state, password) {
@ -46,9 +49,13 @@ export default new Vuex.Store({
state['ssl'] = ssl
persistentStore.set('ssl', ssl)
},
setPlaySound(state, playsound) {
state['playsound'] = playsound
persistentStore.set('playsound', playsound)
setSubjectLine(state, subjectLine) {
state['subjectLine'] = subjectLine
persistentStore.set('subjectLine', subjectLine)
},
setSystemSound(state, systemSound) {
state['systemSound'] = systemSound
persistentStore.set('systemSound', systemSound)
},
setStartup(state, startup) {
state['startup'] = startup
@ -71,6 +78,10 @@ export default new Vuex.Store({
state['enableTunnel'] = enableTunnel
persistentStore.set('enableTunnel', enableTunnel)
},
setNotifSound(state, notifSound) {
state['notifSound'] = notifSound
persistentStore.set('notifSound', notifSound)
},
setCacheMessages(state, cacheMessages) {
state['cacheMessages'] = cacheMessages
persistentStore.set('cacheMessages', cacheMessages)
@ -79,6 +90,19 @@ export default new Vuex.Store({
if (!state['messagesCache'][messages.id]) state['messagesCache'][messages.id] = []
state['messagesCache'][messages.id] = messages.data
},
muteChat(state, chatId) {
if (state.mutedChats.includes(chatId)) return
state.mutedChats.push(chatId)
persistentStore.set('mutedChats', state.mutedChats)
},
unmuteChat(state, chatId) {
if (!state.mutedChats.includes(chatId)) return
const index = state.mutedChats.indexOf(chatId)
if (index > -1) {
state.mutedChats.splice(index, 1)
}
persistentStore.set('mutedChats', state.mutedChats)
},
resetMessages(state) {
state['messagesCache'] = []
}