mirror of
https://github.com/Cronocide/WebMessage.git
synced 2025-01-22 11:18:25 +00:00
Fixes, rewrites, and stable Win notifications
(finally)
This commit is contained in:
parent
1ca699727b
commit
518baba5ea
@ -23,7 +23,6 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.2",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.0-3",
|
||||
"@techassi/vue-lazy-image": "^1.0.0",
|
||||
"@trevoreyre/autocomplete-vue": "^2.2.0",
|
||||
"auto-launch": "^5.0.5",
|
||||
"axios": "^0.21.1",
|
||||
"bplist-parser": "^0.3.0",
|
||||
@ -40,18 +39,15 @@
|
||||
"jquery": "^3.6.0",
|
||||
"moment": "^2.29.1",
|
||||
"node-notifier": "^9.0.1",
|
||||
"powertoast": "^1.2.3",
|
||||
"rangy": "^1.3.0",
|
||||
"simplebar-vue": "^1.6.0",
|
||||
"usbmux": "^0.1.0",
|
||||
"vue": "^3.0.0",
|
||||
"vue-avatar": "^2.3.3",
|
||||
"vue-class-component": "^8.0.0-0",
|
||||
"vue-confirm-dialog": "^1.0.2",
|
||||
"vue-feather": "^2.0.0-beta",
|
||||
"vue-js-popover": "^1.2.1",
|
||||
"vue-linkify": "^1.0.1",
|
||||
"vue-long-click": "^0.0.4",
|
||||
"vue-native-websocket": "^2.0.14",
|
||||
"vue-native-websocket-vue3": "^3.1.0",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vuex": "^4.0.0-0"
|
||||
@ -61,6 +57,7 @@
|
||||
"@types/electron-devtools-installer": "^2.2.0",
|
||||
"@types/electron-localshortcut": "^3.1.0",
|
||||
"@types/jquery": "^3.5.5",
|
||||
"@types/node-notifier": "^8.0.0",
|
||||
"@types/rangy": "^0.0.33",
|
||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||
"@typescript-eslint/parser": "^2.33.0",
|
||||
|
@ -182,6 +182,8 @@ function showWin() {
|
||||
else {
|
||||
win.setSkipTaskbar(false)
|
||||
win.show()
|
||||
win.setAlwaysOnTop(true)
|
||||
win.setAlwaysOnTop(false) // weird work around but it works
|
||||
}
|
||||
if (app.dock) app.dock.show()
|
||||
|
||||
@ -267,12 +269,6 @@ ipcMain.on('loaded', () => {
|
||||
registerShortcuts()
|
||||
})
|
||||
|
||||
// ipcMain.on('notification', (event, opts) => {
|
||||
// Notifier.notify("hello??")
|
||||
// Notifier.notify(opts)
|
||||
// console.log("notifying with", opts)
|
||||
// })
|
||||
|
||||
ipcMain.on('rightClickMessage', (event, args) => {
|
||||
rightClickedMessage = args
|
||||
})
|
||||
@ -292,7 +288,7 @@ ipcMain.on('startup_check', () => {
|
||||
autoLauncher
|
||||
.isEnabled()
|
||||
.then(function(isEnabled) {
|
||||
const optionEnabled = persistentStore.get('startup', false)
|
||||
const optionEnabled = persistentStore.get('launchOnStartup', false)
|
||||
|
||||
if (optionEnabled && !isEnabled) {
|
||||
autoLauncher.enable()
|
||||
@ -317,11 +313,15 @@ ipcMain.on('quit_app', () => {
|
||||
app.quit()
|
||||
})
|
||||
|
||||
ipcMain.on('minimizeToTray', () => {
|
||||
function minimizeToTray () {
|
||||
if (!win) return
|
||||
win.setSkipTaskbar(true)
|
||||
win.hide()
|
||||
if (app.dock) app.dock.hide()
|
||||
}
|
||||
|
||||
ipcMain.on('minimizeToTray', () => {
|
||||
minimizeToTray()
|
||||
})
|
||||
|
||||
ipcMain.on('show_win', () => {
|
||||
@ -351,7 +351,16 @@ function registerLocalFileProtocols() {
|
||||
}
|
||||
})
|
||||
|
||||
// protocol.registerHttpProtocol('WebMessage', (request, callback) => {})
|
||||
app.removeAsDefaultProtocolClient('webmessage');
|
||||
|
||||
// If we are running a non-packaged version of the app && on windows
|
||||
if(isDevelopment && process.platform === 'win32') {
|
||||
// Set the path of electron.exe and your app.
|
||||
// These two additional parameters are only available on windows.
|
||||
app.setAsDefaultProtocolClient('webmessage', process.execPath, [path.resolve(process.argv[1])])
|
||||
} else {
|
||||
app.setAsDefaultProtocolClient('webmessage');
|
||||
}
|
||||
}
|
||||
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
@ -359,8 +368,12 @@ const gotTheLock = app.requestSingleInstanceLock()
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
app.on('second-instance', () => {
|
||||
app.on('second-instance', (event, args) => {
|
||||
showWin()
|
||||
let uriLocation = args.pop() as string
|
||||
let personId = uriLocation.split(':').pop()
|
||||
if (!personId || personId.trim() == '') return
|
||||
if (win) win.webContents.send('navigateChat', personId)
|
||||
})
|
||||
|
||||
app.whenReady().then(() => {
|
||||
|
186
src/components/AutoComplete/AutocompleteCore.js
Normal file
186
src/components/AutoComplete/AutocompleteCore.js
Normal file
@ -0,0 +1,186 @@
|
||||
import closest from './util/closest.js'
|
||||
import isPromise from './util/isPromise.js'
|
||||
|
||||
class AutocompleteCore {
|
||||
value = ''
|
||||
searchCounter = 0
|
||||
results = []
|
||||
selectedIndex = -1
|
||||
|
||||
constructor({
|
||||
search,
|
||||
autoSelect = false,
|
||||
setValue = () => {},
|
||||
setAttribute = () => {},
|
||||
onUpdate = () => {},
|
||||
onSubmit = () => {},
|
||||
onShow = () => {},
|
||||
onHide = () => {},
|
||||
onLoading = () => {},
|
||||
onLoaded = () => {},
|
||||
} = {}) {
|
||||
this.search = isPromise(search) ? search : value => Promise.resolve(search(value))
|
||||
this.autoSelect = autoSelect
|
||||
this.setValue = setValue
|
||||
this.setAttribute = setAttribute
|
||||
this.onUpdate = onUpdate
|
||||
this.onSubmit = onSubmit
|
||||
this.onShow = onShow
|
||||
this.onHide = onHide
|
||||
this.onLoading = onLoading
|
||||
this.onLoaded = onLoaded
|
||||
}
|
||||
|
||||
destroy = () => {
|
||||
this.search = null
|
||||
this.setValue = null
|
||||
this.setAttribute = null
|
||||
this.onUpdate = null
|
||||
this.onSubmit = null
|
||||
this.onShow = null
|
||||
this.onHide = null
|
||||
this.onLoading = null
|
||||
this.onLoaded = null
|
||||
}
|
||||
|
||||
handleInput = event => {
|
||||
const { value } = event.target
|
||||
this.updateResults(value)
|
||||
this.value = value
|
||||
}
|
||||
|
||||
handleKeyDown = event => {
|
||||
const { key } = event
|
||||
|
||||
switch (key) {
|
||||
case 'Up': // IE/Edge
|
||||
case 'Down': // IE/Edge
|
||||
case 'ArrowUp':
|
||||
case 'ArrowDown': {
|
||||
const selectedIndex = key === 'ArrowUp' || key === 'Up' ? this.selectedIndex - 1 : this.selectedIndex + 1
|
||||
event.preventDefault()
|
||||
this.handleArrows(selectedIndex)
|
||||
break
|
||||
}
|
||||
case 'Tab': {
|
||||
this.selectResult()
|
||||
break
|
||||
}
|
||||
case 'Enter': {
|
||||
const selectedResult = this.results[this.selectedIndex]
|
||||
this.selectResult()
|
||||
this.onSubmit(selectedResult)
|
||||
break
|
||||
}
|
||||
case 'Esc': // IE/Edge
|
||||
case 'Escape': {
|
||||
this.hideResults()
|
||||
this.setValue()
|
||||
break
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
handleFocus = event => {
|
||||
const { value } = event.target
|
||||
this.updateResults(value)
|
||||
this.value = value
|
||||
}
|
||||
|
||||
handleBlur = () => {
|
||||
this.hideResults()
|
||||
}
|
||||
|
||||
// The mousedown event fires before the blur event. Calling preventDefault() when
|
||||
// the results list is clicked will prevent it from taking focus, firing the
|
||||
// blur event on the input element, and closing the results list before click fires.
|
||||
handleResultMouseDown = event => {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
handleResultClick = event => {
|
||||
const { target } = event
|
||||
const result = closest(target, '[data-result-index]')
|
||||
if (result) {
|
||||
this.selectedIndex = parseInt(result.dataset.resultIndex, 10)
|
||||
const selectedResult = this.results[this.selectedIndex]
|
||||
this.selectResult()
|
||||
this.onSubmit(selectedResult)
|
||||
}
|
||||
}
|
||||
|
||||
handleArrows = selectedIndex => {
|
||||
// Loop selectedIndex back to first or last result if out of bounds
|
||||
const resultsCount = this.results.length
|
||||
this.selectedIndex = ((selectedIndex % resultsCount) + resultsCount) % resultsCount
|
||||
|
||||
// Update results and aria attributes
|
||||
this.onUpdate(this.results, this.selectedIndex)
|
||||
}
|
||||
|
||||
selectResult = () => {
|
||||
const selectedResult = this.results[this.selectedIndex]
|
||||
if (selectedResult) {
|
||||
this.setValue(selectedResult)
|
||||
}
|
||||
this.hideResults()
|
||||
}
|
||||
|
||||
updateResults = value => {
|
||||
const currentSearch = ++this.searchCounter
|
||||
this.onLoading()
|
||||
this.search(value).then(results => {
|
||||
if (currentSearch !== this.searchCounter) {
|
||||
return
|
||||
}
|
||||
this.results = results
|
||||
this.onLoaded()
|
||||
|
||||
if (this.results.length === 0) {
|
||||
this.hideResults()
|
||||
return
|
||||
}
|
||||
|
||||
this.selectedIndex = this.autoSelect ? 0 : -1
|
||||
this.onUpdate(this.results, this.selectedIndex)
|
||||
this.showResults()
|
||||
})
|
||||
}
|
||||
|
||||
showResults = () => {
|
||||
this.setAttribute('aria-expanded', true)
|
||||
this.onShow()
|
||||
}
|
||||
|
||||
hideResults = () => {
|
||||
this.selectedIndex = -1
|
||||
this.results = []
|
||||
this.setAttribute('aria-expanded', false)
|
||||
this.setAttribute('aria-activedescendant', '')
|
||||
this.onUpdate(this.results, this.selectedIndex)
|
||||
this.onHide()
|
||||
}
|
||||
|
||||
// Make sure selected result isn't scrolled out of view
|
||||
checkSelectedResultVisible = resultsElement => {
|
||||
const selectedResultElement = resultsElement.querySelector(`[data-result-index="${this.selectedIndex}"]`)
|
||||
if (!selectedResultElement) {
|
||||
return
|
||||
}
|
||||
|
||||
const resultsPosition = resultsElement.getBoundingClientRect()
|
||||
const selectedPosition = selectedResultElement.getBoundingClientRect()
|
||||
|
||||
if (selectedPosition.top < resultsPosition.top) {
|
||||
// Element is above viewable area
|
||||
resultsElement.scrollTop -= resultsPosition.top - selectedPosition.top
|
||||
} else if (selectedPosition.bottom > resultsPosition.bottom) {
|
||||
// Element is below viewable area
|
||||
resultsElement.scrollTop += selectedPosition.bottom - resultsPosition.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AutocompleteCore
|
337
src/components/AutoComplete/index.vue
Normal file
337
src/components/AutoComplete/index.vue
Normal file
@ -0,0 +1,337 @@
|
||||
// Forked from trevoreyre/autocomplete
|
||||
|
||||
<template>
|
||||
<div ref="root">
|
||||
<slot
|
||||
:rootProps="rootProps"
|
||||
:inputProps="inputProps"
|
||||
:inputListeners="inputListeners"
|
||||
:resultListProps="resultListProps"
|
||||
:resultListListeners="resultListListeners"
|
||||
:results="results"
|
||||
:resultProps="resultProps"
|
||||
>
|
||||
<div v-bind="rootProps">
|
||||
<input
|
||||
ref="input"
|
||||
v-bind="inputProps"
|
||||
@input="handleInput"
|
||||
@keydown="core.handleKeyDown"
|
||||
@focus="core.handleFocus"
|
||||
@blur="core.handleBlur"
|
||||
v-on="$attrs"
|
||||
/>
|
||||
<ul ref="resultList" v-bind="resultListProps" v-on="resultListListeners">
|
||||
<template v-for="(result, index) in results">
|
||||
<slot name="result" :result="result" :props="resultProps[index]">
|
||||
<li :key="resultProps[index].id" v-bind="resultProps[index]">
|
||||
{{ getResultValue(result) }}
|
||||
</li>
|
||||
</slot>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AutocompleteCore from './AutocompleteCore.js'
|
||||
import uniqueId from './util/uniqueId.js'
|
||||
import getRelativePosition from './util/getRelativePosition.js'
|
||||
import debounce from './util/debounce.js'
|
||||
|
||||
export default {
|
||||
name: 'Autocomplete',
|
||||
inheritAttrs: false,
|
||||
|
||||
props: {
|
||||
search: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
baseClass: {
|
||||
type: String,
|
||||
default: 'autocomplete',
|
||||
},
|
||||
autoSelect: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
getResultValue: {
|
||||
type: Function,
|
||||
default: result => result,
|
||||
},
|
||||
defaultValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
debounceTime: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
const core = new AutocompleteCore({
|
||||
search: this.search,
|
||||
autoSelect: this.autoSelect,
|
||||
setValue: this.setValue,
|
||||
onUpdate: this.handleUpdate,
|
||||
onSubmit: this.handleSubmit,
|
||||
onShow: this.handleShow,
|
||||
onHide: this.handleHide,
|
||||
onLoading: this.handleLoading,
|
||||
onLoaded: this.handleLoaded,
|
||||
})
|
||||
if (this.debounceTime > 0) {
|
||||
core.handleInput = debounce(core.handleInput, this.debounceTime)
|
||||
}
|
||||
|
||||
return {
|
||||
core,
|
||||
value: this.defaultValue,
|
||||
resultListId: uniqueId(`${this.baseClass}-result-list-`),
|
||||
results: [],
|
||||
selectedIndex: -1,
|
||||
expanded: false,
|
||||
loading: false,
|
||||
position: 'below',
|
||||
resetPosition: true,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
rootProps() {
|
||||
return {
|
||||
class: this.baseClass,
|
||||
style: { position: 'relative' },
|
||||
'data-expanded': this.expanded,
|
||||
'data-loading': this.loading,
|
||||
'data-position': this.position,
|
||||
}
|
||||
},
|
||||
inputProps() {
|
||||
return {
|
||||
class: `${this.baseClass}-input`,
|
||||
value: this.value,
|
||||
role: 'combobox',
|
||||
autocomplete: 'off',
|
||||
autocapitalize: 'off',
|
||||
autocorrect: 'off',
|
||||
spellcheck: 'false',
|
||||
'aria-autocomplete': 'list',
|
||||
'aria-haspopup': 'listbox',
|
||||
'aria-owns': this.resultListId,
|
||||
'aria-expanded': this.expanded ? 'true' : 'false',
|
||||
'aria-activedescendant': this.selectedIndex > -1 ? this.resultProps[this.selectedIndex].id : '',
|
||||
...this.$attrs,
|
||||
}
|
||||
},
|
||||
inputListeners() {
|
||||
return {
|
||||
input: this.handleInput,
|
||||
keydown: this.core.handleKeyDown,
|
||||
focus: this.core.handleFocus,
|
||||
blur: this.core.handleBlur,
|
||||
}
|
||||
},
|
||||
resultListProps() {
|
||||
const yPosition = this.position === 'below' ? 'top' : 'bottom'
|
||||
return {
|
||||
id: this.resultListId,
|
||||
class: `${this.baseClass}-result-list`,
|
||||
role: 'listbox',
|
||||
style: {
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
width: '100%',
|
||||
visibility: this.expanded ? 'visible' : 'hidden',
|
||||
pointerEvents: this.expanded ? 'auto' : 'none',
|
||||
[yPosition]: '100%',
|
||||
},
|
||||
}
|
||||
},
|
||||
resultListListeners() {
|
||||
return {
|
||||
mousedown: this.core.handleResultMouseDown,
|
||||
click: this.core.handleResultClick,
|
||||
}
|
||||
},
|
||||
resultProps() {
|
||||
return this.results.map((result, index) => ({
|
||||
id: `${this.baseClass}-result-${index}`,
|
||||
class: `${this.baseClass}-result`,
|
||||
'data-result-index': index,
|
||||
role: 'option',
|
||||
...(this.selectedIndex === index ? { 'aria-selected': 'true' } : {}),
|
||||
}))
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
document.body.addEventListener('click', this.handleDocumentClick)
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
document.body.removeEventListener('click', this.handleDocumentClick)
|
||||
},
|
||||
|
||||
updated() {
|
||||
if (!this.$refs.input || !this.$refs.resultList) {
|
||||
return
|
||||
}
|
||||
if (this.resetPosition && this.results.length > 0) {
|
||||
this.resetPosition = false
|
||||
this.position = getRelativePosition(this.$refs.input, this.$refs.resultList)
|
||||
}
|
||||
this.core.checkSelectedResultVisible(this.$refs.resultList)
|
||||
},
|
||||
|
||||
methods: {
|
||||
setValue(result) {
|
||||
this.value = result ? this.getResultValue(result) : ''
|
||||
},
|
||||
|
||||
handleUpdate(results, selectedIndex) {
|
||||
this.results = results
|
||||
this.selectedIndex = selectedIndex
|
||||
this.$emit('update', results, selectedIndex)
|
||||
},
|
||||
|
||||
handleShow() {
|
||||
this.expanded = true
|
||||
},
|
||||
|
||||
handleHide() {
|
||||
this.expanded = false
|
||||
this.resetPosition = true
|
||||
},
|
||||
|
||||
handleLoading() {
|
||||
this.loading = true
|
||||
},
|
||||
|
||||
handleLoaded() {
|
||||
this.loading = false
|
||||
},
|
||||
|
||||
handleInput(event) {
|
||||
this.value = event.target.value
|
||||
this.core.handleInput(event)
|
||||
},
|
||||
|
||||
handleSubmit(selectedResult) {
|
||||
this.$emit('submit', selectedResult)
|
||||
},
|
||||
|
||||
handleDocumentClick(event) {
|
||||
if (this.$refs.root.contains(event.target)) {
|
||||
return
|
||||
}
|
||||
this.core.hideResults()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.autocomplete-input {
|
||||
border: 1px solid #eee;
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
padding: 12px 12px 12px 48px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
flex: 1;
|
||||
background-color: #eee;
|
||||
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PGNpcmNsZSBjeD0iMTEiIGN5PSIxMSIgcj0iOCIvPjxwYXRoIGQ9Ik0yMSAyMWwtNC00Ii8+PC9zdmc+');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 12px center;
|
||||
}
|
||||
|
||||
.autocomplete-input:focus,
|
||||
.autocomplete-input[aria-expanded='true'] {
|
||||
border-color: rgba(0, 0, 0, 0.12);
|
||||
background-color: #fff;
|
||||
outline: none;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.16);
|
||||
}
|
||||
|
||||
[data-position='below'] .autocomplete-input[aria-expanded='true'] {
|
||||
border-bottom-color: transparent;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
[data-position='above'] .autocomplete-input[aria-expanded='true'] {
|
||||
border-top-color: transparent;
|
||||
border-radius: 0 0 8px 8px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Loading spinner */
|
||||
.autocomplete[data-loading='true']::after {
|
||||
content: '';
|
||||
border: 3px solid rgba(0, 0, 0, 0.12);
|
||||
border-right: 3px solid rgba(0, 0, 0, 0.48);
|
||||
border-radius: 100%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
animation: rotate 1s infinite linear;
|
||||
}
|
||||
|
||||
.autocomplete-result-list {
|
||||
margin: 0;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
max-height: 296px;
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
list-style: none;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.16);
|
||||
}
|
||||
|
||||
[data-position='below'] .autocomplete-result-list {
|
||||
margin-top: -1px;
|
||||
border-top-color: transparent;
|
||||
border-radius: 0 0 8px 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
[data-position='above'] .autocomplete-result-list {
|
||||
margin-bottom: -1px;
|
||||
border-bottom-color: transparent;
|
||||
border-radius: 8px 8px 0 0;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
/* Single result item */
|
||||
.autocomplete-result {
|
||||
cursor: default;
|
||||
padding: 12px 12px 12px 48px;
|
||||
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjY2NjIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PGNpcmNsZSBjeD0iMTEiIGN5PSIxMSIgcj0iOCIvPjxwYXRoIGQ9Ik0yMSAyMWwtNC00Ii8+PC9zdmc+');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 12px center;
|
||||
}
|
||||
|
||||
.autocomplete-result:hover,
|
||||
.autocomplete-result[aria-selected='true'] {
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: translateY(-50%) rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: translateY(-50%) rotate(359deg);
|
||||
}
|
||||
}
|
||||
</style>
|
22
src/components/AutoComplete/util/closest.js
Normal file
22
src/components/AutoComplete/util/closest.js
Normal file
@ -0,0 +1,22 @@
|
||||
// Polyfill for element.closest, to support Internet Explorer. It's a relatively
|
||||
// simple polyfill, so we'll just include it rather than require the user to
|
||||
// include the polyfill themselves. Adapted from
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
|
||||
import matches from './matches.js'
|
||||
|
||||
const closestPolyfill = (el, selector) => {
|
||||
let element = el
|
||||
while (element && element.nodeType === 1) {
|
||||
if (matches(element, selector)) {
|
||||
return element
|
||||
}
|
||||
element = element.parentNode
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const closest = (element, selector) => {
|
||||
return element.closest ? element.closest(selector) : closestPolyfill(element, selector)
|
||||
}
|
||||
|
||||
export default closest
|
29
src/components/AutoComplete/util/debounce.js
Normal file
29
src/components/AutoComplete/util/debounce.js
Normal file
@ -0,0 +1,29 @@
|
||||
// Credit David Walsh (https://davidwalsh.name/javascript-debounce-function)
|
||||
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds. If `immediate` is passed, trigger the function on the
|
||||
// leading edge, instead of the trailing.
|
||||
const debounce = (func, wait, immediate) => {
|
||||
let timeout
|
||||
|
||||
return function executedFunction() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const context = this
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
const args = arguments
|
||||
|
||||
const later = function() {
|
||||
timeout = null
|
||||
if (!immediate) func.apply(context, args)
|
||||
}
|
||||
|
||||
const callNow = immediate && !timeout
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(later, wait)
|
||||
|
||||
if (callNow) func.apply(context, args)
|
||||
}
|
||||
}
|
||||
|
||||
export default debounce
|
18
src/components/AutoComplete/util/getRelativePosition.js
Normal file
18
src/components/AutoComplete/util/getRelativePosition.js
Normal file
@ -0,0 +1,18 @@
|
||||
// Calculates whether element2 should be above or below element1. Always
|
||||
// places element2 below unless all of the following:
|
||||
// 1. There isn't enough visible viewport below to fit element2
|
||||
// 2. There is more room above element1 than there is below
|
||||
// 3. Placing elemen2 above 1 won't overflow window
|
||||
const getRelativePosition = (element1, element2) => {
|
||||
const position1 = element1.getBoundingClientRect()
|
||||
const position2 = element2.getBoundingClientRect()
|
||||
|
||||
const positionAbove =
|
||||
/* 1 */ position1.bottom + position2.height > window.innerHeight &&
|
||||
/* 2 */ window.innerHeight - position1.bottom < position1.top &&
|
||||
/* 3 */ window.pageYOffset + position1.top - position2.height > 0
|
||||
|
||||
return positionAbove ? 'above' : 'below'
|
||||
}
|
||||
|
||||
export default getRelativePosition
|
5
src/components/AutoComplete/util/isPromise.js
Normal file
5
src/components/AutoComplete/util/isPromise.js
Normal file
@ -0,0 +1,5 @@
|
||||
// Returns true if the value has a "then" function. Adapted from
|
||||
// https://github.com/graphql/graphql-js/blob/499a75939f70c4863d44149371d6a99d57ff7c35/src/jsutils/isPromise.js
|
||||
const isPromise = value => Boolean(value && typeof value.then === 'function')
|
||||
|
||||
export default isPromise
|
15
src/components/AutoComplete/util/matches.js
Normal file
15
src/components/AutoComplete/util/matches.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Polyfill for element.matches, to support Internet Explorer. It's a relatively
|
||||
// simple polyfill, so we'll just include it rather than require the user to
|
||||
// include the polyfill themselves. Adapted from
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Element/matches#Polyfill
|
||||
const matches = (element, selector) => {
|
||||
return element.matches
|
||||
? element.matches(selector)
|
||||
: element.msMatchesSelector
|
||||
? element.msMatchesSelector(selector)
|
||||
: element.webkitMatchesSelector
|
||||
? element.webkitMatchesSelector(selector)
|
||||
: null
|
||||
}
|
||||
|
||||
export default matches
|
6
src/components/AutoComplete/util/uniqueId.js
Normal file
6
src/components/AutoComplete/util/uniqueId.js
Normal file
@ -0,0 +1,6 @@
|
||||
// Generates a unique ID, with optional prefix. Adapted from
|
||||
// https://github.com/lodash/lodash/blob/61acdd0c295e4447c9c10da04e287b1ebffe452c/uniqueId.js
|
||||
let idCounter = 0
|
||||
const uniqueId = (prefix = '') => `${prefix}${++idCounter}`
|
||||
|
||||
export default uniqueId
|
@ -188,7 +188,7 @@ export default {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
>>> .feather {
|
||||
.feather:deep() {
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
|
@ -364,6 +364,7 @@ export default {
|
||||
mounted() {
|
||||
this.loadValues()
|
||||
|
||||
ipcRenderer.send('startup_check')
|
||||
ipcRenderer.send('app_version')
|
||||
ipcRenderer.on('app_version', (event, arg) => {
|
||||
ipcRenderer.removeAllListeners('app_version')
|
||||
|
@ -123,7 +123,6 @@ export default createStore({
|
||||
|
||||
state['isTypingTimer'][data.chatId] = setTimeout(() => {
|
||||
state['isTyping'][data.chatId] = false
|
||||
console.log('closing')
|
||||
}, 60000)
|
||||
},
|
||||
},
|
||||
|
9
src/types/modules.d.ts
vendored
9
src/types/modules.d.ts
vendored
@ -6,17 +6,12 @@ declare module 'vue-linkify'
|
||||
declare module 'vue-confirm-dialog'
|
||||
declare module 'vue-long-click'
|
||||
declare module 'vue-js-popover'
|
||||
declare module 'powertoast'
|
||||
declare module 'bplist-parser'
|
||||
declare module '@techassi/vue-lazy-image'
|
||||
declare module './mixin'
|
||||
|
||||
declare module '@trevoreyre/autocomplete-vue'
|
||||
declare module '@/components/VideoPlayer'
|
||||
declare module '@/components/ExpandableImage'
|
||||
declare module '@/components/DownloadAttachment'
|
||||
declare module '@/components/UploadButton'
|
||||
declare module '@/components/ReactionMenu'
|
||||
declare module '@/components/ReactionBubbles'
|
||||
declare module '@trevoreyre/autocomplete-vue/index.js'
|
||||
declare module '@/views/Home'
|
||||
declare module '@/views/Chat'
|
||||
declare module '@/views/Blank'
|
||||
|
@ -281,21 +281,19 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Autocomplete from '@trevoreyre/autocomplete-vue'
|
||||
import VideoPlayer from '@/components/VideoPlayer'
|
||||
import ExpandableImage from '@/components/ExpandableImage'
|
||||
import DownloadAttachment from '@/components/DownloadAttachment'
|
||||
import UploadButton from '@/components/UploadButton'
|
||||
import ReactionMenu from '@/components/ReactionMenu'
|
||||
import ReactionBubbles from '@/components/ReactionBubbles'
|
||||
import Autocomplete from '@/components/AutoComplete/index.vue'
|
||||
import VideoPlayer from '@/components/VideoPlayer.vue'
|
||||
import ExpandableImage from '@/components/ExpandableImage.vue'
|
||||
import DownloadAttachment from '@/components/DownloadAttachment.vue'
|
||||
import UploadButton from '@/components/UploadButton.vue'
|
||||
import ReactionMenu from '@/components/ReactionMenu.vue'
|
||||
import ReactionBubbles from '@/components/ReactionBubbles.vue'
|
||||
import TypingIndicator from '@/components/TypingIndicator.vue'
|
||||
import PayloadAttachment from '@/components/PayloadAttachment.vue'
|
||||
import Avatar from '@/components/Avatar.vue'
|
||||
import AudioPlayer from '@/components/AudioPlayer.vue'
|
||||
import UnsupportedMessage from '@/components/UnsupportedMessage.vue'
|
||||
import { Picker } from 'emoji-mart-vue-fast/src/index'
|
||||
import './messages'
|
||||
import '@trevoreyre/autocomplete-vue/dist/style.css'
|
||||
import 'emoji-mart-vue-fast/css/emoji-mart.css'
|
||||
|
||||
import functionsComp from './functions'
|
||||
|
@ -151,6 +151,8 @@ export default () => {
|
||||
|
||||
delete state.messageText[watchRoute.params.id as string]
|
||||
delete state.subjectText[watchRoute.params.id as string]
|
||||
state.lastTypingValue = ''
|
||||
state.lastTypingStamp = 0
|
||||
|
||||
const messageInputRef = currentInstance?.refs.messageInput as HTMLElement
|
||||
const subjectInputRef = currentInstance?.refs.subjectInput as HTMLElement
|
||||
@ -338,7 +340,7 @@ export default () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const onSubmit = (result: any) => {
|
||||
if (result && result.personId) {
|
||||
router?.push('/message/' + result.personId)
|
||||
router?.push('/chat/' + result.personId)
|
||||
} else if (result) {
|
||||
autoCompleteInput(result)
|
||||
} else {
|
||||
@ -368,6 +370,7 @@ export default () => {
|
||||
search,
|
||||
getResultValue,
|
||||
onSubmit,
|
||||
sendText,
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ const newMessageHandler = (response: any) => {
|
||||
if (route.params.id == 'new') {
|
||||
if (Object.keys(message).length > 0) {
|
||||
if (message[0].chatId == state.receiver) {
|
||||
router.push('/message/' + message[0].personId)
|
||||
router.push('/chat/' + message[0].personId)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@ -169,7 +169,6 @@ const newMessageHandler = (response: any) => {
|
||||
if (messageData.sender != 1 && remote.Notification.isSupported()) {
|
||||
if (store?.state.mutedChats.includes(messageData.personId)) return
|
||||
// if (this.lastNotificationGUID == messageData.guid) return
|
||||
if (route?.params.id == messageData.personId && document.hasFocus()) return
|
||||
|
||||
let body = messageData.text.replace(/\u{fffc}/gu, '')
|
||||
if (messageData.group && messageData.group.startsWith('chat')) {
|
||||
@ -186,7 +185,7 @@ const newMessageHandler = (response: any) => {
|
||||
icon: null as Nullable<string>,
|
||||
}
|
||||
|
||||
if (document.hasFocus()) return
|
||||
if (document.hasFocus() && remote.getCurrentWindow().isVisible() && route?.params.id == messageData.personId) return
|
||||
sendNotifierNotification(notificationOptions, messageData, chatData)
|
||||
} else if (messageData.sender != 1) {
|
||||
console.log('Notifications are not supported on this system.')
|
||||
@ -322,6 +321,9 @@ const onSocketDisconnect = (e: Event | CloseEvent) => {
|
||||
store?.commit('resetMessages')
|
||||
router?.push('/').catch(() => {})
|
||||
state.chats = []
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
if (windowState.status == 0) setTimeout(connectWS, 1000)
|
||||
}
|
||||
|
||||
const onSocketConnected = () => {
|
||||
@ -445,5 +447,6 @@ export default () => {
|
||||
deleteChat,
|
||||
composeMessage,
|
||||
markAsRead,
|
||||
connectWS,
|
||||
}
|
||||
}
|
||||
|
@ -1,64 +1,71 @@
|
||||
<template>
|
||||
<!-- <vue-confirm-dialog class="confirmDialog"></vue-confirm-dialog> -->
|
||||
<settings ref="settingsModal" @saved="chats.connectWS"></settings>
|
||||
<div id="nav" :class="{ notrans: !$store.state.acceleration }">
|
||||
<div class="titlebar">
|
||||
<div class="buttons" v-if="$store.state.macstyle || window.process.platform === 'darwin'">
|
||||
<div class="close" @click="window.closeWindow">
|
||||
<span class="closebutton"><span>x</span></span>
|
||||
<div
|
||||
id="vueApp"
|
||||
:class="{
|
||||
nostyle: !($store.state.macstyle || window.process.platform === 'darwin'),
|
||||
maximized: (window.state.maximized || !$store.state.acceleration) && window.process.platform !== 'darwin',
|
||||
}"
|
||||
>
|
||||
<settings ref="settingsModal" @saved="chats.connectWS"></settings>
|
||||
<div id="nav" :class="{ notrans: !$store.state.acceleration }">
|
||||
<div class="titlebar">
|
||||
<div class="buttons" v-if="$store.state.macstyle || window.process.platform === 'darwin'">
|
||||
<div class="close" @click="window.closeWindow">
|
||||
<span class="closebutton"><span>x</span></span>
|
||||
</div>
|
||||
<div class="minimize" @click="window.minimizeWindow">
|
||||
<span class="minimizebutton"><span>–</span></span>
|
||||
</div>
|
||||
<div class="zoom" @click="window.maximizeWindow">
|
||||
<span class="zoombutton"><span>+</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="minimize" @click="window.minimizeWindow">
|
||||
<span class="minimizebutton"><span>–</span></span>
|
||||
<div class="statusIndicator" style="margin-top: 1px;" v-tooltip:bottom.tooltip="window.statusText.value">
|
||||
<feather type="circle" stroke="rgba(25,25,25,0.5)" :fill="window.statusColor.value" size="10"></feather>
|
||||
</div>
|
||||
<div class="zoom" @click="window.maximizeWindow">
|
||||
<span class="zoombutton"><span>+</span></span>
|
||||
<div class="menuBtn">
|
||||
<feather type="settings" stroke="rgba(152,152,152,0.5)" size="20" @click="$refs.settingsModal.openModal()"></feather>
|
||||
</div>
|
||||
<div class="menuBtn">
|
||||
<feather type="refresh-cw" stroke="rgba(152,152,152,0.5)" size="19" @click="chats.connectWS"></feather>
|
||||
</div>
|
||||
<div class="menuBtn">
|
||||
<feather type="edit" stroke="rgba(36,132,255,0.65)" size="20" @click="chats.composeMessage"></feather>
|
||||
</div>
|
||||
<div class="menuBtn" v-if="window.updateAvailable">
|
||||
<feather type="download" stroke="rgba(152,255,152,0.65)" size="20" @click="window.restart"></feather>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statusIndicator" style="margin-top: 1px;" v-tooltip:bottom.tooltip="window.statusText.value">
|
||||
<feather type="circle" stroke="rgba(25,25,25,0.5)" :fill="window.statusColor.value" size="10"></feather>
|
||||
<div class="searchContainer">
|
||||
<input type="search" placeholder="Search" class="textinput" v-model="chats.search" />
|
||||
</div>
|
||||
<div class="menuBtn">
|
||||
<feather type="settings" stroke="rgba(152,152,152,0.5)" size="20" @click="$refs.settingsModal.openModal()"></feather>
|
||||
</div>
|
||||
<div class="menuBtn">
|
||||
<feather type="refresh-cw" stroke="rgba(152,152,152,0.5)" size="19" @click="chats.connectWS"></feather>
|
||||
</div>
|
||||
<div class="menuBtn">
|
||||
<feather type="edit" stroke="rgba(36,132,255,0.65)" size="20" @click="chats.composeMessage"></feather>
|
||||
</div>
|
||||
<div class="menuBtn" v-if="window.updateAvailable">
|
||||
<feather type="download" stroke="rgba(152,255,152,0.65)" size="20" @click="window.restart"></feather>
|
||||
<div class="chats scrollable" ref="chatsContainer">
|
||||
<chat
|
||||
v-for="chat in chats.filteredChats.value"
|
||||
:key="chat.id"
|
||||
:chatid="chat.personId"
|
||||
:author="chat.author"
|
||||
:text="chat.text"
|
||||
:date="chat.date"
|
||||
:read="chat.read"
|
||||
:docid="chat.docid"
|
||||
:showNum="chat.showNum"
|
||||
:isGroup="chat.personId.startsWith('chat') && !chat.personId.includes('@') && chat.personId.length >= 20"
|
||||
@deleted="deleteChat(chat)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchContainer">
|
||||
<input type="search" placeholder="Search" class="textinput" v-model="chats.search" />
|
||||
<div id="content">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="fade" mode="out-in">
|
||||
<component :is="Component" @markAsRead="chats.markAsRead" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
<div class="chats scrollable" ref="chatsContainer">
|
||||
<chat
|
||||
v-for="chat in chats.filteredChats.value"
|
||||
:key="chat.id"
|
||||
:chatid="chat.personId"
|
||||
:author="chat.author"
|
||||
:text="chat.text"
|
||||
:date="chat.date"
|
||||
:read="chat.read"
|
||||
:docid="chat.docid"
|
||||
:showNum="chat.showNum"
|
||||
:isGroup="chat.personId.startsWith('chat') && !chat.personId.includes('@') && chat.personId.length >= 20"
|
||||
@deleted="deleteChat(chat)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="content">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="fade" mode="out-in">
|
||||
<component :is="Component" @markAsRead="chats.markAsRead" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import Chat from '@/components/Chat.vue'
|
||||
import Settings from '@/components/Settings.vue'
|
||||
import Tooltip from '@/components/Tooltip.vue'
|
||||
@ -221,7 +228,7 @@ body {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#app {
|
||||
#vueApp {
|
||||
font-family: 'Roboto', -apple-system, BlinkMacSystemFont, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { ipcRenderer } from 'electron'
|
||||
import { reactive } from 'vue'
|
||||
import { Router, useRouter } from 'vue-router'
|
||||
import { RouteLocationNormalizedLoaded, Router, useRoute, useRouter } from 'vue-router'
|
||||
import { Store, useStore } from 'vuex'
|
||||
import { remote } from 'electron'
|
||||
import toast from 'powertoast'
|
||||
|
||||
interface NotificationOptions {
|
||||
appID: string
|
||||
@ -10,8 +12,13 @@ interface NotificationOptions {
|
||||
sound: boolean
|
||||
reply?: boolean
|
||||
icon?: Nullable<string>
|
||||
actions?: string[]
|
||||
cropIcon?: boolean
|
||||
silent?: boolean
|
||||
onClick?: string
|
||||
}
|
||||
|
||||
let route: Nullable<RouteLocationNormalizedLoaded> = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let store: Nullable<Store<any>> = null
|
||||
let router: Nullable<Router> = null
|
||||
@ -20,6 +27,19 @@ const state = reactive({
|
||||
notifSound: null as Nullable<HTMLAudioElement>,
|
||||
})
|
||||
|
||||
const sendPowertoastNotification = (
|
||||
options: NotificationOptions,
|
||||
messageData: { authorDocid: string; personId: string },
|
||||
chatData: { id: string }
|
||||
) => {
|
||||
options.silent = !options.sound
|
||||
options.cropIcon = true
|
||||
options.onClick = 'webmessage:' + messageData.personId
|
||||
toast(options).catch((err: string) => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
const sendNotifierNotification = (
|
||||
options: NotificationOptions,
|
||||
messageData: { authorDocid: string; personId: string },
|
||||
@ -28,7 +48,7 @@ const sendNotifierNotification = (
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const https = require('https')
|
||||
const tempDir = path.join(__dirname, '..', 'temp')
|
||||
const tempDir = path.join(remote.app.getAppPath(), '..', 'temp')
|
||||
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
fs.mkdirSync(tempDir)
|
||||
@ -42,36 +62,44 @@ const sendNotifierNotification = (
|
||||
agent: new https.Agent({ rejectUnauthorized: false }),
|
||||
}
|
||||
|
||||
console.log('trying to download', dlOptions)
|
||||
|
||||
download
|
||||
.image(dlOptions)
|
||||
.then(() => {
|
||||
options.icon = fileDir
|
||||
if (fs.statSync(fileDir).size == 0) throw Error('Empty image')
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((err: any) => {
|
||||
console.log(err)
|
||||
options.icon = __static + '/icon.png'
|
||||
})
|
||||
.finally(() => {
|
||||
console.log('done downloading')
|
||||
if (!store?.state.systemSound) state.notifSound?.play()
|
||||
if (document.hasFocus() && remote.getCurrentWindow().isVisible() && route?.params.id != messageData.personId) return
|
||||
|
||||
const NotificationCenter = require('node-notifier').NotificationCenter
|
||||
let Notifier = null
|
||||
let notifier: any = null
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
Notifier = new NotificationCenter({
|
||||
notifier = new NotificationCenter({
|
||||
withFallback: true,
|
||||
customPath: path.join(
|
||||
__dirname,
|
||||
'../terminal-notifier/vendor/mac.noindex/terminal-notifier.app/Contents/MacOS/terminal-notifier'
|
||||
),
|
||||
})
|
||||
} else if (process.platform == 'win32') {
|
||||
sendPowertoastNotification(options, messageData, chatData)
|
||||
return
|
||||
} else {
|
||||
Notifier = require('node-notifier')
|
||||
notifier = require('node-notifier')
|
||||
}
|
||||
|
||||
Notifier.notify(options, (err: string, action: string) => {
|
||||
notifier.notify(options, (err: any, action: any) => {
|
||||
if (err) return
|
||||
if ((action == 'activate' || action == undefined) && chatData && chatData.id) {
|
||||
if ((action == 'activate' || action == 'click') && chatData && chatData.id) {
|
||||
ipcRenderer.send('show_win')
|
||||
router?.push('/chat/' + messageData.personId)
|
||||
}
|
||||
@ -83,6 +111,7 @@ export { state, sendNotifierNotification }
|
||||
|
||||
export default () => {
|
||||
store = useStore()
|
||||
route = useRoute()
|
||||
router = useRouter()
|
||||
|
||||
return { state }
|
||||
|
@ -127,8 +127,13 @@ export default () => {
|
||||
}
|
||||
})
|
||||
|
||||
ipcRenderer.on('navigateChat', (sender, personId) => {
|
||||
router?.push('/chat/' + personId).catch(() => {})
|
||||
})
|
||||
|
||||
ipcRenderer.on('win_id', (e, id) => {
|
||||
state.win = remote.BrowserWindow.fromId(id)
|
||||
// state.win = remote.BrowserWindow.fromId(id)
|
||||
state.win = remote.getCurrentWindow()
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
if (state.maximizing) return
|
||||
|
@ -21,6 +21,13 @@ module.exports = {
|
||||
// preload: 'src/preload.js',
|
||||
builderOptions: {
|
||||
appId: 'com.sgtaziz.WebMessage',
|
||||
protocols: [
|
||||
{
|
||||
name: 'WebMessage',
|
||||
role: 'Viewer',
|
||||
schemes: ['webmessage'],
|
||||
},
|
||||
],
|
||||
productName: 'WebMessage',
|
||||
publish: ['github'],
|
||||
snap: {
|
||||
@ -38,8 +45,8 @@ module.exports = {
|
||||
to: 'resources/vendor',
|
||||
},
|
||||
{
|
||||
from: 'vendor/',
|
||||
to: 'resources/terminal-notifier/vendor',
|
||||
from: 'terminal-notifier/',
|
||||
to: 'resources/terminal-notifier',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
83
yarn.lock
83
yarn.lock
@ -1041,11 +1041,6 @@
|
||||
dependencies:
|
||||
vue "^3.0.0"
|
||||
|
||||
"@trevoreyre/autocomplete-vue@^2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@trevoreyre/autocomplete-vue/-/autocomplete-vue-2.2.0.tgz"
|
||||
integrity sha512-A5j986nM6htTbCpEW9BbwlqCobIMD4+uicAYGCSI8DM1ojK8meCyVI23jK+gAxi+vjhraCBneKI+vbwB8sL0ig==
|
||||
|
||||
"@types/anymatch@*":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz"
|
||||
@ -1169,15 +1164,22 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz"
|
||||
integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==
|
||||
|
||||
"@types/node-notifier@^8.0.0":
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-notifier/-/node-notifier-8.0.0.tgz#51100d67155ed1500a8aaa633987109f59a0637d"
|
||||
integrity sha512-CseIDQOC/I+yvj/4ItpG4ATcwooQlGPDDJweII8nspjjZg4ZBuvkyHg9P81QkElgU9FpYlb5A27BRggD3idTCQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/node@*":
|
||||
version "14.14.34"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.34.tgz"
|
||||
integrity sha512-dBPaxocOK6UVyvhbnpFIj2W+S+1cBTkHQbFQfeeJhoKFbzYcVUGHvddeWPSucKATb3F0+pgDq0i6ghEaZjsugA==
|
||||
|
||||
"@types/node@^12.0.12":
|
||||
version "12.20.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.5.tgz"
|
||||
integrity sha512-5Oy7tYZnu3a4pnJ//d4yVvOImExl4Vtwf0D40iKUlU+XlUsyV9iyFWyCFlwy489b72FMAik/EFwRkNLjjOdSPg==
|
||||
version "12.20.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.7.tgz#1cb61fd0c85cb87e728c43107b5fd82b69bc9ef8"
|
||||
integrity sha512-gWL8VUkg8VRaCAUgG9WmhefMqHmMblxe2rVpMF86nZY/+ZysU+BkAp+3cz03AixWDSSz0ks5WX59yAhv/cDwFA==
|
||||
|
||||
"@types/node@^14.6.2":
|
||||
version "14.14.35"
|
||||
@ -2937,11 +2939,6 @@ camelcase@^6.0.0, camelcase@^6.2.0:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz"
|
||||
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
|
||||
|
||||
can-use-dom@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/can-use-dom/-/can-use-dom-0.1.0.tgz"
|
||||
integrity sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo=
|
||||
|
||||
caniuse-api@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz"
|
||||
@ -3528,7 +3525,7 @@ core-js@^2.5.7:
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz"
|
||||
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
|
||||
|
||||
core-js@^3.0.1, core-js@^3.1.3, core-js@^3.6.5:
|
||||
core-js@^3.1.3, core-js@^3.6.5:
|
||||
version "3.9.1"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.1.tgz"
|
||||
integrity sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg==
|
||||
@ -4355,9 +4352,9 @@ electron@*:
|
||||
extract-zip "^1.0.3"
|
||||
|
||||
electron@^11.0.0:
|
||||
version "11.3.0"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-11.3.0.tgz"
|
||||
integrity sha512-MhdS0gok3wZBTscLBbYrOhLaQybCSAfkupazbK1dMP5c+84eVMxJE/QGohiWQkzs0tVFIJsAHyN19YKPbelNrQ==
|
||||
version "11.4.2"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-11.4.2.tgz#02005d9f5d77ea6485efeffdb5c5433c769ddda4"
|
||||
integrity sha512-P0PRLH7cXp8ZdpA9yVPe7jRVM+QeiAtsadqmqS6XY3AYrsH+7bJnVrNuw6p/fcmp+b/UxWaCexobqQpyFJ5Qkw==
|
||||
dependencies:
|
||||
"@electron/get" "^1.0.1"
|
||||
"@types/node" "^12.0.12"
|
||||
@ -6932,11 +6929,6 @@ lodash.merge@^4.6.1:
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash.throttle@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz"
|
||||
integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
|
||||
|
||||
lodash.transform@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.transform/-/lodash.transform-4.6.0.tgz"
|
||||
@ -7527,7 +7519,7 @@ node-libs-browser@^2.2.1:
|
||||
|
||||
node-notifier@^9.0.1:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-9.0.1.tgz"
|
||||
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-9.0.1.tgz#cea837f4c5e733936c7b9005e6545cea825d1af4"
|
||||
integrity sha512-fPNFIp2hF/Dq7qLDzSg4vZ0J4e9v60gJR+Qx7RbjbWqzPDdEqeVpEx5CFeDAELIl+A/woaaNn1fQ5nEVerMxJg==
|
||||
dependencies:
|
||||
growly "^1.3.0"
|
||||
@ -8647,6 +8639,11 @@ postcss@^8.1.10:
|
||||
nanoid "^3.1.20"
|
||||
source-map "^0.6.1"
|
||||
|
||||
powertoast@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/powertoast/-/powertoast-1.2.3.tgz#343d993b842eb0f9addb440867135f466d1af943"
|
||||
integrity sha512-3p5m3ltqeEaQQbjMVNSyA9i4f52OgGVu5MK6IERWtX9L5+oFLwEb9gnQHDoNv+clTPDqhS3As5vJ0fTbZrCo5w==
|
||||
|
||||
prelude-ls@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz"
|
||||
@ -9142,11 +9139,6 @@ requires-port@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz"
|
||||
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
||||
|
||||
resize-observer-polyfill@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz"
|
||||
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
|
||||
|
||||
resolve-cwd@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz"
|
||||
@ -9583,26 +9575,6 @@ simple-swizzle@^0.2.2:
|
||||
dependencies:
|
||||
is-arrayish "^0.3.1"
|
||||
|
||||
simplebar-vue@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/simplebar-vue/-/simplebar-vue-1.6.0.tgz"
|
||||
integrity sha512-O43JuN0eeI2kfspm+BME8DmfFKLkiOYRaMlGoTOExjiUHVrrGdXnyxRjewCWCSTfBTKy4tACv+mdrYfVxLZdBQ==
|
||||
dependencies:
|
||||
core-js "^3.0.1"
|
||||
simplebar "^5.3.0"
|
||||
|
||||
simplebar@^5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/simplebar/-/simplebar-5.3.0.tgz"
|
||||
integrity sha512-LgrGdIWpwHLLlI9HqfnGql62H/iZlF0KDZ7w3ZNbd2ZLwh9NKsODLHPzQgUlqQ8aZe7Y6/1xJMXK1PU5e810+w==
|
||||
dependencies:
|
||||
can-use-dom "^0.1.0"
|
||||
core-js "^3.0.1"
|
||||
lodash.debounce "^4.0.8"
|
||||
lodash.memoize "^4.1.2"
|
||||
lodash.throttle "^4.1.1"
|
||||
resize-observer-polyfill "^1.5.1"
|
||||
|
||||
slash@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz"
|
||||
@ -10896,11 +10868,6 @@ vm-browserify@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz"
|
||||
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
|
||||
|
||||
vue-avatar@^2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-avatar/-/vue-avatar-2.3.3.tgz"
|
||||
integrity sha512-Z57ILRTkFIAuCH9JiFBxX74C5zua5ub/jRDM/KZ+QKXNfscvmUOgWBs3kA2+wrpZMowIvfLHIT0gvQu1z+zpLg==
|
||||
|
||||
vue-class-component@^8.0.0-0:
|
||||
version "8.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-8.0.0-rc.1.tgz"
|
||||
@ -10932,11 +10899,6 @@ vue-cli-plugin-electron-builder@~2.0.0-rc.6:
|
||||
webpack-merge "^4.2.2"
|
||||
yargs "^15.3.1"
|
||||
|
||||
vue-confirm-dialog@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/vue-confirm-dialog/-/vue-confirm-dialog-1.0.2.tgz"
|
||||
integrity sha512-gTo1bMDWOLd/6ihmWv8VlPxhc9QaKoE5YqlsKydUOfrrQ3Q3taljF6yI+1TMtAtJLrvZ8DYrePhgBhY1VCJzbQ==
|
||||
|
||||
vue-eslint-parser@^7.0.0, vue-eslint-parser@^7.6.0:
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.6.0.tgz"
|
||||
@ -11004,11 +10966,6 @@ vue-native-websocket-vue3@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/vue-native-websocket-vue3/-/vue-native-websocket-vue3-3.1.0.tgz#e66ce6b88a1991b9f4a0404fb872e39ac3248401"
|
||||
integrity sha512-pa53/81O7V+rm3asVDx402ruCUgPBE1CKAp+TEachvhN8geUqsIclj8N56+EoUFu/hpsbEI7eIVvVvGRNTn33A==
|
||||
|
||||
vue-native-websocket@^2.0.14:
|
||||
version "2.0.14"
|
||||
resolved "https://registry.yarnpkg.com/vue-native-websocket/-/vue-native-websocket-2.0.14.tgz"
|
||||
integrity sha512-oK8+xG1gmqRs4JngHGwEc4zWoRjsdMB20Sz8pemkh4lW2Vr2676/cDRtd30aGnO2xF7Oxf003wS5JO0kypUsCw==
|
||||
|
||||
vue-observe-visibility@^0.4.4:
|
||||
version "0.4.6"
|
||||
resolved "https://registry.yarnpkg.com/vue-observe-visibility/-/vue-observe-visibility-0.4.6.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user