fantasia-archive/src/App.vue

481 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()
}
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>