Added a night mode and no way to toggle it!
Tweaked a lot of styles and added some cool animations Added a little to the help text Quickly adding a note, saving and closing no longer causes half formed or empty notes to appear Close Editor animation Display cards text show at the bottom of card Added a delete function, and it works Added browser title attributes More debugging and error checking on scraped links Updated not search to display title and text below the title
This commit is contained in:
parent
b0a8071b41
commit
fcee24a61d
@ -1,18 +1,110 @@
|
|||||||
/*body, h3, h2, h1, p {
|
|
||||||
color: #3d3d3d;
|
:root {
|
||||||
}*/
|
--primary_color: #1C84DA;
|
||||||
|
--secondary_color: #1EAEDB;
|
||||||
|
|
||||||
|
--element_background_color: #FFF;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--background_color: #fff;
|
||||||
|
--text_color: #3d3d3d;
|
||||||
|
--outline_color: rgba(34,36,38,.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Night mode colors */
|
||||||
|
:root {
|
||||||
|
--background_color: #000;
|
||||||
|
--text_color: #a98457;
|
||||||
|
--outline_color: #a98457;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OVERWRITE DEFAULT SEMANTIC STYLES FOR CUSTOM/NIGHT MODES*/
|
||||||
|
body{
|
||||||
|
color: var(--text_color);
|
||||||
|
background-color: var(--background_color);
|
||||||
|
}
|
||||||
|
.ui.form input:not([type]),
|
||||||
|
.ui.form input:not([type]):focus {
|
||||||
|
color: var(--text_color);
|
||||||
|
background-color: var(--background_color);
|
||||||
|
border-color: var(--border_color);
|
||||||
|
}
|
||||||
|
.ui.basic.label {
|
||||||
|
color: var(--text_color);
|
||||||
|
background-color: var(--background_color);
|
||||||
|
border-color: var(--border_color);
|
||||||
|
}
|
||||||
|
div.ui.basic.green.label {
|
||||||
|
background-color: var(--background_color) !important;
|
||||||
|
}
|
||||||
|
.ui.basic.button, .ui.basic.buttons .button {
|
||||||
|
background-color: var(--background_color) !important;
|
||||||
|
color: var(--text_color) !important;
|
||||||
|
border: 1px solid;
|
||||||
|
border-color: var(--border_color) !important;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.ui.basic.button:focus, .ui.basic.button:hover {
|
||||||
|
background-color: var(--background_color) !important;
|
||||||
|
color: var(--text_color) !important;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.ui.tabular.menu .item {
|
||||||
|
background-color: var(--background_color) !important;
|
||||||
|
color: var(--text_color) !important;
|
||||||
|
}
|
||||||
|
.ui.tabular.menu .item.active {
|
||||||
|
background-color: var(--background_color) !important;
|
||||||
|
color: var(--text_color) !important;
|
||||||
|
border-color: var(--border_color) !important;
|
||||||
|
}
|
||||||
|
/* OVERWRITE DEFAULT SEMANTIC STYLES FOR CUSTOM/NIGHT MODES*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.clickable {
|
.clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.textarea-height {
|
||||||
|
height: calc(100% - 105px);
|
||||||
|
}
|
||||||
.ck-content {
|
.ck-content {
|
||||||
font-family: 'Open Sans' !important;
|
font-family: 'Open Sans' !important;
|
||||||
font-size: 1.3rem !important;
|
font-size: 1.3rem !important;
|
||||||
background-color: rgba(255, 255, 255, 0);
|
background-color: rgba(255, 255, 255, 0);
|
||||||
height: 100%;
|
height: calc(100% - 40px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
.ck .ck-editor__nested-editable:focus {
|
||||||
|
background-color: var(--background_color) !important;
|
||||||
|
}
|
||||||
.ui.white.button {
|
.ui.white.button {
|
||||||
background: #FFF;
|
background: #FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fade-in-fwd {
|
||||||
|
animation: fade-in-fwd 0.8s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------
|
||||||
|
* Generated by Animista on 2019-7-25 17:12:5
|
||||||
|
* w: http://animista.net, t: @cssanimista
|
||||||
|
* ---------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ----------------------------------------
|
||||||
|
* animation fade-in-fwd
|
||||||
|
* ----------------------------------------
|
||||||
|
*/
|
||||||
|
@keyframes fade-in-fwd {
|
||||||
|
0% {
|
||||||
|
transform: translateZ(-80px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateZ(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
40
client/src/components/DeleteButtonComponent.vue
Normal file
40
client/src/components/DeleteButtonComponent.vue
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<span>
|
||||||
|
<span class="clickable" @click="confirmDelete()" v-if="click == 0">
|
||||||
|
<i class="grey trash alternate icon"></i>
|
||||||
|
</span>
|
||||||
|
<span class="clickable" @click="actuallyDelete()" @mouseleave="reset" v-if="click == 1" data-tooltip="Click again to delete." data-position="left center">
|
||||||
|
<i class="red trash alternate icon"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NoteTitleDisplayCard',
|
||||||
|
props: [ 'noteId', 'displayText' ],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
click: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
confirmDelete(){
|
||||||
|
this.click++
|
||||||
|
},
|
||||||
|
actuallyDelete(){
|
||||||
|
axios.post('/api/notes/delete', {'noteId':this.noteId}).then(response => {
|
||||||
|
if(response.data == true){
|
||||||
|
this.$bus.$emit('note_deleted')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
reset(){
|
||||||
|
this.click = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -2,6 +2,9 @@
|
|||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
|
|
||||||
|
CTRL + SHIFT + V - paste without formatting
|
||||||
|
CTRL + Z - Undo in note, <b>Undo youtube video player embed.</b>
|
||||||
|
|
||||||
<h2>Block formatting</h2>
|
<h2>Block formatting</h2>
|
||||||
|
|
||||||
<p>The following block formatting options are available:</p>
|
<p>The following block formatting options are available:</p>
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="InputNotes" class="master-note-edit" :class="[ 'position-'+position ]" @keyup.esc="close" :style="{'background-color':color, 'color':fontColor}">
|
<div
|
||||||
|
id="InputNotes" class="master-note-edit"
|
||||||
|
@keyup.esc="close"
|
||||||
|
:class="[{'size-down':(sizeDown == true)}, 'position-'+position ]"
|
||||||
|
:style="{'background-color':color, 'color':fontColor}"
|
||||||
|
>
|
||||||
|
|
||||||
<div v-if="fancyInput == 1" class="textarea-height no-flow">
|
<div v-if="fancyInput == 1" class="textarea-height no-flow">
|
||||||
<ckeditor ref="main-edit"
|
<ckeditor ref="main-edit"
|
||||||
@ -16,9 +20,9 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="ui buttons">
|
<div class="ui buttons">
|
||||||
<div class="ui right floated green button">{{statusText}}</div>
|
<div @click="close" class="ui button">Close + Save (ESC)</div>
|
||||||
<div class="ui button">Delete</div>
|
<div class="ui button">Delete</div>
|
||||||
<div @click="close" class="ui button">Close (ESC)</div>
|
|
||||||
<div @click="onToggleFancyInput" class="ui button">
|
<div @click="onToggleFancyInput" class="ui button">
|
||||||
Fancy ({{fancyInput?'On':'Off'}})
|
Fancy ({{fancyInput?'On':'Off'}})
|
||||||
</div>
|
</div>
|
||||||
@ -39,6 +43,7 @@
|
|||||||
<button @click="onChangeColor" class="ui icon grey button"></button>
|
<button @click="onChangeColor" class="ui icon grey button"></button>
|
||||||
<button @click="onChangeColor" class="ui icon black button"></button>
|
<button @click="onChangeColor" class="ui icon black button"></button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ui right floated green button">{{statusText}}</div>
|
||||||
<!-- <p>
|
<!-- <p>
|
||||||
Last Updated: {{$helpers.timeAgo(updated)}}
|
Last Updated: {{$helpers.timeAgo(updated)}}
|
||||||
</p> -->
|
</p> -->
|
||||||
@ -64,7 +69,7 @@
|
|||||||
return {
|
return {
|
||||||
currentNoteId: 0,
|
currentNoteId: 0,
|
||||||
noteText: '',
|
noteText: '',
|
||||||
statusText: 'Save',
|
statusText: 'Saved',
|
||||||
lastNoteHash: null,
|
lastNoteHash: null,
|
||||||
saveDebounce: null, //Prevent save from being called numerous times quickly
|
saveDebounce: null, //Prevent save from being called numerous times quickly
|
||||||
lastSaved: 0,
|
lastSaved: 0,
|
||||||
@ -74,6 +79,7 @@
|
|||||||
fancyInput: 0, //Default to basic text edit. Upgrade if set to 1
|
fancyInput: 0, //Default to basic text edit. Upgrade if set to 1
|
||||||
color: '#FFF',
|
color: '#FFF',
|
||||||
fontColor: '#000',
|
fontColor: '#000',
|
||||||
|
sizeDown: false,
|
||||||
|
|
||||||
editor: DecoupledEditor,
|
editor: DecoupledEditor,
|
||||||
editorConfig: {
|
editorConfig: {
|
||||||
@ -127,7 +133,7 @@
|
|||||||
|
|
||||||
if(this.color == "rgb(255, 255, 255)" || this.color == '#FFF'){
|
if(this.color == "rgb(255, 255, 255)" || this.color == '#FFF'){
|
||||||
this.color = null
|
this.color = null
|
||||||
this.fontColor = '#000'
|
this.fontColor = null
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lastNoteHash = 0 //Update hash to force note update on next save
|
this.lastNoteHash = 0 //Update hash to force note update on next save
|
||||||
@ -150,7 +156,7 @@
|
|||||||
this.fontColor = '#FFF'
|
this.fontColor = '#FFF'
|
||||||
if(this.color == "rgb(255, 255, 255)" || this.color == '#FFF' || this.color == null){
|
if(this.color == "rgb(255, 255, 255)" || this.color == '#FFF' || this.color == null){
|
||||||
this.color = null
|
this.color = null
|
||||||
this.fontColor = '#000'
|
this.fontColor = null
|
||||||
}
|
}
|
||||||
|
|
||||||
if(response.data.raw_input == 1){
|
if(response.data.raw_input == 1){
|
||||||
@ -201,14 +207,16 @@
|
|||||||
|
|
||||||
} );
|
} );
|
||||||
},
|
},
|
||||||
|
//Used by simple editor
|
||||||
onKeyup(){
|
onKeyup(){
|
||||||
let vm = this
|
let vm = this
|
||||||
|
vm.statusText = 'Modified'
|
||||||
//Each note, save after 5 seconds, focus lost or 30 characters typed.
|
//Each note, save after 5 seconds, focus lost or 30 characters typed.
|
||||||
clearTimeout(vm.editDebounce)
|
clearTimeout(vm.editDebounce)
|
||||||
vm.editDebounce = setTimeout(() => {
|
vm.editDebounce = setTimeout(() => {
|
||||||
vm.save()
|
vm.save()
|
||||||
}, 5000)
|
}, 5000)
|
||||||
//Save after 20 keystrokes
|
//Save after 30 keystrokes
|
||||||
vm.keyPressesCounter = (vm.keyPressesCounter + 1)
|
vm.keyPressesCounter = (vm.keyPressesCounter + 1)
|
||||||
if(vm.keyPressesCounter > 30){
|
if(vm.keyPressesCounter > 30){
|
||||||
vm.keyPressesCounter = 0
|
vm.keyPressesCounter = 0
|
||||||
@ -216,12 +224,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
save(){
|
save(){
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
clearTimeout(this.editDebounce)
|
clearTimeout(this.editDebounce)
|
||||||
|
|
||||||
//Don't save note if its hash doesn't change
|
//Don't save note if its hash doesn't change
|
||||||
if( this.lastNoteHash == this.hashString(this.noteText) ){
|
if( this.lastNoteHash == this.hashString(this.noteText) ){
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(true)
|
||||||
return
|
return
|
||||||
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
const postData = {
|
const postData = {
|
||||||
@ -238,14 +249,15 @@
|
|||||||
//Only notify user if saving - may help with debugging in the future
|
//Only notify user if saving - may help with debugging in the future
|
||||||
vm.statusText = 'Saving'
|
vm.statusText = 'Saving'
|
||||||
axios.post('/api/notes/update', postData).then( response => {
|
axios.post('/api/notes/update', postData).then( response => {
|
||||||
vm.statusText = 'Save'
|
vm.statusText = 'Saved'
|
||||||
vm.updated = Math.round((+new Date)/1000)
|
vm.updated = Math.round((+new Date)/1000)
|
||||||
|
|
||||||
//Update last saved note hash
|
//Update last saved note hash
|
||||||
vm.lastNoteHash = vm.hashString(vm.noteText)
|
vm.lastNoteHash = vm.hashString(vm.noteText)
|
||||||
|
resolve(true)
|
||||||
})
|
})
|
||||||
}, 500)
|
}, 500)
|
||||||
|
})
|
||||||
|
|
||||||
},
|
},
|
||||||
hashString(text){
|
hashString(text){
|
||||||
@ -261,8 +273,11 @@
|
|||||||
return hash;
|
return hash;
|
||||||
},
|
},
|
||||||
close(){
|
close(){
|
||||||
this.save()
|
this.sizeDown = true
|
||||||
|
this.save().then( () => {
|
||||||
this.$bus.$emit('close_active_note', this.position)
|
this.$bus.$emit('close_active_note', this.position)
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,9 +285,7 @@
|
|||||||
|
|
||||||
<style type="text/css" scoped>
|
<style type="text/css" scoped>
|
||||||
|
|
||||||
.textarea-height {
|
|
||||||
height: calc(100% - 177px);
|
|
||||||
}
|
|
||||||
.no-flow {
|
.no-flow {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@ -280,7 +293,7 @@
|
|||||||
.raw-edit {
|
.raw-edit {
|
||||||
font-family: 'Open Sans' !important;
|
font-family: 'Open Sans' !important;
|
||||||
font-size: 1.3rem !important;
|
font-size: 1.3rem !important;
|
||||||
background: white;
|
background: rgba(0,0,0,0);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
resize: none;
|
resize: none;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
@ -292,7 +305,8 @@
|
|||||||
.master-note-edit {
|
.master-note-edit {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: white;
|
background: var(--background_color);
|
||||||
|
/*color: var(--text_color);*/
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
|
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
|
||||||
}
|
}
|
||||||
@ -320,4 +334,19 @@
|
|||||||
right: 50%;
|
right: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.size-down {
|
||||||
|
animation: size-down 0.5s linear both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes size-down {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
top: 405vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
@ -1,7 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ui clickable segment" @click="onClick(note.id)" :style="{'background-color':color, 'color':fontColor}">
|
<div class="note-title-display-card fade-in-fwd" :style="{'background-color':color, 'color':fontColor}">
|
||||||
<h3>{{note.text}}</h3>
|
|
||||||
<p>Edited: {{$helpers.timeAgo(note.updated)}}</p>
|
<div class="ui grid max-height">
|
||||||
|
|
||||||
|
<div class="top aligned row">
|
||||||
|
<div class="sixteen wide column overflow-hidden">
|
||||||
|
<h3 @click="onClick(note.id)" class="clickable">{{note.title}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="sixteen wide column overflow-hidden">
|
||||||
|
<p @click="onClick(note.id)" class="clickable">{{note.subtext}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bottom aligned row">
|
||||||
|
<div class="ten wide column clickable" @click="onClick(note.id)">Edited: {{$helpers.timeAgo(note.updated)}}</div>
|
||||||
|
|
||||||
|
<div class="six wide right aligned column">
|
||||||
|
<span v-if="note.attachment_count > 0" class>
|
||||||
|
<i class="grey linkify icon"></i> {{note.attachment_count}}
|
||||||
|
</span>
|
||||||
|
<span v-if="note.tag_count == 1" data-tooltip="Note has 1 tag">
|
||||||
|
<i class="grey tags icon"></i> {{note.tag_count}}
|
||||||
|
</span>
|
||||||
|
<span v-if="note.tag_count > 1" :data-tooltip="`Note has ${note.tag_count} tags`">
|
||||||
|
<i class="grey tags icon"></i> {{note.tag_count}}
|
||||||
|
</span>
|
||||||
|
<delete-button :note-id="note.id" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Display highlights from solr results -->
|
<!-- Display highlights from solr results -->
|
||||||
<div v-if="note.note_highlights.length > 0" class="term-usage">
|
<div v-if="note.note_highlights.length > 0" class="term-usage">
|
||||||
@ -9,13 +37,14 @@
|
|||||||
<div class="usage-row" v-for="highlight in note.note_highlights" v-html="highlight"></div>
|
<div class="usage-row" v-for="highlight in note.note_highlights" v-html="highlight"></div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="note.attachment_highlights.length > 0" class="term-usage">
|
<div v-if="note.attachment_highlights.length > 0" class="term-usage">
|
||||||
<p>Note URL Text</p>
|
<p><i class="linkify icon"></i> Note URL Text</p>
|
||||||
<div class="usage-row" v-for="highlight in note.attachment_highlights" v-html="highlight"></div>
|
<div class="usage-row" v-for="highlight in note.attachment_highlights" v-html="highlight"></div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="note.tag_highlights.length > 0" class="term-usage">
|
<div v-if="note.tag_highlights.length > 0" class="term-usage">
|
||||||
Tag
|
<i class="tags icon"></i> Tag
|
||||||
<div class="ui icon large label" v-for="highlight in note.tag_highlights" v-html="highlight"></div>
|
<div class="ui icon large label" v-for="highlight in note.tag_highlights" v-html="highlight"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -24,11 +53,14 @@
|
|||||||
export default {
|
export default {
|
||||||
name: 'NoteTitleDisplayCard',
|
name: 'NoteTitleDisplayCard',
|
||||||
props: [ 'onClick', 'data' ],
|
props: [ 'onClick', 'data' ],
|
||||||
|
components: {
|
||||||
|
'delete-button': require('./DeleteButtonComponent.vue').default,
|
||||||
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
note: null,
|
note: null,
|
||||||
color: '#FFF',
|
color: null, //'#FFF',
|
||||||
fontColor: '#000'
|
fontColor: null, //'#000'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeMount(){
|
beforeMount(){
|
||||||
@ -43,6 +75,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
|
||||||
.term-usage {
|
.term-usage {
|
||||||
border: 1px solid #DDD;
|
border: 1px solid #DDD;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@ -57,4 +90,34 @@
|
|||||||
border-top: 1px solid #DDD;
|
border-top: 1px solid #DDD;
|
||||||
margin: 8px 0 0;
|
margin: 8px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note-title-display-card {
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 1px 2px 0 rgba(34,36,38,.15);
|
||||||
|
margin: 0 15px 15px 0;
|
||||||
|
padding: 1em;
|
||||||
|
border-radius: .28571429rem;
|
||||||
|
border: 1px solid;
|
||||||
|
border-color: var(--border_color);
|
||||||
|
width: 31.5%;
|
||||||
|
/*transition: width 0.2s;*/
|
||||||
|
}
|
||||||
|
.one-column .note-title-display-card {
|
||||||
|
margin-right: 65%;
|
||||||
|
width: 18%;
|
||||||
|
}
|
||||||
|
.overflow-hidden {
|
||||||
|
overflow: hidden;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.max-height {
|
||||||
|
height: calc(100% + 30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 740px) {
|
||||||
|
.note-title-display-card {
|
||||||
|
width: 100%;
|
||||||
|
margin: 15px 0 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="NotesPage">
|
<div id="NotesPage">
|
||||||
<div class="ui segment">
|
<div class="ui basic segment">
|
||||||
<search-bar />
|
<search-bar />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
<!-- tags display -->
|
<!-- tags display -->
|
||||||
<div class="ui two wide large screen only column">
|
<div class="ui two wide large screen only column">
|
||||||
<div class="ui basic fluid button" @click="reset">Reset</div>
|
<div class="ui basic fluid button" @click="reset"><i class="undo icon"></i>All Notes</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui clickable basic fluid large label" v-for="tag in commonTags" @click="toggleTagFilter(tag.id)"
|
<div class="ui clickable basic fluid large label" v-for="tag in commonTags" @click="toggleTagFilter(tag.id)"
|
||||||
:class="{ 'green':(searchTags.includes(tag.id)) }">
|
:class="{ 'green':(searchTags.includes(tag.id)) }">
|
||||||
@ -61,7 +61,7 @@
|
|||||||
<!-- Note title cards -->
|
<!-- Note title cards -->
|
||||||
<div class="ui fourteen wide computer sixteen wide mobile column">
|
<div class="ui fourteen wide computer sixteen wide mobile column">
|
||||||
<h2>Notes ({{notes.length}})</h2>
|
<h2>Notes ({{notes.length}})</h2>
|
||||||
<div v-if="notes !== null">
|
<div v-if="notes !== null" class="note-card-display-area" :class="{'one-column':(activeNoteId1 != null || activeNoteId2 != null )}">
|
||||||
<note-title-display-card
|
<note-title-display-card
|
||||||
v-for="note in notes"
|
v-for="note in notes"
|
||||||
:onClick="openNote"
|
:onClick="openNote"
|
||||||
@ -113,7 +113,9 @@
|
|||||||
this.$bus.$on('close_active_note', position => {
|
this.$bus.$on('close_active_note', position => {
|
||||||
this.closeNote(position)
|
this.closeNote(position)
|
||||||
})
|
})
|
||||||
|
this.$bus.$on('note_deleted', () => {
|
||||||
|
this.search()
|
||||||
|
})
|
||||||
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -223,6 +225,7 @@
|
|||||||
},
|
},
|
||||||
destroyLoginToken() {
|
destroyLoginToken() {
|
||||||
this.$store.commit('destroyLoginToken')
|
this.$store.commit('destroyLoginToken')
|
||||||
|
this.$router.push('/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,4 +234,8 @@
|
|||||||
.detail {
|
.detail {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
.note-card-display-area {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -9,6 +9,14 @@ import store from './stores/mainStore';
|
|||||||
import App from './App'
|
import App from './App'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
|
|
||||||
|
// This callback runs before every route change, including on page load.
|
||||||
|
// Sets the title of the page using vue router
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
|
||||||
|
document.title = to.meta.title;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
//Attach event bus to main vue object, all components will inherit event bus
|
//Attach event bus to main vue object, all components will inherit event bus
|
||||||
import EventBus from './EventBus'
|
import EventBus from './EventBus'
|
||||||
import Helpers from './Helpers'
|
import Helpers from './Helpers'
|
||||||
|
@ -13,21 +13,25 @@ export default new Router({
|
|||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'HelloWorld',
|
name: 'HelloWorld',
|
||||||
|
meta: {title:'Home'},
|
||||||
component: HelloWorld
|
component: HelloWorld
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
|
meta: {title:'Login'},
|
||||||
component: Login
|
component: Login
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/notes',
|
path: '/notes',
|
||||||
name: 'Notes',
|
name: 'Notes',
|
||||||
|
meta: {title:'Notes'},
|
||||||
component: Notes
|
component: Notes
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/help',
|
path: '/help',
|
||||||
name: 'Help',
|
name: 'Help',
|
||||||
|
meta: {title:'Help'},
|
||||||
component: HelpPage
|
component: HelpPage
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -17,8 +17,6 @@ export default new Vuex.Store({
|
|||||||
},
|
},
|
||||||
setLoginToken(state, userData){
|
setLoginToken(state, userData){
|
||||||
|
|
||||||
console.log(userData)
|
|
||||||
|
|
||||||
const username = userData.username
|
const username = userData.username
|
||||||
const token = userData.token
|
const token = userData.token
|
||||||
|
|
||||||
|
@ -38,7 +38,10 @@ Attachment.scanTextForWebsites = (userId, noteId, noteText) => {
|
|||||||
|
|
||||||
//Find all URLs in text
|
//Find all URLs in text
|
||||||
const urlPattern = /(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[A-Z0-9+&@#/%=~_|$])/igm
|
const urlPattern = /(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[A-Z0-9+&@#/%=~_|$])/igm
|
||||||
let foundUrls = noteText.match(urlPattern)
|
let allUrls = noteText.match(urlPattern)
|
||||||
|
|
||||||
|
//Remove all duplicates
|
||||||
|
let foundUrls = [...new Set(allUrls)]
|
||||||
|
|
||||||
//Go through each attachment, check for existing URLs
|
//Go through each attachment, check for existing URLs
|
||||||
attachments.forEach(attachment => {
|
attachments.forEach(attachment => {
|
||||||
@ -54,7 +57,7 @@ Attachment.scanTextForWebsites = (userId, noteId, noteText) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
//No newly scraped URLs, resolve with looked up attachment text
|
//No newly scraped URLs, resolve with looked up attachment text
|
||||||
if(foundUrls.length == 0){
|
if(foundUrls == null || foundUrls.length == 0){
|
||||||
resolve(solrAttachmentText)
|
resolve(solrAttachmentText)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,12 +169,14 @@ Attachment.processUrl = (userId, noteId, url) => {
|
|||||||
|
|
||||||
let finalWords = []
|
let finalWords = []
|
||||||
for(let i=0; i<15; i++){
|
for(let i=0; i<15; i++){
|
||||||
if(sortable[i][0]){
|
if(sortable[i] && sortable[i][0]){
|
||||||
finalWords.push(sortable[i][0])
|
finalWords.push(sortable[i][0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
desiredSearchText += finalWords.join(', ')
|
desiredSearchText += finalWords.join(', ')
|
||||||
|
console.log('TexT Scraped')
|
||||||
|
console.log(desiredSearchText)
|
||||||
|
|
||||||
const created = Math.round((+new Date)/1000)
|
const created = Math.round((+new Date)/1000)
|
||||||
|
|
||||||
|
@ -75,7 +75,20 @@ Notes.update = (userId, noteId, noteText, fancyInput, color) => {
|
|||||||
|
|
||||||
Notes.delete = (userId, noteId) => {
|
Notes.delete = (userId, noteId) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
//Create new note, return created or finger your butt
|
// DELETE FROM notes WHERE notes.id = 290 AND notes.user = 61;
|
||||||
|
// DELETE FROM attachment WHERE attachment.note_id = 290 AND attachment.user_id = 61;
|
||||||
|
// DELETE FROM notes_tags WHERE notes_tags.note_id = 290 AND notes_tags.user_id = 61;
|
||||||
|
db.promise().query('DELETE FROM notes WHERE notes.id = ? AND notes.user = ?', [noteId,userId])
|
||||||
|
.then((rows, fields) => {
|
||||||
|
db.promise().query('DELETE FROM attachment WHERE attachment.note_id = ? AND attachment.user_id = ?', [noteId,userId])
|
||||||
|
.then((rows, fields)=> {
|
||||||
|
db.promise().query('DELETE FROM notes_tags WHERE notes_tags.note_id = ? AND notes_tags.user_id = ?', [noteId,userId])
|
||||||
|
.then((rows, fields)=> {
|
||||||
|
console.log('All Deleted')
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,9 +166,10 @@ Notes.search = (userId, searchQuery, searchTags) => {
|
|||||||
|
|
||||||
//Default note lookup gets all notes
|
//Default note lookup gets all notes
|
||||||
let noteSearchQuery = `
|
let noteSearchQuery = `
|
||||||
SELECT notes.id, SUBSTRING(text, 1, 200) as text, updated, color
|
SELECT notes.id, SUBSTRING(notes.text, 1, 400) as text, updated, color, count(distinct notes_tags.id) as tag_count, count(distinct attachment.id) as attachment_count
|
||||||
FROM notes
|
FROM notes
|
||||||
LEFT JOIN notes_tags ON (notes.id = notes_tags.note_id)
|
LEFT JOIN notes_tags ON (notes.id = notes_tags.note_id)
|
||||||
|
LEFT JOIN attachment ON (notes.id = attachment.note_id AND attachment.attachment_type = 1)
|
||||||
WHERE user = ?`
|
WHERE user = ?`
|
||||||
let searchParams = [userId]
|
let searchParams = [userId]
|
||||||
|
|
||||||
@ -193,16 +207,30 @@ Notes.search = (userId, searchQuery, searchTags) => {
|
|||||||
noteIds.push(note.id)
|
noteIds.push(note.id)
|
||||||
|
|
||||||
//Attempt to pull string out of first tag in note
|
//Attempt to pull string out of first tag in note
|
||||||
let reg = note.text.match(/<([\w]+)[^>]*>(.*?)<\/\1>/)
|
let reg = note.text.match(/<([\w]+)[^>]*>(.*?)<\/\1>/g)
|
||||||
if(reg != null){
|
|
||||||
note.text = reg[2]
|
//Pull out first html tag contents, that is the title
|
||||||
|
if(reg != null && reg[0]){
|
||||||
|
note.title = reg[0] //First line from HTML
|
||||||
|
} else {
|
||||||
|
note.title = note.text //Entire note
|
||||||
}
|
}
|
||||||
|
|
||||||
//Return all notes with HTML tags pulled out
|
//Clean up html title
|
||||||
note.text = note.text
|
note.title = note.title
|
||||||
.replace(/&[#A-Za-z0-9]+;/g,'') //Rip out all HTML entities
|
.replace(/&[#A-Za-z0-9]+;/g,'') //Rip out all HTML entities
|
||||||
.replace(/<[^>]+>/g, '') //Rip out all HTML tags
|
.replace(/<[^>]+>/g, '') //Rip out all HTML tags
|
||||||
|
|
||||||
|
//Generate Subtext
|
||||||
|
if(note.text != '' && note.title != ''){
|
||||||
|
note.subtext = note.text
|
||||||
|
.replace(/&[#A-Za-z0-9]+;/g,' ') //Rip out all HTML entities
|
||||||
|
.replace(/<[^>]+>/g, ' ') //Rip out all HTML tags
|
||||||
|
.replace(/\s+/g, ' ') //Remove all whitespace
|
||||||
|
.substring(note.title.length + 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
note.note_highlights = []
|
note.note_highlights = []
|
||||||
note.attachment_highlights = []
|
note.attachment_highlights = []
|
||||||
note.tag_highlights = []
|
note.tag_highlights = []
|
||||||
|
@ -24,6 +24,11 @@ router.post('/get', function (req, res) {
|
|||||||
.then( data => res.send(data) )
|
.then( data => res.send(data) )
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.post('/delete', function (req, res) {
|
||||||
|
Notes.delete(userId, req.body.noteId)
|
||||||
|
.then( data => res.send(data) )
|
||||||
|
})
|
||||||
|
|
||||||
router.post('/create', function (req, res) {
|
router.post('/create', function (req, res) {
|
||||||
Notes.create(userId, req.body.title)
|
Notes.create(userId, req.body.title)
|
||||||
.then( id => res.send({id}) )
|
.then( id => res.send({id}) )
|
||||||
|
Loading…
Reference in New Issue
Block a user