Big update to deploy scripts, and deploy prosecss
This commit is contained in:
1 parent
2d5d23b104
commit
590ae01055
141 files changed
+8483
-53810
No files matched your search
+143
-65
@@ -117,7 +117,8 @@
|
||||
</div>
|
||||
|
||||
<!-- render each section based on notes in set -->
|
||||
<div v-for="section,index in noteSections" v-if="section.length > 0" class="note-card-section">
|
||||
<template v-for="section,index in noteSections" :key="index">
|
||||
<div v-if="section.length > 0" class="note-card-section">
|
||||
<h5 class="ui tiny dividing header"><i :class="`green ${sectionData[index][0]} icon`"></i>{{ sectionData[index][1] }}</h5>
|
||||
|
||||
<div class="note-card-display-area">
|
||||
@@ -133,6 +134,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<div class="loading-section" v-if="showLoading">
|
||||
@@ -163,20 +165,21 @@
|
||||
|
||||
<!-- flexbox note container evenly spaces open notes -->
|
||||
<div class="note-panel-container" :class="{ 'note-panel-fullwidth':collapseFloatingList}" v-if="openNotes.length">
|
||||
<note-input-panel
|
||||
v-for="noteId in openNotes"
|
||||
<template v-for="noteId in openNotes" :key="noteId">
|
||||
<note-input-panel
|
||||
v-if="noteId != null"
|
||||
:key="noteId"
|
||||
:noteid="noteId"
|
||||
:url-data="$route.params"
|
||||
:open-notes="openNotes.length"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
@@ -184,15 +187,15 @@
|
||||
name: 'NotesPage',
|
||||
components: {
|
||||
|
||||
'note-input-panel': () => import(/* webpackChunkName: "NoteInputPanel" */ '@/components/NoteInputPanel.vue'),
|
||||
'note-input-panel': defineAsyncComponent(() => import(/* webpackChunkName: "NoteInputPanel" */ '@/components/NoteInputPanel.vue')),
|
||||
|
||||
'note-title-display-card': require('@/components/NoteTitleDisplayCard.vue').default,
|
||||
// 'fast-filters': require('@/components/FastFilters.vue').default,
|
||||
'search-input': require('@/components/SearchInput.vue').default,
|
||||
'attachment-display': require('@/components/AttachmentDisplayCard').default,
|
||||
'tag-display':require('@/components/TagDisplayComponent.vue').default,
|
||||
'loading-icon':require('@/components/LoadingIconComponent.vue').default,
|
||||
'paste-button':require('@/components/PasteButton.vue').default,
|
||||
'note-title-display-card': defineAsyncComponent(() => import('@/components/NoteTitleDisplayCard.vue')),
|
||||
// 'fast-filters': defineAsyncComponent(() => import('@/components/FastFilters.vue')),
|
||||
'search-input': defineAsyncComponent(() => import('@/components/SearchInput.vue')),
|
||||
'attachment-display': defineAsyncComponent(() => import('@/components/AttachmentDisplayCard.vue')),
|
||||
'tag-display':defineAsyncComponent(() => import('@/components/TagDisplayComponent.vue')),
|
||||
'loading-icon':defineAsyncComponent(() => import('@/components/LoadingIconComponent.vue')),
|
||||
'paste-button':defineAsyncComponent(() => import('@/components/PasteButton.vue')),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -203,6 +206,9 @@
|
||||
searchTags: [],
|
||||
notes: [],
|
||||
openNotes: [],
|
||||
// fullPath of our own last URL write, so the $route watcher can
|
||||
// recognise it and not treat it as an inbound navigation
|
||||
lastSyncedFullPath: null,
|
||||
collapseFloatingList: false,
|
||||
highlights: [],
|
||||
searchDebounce: null,
|
||||
@@ -258,7 +264,7 @@
|
||||
},
|
||||
beforeMount(){
|
||||
|
||||
this.$parent.loginGateway()
|
||||
this.$root.loginGateway()
|
||||
|
||||
//If user is on title view,
|
||||
this.titleView = this.$store.getters.getIsUserOnMobile
|
||||
@@ -363,7 +369,7 @@
|
||||
// document.addEventListener('visibilitychange', this.visibiltyChangeAction);
|
||||
|
||||
},
|
||||
beforeDestroy(){
|
||||
beforeUnmount(){
|
||||
window.removeEventListener('scroll', this.onScroll)
|
||||
// document.removeEventListener('visibilitychange', this.visibiltyChangeAction)
|
||||
|
||||
@@ -380,11 +386,26 @@
|
||||
},
|
||||
mounted() {
|
||||
|
||||
//Open note on PAGE LOAD if ID is set
|
||||
if(this.$route.params.id > 1){
|
||||
this.openNote(this.$route.params.id)
|
||||
// Restore every note named in ?open=
|
||||
const ids = this.parseOpenQuery(this.$route.query.open)
|
||||
|
||||
// Fold the legacy /notes/open/:id deep link in, first position
|
||||
const paramId = parseInt(this.$route.params.id, 10)
|
||||
if(Number.isInteger(paramId) && paramId > 1 && !ids.includes(paramId)){
|
||||
ids.unshift(paramId)
|
||||
}
|
||||
|
||||
// Push directly rather than via openNote(), which would write the URL
|
||||
// once per note; one normalising write follows instead.
|
||||
ids.forEach(id => {
|
||||
if(!this.openNotes.includes(id)){
|
||||
this.openNotes.push(id)
|
||||
}
|
||||
})
|
||||
|
||||
// Unconditional, so a junk ?open=abc gets scrubbed too
|
||||
this.syncOpenNotesToUrl()
|
||||
|
||||
//Loads initial batch and tags
|
||||
this.reset()
|
||||
|
||||
@@ -395,17 +416,31 @@
|
||||
},
|
||||
'$route' (to, from) {
|
||||
|
||||
// Our own URL write, not an inbound navigation
|
||||
if(to.fullPath === this.lastSyncedFullPath){ return }
|
||||
|
||||
// Reload the notes if returning to this page
|
||||
if(to.fullPath == '/notes' && !from.fullPath.includes('/notes/open/')){
|
||||
// The discriminator is the absence of ?open=, NOT the path. A menu's
|
||||
// $router.go(-1) lands on /notes?open=5 with `from` containing
|
||||
// '/notes/open/', so a path comparison here would close every note
|
||||
// every time a side menu is dismissed.
|
||||
const landingOnBareNotes = (to.path === '/notes' && !to.query.open)
|
||||
|
||||
// Reload the notes if arriving at an empty /notes from elsewhere
|
||||
if(landingOnBareNotes && !from.path.startsWith('/notes/open/') && from.path !== '/notes'){
|
||||
this.reset()
|
||||
}
|
||||
|
||||
// Close all notes if returning to /notes page
|
||||
if(to.fullPath == '/notes' && from.fullPath.includes('/notes/open/')){
|
||||
// Explicit navigation to a note-less /notes closes everything
|
||||
if(landingOnBareNotes && this.openNotes.length > 0){
|
||||
this.closeAllNotes()
|
||||
}
|
||||
|
||||
// Stale history entry (e.g. go(-1) after another note was closed):
|
||||
// rewrite the URL from the array. Never read it.
|
||||
if(to.path === '/notes' && to.query.open && to.query.open !== this.openNotes.join(',')){
|
||||
this.syncOpenNotesToUrl()
|
||||
}
|
||||
|
||||
//Lookup tags set in URL
|
||||
if(to.params.tag && this.$store.getters.totals && this.$store.getters.totals['tags'][to.params.tag]){
|
||||
|
||||
@@ -432,81 +467,121 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openNote(id, event = null){
|
||||
// Where the open-note URL should point. A menu or a tag search owns the
|
||||
// path, so only a plain notes view gets normalised back to /notes.
|
||||
openNotesTargetPath(){
|
||||
|
||||
//
|
||||
// A side menu is open; its close button is a $router.go(-1), so the
|
||||
// path has to stay put or there is nowhere sane to land.
|
||||
if(this.$route.params.openMenu){ return this.$route.path }
|
||||
|
||||
const intId = parseInt(id)
|
||||
if(this.openNotes.includes(intId)){
|
||||
// Preserve a tag search
|
||||
if(this.$route.params.tag){ return this.$route.path }
|
||||
|
||||
console.log('Open already open note?')
|
||||
return '/notes'
|
||||
},
|
||||
// Mirror openNotes into ?open=. This is the ONLY place the open-note
|
||||
// URL is written.
|
||||
syncOpenNotesToUrl(){
|
||||
|
||||
// const openIndex = this.openNotes.indexOf(intId)
|
||||
// if(openIndex != -1){
|
||||
// console.log('Open note and remove it ', intId + ' on index ' + openIndex)
|
||||
// this.openNotes.splice(openIndex, 1)
|
||||
// }
|
||||
// this.$bus.$emit('close_note_by_id', intId)
|
||||
const query = Object.assign({}, this.$route.query)
|
||||
|
||||
if(this.openNotes.length > 0){
|
||||
query.open = this.openNotes.join(',')
|
||||
}
|
||||
else {
|
||||
delete query.open
|
||||
}
|
||||
|
||||
const target = { path: this.openNotesTargetPath(), query }
|
||||
const targetFullPath = this.$router.resolve(target).fullPath
|
||||
|
||||
// Already correct -- don't navigate at all
|
||||
if(targetFullPath === this.$route.fullPath){
|
||||
this.lastSyncedFullPath = targetFullPath
|
||||
return
|
||||
}
|
||||
|
||||
// Set before navigating so the watcher sees it
|
||||
this.lastSyncedFullPath = targetFullPath
|
||||
|
||||
this.$router.replace(target).catch(() => {})
|
||||
},
|
||||
// Tolerant parse of ?open=. Handles 'abc', '1,,2,2', '-4' and the
|
||||
// repeated form ?open=1&open=2 (which arrives as an array).
|
||||
parseOpenQuery(raw){
|
||||
|
||||
if(raw === undefined || raw === null){ return [] }
|
||||
|
||||
// Cap: a hand-crafted URL shouldn't be able to fire 50 note fetches
|
||||
const MAX_RESTORE = 8
|
||||
|
||||
return [].concat(raw)
|
||||
.join(',')
|
||||
.split(',')
|
||||
.map(value => parseInt(value, 10))
|
||||
.filter(value => Number.isInteger(value) && value > 0)
|
||||
.filter((value, index, all) => all.indexOf(value) === index)
|
||||
.slice(0, MAX_RESTORE)
|
||||
},
|
||||
openNote(id, event = null){
|
||||
|
||||
const intId = parseInt(id, 10)
|
||||
|
||||
// Also absorbs the undefined the '$route.params.id' watcher now gets
|
||||
// when we normalise /notes/open/5 into /notes?open=5
|
||||
if(!Number.isInteger(intId) || intId < 1){ return }
|
||||
|
||||
//Don't open note if a link is clicked in display card
|
||||
if(event && event.target && event.target.nodeName){
|
||||
const nodeClick = event.target.nodeName
|
||||
if(nodeClick == 'A'){ return }
|
||||
}
|
||||
|
||||
// Push note to stack if not open
|
||||
if(Number.isInteger(intId) && !this.openNotes.includes(intId)){
|
||||
this.openNotes.push(intId)
|
||||
if(this.openNotes.includes(intId)){
|
||||
// Already open, but an external push('/notes/open/5') still needs
|
||||
// the URL normalised back to the full set
|
||||
this.syncOpenNotesToUrl()
|
||||
return
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
// change route if open ID is not the same as current ID
|
||||
if(this.$route.params.id != id){
|
||||
console.log('Open note, change route -> route id ' + this.$route.params.id + ' note id ->' + id + ', ' +(this.$route.params.id == id))
|
||||
this.$router.push('/notes/open/'+id)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
return
|
||||
this.openNotes.push(intId)
|
||||
this.syncOpenNotesToUrl()
|
||||
},
|
||||
closeNote(noteId, modified){
|
||||
closeNote(noteId, modified, syncUrl = true){
|
||||
|
||||
console.log('close note', this.$route.fullPath)
|
||||
// parseInt: indexOf is strict, and ids arriving from the URL are
|
||||
// strings. Without this the splice silently misses.
|
||||
const intId = parseInt(noteId, 10)
|
||||
const openIndex = this.openNotes.indexOf(intId)
|
||||
const didClose = (openIndex !== -1)
|
||||
|
||||
const openIndex = this.openNotes.indexOf(noteId)
|
||||
if(openIndex != -1){
|
||||
console.log('Removing note id ', noteId + ' on index ' + openIndex)
|
||||
if(didClose){
|
||||
this.openNotes.splice(openIndex, 1)
|
||||
}
|
||||
|
||||
// //A note has been closed
|
||||
// if(this.$route.fullPath != '/notes'){
|
||||
// this.$router.push('/notes')
|
||||
// }
|
||||
if(this.openNotes.length == 0 && this.$route.fullPath != '/notes'){
|
||||
this.$router.push('/notes')
|
||||
// Only write the URL when the array actually changed. NoteInputPanel
|
||||
// emits close_active_note twice per note (closeButtonAction -> close(),
|
||||
// then beforeUnmount -> close()); the second arrives after the splice.
|
||||
if(didClose && syncUrl){
|
||||
this.syncOpenNotesToUrl()
|
||||
}
|
||||
|
||||
if(modified){
|
||||
console.log('Just closed Note -> ' + noteId + ', modified -> ', modified)
|
||||
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||
//Focus and animate if modified
|
||||
this.updateSingleNote(noteId, modified)
|
||||
}
|
||||
|
||||
console.log('closeNote(): Open notes length ', this.openNotes.length)
|
||||
},
|
||||
closeAllNotes(){
|
||||
console.log('Close all notes ------------')
|
||||
|
||||
// Backwards, so the in-loop splice can't skip an entry. URL writes are
|
||||
// deferred so this is one navigation rather than N.
|
||||
for (let i = this.openNotes.length - 1; i >= 0; i--) {
|
||||
console.log('Close all notes -> ' + this.openNotes[i])
|
||||
this.closeNote(this.openNotes[i])
|
||||
this.closeNote(this.openNotes[i], false, false)
|
||||
}
|
||||
console.log('----------------')
|
||||
|
||||
this.syncOpenNotesToUrl()
|
||||
},
|
||||
toggleTagFilter(tagId){
|
||||
|
||||
@@ -741,7 +816,10 @@
|
||||
|
||||
return resolve(true)
|
||||
})
|
||||
.catch(error => { this.$bus.$emit('notification', 'Failed to Search Notes') })
|
||||
.catch(error => {
|
||||
console.error('[NotesPage] search failed:', error)
|
||||
this.$bus.$emit('notification', 'Failed to Search Notes')
|
||||
})
|
||||
})
|
||||
},
|
||||
rebuildNoteCategorise(){
|
||||
|
||||
Reference in new issue
Block a user