mirror of
https://github.com/Elvanos/fantasia-archive.git
synced 2024-09-29 08:41:49 +13:00
487 lines
13 KiB
Vue
487 lines
13 KiB
Vue
<template>
|
|
<div id="q-app">
|
|
<appWindowButtons />
|
|
<router-view />
|
|
|
|
<q-window
|
|
v-model="documentPreviewWindowVisible"
|
|
no-resize
|
|
dark
|
|
headless
|
|
ref="documentPreviewWindow"
|
|
@input="refreshDocumentPreviewWindow"
|
|
no-move
|
|
:content-class="{'bg-gunmetal-light text-accent docPreviewWindow': true, '-noBar': disableDocumentControlBar}"
|
|
>
|
|
<div class="fit">
|
|
<q-btn
|
|
icon="mdi-close"
|
|
color="secondary"
|
|
round
|
|
flat
|
|
size="md"
|
|
class="previewCloseButton"
|
|
@click="refreshDocumentPreviewWindow(false)"
|
|
>
|
|
<q-tooltip
|
|
:delay="500"
|
|
anchor="bottom middle"
|
|
self="top middle"
|
|
>
|
|
Close document preview
|
|
</q-tooltip>
|
|
</q-btn>
|
|
|
|
<documentPreview
|
|
:document-id="documentPreviewElementID"
|
|
:display-mode="'document'"
|
|
/>
|
|
|
|
</div>
|
|
</q-window>
|
|
|
|
<q-window
|
|
v-model="advSearchWindowVisible"
|
|
no-resize
|
|
dark
|
|
title="Advanced Search Cheatsheet"
|
|
:height="625"
|
|
:width="500"
|
|
:start-x="50"
|
|
:start-y="150"
|
|
:actions="['pin', 'close']"
|
|
content-class="bg-gunmetal-light text-accent advSearchWindow"
|
|
>
|
|
<div class="q-pa-md fit">
|
|
<q-markdown no-heading-anchor-links>
|
|
{{$t('documents.advancedSearchCheatSheet')}}
|
|
</q-markdown>
|
|
</div>
|
|
</q-window>
|
|
|
|
<q-window
|
|
v-model="corkboardWindowVisible"
|
|
dark
|
|
title="Note board"
|
|
gripper-border-color="primary"
|
|
gripper-background-color="primary"
|
|
:height="600"
|
|
:width="350"
|
|
:start-x="350"
|
|
:start-y="100"
|
|
:actions="['pin', 'close']"
|
|
content-class="bg-gunmetal-light text-accent noteBoardWindow"
|
|
>
|
|
<form
|
|
class="corkboardInput"
|
|
autocorrect="off"
|
|
autocapitalize="off"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
>
|
|
<q-input
|
|
|
|
v-model="corkboardContent"
|
|
filled
|
|
dark
|
|
@keyup="processCorkboardInput"
|
|
type="textarea"
|
|
/>
|
|
</form>
|
|
</q-window>
|
|
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import BaseClass from "src/BaseClass"
|
|
import { Component, Watch } from "vue-property-decorator"
|
|
import { defaultKeybinds } from "src/scripts/appSettings/defaultKeybinds"
|
|
import appWindowButtons from "src/components/appHeader/AppWindowButtons.vue"
|
|
import PouchDB from "pouchdb"
|
|
import { OptionsStateInteface } from "./store/module-options/state"
|
|
import { colors } from "quasar"
|
|
import { tipsTricks } from "src/scripts/utilities/tipsTricks"
|
|
import { shell } from "electron"
|
|
import { summonAllPlusheForms } from "src/scripts/utilities/plusheMascot"
|
|
import { saveCorkboard, retrieveCorkboard, retrieveCurrentProjectName } from "src/scripts/projectManagement/projectManagent"
|
|
import documentPreview from "src/components/DocumentPreview.vue"
|
|
@Component({
|
|
components: {
|
|
documentPreview: documentPreview,
|
|
appWindowButtons: appWindowButtons
|
|
}
|
|
})
|
|
export default class App extends BaseClass {
|
|
/****************************************************************/
|
|
// APP START & END SETUP
|
|
/****************************************************************/
|
|
|
|
async created () {
|
|
// Catch middle clicks
|
|
window.addEventListener("auxclick", this.reactToMiddleClick)
|
|
|
|
// Add a secondary blocker to prevent the middle-mouse button scrolling
|
|
document.body.onmousedown = function (e) {
|
|
if (e.button === 1) {
|
|
e.preventDefault()
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Load settings
|
|
await this.loadSettings()
|
|
|
|
await this.loadCorkboardCotent()
|
|
|
|
const currentProjectName = await retrieveCurrentProjectName()
|
|
|
|
this.SSET_setProjectName(currentProjectName)
|
|
|
|
// Load the popup hint on
|
|
this.loadHintPopup()
|
|
|
|
// React to keybind presses
|
|
window.addEventListener("keydown", this.triggerKeyPush)
|
|
|
|
// Catch normal clicks inside wysiwyg
|
|
window.addEventListener("click", this.openWysiwygLink)
|
|
}
|
|
|
|
destroyed () {
|
|
window.removeEventListener("auxclick", this.reactToMiddleClick)
|
|
|
|
this.deregisterCustomKeybinds()
|
|
this.deregisterDefaultKeybinds()
|
|
window.removeEventListener("keydown", this.triggerKeyPush)
|
|
}
|
|
|
|
/****************************************************************/
|
|
// START NOTIFICATION
|
|
/****************************************************************/
|
|
|
|
/**
|
|
* Model for the startup notification
|
|
*/
|
|
starupNotif = null as any
|
|
|
|
/**
|
|
* Notification checker
|
|
* Can go up to 3
|
|
*/
|
|
popupCheck = 0
|
|
|
|
/**
|
|
* Show the actual popup
|
|
*/
|
|
loadHintPopup () {
|
|
const options = this.SGET_options
|
|
|
|
// Considering there is a bit of a delay between the initial load of the store DB content, we give the program 3 attempts to load the data over 3 seconds. If no is loaded in that time, we assume that the settings are not set at all and display the hint as normal.
|
|
if ((!options._id || !options._rev) && this.popupCheck < 3) {
|
|
setTimeout(() => {
|
|
this.popupCheck++
|
|
this.loadHintPopup()
|
|
}, 1000)
|
|
return
|
|
}
|
|
|
|
if (options.hideTooltipsStart) {
|
|
return
|
|
}
|
|
|
|
const messageToShow = tipsTricks[Math.floor(Math.random() * tipsTricks.length)]
|
|
const plusheForm = summonAllPlusheForms[Math.floor(Math.random() * summonAllPlusheForms.length)]
|
|
this.starupNotif = this.$q.notify({
|
|
|
|
timeout: 15000,
|
|
icon: (this.hidePlushes) ? "mdi-help" : undefined,
|
|
color: "info",
|
|
message: "Did you know?",
|
|
avatar: (!this.hidePlushes) ? plusheForm : undefined,
|
|
caption: messageToShow,
|
|
actions: [{ icon: "mdi-close", color: "white" }]
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Hide the startup notification if the user changed the route before it disappeared
|
|
*/
|
|
@Watch("$route", { deep: true })
|
|
onUrlChange () {
|
|
if (typeof this.starupNotif === "function") {
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
this.starupNotif()
|
|
}
|
|
}
|
|
|
|
/****************************************************************/
|
|
// KEYBIND HANDLING
|
|
/****************************************************************/
|
|
|
|
/**
|
|
* React to keybind combinations being pushed and submit them to the store
|
|
*/
|
|
triggerKeyPush (e:any) {
|
|
// console.log("")
|
|
// console.log(`Key: ${e.key}`)
|
|
// console.log(`Ctrl: ${e.ctrlKey}`)
|
|
// console.log(`Shift: ${e.shiftKey}`)
|
|
// console.log(`Alt: ${e.altKey}`)
|
|
// console.log(e)
|
|
|
|
if (e?.altKey === true || e?.ctrlKey || e?.shiftKey) {
|
|
const ouputKeycombo = {
|
|
altKey: e.altKey,
|
|
ctrlKey: e.ctrlKey,
|
|
shiftKey: e.shiftKey,
|
|
which: e.which
|
|
}
|
|
|
|
this.SSET_updatePressedKey(ouputKeycombo)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers a default keybind into the store
|
|
*/
|
|
registerDefaultKeybinds () {
|
|
// @ts-ignore
|
|
defaultKeybinds.forEach(e => this.SSET_registerDefaultKeybind(e))
|
|
}
|
|
|
|
/**
|
|
* Removes a default keybind from the store
|
|
*/
|
|
deregisterDefaultKeybinds () {
|
|
// @ts-ignore
|
|
defaultKeybinds.forEach(e => this.SSET_deregisterDefaultKeybind(e))
|
|
}
|
|
|
|
/**
|
|
* Registers a custom keybind into the store
|
|
*/
|
|
registerCustomKeybinds () {
|
|
setTimeout(() => {
|
|
this.SGET_options.userKeybindList.forEach(e => this.SSET_registerUserKeybind(e))
|
|
}, 1000)
|
|
}
|
|
|
|
/**
|
|
* Removes a custom keybind from the store
|
|
*/
|
|
deregisterCustomKeybinds () {
|
|
// @ts-ignore
|
|
defaultKeybinds.forEach(e => this.SSET_deregisterUserKeybind(e))
|
|
}
|
|
|
|
/****************************************************************/
|
|
// VARIOUS APP FUNCTIONALITY
|
|
/****************************************************************/
|
|
|
|
/**
|
|
* Open wysiwyg links in default browser window
|
|
*/
|
|
openWysiwygLink (event: MouseEvent) {
|
|
event.preventDefault()
|
|
// @ts-ignore
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
if (event.target && event.target.tagName.toLowerCase() === "a" && event.target.closest(".fieldWysiwyg")) {
|
|
const isValidHttpUrl = (string:string) => {
|
|
let url
|
|
|
|
try {
|
|
url = new URL(string)
|
|
}
|
|
catch (_) {
|
|
return false
|
|
}
|
|
|
|
return url.protocol === "http:" || url.protocol === "https:"
|
|
}
|
|
|
|
// @ts-ignore
|
|
if (isValidHttpUrl(event.target.href)) {
|
|
// @ts-ignore
|
|
shell.openExternal(event.target.href).catch(e => console.log(e))
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* React to middle mouse button clicks
|
|
*/
|
|
reactToMiddleClick (e: {button: number, preventDefault: ()=> void}) {
|
|
if (e.button === 1) {
|
|
e.preventDefault()
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load settings for the first time upon app load
|
|
*/
|
|
async loadSettings () {
|
|
const SettingsDB = new PouchDB("fa-settings")
|
|
const settingsData = await SettingsDB.allDocs({ include_docs: true })
|
|
const settings = settingsData?.rows[0]?.doc as unknown as OptionsStateInteface
|
|
|
|
if (settings) {
|
|
this.SSET_options(settings)
|
|
}
|
|
|
|
this.registerDefaultKeybinds()
|
|
this.registerCustomKeybinds()
|
|
|
|
await SettingsDB.close()
|
|
}
|
|
|
|
/**
|
|
* Update dark/light mode across the app based on what is currently in the store
|
|
*/
|
|
@Watch("SGET_options", { deep: true })
|
|
onSettingsChange () {
|
|
const options = this.SGET_options
|
|
|
|
this.hidePlushes = options.hidePlushes
|
|
this.$q.dark.set(options.darkMode)
|
|
if (options.darkMode) {
|
|
colors.setBrand("dark", "#1b333e")
|
|
colors.setBrand("primary", "#ffd673")
|
|
}
|
|
else {
|
|
colors.setBrand("dark", "#18303a")
|
|
colors.setBrand("primary", "#e8bb50")
|
|
}
|
|
|
|
this.disableDocumentControlBar = options.disableDocumentControlBar
|
|
this.refreshDocumentPreviewWindow()
|
|
|
|
if (options.disableSpellCheck) {
|
|
document.body.setAttribute("spellcheck", "false")
|
|
}
|
|
else {
|
|
document.body.setAttribute("spellcheck", "true")
|
|
}
|
|
}
|
|
|
|
disableDocumentControlBar = false
|
|
|
|
/**
|
|
* Hides the mascot... nooo :(
|
|
*/
|
|
hidePlushes = false
|
|
|
|
@Watch("SGET_getAdvSearchWindowVisible")
|
|
onAdvSearchWindowOpen () {
|
|
this.advSearchWindowVisible = true
|
|
}
|
|
|
|
advSearchWindowVisible = false
|
|
|
|
@Watch("SGET_getNoteCorkboardWindowVisible")
|
|
onCorkboardWindowOpen () {
|
|
this.corkboardWindowVisible = true
|
|
}
|
|
|
|
corkboardWindowVisible = false
|
|
|
|
corkboardContent = ""
|
|
|
|
/**
|
|
* Debounce timer to prevent buggy input sync
|
|
*/
|
|
corkboardTimer = null as any
|
|
|
|
processCorkboardInput () {
|
|
clearTimeout(this.corkboardTimer)
|
|
this.corkboardTimer = setTimeout(() => {
|
|
saveCorkboard(this.corkboardContent).catch(e => console.log(e))
|
|
}, 1000)
|
|
}
|
|
|
|
/**
|
|
* Corkboard checker
|
|
* Can go up to 3
|
|
*/
|
|
corkboardCheck = 0
|
|
|
|
async loadCorkboardCotent () {
|
|
const options = this.SGET_options
|
|
|
|
this.corkboardContent = await retrieveCorkboard()
|
|
|
|
// Considering there is a bit of a delay between the initial load of the store DB content, we give the program 3 attempts to load the data over 3 seconds. If no is loaded in that time, we assume that the settings are not set at all and display the hint as normal.
|
|
if ((!options._id || !options._rev) && this.corkboardCheck < 3) {
|
|
setTimeout(() => {
|
|
this.corkboardCheck++
|
|
this.loadCorkboardCotent().catch(e => console.log(e))
|
|
}, 1000)
|
|
return
|
|
}
|
|
|
|
if (options.preventFilledNoteBoardPopup) {
|
|
return
|
|
}
|
|
|
|
if (this.corkboardContent.length) {
|
|
this.corkboardWindowVisible = true
|
|
}
|
|
}
|
|
|
|
documentPreviewWindowVisible = false
|
|
documentPreviewElementID = ""
|
|
|
|
@Watch("SGET_getDocumentPreviewWindowID")
|
|
reactToPreviewIDChange () {
|
|
this.refreshDocumentPreviewWindow()
|
|
}
|
|
|
|
@Watch("SGET_getDocumentPreviewVisible")
|
|
reactToPreviewVisibilityChange () {
|
|
if (this.SGET_getDocumentPreviewVisible !== "") {
|
|
this.refreshDocumentPreviewWindow()
|
|
}
|
|
}
|
|
|
|
refreshDocumentPreviewWindow (input = true) {
|
|
this.documentPreviewElementID = this.SGET_getDocumentPreviewWindowID
|
|
const newOpenString = this.SGET_getDocumentPreviewVisible
|
|
|
|
if (!input || newOpenString.length === 0) {
|
|
this.SSET_setDocumentPreviewWindowVisible(false)
|
|
this.documentPreviewWindowVisible = false
|
|
}
|
|
else {
|
|
this.documentPreviewWindowVisible = true
|
|
}
|
|
|
|
if (this.documentPreviewWindowVisible) {
|
|
/* eslint-disable */
|
|
//@ts-ignore
|
|
this.$refs.documentPreviewWindow.setX(0)
|
|
//@ts-ignore
|
|
this.$refs.documentPreviewWindow.setY(95)
|
|
//@ts-ignore
|
|
/* eslint-enable */
|
|
}
|
|
}
|
|
|
|
/****************************************************************/
|
|
// Local keybinds
|
|
/****************************************************************/
|
|
|
|
@Watch("SGET_getCurrentKeyBindData", { deep: true })
|
|
processKeyPush () {
|
|
// Toggle the Advanced search cheatsheet
|
|
if (this.determineKeyBind("toggleAdvSearchCheatsheet")) {
|
|
this.advSearchWindowVisible = !this.advSearchWindowVisible
|
|
}
|
|
|
|
// Toggle Note Board - CTRL + ALT + SHIFT + P
|
|
if (this.determineKeyBind("toggleNoteCorkboard")) {
|
|
this.corkboardWindowVisible = !this.corkboardWindowVisible
|
|
}
|
|
}
|
|
}
|
|
</script>
|