0.1.2 release

This commit is contained in:
Elvanos 2021-02-26 02:50:46 +01:00
parent c23cba9f60
commit 64b3eae526
59 changed files with 1594 additions and 709 deletions

View file

@ -4,16 +4,25 @@
### Bugfixes
- Fixed a safeguard for opening multiple overlapping dialogs unintentionally
### New features
- Reworked hierarchical left tree
- Added "Add under parent" button the hiearachical tree
- Added mouse button and improved keyboard support to the hierarchical tree
- Added "Add under parent" button to the hierarchical tree, document page view, and quick search existing documents
- Added mouse button support and improved keyboard support to the hierarchical tree
- Reworked the top bar of the app to include tabs, window control elements, and basic menus of the app
- Added a check upon closing the app to avoid unintentional loss of data due to unsaved documents
### QoL adjustments
- Added middle-click closing for the tabs
- Reversed default custom sorting for "Order" field in the left side tree
- Added automatical opening of the project page after opening an existing project from a folder
- Reversed default custom sorting for the "Order" field in the left side tree
- Modified auto-closing behavior of hierarchical left tree nodes when moving/adding/removing documents
- Added a delay for tooltip popups on fields of documents
- Remove persistence from the document with an active edits confirmation dialog
- Unified graphical interface coloring of Quick-add and Quick-search dialogs to work consistently with the coloring of individual documents/document types same as the left hierarchical tre
## 0.1.1

View file

@ -1,6 +1,6 @@
{
"name": "fantasiaarchive",
"version": "0.1.1",
"version": "0.1.2",
"description": "A database manager for world building",
"productName": "Fantasia archive",
"author": "Elvanos <elvanos66@gmail.com>",

View file

@ -22,16 +22,17 @@ function createWindow () {
*/
mainWindow = new BrowserWindow({
useContentSize: true,
webPreferences: {
// Change from /quasar.conf.js > electron > nodeIntegration;
// More info: https://quasar.dev/quasar-cli/developing-electron-apps/node-integration
nodeIntegration: process.env.QUASAR_NODE_INTEGRATION,
nodeIntegrationInWorker: process.env.QUASAR_NODE_INTEGRATION,
enableRemoteModule: true
frame: false,
// More info: /quasar-cli/developing-electron-apps/electron-preload-script
// preload: path.resolve(__dirname, 'electron-preload.js')
}
webPreferences: {
// Change from /quasar.conf.js > electron > nodeIntegration;
// More info: https://quasar.dev/quasar-cli/developing-electron-apps/node-integration
nodeIntegration: process.env.QUASAR_NODE_INTEGRATION,
nodeIntegrationInWorker: process.env.QUASAR_NODE_INTEGRATION,
enableRemoteModule: true,
// More info: /quasar-cli/developing-electron-apps/electron-preload-script
// preload: path.resolve(__dirname, 'electron-preload.js')
}
})
mainWindow.setMenu(null)

View file

@ -1,5 +1,6 @@
<template>
<div id="q-app">
<appWindowButtons />
<router-view />
</div>
</template>
@ -7,8 +8,15 @@
<script lang="ts">
import BaseClass from "src/BaseClass"
import { Component } from "vue-property-decorator"
import { defaultKeybinds } from "src/appSettings/defaultKeybinds"
@Component
import { defaultKeybinds } from "src/scripts/appSettings/defaultKeybinds"
import appWindowButtons from "src/components/appHeader/AppWindowButtons.vue"
@Component({
components: {
appWindowButtons: appWindowButtons
}
})
export default class App extends BaseClass {
created () {
window.addEventListener("auxclick", this.reactToMiddleClick)
@ -21,12 +29,27 @@ export default class App extends BaseClass {
}
this.registerDefaultKeybinds()
window.addEventListener("keyup", this.triggerKeyPush)
}
triggerKeyPush (e:any) {
if (e?.altKey === true || e?.ctrlKey || e?.shiftKey) {
const ouputKeycombo = {
altKey: e.altKey,
ctrlKey: e.ctrlKey,
shiftKey: e.shiftKey,
keyCode: e.keyCode
}
this.SSET_updatePressedKey(ouputKeycombo)
}
}
destroyed () {
window.removeEventListener("auxclick", this.reactToMiddleClick)
this.deregisterDefaultKeybinds()
window.removeEventListener("keyup", this.triggerKeyPush)
}
reactToMiddleClick (e: {button: number, preventDefault: ()=> void}) {

View file

@ -2,15 +2,6 @@ import { KeyManagementInterface } from "./store/module-keybinds/state"
import { I_OpenedDocument } from "./interfaces/I_OpenedDocument"
import { Component, Vue } from "vue-property-decorator"
import { namespace } from "vuex-class"
import { remote } from "electron"
// @ts-ignore
import replicationStream from "pouchdb-replication-stream/dist/pouchdb.replication-stream.min.js"
// @ts-ignore
import load from "pouchdb-load"
import PouchDB from "pouchdb"
import fs from "fs"
import path from "path"
import { I_Blueprint } from "src/interfaces/I_Blueprint"
import { I_NewObjectTrigger } from "src/interfaces/I_NewObjectTrigger"
import { uid } from "quasar"
@ -23,6 +14,10 @@ const Keybinds = namespace("keybindsModule")
@Component
export default class BaseClass extends Vue {
generateUID () : string {
return uid()
}
/****************************************************************/
// Keybinds management
/****************************************************************/
@ -121,128 +116,6 @@ export default class BaseClass extends Vue {
}
}
/****************************************************************/
// Project management
/****************************************************************/
/**
* Creates a brand new project and deleted any present data avaiable right now
* @param projectName The name of the new project
*/
async createNewProject (projectName: string) {
await this.removeCurrentProject()
const ProjectDB = new PouchDB("project-data")
const newProject = { _id: projectName }
await ProjectDB.put(newProject)
this.$router.push({ path: "/project" }).catch((e: {name: string}) => {
const errorName : string = e.name
if (errorName === "NavigationDuplicated") {
return
}
console.log(e)
})
}
/**
* Open an file dialog asking the use for location where to export the project
* @param projectName The name of the project to export
*/
exportProject (projectName: string) {
remote.dialog.showOpenDialog({
properties: ["openDirectory"]
}).then(async (result) => {
/*eslint-disable */
const folderPath = result.filePaths[0]
PouchDB.plugin(replicationStream.plugin)
// @ts-ignore
PouchDB.adapter("writableStream", replicationStream.adapters.writableStream)
// @ts-ignore
const allDBS = await indexedDB.databases()
const DBnames: string[] = allDBS.map((db: {name: string}) => {
return db.name.replace("_pouch_", "")
})
for (const db of DBnames) {
const CurrentDB = new PouchDB(db)
if (!fs.existsSync(`${folderPath}/${projectName}`)) {
fs.mkdirSync(`${folderPath}/${projectName}`)
}
const ws = fs.createWriteStream(`${folderPath}/${projectName}/${db}.txt`)
// @ts-ignore
await CurrentDB.dump(ws)
}
/* eslint-enable */
}).catch(err => {
console.log(err)
})
}
/**
* Delete the current project and all its data
*/
async removeCurrentProject () {
/*eslint-disable */
// @ts-ignore
const allDBS = await indexedDB.databases()
const DBnames: string[] = allDBS.map((db: {name: string}) => {
return db.name.replace("_pouch_", "")
})
for (const db of DBnames) {
const CurrentDB = new PouchDB(db)
await CurrentDB.destroy()
}
/* eslint-enable */
}
openExistingProject () {
/*eslint-disable */
remote.dialog.showOpenDialog({
properties: ["openDirectory"]
}).then(async (result) => {
const folderPath = result.filePaths[0]
if(!folderPath){return}
await this.removeCurrentProject()
//@ts-ignore
PouchDB.plugin({
loadIt: load.load
})
const allFiles = fs.readdirSync(folderPath)
for (const file of allFiles) {
const currentDBName = path.parse(file).name
const CurrentDB = new PouchDB(currentDBName)
const fileContents = fs.readFileSync(`${folderPath}/${file}`, {encoding: 'utf8'})
// @ts-ignore
await CurrentDB.loadIt(fileContents)
}
}).catch(err => {
console.log(err)
})
/* eslint-enable */
}
async retrieveCurrentProjectName () {
const ProjectDB = new PouchDB("project-data")
const projectData = await ProjectDB.allDocs({ include_docs: true })
return projectData?.rows[0]?.id
}
/****************************************************************/
// Blueprint management
/****************************************************************/
@ -296,6 +169,7 @@ export default class BaseClass extends Vue {
@OpenedDocuments.Mutation("addDocument") SSET_addOpenedDocument!: (input: I_OpenedDocument) => void
@OpenedDocuments.Mutation("updateDocument") SSET_updateOpenedDocument!: (input: I_OpenedDocument) => void
@OpenedDocuments.Mutation("removeDocument") SSET_removeOpenedDocument!: (input: I_OpenedDocument) => void
@OpenedDocuments.Mutation("resetDocuments") SSET_resetDocuments!: () => void
/**
* Retrieves value of requested field. If the field doesn't exist, returns false instead

View file

@ -0,0 +1,64 @@
<template>
<q-header
elevated
class="bg-dark text-cultured"
>
<div class="appHeaderInner">
<appControl
class="appControl"
:is-project="isProject"
/>
<topTabs
class="topTabs"
/>
</div>
</q-header>
</template>
<script lang="ts">
import { Component, Prop } from "vue-property-decorator"
import BaseClass from "src/BaseClass"
import topTabs from "src/components/appHeader/TopTabs.vue"
import appControl from "src/components/appHeader/AppControl.vue"
@Component({
components: {
topTabs: topTabs,
appControl: appControl
}
})
export default class AppHeader extends BaseClass {
@Prop() readonly isProject!: boolean
}
</script>
<style lang="scss" scoped>
.appHeaderInner {
display: flex;
width: 100%;
min-height: 40px;
.appControl {
width: 375px;
flex-shrink: 0;
flex-grow: 0;
}
.topTabs {
max-width: calc(100% - 535px);
}
.appWindowButtons {
//width: 200px;
flex-shrink: 0;
flex-grow: 0;
margin-left: auto;
}
}
</style>

View file

@ -19,18 +19,19 @@
dark
:filter="treeFilter"
:selected.sync="selectedTreeNode"
:expanded.sync="expandedTreeNodes"
>
<template v-slot:default-header="prop">
<div class="row items-center col-grow" @click.stop.prevent="processNodeClick(prop.node)">
<q-icon
:style="`color: ${prop.node.color}; width: 22px !important;`"
:size="(prop.node.icon.includes('fas')? '16px': '21px')"
:name="prop.node.icon"
class="q-mr-sm" />
<div class="documentLabel"
:style="`color: ${prop.node.color}`"
@click.stop.prevent.middle="processNodeLabelMiddleClick(prop.node)"
>
<q-icon
:style="`color: ${prop.node.color}; width: 22px !important;`"
:size="(prop.node.icon.includes('fas')? '16px': '21px')"
:name="prop.node.icon"
class="q-mr-sm self-center" />
{{ prop.node.label }}
<span
class="text-primary text-weight-medium q-ml-xs"
@ -38,47 +39,56 @@
({{prop.node.documentCount}})
</span>
<q-badge
class="q-ml-xs"
style="font-size: 12px;"
v-if="prop.node.sticker"
color="primary"
outline
align="top">{{prop.node.sticker}}
<q-tooltip>
class="treeBadge"
v-if="prop.node.sticker"
color="primary"
outline
floating
>
{{prop.node.sticker}}
<q-tooltip
:delay="500"
>
Order priority of the document
</q-tooltip>
</q-badge>
<div class="treeButtonGroup">
<q-btn
tabindex="-1"
v-if="!prop.node.specialLabel || prop.node.isRoot"
round
dense
color="primary"
class="z-1 q-ml-sm treeButton treeButton--add"
icon="mdi-plus"
size="8px"
@click.stop.prevent="processNodeNewDocumentButton(prop.node)"
>
<q-tooltip>
Add a new document belonging under {{ prop.node.label }}
</q-tooltip>
</q-btn>
<q-btn
tabindex="-1"
v-if="prop.node.children && prop.node.children.length > 0 && !prop.node.isRoot"
round
flat
dense
color="primary"
color="dark"
class="z-1 q-ml-sm treeButton treeButton--edit"
icon="mdi-pencil"
size="8px"
@click.stop.prevent="openExistingDocumentRoute(prop.node)"
>
<q-tooltip>
<q-tooltip
:delay="300"
>
Open/Edit {{ prop.node.label }}
</q-tooltip>
</q-btn>
<q-btn
tabindex="-1"
v-if="!prop.node.specialLabel || prop.node.isRoot"
round
flat
dense
color="dark"
class="z-1 q-ml-sm treeButton treeButton--add"
icon="mdi-plus"
size="8px"
@click.stop.prevent="processNodeNewDocumentButton(prop.node)"
>
<q-tooltip
:delay="300"
>
Add a new document belonging under {{ prop.node.label }}
</q-tooltip>
</q-btn>
</div>
</div>
</div>
@ -120,8 +130,8 @@ import BaseClass from "src/BaseClass"
import { I_ShortenedDocument } from "src/interfaces/I_OpenedDocument"
import { I_NewObjectTrigger } from "src/interfaces/I_NewObjectTrigger"
import PouchDB from "pouchdb"
import { engageBlueprints, retrieveAllBlueprints } from "src/databaseManager/blueprintManager"
// import { cleanDatabases } from "src/databaseManager/cleaner"
import { engageBlueprints, retrieveAllBlueprints } from "src/scripts/databaseManager/blueprintManager"
// import { cleanDatabases } from "src/scripts/databaseManager/cleaner"
import { I_Blueprint } from "src/interfaces/I_Blueprint"
@Component({
@ -236,6 +246,11 @@ export default class ObjectTree extends BaseClass {
*/
selectedTreeNode = null
/**
* Holds all currently expanded notes
*/
expandedTreeNodes = []
/**
* Filter model for the tree
*/
@ -500,7 +515,7 @@ export default class ObjectTree extends BaseClass {
.objectTree {
.q-tree__arrow {
margin-right: 0;
padding: 4px;
padding: 4px 4px 4px 0;
}
.q-tree__node-header {
@ -508,11 +523,10 @@ export default class ObjectTree extends BaseClass {
}
.documentLabel {
max-width: calc(100% - 30px);
width: 100%;
display: flex;
justify-content: space-between;
padding: 4px 4px 4px 0;
padding: 4px 4px 4px 4px;
}
.treeButtonGroup {
@ -525,27 +539,33 @@ export default class ObjectTree extends BaseClass {
}
}
.treeButton {
opacity: 0.8;
.treeBadge {
left: inherit;
right: calc(100% + 3px);
padding: 3px 2px;
border: none;
background: rgba($primary, 0.15);
top: 50%;
transform: translateY(-50%);
min-width: 24px;
justify-content: center;
}
&:hover {
opacity: 1;
}
.treeButton {
.q-focus-helper{}
&--add {
.q-icon {
font-size: 20px;
color: $primary;
}
}
&--edit {
.q-icon {
font-size: 14px;
color: #fff;
}
}
.q-icon {
color: $dark;
}
}
</style>

View file

@ -0,0 +1,177 @@
<template>
<div
:class="{'AppControl': isProject}"
>
<projectCloseCheckDialog
:dialog-trigger="projectCloseCheckDialogTrigger"
:dialog-mode="'projectClose'"
@trigger-dialog-close="projectCloseCheckDialogClose"
/>
<q-btn-group
flat
class="AppControl__buttons"
>
<!-- Options button -->
<q-btn
flat
v-if="true === false"
:ripple="false"
dark
size='md'
no-caps
>
Options
</q-btn>
<!-- Project button-->
<q-btn
flat
:ripple="false"
dark
size='md'
no-caps
>
Project
<q-menu
@show="checkProjectStatus"
anchor="bottom left"
class="bg-gunmetal-light"
dark
>
<q-list class="bg-gunmetal-light" dark>
<q-item
v-close-popup
clickable
active
active-class="bg-gunmetal-light text-cultured"
@click="navigateToProjectPage"
:disable="!projectExists || isProjectPage"
>
<q-item-section>Go to project screen</q-item-section>
</q-item>
<q-item
v-close-popup
clickable
active
active-class="bg-gunmetal-light text-cultured"
@click="exportProject(projectName)"
:disable="!projectExists"
>
<q-item-section>Export project</q-item-section>
</q-item>
<q-separator dark />
<q-item
v-close-popup
clickable
active
active-class="bg-gunmetal-light text-cultured"
@click="projectCloseCheckDialogAssignUID"
:disable="!projectExists || isFrontpage"
>
<q-item-section>Close project</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
<!-- Help button-->
<q-btn
flat
:ripple="false"
dark
size='md'
no-caps
>
Help
<q-menu anchor="bottom left" class="bg-gunmetal-light" dark>
<q-list class="bg-gunmetal-light" dark>
<q-item
@click="toggleDevTools"
v-close-popup
clickable
active
active-class="bg-gunmetal-light text-cultured"
>
<q-item-section>Toggle developer tools</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</q-btn-group>
</div>
</template>
<script lang="ts">
import { Component, Prop } from "vue-property-decorator"
import BaseClass from "src/BaseClass"
import projectCloseCheckDialog from "src/components/dialogs/ProjectCloseCheck.vue"
import { retrieveCurrentProjectName, exportProject } from "src/scripts/projectManagement/projectManagent"
import { toggleDevTools } from "src/scripts/utilities/devTools"
@Component({
components: { projectCloseCheckDialog }
})
export default class AppControl extends BaseClass {
@Prop() readonly isProject!: boolean
toggleDevTools = toggleDevTools
retrieveCurrentProjectName = retrieveCurrentProjectName
exportProject = exportProject
projectExists: undefined | string | boolean = false
isFrontpage = true
isProjectPage = true
projectName = ""
created () {
this.checkProjectStatus().catch(e => console.log(e))
}
async checkProjectStatus () {
this.projectName = await retrieveCurrentProjectName()
this.projectExists = !!(await retrieveCurrentProjectName())
this.isFrontpage = (this.$route.path === "/")
this.isProjectPage = (this.$route.path === "/project")
}
projectCloseCheckDialogTrigger: string | false = false
projectCloseCheckDialogClose () {
this.projectCloseCheckDialogTrigger = false
}
projectCloseCheckDialogAssignUID () {
this.projectCloseCheckDialogTrigger = this.generateUID()
}
navigateToProjectPage () {
this.$router.push({ path: "/project" }).catch((e: {name: string}) => {
if (e && e.name !== "NavigationDuplicated") {
console.log(e)
}
})
}
}
</script>
<style lang="scss" scoped>
.AppControl {
background: rgba(0, 0, 0, 0.1);
&__buttons {
height: 40px;
}
}
</style>

View file

@ -0,0 +1,124 @@
<template>
<q-btn-group
flat
class="appWindowButtons"
>
<projectCloseCheckDialog
:dialog-trigger="projectCloseCheckDialogTrigger"
:dialog-mode="'appClose'"
@trigger-dialog-close="projectCloseCheckDialogClose"
/>
<!-- Minimize button-->
<q-btn
flat
:ripple="false"
:class="{'minimize': osSystem === 'darwin'}"
dark
size='sm'
@click="minimize">
<q-icon name="mdi-window-minimize"></q-icon>
</q-btn>
<!-- MinMax button-->
<q-btn
flat
:ripple="false"
:class="{'minMax': osSystem === 'darwin'}"
dark
size='sm'
@click="maximize">
<q-icon :name="(isMaximized)? 'mdi-window-restore' : 'mdi-window-maximize'"></q-icon>
</q-btn>
<!-- Close button-->
<q-btn
flat
:ripple="false"
dark
size='sm'
@click="projectCloseCheckDialogAssignUID"
:class="[{'close': osSystem === 'darwin'}]"
>
<q-icon name="mdi-window-close"></q-icon>
</q-btn>
</q-btn-group>
</template>
<script lang="ts">
import { Component } from "vue-property-decorator"
import { remote } from "electron"
import BaseClass from "src/BaseClass"
import projectCloseCheckDialog from "src/components/dialogs/ProjectCloseCheck.vue"
@Component({
components: { projectCloseCheckDialog }
})
export default class AppWindowButtons extends BaseClass {
isMaximized = false
osSystem = remote.process.platform
created () {
window.addEventListener("resize", this.checkMaximized)
this.checkMaximized()
}
destroyed () {
window.addEventListener("resize", this.checkMaximized)
}
checkMaximized () {
this.isMaximized = remote.getCurrentWindow().isMaximized()
}
minimize () {
remote.getCurrentWindow().minimize()
}
maximize () {
const win = remote.getCurrentWindow()
if (win.isMaximized()) {
win.unmaximize()
}
else {
win.maximize()
}
}
projectCloseCheckDialogTrigger: string | false = false
projectCloseCheckDialogClose () {
this.projectCloseCheckDialogTrigger = false
}
projectCloseCheckDialogAssignUID () {
this.projectCloseCheckDialogTrigger = this.generateUID()
}
}
</script>
<style lang="scss" scoped>
.appWindowButtons {
position: fixed;
right: 0;
top: 0;
height: 40px;
z-index: 99999999;
color: #fff;
}
</style>
<style lang="scss" >
.appWindowButtons__closeDialogList {
width: 100%;
}
</style>

View file

@ -1,80 +1,80 @@
<template>
<transition
enter-active-class="animated slideInDown"
leave-active-class="animated slideOutUp"
appear
:duration="600"
<span>
<q-dialog
v-if="currentlyCheckedDocument"
v-model="documentCloseDialogConfirm"
>
<q-header
v-if="localDocuments.length > 0"
elevated
class="bg-dark text-cultured"
>
<q-dialog
v-if="currentlyCheckedDocument"
v-model="documentCloseDialogConfirm"
persistent>
<q-card>
<q-card-section class="row items-center">
<span class="q-ml-sm">Discard changes to {{retrieveFieldValue(currentlyCheckedDocument,'name')}}?</span>
</q-card-section>
<q-card>
<q-card-section class="row items-center">
<span class="q-ml-sm">Discard changes to {{retrieveFieldValue(currentlyCheckedDocument,'name')}}?</span>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="Cancel" color="primary" v-close-popup />
<q-btn
flat
label="Discard changes"
color="primary"
v-close-popup
@click="closeDocument(currentlyCheckedDocument)" />
</q-card-actions>
</q-card>
</q-dialog>
<q-card-actions align="right">
<q-btn flat label="Cancel" color="primary" v-close-popup />
<q-btn
flat
label="Discard changes"
color="red"
v-close-popup
@click="closeDocument(currentlyCheckedDocument)" />
</q-card-actions>
</q-card>
</q-dialog>
<q-tabs
align="left"
inline-label
class="tabsWrapper"
no-caps>
<transition-group
name="list"
tag="div"
class="headerTransitionWrapper"
enter-active-class="animated fadeIn"
leave-active-class="animated fadeOut"
appear
:duration="300">
<q-tabs
v-if="localDocuments.length > 0"
align="left"
inline-label
outside-arrows
mobile-arrows
class="tabsWrapper"
dense
no-caps>
<transition-group
name="list"
tag="div"
class="headerTransitionWrapper"
enter-active-class="animated fadeIn"
leave-active-class="animated fadeOut"
appear
:duration="150">
<q-route-tab
:ripple="false"
v-for="document in localDocuments"
:to="`/project/display-content/${document.type}/${document._id}`"
:key="document.type+document._id"
:icon="(retrieveFieldValue(document,'categorySwitch') ? 'fas fa-folder-open' : document.icon)"
:label="retrieveFieldValue(document,'name')"
:style="`color: ${retrieveFieldValue(document,'documentColor')}`"
:alert="document.hasEdits"
alert-icon="mdi-feather"
@click.prevent.middle="checkForCloseOpenedDocument(document)"
>
<q-btn
round
dense
class="z-max q-ml-sm"
:class="{'q-mr-sm': document.hasEdits}"
size="xs"
icon="close"
style="color: #fff;"
@click.stop.prevent="checkForCloseOpenedDocument(document)"
/>
</q-route-tab>
<q-route-tab
:ripple="false"
v-for="document in localDocuments"
:to="`/project/display-content/${document.type}/${document._id}`"
:key="document.type+document._id"
:icon="(retrieveFieldValue(document,'categorySwitch') ? 'fas fa-folder-open' : document.icon)"
:label="retrieveFieldValue(document,'name')"
:style="`color: ${retrieveFieldValue(document,'documentColor')};`"
:class="[{'isBold': (retrieveFieldValue(document,'documentColor') !== '#ffffff' && retrieveFieldValue(document,'documentColor') !== '#fff') && retrieveFieldValue(document,'documentColor') !== ''}]"
:alert="document.hasEdits"
alert-icon="mdi-feather"
@click.prevent.middle="checkForCloseOpenedDocument(document)"
>
<q-tooltip
:delay="700"
>
{{retrieveFieldValue(document,'name')}}
</q-tooltip>
<q-btn
round
dense
class="z-max q-ml-auto"
:class="{'q-mr-sm': document.hasEdits}"
size="xs"
icon="close"
style="color: #fff;"
@click.stop.prevent="checkForCloseOpenedDocument(document)"
/>
</q-route-tab>
</transition-group>
</q-tabs>
</transition-group>
</q-tabs>
</q-header>
</transition>
</span>
</template>
@ -90,7 +90,7 @@ import { I_OpenedDocument } from "src/interfaces/I_OpenedDocument"
@Component({
components: { }
})
export default class TppTabs extends BaseClass {
export default class TopTabs extends BaseClass {
documentCloseDialogConfirm = false
currentlyCheckedDocument = null as unknown as I_OpenedDocument
@ -212,12 +212,60 @@ export default class TppTabs extends BaseClass {
}
.tabsWrapper .fas {
font-size: 16px;
}
.tabsWrapper .mdi {
font-size: 18px;
}
</style>
<style lang="scss">
.tabsWrapper .fas {
font-size: 18px;
.tabsWrapper {
.q-tabs__arrow {
text-shadow: none !important;
}
.isBold .q-tab__label {
font-weight: 500 !important;
}
.q-tab {
padding: 0 10px;
&__content {
min-width: 170px;
width: 170px;
justify-content: flex-start;
text-align: left;
}
&__label {
overflow: hidden;
text-overflow: ellipsis;
padding-top: 2px;
font-weight: 400;
font-size: 13px;
}
}
.fas {
font-size: 16px;
}
.mdi {
font-size: 18px;
}
&.q-tabs--dense .q-tab {
min-height: 40px;
}
.q-tab__alert-icon {
font-size: 16px;
top: 4px;
right: -10px;
color: $primary;
}
}
</style>

View file

@ -0,0 +1,209 @@
<template>
<q-dialog
v-model="dialogModel"
@hide="triggerDialogClose"
>
<q-card
dark
class="existingDocumentPopup"
>
<q-card-section class="row items-center">
<h6 class="text-center q-my-sm">Open existing document</h6>
</q-card-section>
<q-card-section class="row items-center">
<q-select
ref="ref_existingDocument"
style="flex-grow: 1;"
dense
dark
:options="filteredExistingInput"
use-input
outlined
input-debounce="0"
v-model="existingDocumentModel"
@filter="filterExistingSelect"
@input="openExistingInput"
>
<template v-slot:option="{ itemProps, itemEvents, opt }">
<q-item
v-bind="itemProps"
v-on="itemEvents"
:style="`color: ${opt.color}`"
>
<q-item-section avatar>
<q-icon
:style="`color: ${opt.color}`"
:name="(opt.isCategory) ? 'fas fa-folder-open' : opt.icon"
/>
</q-item-section>
<q-item-section>
<q-item-label v-html="opt.label" ></q-item-label>
</q-item-section>
<q-btn
tabindex="-1"
round
flat
dense
dark
color="primary"
class="z-1 q-ml-md"
icon="mdi-plus"
size="md"
@click.stop.prevent="addNewItemUnderSelected(opt)"
>
<q-tooltip
:delay="300"
>
Add a new document belonging under {{ opt.label }}
</q-tooltip>
</q-btn>
</q-item>
</template>
</q-select>
</q-card-section>
<q-card-section>
<q-card-actions align="center" class="q-mb-sm">
<q-btn flat label="Close window" color="primary" v-close-popup />
</q-card-actions>
</q-card-section>
</q-card>
</q-dialog>
</template>
<script lang="ts">
import { Component, Watch } from "vue-property-decorator"
import { I_ShortenedDocument } from "src/interfaces/I_OpenedDocument"
import PouchDB from "pouchdb"
import DialogBase from "src/components/dialogs/_DialogBase"
@Component({
components: { }
})
export default class ExistingDocumentDialog extends DialogBase {
@Watch("dialogTrigger")
openDialog (val: string|false) {
if (val) {
if (this.SGET_getDialogsState) {
return
}
this.SSET_setDialogState(true)
this.dialogModel = true
this.populateExistingObjectDialog().catch(e => console.log(e))
}
}
existingObjectList = [] as I_ShortenedDocument[]
async populateExistingObjectDialog () {
let allDocs = [] as I_ShortenedDocument[]
for (const blueprint of this.SGET_allBlueprints) {
const CurrentObjectDB = new PouchDB(blueprint._id)
const dbDocuments = await CurrentObjectDB.allDocs({ include_docs: true })
const formattedDocuments = dbDocuments.rows.map(singleDocument => {
const doc = singleDocument.doc as unknown as I_ShortenedDocument
return {
label: doc.extraFields.find(e => e.id === "name")?.value,
icon: doc.icon,
id: doc._id,
url: doc.url,
type: doc.type,
color: doc.extraFields.find(e => e.id === "documentColor")?.value,
isCategory: doc.extraFields.find(e => e.id === "categorySwitch")?.value
} as unknown as I_ShortenedDocument
}).sort((a, b) => a.label.localeCompare(b.label))
// @ts-ignore
allDocs = [...allDocs, ...formattedDocuments]
}
this.existingObjectList = allDocs
this.$nextTick(function () {
/*eslint-disable */
setTimeout( () =>{
if(this.$refs.ref_existingDocument){
// @ts-ignore
this.$refs.ref_existingDocument.focus()
}
}, 300)
/* eslint-enable */
})
}
existingDocumentModel = null
filteredExistingInput = null as unknown as I_ShortenedDocument[]
filterExistingSelect (val: string, update: (e: () => void) => void) {
if (val === "") {
update(() => {
this.filteredExistingInput = this.existingObjectList
/*eslint-disable */
if(this.$refs.ref_existingDocument && this.filteredExistingInput.length > 0){
setTimeout(() => {
// @ts-ignore
this.$refs.ref_existingDocument.setOptionIndex(-1)
// @ts-ignore
this.$refs.ref_existingDocument.moveOptionSelection(1, true)
}, 300)
/* eslint-enable */
}
})
return
}
update(() => {
const needle = val.toLowerCase()
this.filteredExistingInput = this.existingObjectList.filter(v => v.label.toLowerCase().indexOf(needle) > -1)
/*eslint-disable */
if(this.$refs.ref_existingDocument && this.filteredExistingInput.length > 0){
setTimeout(() => {
// @ts-ignore
this.$refs.ref_existingDocument.setOptionIndex(-1)
// @ts-ignore
this.$refs.ref_existingDocument.moveOptionSelection(1, true)
}, 300)
/* eslint-enable */
}
})
}
openExistingInput (e: I_ShortenedDocument) {
this.dialogModel = false
// @ts-ignore
this.openExistingDocumentRoute(e)
this.existingDocumentModel = null
}
addNewItemUnderSelected (parent: any) {
const routeObject = {
_id: parent.type,
parent: parent.id
}
// @ts-ignore
this.addNewObjectRoute(routeObject)
}
}
</script>
<style lang="scss" scoped>
.existingDocumentPopup {
min-width: 600px;
margin-top: 100px;
align-self: flex-start;
h6 {
display: block;
text-align: center;
width: 100%;
}
}
</style>

View file

@ -0,0 +1,62 @@
<template>
<q-dialog
v-model="dialogModel"
@hide="triggerDialogClose"
>
<q-card
class="keyBindsDialog"
>
<q-card-section class="row items-center">
<h6 class="text-center q-my-sm">Keybind list</h6>
</q-card-section>
<q-card-section>
<q-markup-table>
<thead>
<tr>
<th class="text-left">Action</th>
<th class="text-left">Keybind</th>
</tr>
</thead>
<tbody>
<tr v-for="keybind in SGET_getCurrentKeyBindData.defaults" :key="keybind.id">
<td class="text-left" v-html="keybind.tooltip"/>
<td class="text-left" v-html="retrieveKeybindString(keybind)"/>
</tr>
</tbody>
</q-markup-table>
</q-card-section>
<q-card-actions align="center" class="q-mb-lg q-mt-md">
<q-btn flat label="Close window" color="primary" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script lang="ts">
import { Component, Watch } from "vue-property-decorator"
import DialogBase from "src/components/dialogs/_DialogBase"
@Component({
components: { }
})
export default class KeybindCheatsheet extends DialogBase {
@Watch("dialogTrigger")
openDialog (val: string|false) {
if (val) {
if (this.SGET_getDialogsState) {
return
}
this.SSET_setDialogState(true)
this.dialogModel = true
}
}
}
</script>
<style lang="scss" scoped>
</style>

View file

@ -0,0 +1,165 @@
<template>
<q-dialog
v-model="dialogModel"
@hide="triggerDialogClose"
>
<q-card
dark
class="newDocumentPopup"
>
<q-card-section class="row items-center">
<h6 class="text-center q-my-sm">Add new document</h6>
</q-card-section>
<q-card-section class="row items-center">
<q-select
dark
ref="ref_newDocument"
style="flex-grow: 1;"
dense
:options="filteredNewInput"
use-input
outlined
input-debounce="0"
v-model="newDocumentModel"
@filter="filterNewSelect"
@input="triggerNewInput"
>
<template v-slot:option="{ itemProps, itemEvents, opt }">
<q-item
v-bind="itemProps"
v-on="itemEvents"
>
<q-item-section avatar>
<q-icon :name="opt.icon" />
</q-item-section>
<q-item-section>
<q-item-label v-html="opt.label" ></q-item-label>
</q-item-section>
</q-item>
</template>
</q-select>
</q-card-section>
<q-card-section>
<q-card-actions align="center" class="q-mb-sm">
<q-btn flat label="Close window" color="primary" v-close-popup />
</q-card-actions>
</q-card-section>
</q-card>
</q-dialog>
</template>
<script lang="ts">
import { Component, Watch } from "vue-property-decorator"
interface NewObjectDocument {
label: string
icon: string
order: number
_id: string
specialLabel: string
}
import DialogBase from "src/components/dialogs/_DialogBase"
@Component({
components: { }
})
export default class NewDocumentDialog extends DialogBase {
@Watch("dialogTrigger")
openDialog (val: string|false) {
if (val) {
if (this.SGET_getDialogsState) {
return
}
this.SSET_setDialogState(true)
this.dialogModel = true
this.populateNewObjectDialog()
}
}
newObjectList = [] as NewObjectDocument[]
newDocumentModel = null
populateNewObjectDialog () {
// @ts-ignore
this.newObjectList = this.SGET_allBlueprints.map(blueprint => {
return {
label: blueprint.namePlural,
icon: blueprint.icon,
order: blueprint.order,
_id: blueprint._id,
specialLabel: blueprint.nameSingular.toLowerCase()
}
})
this.$nextTick(function () {
/*eslint-disable */
setTimeout( () =>{
// @ts-ignore
this.$refs.ref_newDocument.focus()
}, 100)
/* eslint-enable */
})
}
filteredNewInput = null as unknown as NewObjectDocument[]
filterNewSelect (val: string, update: (e: () => void) => void) {
if (val === "") {
update(() => {
this.filteredNewInput = this.newObjectList
/*eslint-disable */
if(this.$refs.ref_newDocument && this.filteredNewInput.length > 0){
setTimeout(() => {
// @ts-ignore
this.$refs.ref_newDocument.setOptionIndex(-1)
// @ts-ignore
this.$refs.ref_newDocument.moveOptionSelection(1, true)
}, 300)
/* eslint-enable */
}
})
return
}
update(() => {
const needle = val.toLowerCase()
this.filteredNewInput = this.newObjectList.filter(v => v.label.toLowerCase().indexOf(needle) > -1)
/*eslint-disable */
if(this.$refs.ref_newDocument && this.filteredNewInput.length > 0){
setTimeout(() => {
// @ts-ignore
this.$refs.ref_newDocument.setOptionIndex(-1)
// @ts-ignore
this.$refs.ref_newDocument.moveOptionSelection(1, true)
}, 300)
}
/* eslint-enable */
})
}
triggerNewInput (e: NewObjectDocument) {
this.dialogModel = false
this.addNewObjectRoute(e)
this.newDocumentModel = null
}
}
</script>
<style lang="scss" scoped>
.newDocumentPopup {
width: 600px;
margin-top: 100px;
align-self: flex-start;
h6 {
display: block;
text-align: center;
width: 100%;
}
}
</style>

View file

@ -0,0 +1,118 @@
<template>
<q-dialog
v-model="dialogModel"
@hide="triggerDialogClose"
>
<q-card>
<q-card-section class="row justify-center">
<h6 class="text-center q-my-sm">You have unsaved documents opened!</h6>
</q-card-section>
<q-card-section class="row justify-center q-mx-lg">
All unsaved data will be lost upon closing the app unless the documents are saved first.
</q-card-section>
<q-card-section class="row q-mx-lg">
<div class="q-mb-md text-bold">Affected documents:</div>
<q-list class="appWindowButtons__closeDialogList">
<q-item
v-for=" doc in openedDocs"
:key="doc._id"
clickable
v-ripple
active
active-class="bg-primary-1 text-primary"
v-close-popup
:to="doc.url">
<q-item-section avatar>
<q-icon color="black" :name="doc.icon" />
</q-item-section>
<q-item-section class="text-primary">{{retrieveFieldValue(doc,'name')}}</q-item-section>
</q-item>
</q-list>
</q-card-section>
<q-card-actions align="around" class="q-mx-xl q-mt-lg q-mb-md">
<q-btn flat label="Cancel" color="primary" v-close-popup />
<q-btn
flat
:label="exitLabelText"
color="red"
v-close-popup
@click="checkModeAction" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script lang="ts">
import { Component, Watch, Prop } from "vue-property-decorator"
import DialogBase from "src/components/dialogs/_DialogBase"
import { I_OpenedDocument } from "src/interfaces/I_OpenedDocument"
import { remote } from "electron"
@Component({
components: { }
})
export default class ProjectCloseCheck extends DialogBase {
@Prop() readonly dialogMode!: "appClose" | "projectClose"
get exitLabelText () {
if (this.dialogMode === "appClose") {
return "Exit app without saving"
}
if (this.dialogMode === "projectClose") {
return "Close project without saving"
}
}
@Watch("dialogTrigger")
openDialog (val: string|false) {
if (val) {
if (this.SGET_getDialogsState) {
return
}
this.SSET_setDialogState(true)
this.checkForDocumentsWithEdits()
}
}
openedDocs: I_OpenedDocument[]= []
checkForDocumentsWithEdits () {
this.openedDocs = this.SGET_allOpenedDocuments.docs.filter(doc => doc.hasEdits)
if (this.openedDocs.length > 0) {
this.dialogModel = true
}
else {
this.checkModeAction()
}
}
checkModeAction () {
if (this.dialogMode === "appClose") {
this.closeApp()
}
if (this.dialogMode === "projectClose") {
this.SSET_resetDocuments()
this.triggerDialogClose()
this.$router.push({ path: "/" }).catch((e: {name: string}) => {
if (e && e.name !== "NavigationDuplicated") {
console.log(e)
}
})
}
}
closeApp () {
remote.getCurrentWindow().destroy()
}
}
</script>
<style lang="scss" scoped>
</style>

View file

@ -0,0 +1,27 @@
import { Component, Prop, Emit } from "vue-property-decorator"
import BaseClass from "src/BaseClass"
import { namespace } from "vuex-class"
const Dialogs = namespace("dialogsModule")
@Component
export default class DialogBase extends BaseClass {
@Dialogs.Getter("getDialogsState") SGET_getDialogsState!: boolean
@Dialogs.Mutation("setDialogState") SSET_setDialogState!: (input: boolean) => void
dialogModel = false
@Prop() readonly dialogTrigger!: string
@Emit()
triggerDialogClose () {
this.SSET_setDialogState(false)
return true
}
@Emit()
triggerDialogSubmit (val:string) {
return val
}
}

View file

@ -4,7 +4,7 @@
<q-icon v-if="inputIcon" :name="inputIcon" :size="inputIcon.includes('fas')? '15px': '20px'" class="q-mr-md" min="1"/>
{{inputDataBluePrint.name}}
<q-icon v-if="toolTip" name="mdi-help-circle" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
<span v-html="toolTip"/>
</q-tooltip>
</q-icon>

View file

@ -4,7 +4,7 @@
<q-icon v-if="inputIcon" :name="inputIcon" :size="inputIcon.includes('fas')? '15px': '20px'" class="q-mr-md"/>
{{inputDataBluePrint.name}}
<q-icon v-if="toolTip" name="mdi-help-circle" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
<span v-html="toolTip"/>
</q-tooltip>
</q-icon>

View file

@ -4,17 +4,17 @@
<q-icon v-if="inputIcon" :name="inputIcon" :size="inputIcon.includes('fas')? '15px': '20px'" class="q-mr-md"/>
{{inputDataBluePrint.name}}
<q-icon v-if="toolTip" name="mdi-help-circle" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
<span v-html="toolTip"/>
</q-tooltip>
</q-icon>
<q-icon v-if="isOneWayRelationship" name="mdi-arrow-right-bold" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
This is a one-way relationship. <br> Editing this value <b>will not</b> have effect on the connected document/s.
</q-tooltip>
</q-icon>
<q-icon v-if="!isOneWayRelationship" name="mdi-arrow-left-right-bold" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
This is a two-way relationship. <br> Editing this value <b>will</b> also effect the connected document/s.
</q-tooltip>
</q-icon>

View file

@ -4,7 +4,7 @@
<q-icon v-if="inputIcon" :name="inputIcon" :size="inputIcon.includes('fas')? '15px': '20px'" class="q-mr-md"/>
{{inputDataBluePrint.name}}
<q-icon v-if="toolTip" name="mdi-help-circle" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
<span v-html="toolTip"/>
</q-tooltip>
</q-icon>

View file

@ -4,7 +4,7 @@
<q-icon v-if="inputIcon" :name="inputIcon" :size="inputIcon.includes('fas')? '15px': '20px'" class="q-mr-md" min="1"/>
{{inputDataBluePrint.name}}
<q-icon v-if="toolTip" name="mdi-help-circle" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
<span v-html="toolTip"/>
</q-tooltip>
</q-icon>

View file

@ -4,17 +4,17 @@
<q-icon v-if="inputIcon" :name="inputIcon" :size="inputIcon.includes('fas')? '15px': '20px'" class="q-mr-md"/>
{{inputDataBluePrint.name}}
<q-icon v-if="toolTip" name="mdi-help-circle" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
<span v-html="toolTip"/>
</q-tooltip>
</q-icon>
<q-icon v-if="isOneWayRelationship" name="mdi-arrow-right-bold" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
This is a one-way relationship. <br> Editing this value <b>will not</b> have effect on the connected document/s.
</q-tooltip>
</q-icon>
<q-icon v-if="!isOneWayRelationship" name="mdi-arrow-left-right-bold" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
This is a two-way relationship. <br> Editing this value <b>will</b> also effect the connected document/s.
</q-tooltip>
</q-icon>

View file

@ -4,7 +4,7 @@
<q-icon v-if="inputIcon" :name="inputIcon" :size="inputIcon.includes('fas')? '15px': '20px'" class="q-mr-md"/>
{{inputDataBluePrint.name}}
<q-icon v-if="toolTip" name="mdi-help-circle" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
<span v-html="toolTip"/>
</q-tooltip>
</q-icon>

View file

@ -4,7 +4,7 @@
<q-icon v-if="inputIcon" :name="inputIcon" :size="inputIcon.includes('fas')? '15px': '20px'" class="q-mr-md" min="1"/>
{{inputDataBluePrint.name}}
<q-icon v-if="toolTip" name="mdi-help-circle" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
<span v-html="toolTip"/>
</q-tooltip>
</q-icon>

View file

@ -4,7 +4,7 @@
<q-icon v-if="inputIcon" :name="inputIcon" :size="inputIcon.includes('fas')? '15px': '20px'" class="q-mr-md"/>
{{inputDataBluePrint.name}}
<q-icon v-if="toolTip" name="mdi-help-circle" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
<span v-html="toolTip"/>
</q-tooltip>
</q-icon>

View file

@ -4,7 +4,7 @@
<q-icon v-if="inputIcon" :name="inputIcon" :size="inputIcon.includes('fas')? '15px': '20px'" class="q-mr-md"/>
{{inputDataBluePrint.name}}
<q-icon v-if="toolTip" name="mdi-help-circle" size="16px" class="q-ml-md">
<q-tooltip>
<q-tooltip :delay="500">
<span v-html="toolTip"/>
</q-tooltip>
</q-icon>

View file

@ -25,6 +25,7 @@ $warning : #f2c037;
$customColors: (
'gunmetal': #19323c,
'gunmetal-light': lighten(#19323c, 7.5),
'ruby-red': #a4031f,
'satin-sheen-gold': #d2a334,
'gainsboro': #dde4e4,

View file

@ -1,138 +1,23 @@
<template>
<q-layout view="lHh LpR lfr"
>
<q-layout view="hHh LpR lfr">
<q-dialog
v-model="keyBindsDialog"
>
<q-card
class="keyBindsDialog"
>
<q-card-section class="row items-center">
<h6 class="text-center q-my-sm">Keybind list</h6>
</q-card-section>
<!-- Keybind dialog -->
<keybindCheatsheetDialog
:dialog-trigger="keybindsDialogTrigger"
@trigger-dialog-close="keybindsDialogClose"
/>
<q-card-section>
<q-markup-table>
<thead>
<tr>
<th class="text-left">Action</th>
<th class="text-left">Keybind</th>
</tr>
</thead>
<tbody>
<tr v-for="keybind in SGET_getCurrentKeyBindData.defaults" :key="keybind.id">
<td class="text-left" v-html="keybind.tooltip"/>
<td class="text-left" v-html="retrieveKeybindString(keybind)"/>
</tr>
</tbody>
</q-markup-table>
</q-card-section>
<!-- New document dialog -->
<newDocumentDialog
:dialog-trigger="newObjectDialogTrigger"
@trigger-dialog-close="newObjectDialogClose"
/>
<q-card-actions align="center" class="q-mb-lg q-mt-md">
<q-btn flat label="Close window" color="primary" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
<q-dialog
v-model="newDocumentDialog"
>
<q-card
class="newDocumentPopup"
>
<q-card-section class="row items-center">
<h6 class="text-center q-my-sm">Add new document</h6>
</q-card-section>
<q-card-section class="row items-center">
<q-select
ref="ref_newDocument"
style="flex-grow: 1;"
dense
:options="filteredNewInput"
use-input
outlined
input-debounce="0"
v-model="newDocumentModel"
@filter="filterNewSelect"
@input="triggerNewInput"
>
<template v-slot:option="{ itemProps, itemEvents, opt }">
<q-item
v-bind="itemProps"
v-on="itemEvents"
>
<q-item-section avatar>
<q-icon :name="opt.icon" />
</q-item-section>
<q-item-section>
<q-item-label v-html="opt.label" ></q-item-label>
</q-item-section>
</q-item>
</template>
</q-select>
</q-card-section>
<q-card-section>
<q-card-actions align="center" class="q-mb-sm">
<q-btn flat label="Close window" color="primary" v-close-popup />
</q-card-actions>
</q-card-section>
</q-card>
</q-dialog>
<q-dialog
v-model="existingDocumentDialog"
>
<q-card
class="newDocumentPopup"
>
<q-card-section class="row items-center">
<h6 class="text-center q-my-sm">Open existing document</h6>
</q-card-section>
<q-card-section class="row items-center">
<q-select
ref="ref_existingDocument"
style="flex-grow: 1;"
dense
:options="filteredExistingInput"
use-input
outlined
input-debounce="0"
v-model="existingDocumentModel"
@filter="filterExistingSelect"
@input="openExistingInput"
>
<template v-slot:option="{ itemProps, itemEvents, opt }">
<q-item
v-bind="itemProps"
v-on="itemEvents"
:style="`background-color: ${opt.color}99`"
>
<q-item-section avatar>
<q-icon :name="(opt.isCategory) ? 'fas fa-folder-open' : opt.icon" />
</q-item-section>
<q-item-section>
<q-item-label v-html="opt.label" ></q-item-label>
</q-item-section>
</q-item>
</template>
</q-select>
</q-card-section>
<q-card-section>
<q-card-actions align="center" class="q-mb-sm">
<q-btn flat label="Close window" color="primary" v-close-popup />
</q-card-actions>
</q-card-section>
</q-card>
</q-dialog>
<!-- Existing document dialog -->
<existingDocumentDialog
:dialog-trigger="existingObjectDialogTrigger"
@trigger-dialog-close="existingObjectDialogClose"
/>
<!-- Left drawer -->
<q-drawer
@ -150,7 +35,7 @@
<q-btn
icon="mdi-note-plus-outline"
color="primary"
@click="populateNewObjectDialog"
@click="newObjectAssignUID"
>
<q-tooltip>
Quick-add a new document.
@ -160,7 +45,7 @@
<q-btn
icon="mdi-database-search"
color="primary"
@click="populateExistingObjectDialog"
@click="existingObjectAssignUID"
>
<q-tooltip>
Quick-search an existing document.
@ -170,7 +55,7 @@
<q-btn
icon="mdi-keyboard-outline"
color="primary"
@click="keyBindsDialog = true"
@click="keybindsDialogAssignUID"
>
<q-tooltip>
Show keybind cheatsheet.
@ -181,15 +66,9 @@
</q-drawer>
<!-- Header -->
<topTabs/>
<!-- Right drawer -->
<q-drawer
v-model="rightDrawerOpen"
side="right"
elevated>
<!-- drawer content -->
</q-drawer>
<appHeader
:is-project="true"
/>
<q-page-container>
<transition
@ -209,226 +88,95 @@
import { Component, Watch } from "vue-property-decorator"
import BaseClass from "src/BaseClass"
import PouchDB from "pouchdb"
import objectTree from "src/components/ObjectTree.vue"
import topTabs from "src/components/TopTabs.vue"
import { I_ShortenedDocument } from "src/interfaces/I_OpenedDocument"
interface NewObjectDocument {
label: string
icon: string
order: number
_id: string
specialLabel: string
}
import appHeader from "src/components/AppHeader.vue"
import keybindCheatsheetDialog from "src/components/dialogs/KeybindCheatsheet.vue"
import newDocumentDialog from "src/components/dialogs/NewDocument.vue"
import existingDocumentDialog from "src/components/dialogs/ExistingDocument.vue"
@Component({
components: { objectTree, topTabs }
components: {
objectTree,
appHeader,
keybindCheatsheetDialog,
newDocumentDialog,
existingDocumentDialog
}
})
export default class MainLayout extends BaseClass {
leftDrawerOpen = true
rightDrawerOpen = false
/****************************************************************/
// Local settings
/****************************************************************/
/**
* Model for the left drawer of the app containing the hierarchical tree
*/
leftDrawerOpen = true
/****************************************************************/
// Keybinds management
/****************************************************************/
/**
* Local keybinds
*/
@Watch("SGET_getCurrentKeyBindData", { deep: true })
processKeyPush () {
// Keybind cheatsheet
if (this.determineKeyBind("openKeybindsCheatsheet")) {
this.keyBindsDialog = true
this.keybindsDialogAssignUID()
}
// Quick new document
if (this.determineKeyBind("quickNewDocument")) {
this.populateNewObjectDialog()
this.newObjectAssignUID()
}
// Quick open existing document
if (this.determineKeyBind("quickExistingDocument")) {
this.populateExistingObjectDialog().catch(e => console.log(e))
this.existingObjectAssignUID()
}
}
created () {
window.addEventListener("keyup", this.triggerKeyPush)
/****************************************************************/
// Keybings cheatsheet dialog
/****************************************************************/
keybindsDialogTrigger: string | false = false
keybindsDialogClose () {
this.keybindsDialogTrigger = false
}
destroyed () {
window.removeEventListener("keyup", this.triggerKeyPush)
keybindsDialogAssignUID () {
this.keybindsDialogTrigger = this.generateUID()
}
// @ts-ignore
triggerKeyPush (e) {
if (this.newDocumentDialog || this.existingDocumentDialog || this.keyBindsDialog) {
return false
}
if (e?.altKey === true || e?.ctrlKey || e?.shiftKey) {
const ouputKeycombo = {
altKey: e.altKey,
ctrlKey: e.ctrlKey,
shiftKey: e.shiftKey,
keyCode: e.keyCode
}
/****************************************************************/
// New document dialog
/****************************************************************/
this.SSET_updatePressedKey(ouputKeycombo)
}
newObjectDialogTrigger: string | false = false
newObjectDialogClose () {
this.newObjectDialogTrigger = false
}
newDocumentDialog = false
newObjectList = [] as NewObjectDocument[]
newDocumentModel = null
populateNewObjectDialog () {
this.newDocumentDialog = true
// @ts-ignore
this.newObjectList = this.SGET_allBlueprints.map(blueprint => {
return {
label: blueprint.namePlural,
icon: blueprint.icon,
order: blueprint.order,
_id: blueprint._id,
specialLabel: blueprint.nameSingular.toLowerCase()
}
})
this.$nextTick(function () {
/*eslint-disable */
setTimeout( () =>{
// @ts-ignore
this.$refs.ref_newDocument.focus()
}, 100)
/* eslint-enable */
})
newObjectAssignUID () {
this.newObjectDialogTrigger = this.generateUID()
}
filteredNewInput = null as unknown as NewObjectDocument[]
/****************************************************************/
// Existing document dialog
/****************************************************************/
filterNewSelect (val: string, update: (e: () => void) => void) {
if (val === "") {
update(() => {
this.filteredNewInput = this.newObjectList
/*eslint-disable */
if(this.$refs.ref_newDocument && this.filteredNewInput.length > 0){
setTimeout(() => {
// @ts-ignore
this.$refs.ref_newDocument.setOptionIndex(-1)
// @ts-ignore
this.$refs.ref_newDocument.moveOptionSelection(1, true)
}, 300)
/* eslint-enable */
}
})
return
}
update(() => {
const needle = val.toLowerCase()
this.filteredNewInput = this.newObjectList.filter(v => v.label.toLowerCase().indexOf(needle) > -1)
/*eslint-disable */
if(this.$refs.ref_newDocument && this.filteredNewInput.length > 0){
setTimeout(() => {
// @ts-ignore
this.$refs.ref_newDocument.setOptionIndex(-1)
// @ts-ignore
this.$refs.ref_newDocument.moveOptionSelection(1, true)
}, 300)
}
/* eslint-enable */
})
existingObjectDialogTrigger: string | false = false
existingObjectDialogClose () {
this.existingObjectDialogTrigger = false
}
triggerNewInput (e: NewObjectDocument) {
this.newDocumentDialog = false
this.addNewObjectRoute(e)
this.newDocumentModel = null
existingObjectAssignUID () {
this.existingObjectDialogTrigger = this.generateUID()
}
existingObjectList = [] as I_ShortenedDocument[]
async populateExistingObjectDialog () {
this.existingDocumentDialog = true
let allDocs = [] as I_ShortenedDocument[]
for (const blueprint of this.SGET_allBlueprints) {
const CurrentObjectDB = new PouchDB(blueprint._id)
const dbDocuments = await CurrentObjectDB.allDocs({ include_docs: true })
const formattedDocuments = dbDocuments.rows.map(singleDocument => {
const doc = singleDocument.doc as unknown as I_ShortenedDocument
return {
label: doc.extraFields.find(e => e.id === "name")?.value,
icon: doc.icon,
url: doc.url,
color: doc.extraFields.find(e => e.id === "documentColor")?.value,
isCategory: doc.extraFields.find(e => e.id === "categorySwitch")?.value
} as unknown as I_ShortenedDocument
}).sort((a, b) => a.label.localeCompare(b.label))
// @ts-ignore
allDocs = [...allDocs, ...formattedDocuments]
}
this.existingObjectList = allDocs
this.$nextTick(function () {
/*eslint-disable */
setTimeout( () =>{
if(this.$refs.ref_existingDocument){
// @ts-ignore
this.$refs.ref_existingDocument.focus()
}
}, 300)
/* eslint-enable */
})
}
existingDocumentDialog = false
existingDocumentModel = null
filteredExistingInput = null as unknown as I_ShortenedDocument[]
filterExistingSelect (val: string, update: (e: () => void) => void) {
if (val === "") {
update(() => {
this.filteredExistingInput = this.existingObjectList
/*eslint-disable */
if(this.$refs.ref_existingDocument && this.filteredExistingInput.length > 0){
setTimeout(() => {
// @ts-ignore
this.$refs.ref_existingDocument.setOptionIndex(-1)
// @ts-ignore
this.$refs.ref_existingDocument.moveOptionSelection(1, true)
}, 300)
/* eslint-enable */
}
})
return
}
update(() => {
const needle = val.toLowerCase()
this.filteredExistingInput = this.existingObjectList.filter(v => v.label.toLowerCase().indexOf(needle) > -1)
/*eslint-disable */
if(this.$refs.ref_existingDocument && this.filteredExistingInput.length > 0){
setTimeout(() => {
// @ts-ignore
this.$refs.ref_existingDocument.setOptionIndex(-1)
// @ts-ignore
this.$refs.ref_existingDocument.moveOptionSelection(1, true)
}, 300)
/* eslint-enable */
}
})
}
openExistingInput (e: I_ShortenedDocument) {
this.existingDocumentDialog = false
// @ts-ignore
this.openExistingDocumentRoute(e)
this.existingDocumentModel = null
}
keyBindsDialog = false
}
</script>
@ -437,18 +185,6 @@ export default class MainLayout extends BaseClass {
outline: none !important;
}
.newDocumentPopup {
width: 400px;
margin-top: 100px;
align-self: flex-start;
h6 {
display: block;
text-align: center;
width: 100%;
}
}
.keyBindsDialog {
width: 100%;
max-width: 1000px !important;

View file

@ -1,6 +1,12 @@
<template>
<q-layout view="lHh LpR lfr">
<q-page-container>
<!-- Header -->
<appHeader
:is-project="false"
/>
<q-page-container>
<transition
enter-active-class="animated fadeIn"
leave-active-class="animated fadeOut"
@ -17,9 +23,10 @@
import { Component } from "vue-property-decorator"
import BaseClass from "src/BaseClass"
import appHeader from "src/components/AppHeader.vue"
@Component({
components: { }
components: { appHeader }
})
export default class ProjectManagementLayout extends BaseClass {

View file

@ -26,24 +26,31 @@
<div class="col-12 flex justify-end q-mb-lg q-mt-md">
<q-btn
color="primary"
:label="`Save ${bluePrintData.nameSingular}`"
@click="saveDocument"
class="q-mr-xl"
v-if="editMode"
color="primary"
:label="`Add a new ${bluePrintData.nameSingular} belonging under ${retrieveFieldValue(currentData, 'name')}`"
@click="addNewUnderParent"
class="q-mr-xl"
v-if="!currentData.isNew"
/>
<q-btn
color="primary"
:label="`Edit ${bluePrintData.nameSingular}`"
@click="toggleEditMode"
class="q-mr-xl"
v-if="!editMode"
color="primary"
:label="`Save ${bluePrintData.nameSingular}`"
@click="saveDocument"
class="q-mr-xl"
v-if="editMode"
/>
<q-btn
v-if="!currentData.isNew"
color="red"
:label="`Delete ${bluePrintData.nameSingular}`"
@click="openDeleteDialog"
color="primary"
:label="`Edit ${bluePrintData.nameSingular}`"
@click="toggleEditMode"
class="q-mr-xl"
v-if="!editMode"
/>
<q-btn
v-if="!currentData.isNew"
color="red"
:label="`Delete ${bluePrintData.nameSingular}`"
@click="openDeleteDialog"
/>
</div>
@ -179,8 +186,8 @@ import BaseClass from "src/BaseClass"
import { I_Blueprint, I_ExtraFields } from "src/interfaces/I_Blueprint"
import { I_OpenedDocument } from "src/interfaces/I_OpenedDocument"
import PouchDB from "pouchdb"
// import { cleanDatabases } from "src/databaseManager/cleaner"
import { single_changeRelationshipToAnotherObject, many_changeRelationshipToAnotherObject } from "src/databaseManager/relationshipManager"
// import { cleanDatabases } from "src/scripts/databaseManager/cleaner"
import { single_changeRelationshipToAnotherObject, many_changeRelationshipToAnotherObject } from "src/scripts/databaseManager/relationshipManager"
import { extend } from "quasar"
@ -579,8 +586,6 @@ export default class PageDocumentDisplay extends BaseClass {
}
catch (error) {}
console.log(retrievedObject)
currentExtraFields.push(
{
id: "parentDoc",
@ -641,6 +646,15 @@ export default class PageDocumentDisplay extends BaseClass {
const ignoredList = ["breakBasic", "name", "documentColor", "parentDoc", "order", "categorySwitch"]
return (!isCategory || ignoredList.includes(currentFieldID))
}
addNewUnderParent () {
const routeObject = {
_id: this.currentData.type,
parent: this.currentData._id
}
// @ts-ignore
this.addNewObjectRoute(routeObject)
}
}
</script>

View file

@ -5,16 +5,6 @@
<h3>Project screen for {{projectName}}</h3>
</div>
<div class="col-12 q-mb-lg">
<q-btn
color="primary"
size="md"
label="Go to title screen"
class="q-px-xl q-py-xs"
to="/"
/>
</div>
<div class="col-12 q-mb-lg">
<q-btn
color="primary"
@ -32,14 +22,18 @@ import { Component } from "vue-property-decorator"
import BaseClass from "src/BaseClass"
import { retrieveCurrentProjectName, exportProject } from "src/scripts/projectManagement/projectManagent"
@Component({
components: { }
})
export default class ProjectScreen extends BaseClass {
projectName = ""
exportProject = exportProject
async created () {
this.projectName = await this.retrieveCurrentProjectName()
this.projectName = await retrieveCurrentProjectName()
}
}
</script>

View file

@ -29,7 +29,7 @@
color="primary"
v-close-popup
:disable="newProjectName.length === 0"
@click="createNewProject(newProjectName)" />
@click="createNewProject(newProjectName, $router)" />
</q-card-actions>
</q-card>
</q-dialog>
@ -56,7 +56,7 @@
color="primary"
size="md"
class="q-px-xl q-py-xs"
@click="openExistingProject"
@click="openExistingProject($router)"
>
<div>Open existing project</div>
<q-icon
@ -93,27 +93,16 @@
</q-btn>
</div>
<div class="col-12">
<q-btn
color="primary"
size="md"
class="q-px-xl q-py-xs"
@click="toggleDevTools"
>
<div>Toggle dev tools</div>
</q-btn>
</div>
</q-page>
</template>
<script lang="ts">
import { Component } from "vue-property-decorator"
import { remote } from "electron"
import BaseClass from "src/BaseClass"
import { openExistingProject, createNewProject, retrieveCurrentProjectName } from "src/scripts/projectManagement/projectManagent"
@Component({
components: { }
})
@ -122,23 +111,11 @@ export default class WelcomeScreen extends BaseClass {
newProjectName = ""
newProjectDialog = false
toggleDevTools () {
/*eslint-disable */
// @ts-ignore
const devToolsOpened: boolean = remote.getCurrentWindow().isDevToolsOpened()
if (devToolsOpened) {
// @ts-ignore
remote.getCurrentWindow().closeDevTools()
} else {
// @ts-ignore
remote.getCurrentWindow().openDevTools()
}
/* eslint-enable */
}
openExistingProject = openExistingProject
createNewProject = createNewProject
async created () {
this.projectExists = await this.retrieveCurrentProjectName()
this.projectExists = await retrieveCurrentProjectName()
}
}
</script>

View file

@ -1,22 +1,22 @@
import { I_Blueprint } from "./../interfaces/I_Blueprint"
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import PouchDB from "pouchdb"
import _ from "lodash"
import { charactersBlueprint } from "src/databaseManager/blueprints/characters"
import { chaptersBlueprint } from "src/databaseManager/blueprints/chapters"
import { currenciesBlueprint } from "src/databaseManager/blueprints/currencies"
import { eventsBlueprint } from "src/databaseManager/blueprints/events"
import { languagesBlueprint } from "src/databaseManager/blueprints/languages"
import { locationsBlueprint } from "src/databaseManager/blueprints/locations"
import { loreNotesBlueprint } from "src/databaseManager/blueprints/loreNotes"
import { politicalGroupsBlueprint } from "src/databaseManager/blueprints/politicalGroups"
import { racesBlueprint } from "src/databaseManager/blueprints/races"
import { religionsBlueprint } from "src/databaseManager/blueprints/religions"
import { mythsBlueprint } from "src/databaseManager/blueprints/myths"
import { magicBlueprint } from "src/databaseManager/blueprints/magic"
import { techBlueprint } from "src/databaseManager/blueprints/scienceTechnology"
import { itemsBlueprint } from "src/databaseManager/blueprints/items"
import { charactersBlueprint } from "src/scripts/databaseManager/blueprints/characters"
import { chaptersBlueprint } from "src/scripts/databaseManager/blueprints/chapters"
import { currenciesBlueprint } from "src/scripts/databaseManager/blueprints/currencies"
import { eventsBlueprint } from "src/scripts/databaseManager/blueprints/events"
import { languagesBlueprint } from "src/scripts/databaseManager/blueprints/languages"
import { locationsBlueprint } from "src/scripts/databaseManager/blueprints/locations"
import { loreNotesBlueprint } from "src/scripts/databaseManager/blueprints/loreNotes"
import { politicalGroupsBlueprint } from "src/scripts/databaseManager/blueprints/politicalGroups"
import { racesBlueprint } from "src/scripts/databaseManager/blueprints/races"
import { religionsBlueprint } from "src/scripts/databaseManager/blueprints/religions"
import { mythsBlueprint } from "src/scripts/databaseManager/blueprints/myths"
import { magicBlueprint } from "src/scripts/databaseManager/blueprints/magic"
import { techBlueprint } from "src/scripts/databaseManager/blueprints/scienceTechnology"
import { itemsBlueprint } from "src/scripts/databaseManager/blueprints/items"
/**
* Loads all the blueprints and processes them apropriatelly

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const chaptersBlueprint: I_Blueprint = {
_id: "chapters",
order: 20,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "./../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const charactersBlueprint: I_Blueprint = {
_id: "characters",
order: 18,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const currenciesBlueprint: I_Blueprint = {
_id: "currencies",
order: 8,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const eventsBlueprint: I_Blueprint = {
_id: "events",
order: 16,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const itemsBlueprint: I_Blueprint = {
_id: "items",
order: 10,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const languagesBlueprint: I_Blueprint = {
_id: "languages",
order: 9,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const locationsBlueprint: I_Blueprint = {
_id: "locations",
order: 17,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const loreNotesBlueprint: I_Blueprint = {
_id: "loreNotes",
order: 19,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const magicBlueprint: I_Blueprint = {
_id: "magic",
order: 13,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const mythsBlueprint: I_Blueprint = {
_id: "myths",
order: 7,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const politicalGroupsBlueprint: I_Blueprint = {
_id: "politicalGroups",
order: 15,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const racesBlueprint: I_Blueprint = {
_id: "races",
order: 11,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const religionsBlueprint: I_Blueprint = {
_id: "religions",
order: 14,

View file

@ -1,4 +1,4 @@
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import { I_Blueprint } from "../../../interfaces/I_Blueprint"
export const techBlueprint: I_Blueprint = {
_id: "tech",
order: 12,

View file

@ -1,5 +1,5 @@
import { retrieveAllBlueprints } from "src/databaseManager/blueprintManager"
import { I_Blueprint } from "../interfaces/I_Blueprint"
import { retrieveAllBlueprints } from "src/scripts/databaseManager/blueprintManager"
import { I_Blueprint } from "../../interfaces/I_Blueprint"
import PouchDB from "pouchdb"
export const cleanDatabases = async () => {

View file

@ -2,7 +2,7 @@ import PouchDB from "pouchdb"
import { I_Blueprint } from "src/interfaces/I_Blueprint"
import { I_FieldRelationship } from "src/interfaces/I_FieldRelationship"
import { I_ExtraDocumentFields, I_OpenedDocument } from "./../interfaces/I_OpenedDocument"
import { I_ExtraDocumentFields, I_OpenedDocument } from "../../interfaces/I_OpenedDocument"
export const single_changeRelationshipToAnotherObject = async (
field: I_ExtraDocumentFields,

View file

@ -0,0 +1,148 @@
import { remote } from "electron"
// @ts-ignore
import replicationStream from "pouchdb-replication-stream/dist/pouchdb.replication-stream.min.js"
// @ts-ignore
import load from "pouchdb-load"
import PouchDB from "pouchdb"
import fs from "fs"
import path from "path"
/**
* Creates a brand new project and deleted any present data avaiable right now
* @param projectName The name of the new project
* @praram vueRouter The vue router object
*/
export const createNewProject = async (projectName: string, vueRouter: any) => {
await removeCurrentProject()
const ProjectDB = new PouchDB("project-data")
const newProject = { _id: projectName }
await ProjectDB.put(newProject)
/*eslint-disable */
// @ts-ignore
vueRouter.push({ path: "/project" }).catch((e: {name: string}) => {
const errorName : string = e.name
if (errorName === "NavigationDuplicated") {
return
}
console.log(e)
})
/* eslint-enable */
}
/**
* Open an file dialog asking the use for location where to export the project
* @param projectName The name of the project to export
*/
export const exportProject = (projectName: string) => {
remote.dialog.showOpenDialog({
properties: ["openDirectory"]
}).then(async (result) => {
/*eslint-disable */
const folderPath = result.filePaths[0]
PouchDB.plugin(replicationStream.plugin)
// @ts-ignore
PouchDB.adapter("writableStream", replicationStream.adapters.writableStream)
// @ts-ignore
const allDBS = await indexedDB.databases()
const DBnames: string[] = allDBS.map((db: {name: string}) => {
return db.name.replace("_pouch_", "")
})
for (const db of DBnames) {
const CurrentDB = new PouchDB(db)
if (!fs.existsSync(`${folderPath}/${projectName}`)) {
fs.mkdirSync(`${folderPath}/${projectName}`)
}
const ws = fs.createWriteStream(`${folderPath}/${projectName}/${db}.txt`)
// @ts-ignore
await CurrentDB.dump(ws)
}
/* eslint-enable */
}).catch(err => {
console.log(err)
})
}
/**
* Delete the current project and all its data
*/
export const removeCurrentProject = async () => {
/*eslint-disable */
// @ts-ignore
const allDBS = await indexedDB.databases()
const DBnames: string[] = allDBS.map((db: {name: string}) => {
return db.name.replace("_pouch_", "")
})
for (const db of DBnames) {
const CurrentDB = new PouchDB(db)
await CurrentDB.destroy()
}
/* eslint-enable */
}
/**
* Opens a dialog to let user pick whatever project they wish to open and lets them select a directory
* @param vueRouter The vue router object
*/
export const openExistingProject = (vueRouter: any) => {
/*eslint-disable */
remote.dialog.showOpenDialog({
properties: ["openDirectory"]
}).then(async (result) => {
const folderPath = result.filePaths[0]
if (!folderPath) {
return
}
await removeCurrentProject()
// @ts-ignore
PouchDB.plugin({
loadIt: load.load
})
const allFiles = fs.readdirSync(folderPath)
for (const file of allFiles) {
const currentDBName = path.parse(file).name
const CurrentDB = new PouchDB(currentDBName)
const fileContents = fs.readFileSync(`${folderPath}/${file}`, { encoding: "utf8" })
// @ts-ignore
await CurrentDB.loadIt(fileContents)
}
/*eslint-disable */
// @ts-ignore
vueRouter.push({ path: "/project" }).catch((e: {name: string}) => {
const errorName : string = e.name
if (errorName === "NavigationDuplicated") {
return
}
console.log(e)
})
/* eslint-enable */
}).catch(err => {
console.log(err)
})
/* eslint-enable */
}
/**
* Retrieves current project name
*/
export const retrieveCurrentProjectName = async () => {
const ProjectDB = new PouchDB("project-data")
const projectData = await ProjectDB.allDocs({ include_docs: true })
return projectData?.rows[0]?.id
}

View file

@ -0,0 +1,19 @@
import { remote } from "electron"
/**
* Toggles dev tools in the current window
*/
export const toggleDevTools = () => {
/*eslint-disable */
// @ts-ignore
const devToolsOpened: boolean = remote.getCurrentWindow().isDevToolsOpened()
if (devToolsOpened) {
// @ts-ignore
remote.getCurrentWindow().closeDevTools()
} else {
// @ts-ignore
remote.getCurrentWindow().openDevTools()
}
/* eslint-enable */
}

View file

@ -8,6 +8,7 @@ import Vuex from "vuex"
import blueprintsModule from "./module-blueprints"
import openedDocumentsModule from "./module-openedDocuments"
import keybindsModule from "./module-keybinds"
import dialogsModule from "./module-dialogs"
/*
* If not building with SSR mode, you can
@ -28,7 +29,8 @@ export default store(function ({ Vue }) {
modules: {
blueprintsModule,
openedDocumentsModule,
keybindsModule
keybindsModule,
dialogsModule
// example
},

View file

@ -0,0 +1,11 @@
import { ActionTree } from "vuex"
import { StateInterface } from "../index"
import { DialogsStateInterface } from "./state"
const actions: ActionTree<DialogsStateInterface, StateInterface> = {
// someAction (context) {
// }
}
export default actions

View file

@ -0,0 +1,12 @@
import { GetterTree } from "vuex"
import { StateInterface } from "../index"
import { DialogsStateInterface } from "./state"
const getters: GetterTree<DialogsStateInterface, StateInterface> = {
getDialogsState (state) {
return state.dialogExists
}
}
export default getters

View file

@ -0,0 +1,16 @@
import { Module } from "vuex"
import { StateInterface } from "../index"
import state, { DialogsStateInterface } from "./state"
import actions from "./actions"
import getters from "./getters"
import mutations from "./mutations"
const openedDocumentsModule: Module<DialogsStateInterface, StateInterface> = {
namespaced: true,
actions,
getters,
mutations,
state
}
export default openedDocumentsModule

View file

@ -0,0 +1,11 @@
import { MutationTree } from "vuex"
import { DialogsStateInterface } from "./state"
const mutation: MutationTree<DialogsStateInterface> = {
setDialogState (state: DialogsStateInterface, input: boolean) {
state.dialogExists = input
}
}
export default mutation

View file

@ -0,0 +1,11 @@
export interface DialogsStateInterface {
dialogExists: boolean
}
function state (): DialogsStateInterface {
return {
dialogExists: false
}
}
export default state

View file

@ -25,6 +25,11 @@ const mutation: MutationTree<OpenDocumentsStateInterface> = {
const toRemoveIndex = state.documents.docs.findIndex(doc => doc.type === input.type && doc._id === input._id)
state.documents.docs.splice(toRemoveIndex, 1)
state.documents.timestamp = uid()
},
resetDocuments (state: OpenDocumentsStateInterface) {
state.documents.docs = []
state.documents.timestamp = uid()
}
}