Remove TinyMce Added Squire

This commit is contained in:
Max G
2020-02-01 22:21:22 +00:00
parent 68effaa5c3
commit 133a86e09e
91 changed files with 6538 additions and 10672 deletions

View File

@@ -8,6 +8,7 @@
<link rel="shortcut icon" href="/api/static/assets/favicon.ico" type="image/x-icon"/>
<meta name="theme-color" content="#000" />
<link rel="manifest" href="/api/static/assets/manifest.webmanifest">
<title>Notes</title>
</head>

View File

@@ -13,8 +13,10 @@
"dependencies": {
"axios": "^0.18.0",
"es6-promise": "^4.2.6",
"pell": "^1.0.6",
"postcss-loader": "^2.1.6",
"raw-loader": "^0.5.1",
"socket.io-client": "^2.3.0",
"vue": "^2.5.2",
"vue-router": "^3.0.1",
"vuex": "^3.1.0"

View File

@@ -10,6 +10,8 @@
<script>
// import io from 'socket.io-client'
export default {
components: {
'global-site-menu': require('@/components/GlobalSiteMenu.vue').default,
@@ -21,6 +23,12 @@ export default {
},
beforeCreate: function(){
// const socket = io({ path:'/socket' });
const socket = this.$io
socket.on('connect', () => {
this.$store.commit('setSocketIoSocket', socket.id)
})
//Detect if user is on a mobile browser and set a flag in store
this.$store.commit('detectIsUserOnMobile')
@@ -39,7 +47,7 @@ export default {
},
mounted: function(){
},
computed: {
loggedIn () {

View File

@@ -1,140 +1,276 @@
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(/static/fonts/roboto-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(/static/fonts/roboto-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(/static/fonts/roboto-latin-bold.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(/static/fonts/roboto-latin-bold.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
:root {
--background_color: #fff;
--text_color: #3d3d3d;
--outline_color: rgba(34,36,38,.15);
--border_color: rgba(34,36,38,.20);
--background_color: #fff;
--text_color: #3d3d3d;
--outline_color: rgba(34,36,38,.15);
--border_color: rgba(34,36,38,.20);
}
div.ui.basic.segment.no-fluf-segment {
margin-top: 0px;
}
margin-top: 0px;
}
/* OVERWRITE DEFAULT SEMANTIC STYLES FOR CUSTOM/NIGHT MODES*/
body{
color: var(--text_color);
background-color: var(--background_color);
background-color: var(--background_color);
font-family: 'Roboto', 'Helvetica Neue', Arial, Helvetica, sans-serif;
}
.ui.form input:not([type]),
.ui.form input:not([type]):focus,
.ui.form textarea:not([type]),
.ui.form textarea:not([type]):focus {
color: var(--text_color);
background-color: var(--background_color);
border-color: var(--border_color);
color: var(--text_color);
background-color: var(--background_color);
border-color: var(--border_color);
}
.ui.basic.label, .ui.header, .ui.header div.sub.header {
color: var(--text_color);
background-color: var(--background_color);
border-color: var(--border_color);
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;
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;
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;
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;
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;
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*/
/* Styles for public display pages */
.fun {
color: rgba(0, 0, 0, 0.87);
color: var(--text_color);
color: rgba(0, 0, 0, 0.87);
color: var(--text_color);
}
.fun h1 {
font-size: 2em;
font-size: 2em;
}
.fun h2 {
font-size: 1.9em;
font-size: 1.9em;
}
.fun h3 {
font-size: 1.7em;
font-size: 1.7em;
}
.fun p {
/*font-size: 1.5em;*/
/*font-size: 1.5em;*/
}
.fun blockquote {
border-left: 5px solid cornflowerblue;
padding-left: 25px;
margin-left: 5px;
border-left: 5px solid cornflowerblue;
padding-left: 25px;
margin-left: 5px;
}
/* Styles for public display pages */
.note-status-indicator {
position: absolute;
width: 100px;
padding: 16px 0;
box-sizing: border-box;
text-align: center;
color: var(--text_color);
bottom: 0;
right: 0;
z-index: 100;
cursor: pointer;
a:hover {
text-decoration: underline;
}
.note-status-indicator {
position: absolute;
width: 100px;
padding: 16px 0;
box-sizing: border-box;
text-align: center;
color: var(--text_color);
bottom: -13px;
right: -7px;
z-index: 100;
cursor: pointer;
}
/* squire text styles */
.squire-box {
border: none;
height: calc(100% - 60px);
box-sizing: border-box;
padding: 10px 15px 40px;
background: transparent;
overflow-x: scroll;
/*color: var(--text_color);*/
font-size: 1.2em;
line-height: 1.5em;
word-wrap: break-word;
border-bottom: 1px solid #ccc;
}
/*Makes the first line real big */
.squire-box p:first-child {
font-size: 1.4em;
line-height: 1.7em;
}
.squire-box:focus {
outline: none;
}
.squire-box span.size {
line-height: 1.3em;
}
.squire-box a {
cursor: pointer;
}
.note-card-text i,
.squire-box i {
padding: 0.5em 0.99em;
border: 1px solid #CCC;
margin: 1px;
border-radius: 9px;
display: inline-block;
}
.squire-box p {
margin-bottom: 0;
}
.squire-box blockquote {
margin: 0;
padding: 0.8em;
border-left: 2px solid blue;
}
.note-card-text img {
max-width:100%;
height: auto;
max-height: 200px;
}
.squire-box img {
max-width:100%;
height: auto;
}
.note-card-text li > p,
.squire-box p,
.squire-box li > p {
margin-bottom: 0;
}
.note-card-text ul > li,
.squire-box ul > li {
position: relative;
list-style-type: none;
}
.note-card-text ul > li:before,
.squire-box ul > li:before {
content: "\f111";
font-family: 'Icons';
backface-visibility: hidden;
font-style: normal;
font-weight: normal;
text-decoration: inherit;
text-align: center;
line-height: 1.4em;
font-size: 0.75em;
height: 17px;
width: 17px;
display: inline-block;
position: absolute;
left: -30px;
/*border: 2px solid #444;*/
/*border-radius: 4px;*/
bottom: 0;
top: 4px;
cursor: pointer;
opacity: 0.7;
}
ul > li.active:before {
font-family: 'Icons';
content: "\f058";
color: #21BA45;
opacity: 1;
}
/* adjust checkboxes for mobile. Make them a little bigger, easier to click */
@media only screen and (max-width: 740px) {
.note-card-text ul > li,
.squire-box ul > li {
min-height: 30px;
}
.note-card-text ul > li:before,
.squire-box ul > li:before {
content: "\f111";
font-family: outline-icons;
height: 24px;
width: 24px;
left: -40px;
bottom: 0;
top: 0px;
cursor: pointer;
line-height: 0.9em;
font-size: 1.4em;
}
ul > li.active:before {
font-family: 'Icons';
content: "\f058";
color: #21BA45;
opacity: 1;
}
}
.clickable {
cursor: pointer;
}
.relative {
position: relative;
position: relative;
}
.float-right {
float: right;
float: right;
}
.textarea-height {
height: calc(100% - 90px);
height: calc(100% - 90px);
}
.mobile-textarea-height {
height: 100%;
height: 100%;
}
.ui.white.button {
background: #FFF;
}
.input-floating-button {
position: absolute;
top: 19px;
transform: translateY(-50%);
right: 1px;
position: absolute;
top: 19px;
transform: translateY(-50%);
right: 1px;
}
.fade-in-fwd {
@@ -147,12 +283,12 @@ div.ui.basic.green.label {
* ----------------------------------------
*/
@keyframes fade-in-fwd {
0% {
transform: translateZ(-80px);
opacity: 0;
}
100% {
transform: translateZ(0);
opacity: 1;
}
0% {
transform: translateZ(-80px);
opacity: 0;
}
100% {
transform: translateZ(0);
opacity: 1;
}
}

4995
client/src/assets/squire.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,81 +2,125 @@
.attachment-display-card {
width: 100%;
padding: 0 0 20px;
padding: 10px;
display: inline-block;
border: 1px solid;
border-color: var(--border_color);
border-radius: 4px;
margin: 0 0 15px;
max-height: 10000px;
}
.attachment-image {
width: 100%;
max-width: 150px;
float: left;
margin: 0 15px 0 0;
max-width: 100%;
height: auto;
max-height: 300px;
}
.image-placeholder {
width: 150px;
height: 60px;
border: 1px solid #DDD;
float: left;
margin: 0 15px 0 0;
width: 100%;
height: 100%;
max-height: 100px;
}
.image-placeholder:after {
content: 'No Image';
display: block;
width: 20px;
height: 20px;
background:
green;
position: absolute;
top: 0;
left: 0;
}
.text {
width: 100%;
/*height: 100%;*/
background: transparent;
border: none;
border-top: 1px solid;
border-bottom: 1px solid;
font-size: 1.4em;
margin: 0 0 10px;
line-height: 1.4em;
border-color: var(--border_color);
font-size: 1.2em;
line-height: 1.3em;
/*margin: 0 0 10px;*/
padding: 10px 0 10px;
color: var(--text_color);
overflow: hidden;
resize: none;
/*transition: height 0.4s ease; This breaks the resize */
}
.link {
font-size: 1.4em;
margin: 0 0 5px;
margin: 20px 0 20px;
display: inline-block;
white-space:nowrap;
white-space: nowrap;
overflow:hidden;
text-overflow: ellipsis;
width: calc(100% - 180px);
width: 100%;
line-height: 1.4em;
}
.flip-out {
animation: flip-out-hor-top 0.5s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
overflow: hidden;
transition: max-height 0.3s ease;
max-height: 0;
}
@keyframes flip-out-hor-top {
0% {
transform: rotateX(0);
opacity: 1;
}
100% {
transform: rotateX(70deg);
opacity: 0;
}
}
</style>
<template>
<div class="attachment-display-card">
<div class="attachment-display-card" :class="{ 'flip-out':!unfolded }" v-if="visible">
<div class="ui stackable grid">
<!-- image and text -->
<div class="six wide center aligned middle aligned column">
<a :href="linkUrl" target="_blank" >
<img v-if="item.file_location" class="attachment-image" :src="`/api/static/thumb_${item.file_location}`">
<span v-else>
<img class="image-placeholder" loading="lazy" src="/api/static/assets/marketing/void.svg">
No Image
</span>
</a>
</div>
<input class="text" v-on:blur="saveIt()" v-model="text"></input>
<div class="ten wide column">
<textarea ref="edit" class="text" v-on:blur="saveIt()" v-on:keyup="checkKeyup" v-model="text"></textarea>
<div v-if="item.attachment_type == 1">
<div class="image-holder" v-if="item.file_location">
<a v-if="item.file_location" :href="item.url" target="_blank">
<img class="attachment-image" :src="`/api/static/${item.file_location}`">
</a>
<!-- link -->
<a class="link" :href="linkUrl" target="_blank">{{linkText}}</a>
<!-- Buttons -->
<div class="ui small compact basic button" v-on:click="openNote">
<i class="file icon"></i>
Open Note
</div>
<div v-else class="image-placeholder"></div>
<a class="link" v-if="item.url" :href="item.url" target="_blank">{{item.url}}</a>
</div>
<div v-if="item.attachment_type == 2">
<div class="image-holder" v-if="item.file_location">
<a v-if="item.file_location && item.type != 1" :href="`/api/static/${item.file_location}`" target="_blank">
<img class="attachment-image" :src="`/api/static/${item.file_location}`">
</a>
<div class="ui small compact basic button" v-on:click="openEditAttachments"
:class="{ 'disabled':this.searchParams.noteId }">
<i class="folder icon"></i>
Note Attachments
</div>
<div class="ui small compact basic icon button" v-on:click="deleteAttachment">
<i v-if="!working" class="trash alternate outline icon"></i>
<i v-if="working" class="purple spinner loading icon icon"></i>
</div>
<div v-else class="image-placeholder"></div>
<a class="link" v-if="item.file_location && item.type != 1" :href="`/api/static/${item.file_location}`" target="_blank">Download</a>
</div>
<div class="ui small compact basic button" v-on:click="openNote">
Open Note
</div>
</div>
</div>
</template>
<script>
@@ -84,23 +128,74 @@
export default {
props: [ 'item' ],
props: [ 'item', 'searchParams' ],
data: function(){
return {
things: [],
text: '',
type: null,
linkText: 'Link',
linkUrl:null,
unfolded:true,
visible: true,
working: false,
}
},
beforeCreate: function(){
},
mounted: function(){
this.text = this.item.text
this.type = this.item.attachment_type
//1 = URL, 2 = image, >= 3 files
if(this.type == 1 && this.item.url != null){
this.linkText = this.item.url
this.linkUrl = this.item.url
}
if(this.type == 2){
this.linkText = 'Download'
this.linkUrl = `/api/static/${this.item.file_location}`
}
this.$nextTick(() => {
this.checkKeyup()
})
},
methods: {
checkKeyup(){
let elm = this.$refs.edit
if(elm){
elm.style.height = '0'
elm.style.height = elm.scrollHeight +'px'
}
},
openNote(){
const noteId = this.item.note_id
this.$router.push('/notes/open/'+noteId)
},
openEditAttachments(){
const noteId = this.item.note_id
this.$router.push('/attachments/note/'+noteId)
},
deleteAttachment(){
//No double clicks
if(this.working){ return }
this.working = true
axios.post('/api/attachment/delete', {'attachmentId':this.item.id})
.then( ({data}) => {
if(data){
this.unfolded = false
setTimeout( () => {
this.visible = false
}, 600)
}
})
},
saveIt(){
//Don't save text if it didn'th change

View File

@@ -1,34 +1,68 @@
<template>
<div>
<div class="color-picker" :style="{ 'background-color':allStyles['noteBackground'], 'color':allStyles['noteText']}">
<div class="ui grid">
<div class="five wide column">
<p>Note Color</p>
<div v-for="color in colors"
class="color-button"
:style="{ backgroundColor:color }"
v-on:click="chosenColor(color)"
></div>
</div>
<div class="six wide column">
<p>Note Icon
<span v-if="allStyles.noteIcon" >
<i :class="`large ${allStyles.noteIcon} icon`" :style="{ 'color':allStyles.iconColor }"></i>
</span>
</p>
<div v-for="icon in icons" class="icon-button" v-on:click="chosenIcon(icon)" >
<i :class="`large ${icon} icon`" :style="{ 'color':allStyles.iconColor }"></i>
<div class="floating-buttons">
<div class="ui basic segment">
<div class="ui fluid buttons">
<div class="ui compact button" v-on:click="closeThisBitch">
<i class="close icon"></i>
Close
</div>
<div class="ui compact button" v-on:click="clearStyles">
<i class="refresh icon"></i>
Clear All Styles
</div>
</div>
<div class="ui compact fluid button" v-on:click="clearStyles">Clear All Styles</div>
</div>
<div class="five wide column">
<p>Icon Color</p>
<div v-for="color in colors"
class="color-button"
:style="{ backgroundColor:color }"
v-on:click="chooseIconColor(color)"
></div>
</div>
</div>
<div class="ui basic segment">
<div class="ui grid">
<div class="row">
<div class="sixteen wide column">
<br>
<p>Note Color</p>
<div v-for="color in getReducedColors()"
class="color-button"
:style="{ backgroundColor:color }"
v-on:click="chosenColor(color)"
></div>
</div>
</div>
<div class="row">
<div class="sixteen wide column">
<p>Note Icon
<span v-if="allStyles.noteIcon" >
<i :class="`large ${allStyles.noteIcon} icon`" :style="{ 'color':allStyles.iconColor }"></i>
</span>
</p>
<div v-for="icon in icons" class="icon-button" v-on:click="chosenIcon(icon)" >
<i :class="`large ${icon} icon`" :style="{ 'color':allStyles.iconColor }"></i>
</div>
</div>
</div>
<div class="row">
<div class="sixteen wide column">
<p>Icon Color</p>
<div v-for="color in getReducedColors()"
class="color-button"
:style="{ backgroundColor:color }"
v-on:click="chooseIconColor(color)"
>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="shade-boy" v-on:click="closeThisBitch"></div>
</div>
</template>
@@ -55,9 +89,33 @@
this.allStyles = this.styleObject
},
methods: {
getReducedColors(){
let reduced = []
this.colors.forEach((color,i) => {
if(i%20 <= 10){
return
}
let mod = (i % 10)+1 //1 - 10
let lines = [3, 5, 8, 9, 10]
if(lines.includes(mod)){
reduced.push(color)
}
})
reduced.push("#000")
return reduced
},
clearStyles(){
this.$emit('changeColor', this.blankStyle)
},
closeThisBitch(){
this.$emit('close')
},
chosenColor(inColor){
//Set not background to color that was chosen
@@ -99,33 +157,55 @@
}
</script>
<style type="text/css" scoped>
.shade-boy {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 250;
cursor: pointer;
}
.floating-buttons {
position: fixed;
top: 0;
z-index: 444;
right: 20px;
left: calc(30% + 10px)
}
.color-picker {
color: var(--text_color);
background-color: var(--background_color);
position: absolute;
position: fixed;
/*height: 100px;*/
top: 37px;
top: 0;
right: 0;
left: 30%;
bottom: 0;
padding: 10px;
border-radius: 5px;
left: -63px;
z-index: 300;
border: 1px solid;
z-index: 500;
border-left: 1px solid;
border-color: var(--border_color) !important;
width: 750px;
overflow-x: scroll;
}
.icon-button {
height: 30px;
width: 30px;
height: 40px;
width: 14.2%;
display: inline-block;
cursor: pointer;
font-size: 1.3em;
}
.color-button {
height: 20px;
width: 20px;
display: inline-block;
height: 50px;
width: 20%;
display: block;
cursor: pointer;
float: left;
}
</style>

View File

@@ -2,21 +2,20 @@
.hidden-up {
opacity: 0;
position: absolute;
top: -500px;
top: -50000px;
}
</style>
<template>
<div class="ui small compact basic icon button clickable">
<form>
<label :for="`upfile-${noteId}`" class="clickable">
<i class="upload icon"></i>
Upload File <span v-if="uploadPercentage != 0">{{uploadPercentage}}%</span>
</label>
<input class="hidden-up" type="file" :id="`upfile-${noteId}`" ref="file" v-on:change="handleFileUpload()" />
</form>
<form>
<label :for="`upfile-${noteId}`" class="clickable">
<i class="upload icon"></i>
<span v-if="uploadPercentage != 0">{{uploadPercentage}}%</span>
<span v-else>Upload</span>
</label>
<input class="hidden-up" type="file" :id="`upfile-${noteId}`" ref="file" v-on:change="handleFileUpload()" />
<!-- <button v-if="file" v-on:click="uploadFileToServer()">Submit</button> -->
</div>
</form>
</template>
<script>
@@ -50,37 +49,26 @@
}
}
).then(results => {
this.uploadPercentage = 0
this.uploadPercentage = 'Working'
this.file = null
console.log('SUCCESS!!');
console.log('File upload results')
console.log(results.data)
// console.log('File upload results')
// console.log(results.data)
const name = results.data.fileName
const location = results.data.fileLocation
const location = results.data.goodFileName
if(name && location){
const imageCode = `<img alt="yup" height="200px" src="/api/static/${location}">`
this.uploadPercentage = 0
//put cursor at the bottom of the window
tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.getBody(), true);
tinyMCE.activeEditor.selection.collapse(false);
tinymce.activeEditor.execCommand('mceInsertContent', false, imageCode)
const imageCode = `<img alt="image" src="/api/static/${location}">`
this.$bus.$emit('new_file_upload', {noteId: this.noteId, imageCode})
}
//try and stick that file into the active editor
// tinyMCE.execCommand(
// 'mceInsertContent',
// false,
// '<img alt="Smiley face" height="42" width="42" src="' + src + '"/>'
// );
})
.catch(results => {
this.uploadPercentage = 0
console.log('FAILURE!!');
})
},
handleFileUpload() {

View File

@@ -21,12 +21,15 @@
.menu-item {
color: #fff;
padding: 0.8em 0px 0.8em 0.8em;
padding: 0.8em 0px 0.8em 10px;
display: inline-block;
width: 100%;
font-size: 1.15em;
box-sizing: border-box;
}
.menu-item i.icon {
margin-right: 10px;
}
.sub {
padding-left: 20px;
}
@@ -40,6 +43,7 @@
}
.menu-button:hover {
background-color: #534c68;
text-decoration: none;
}
.router-link-active i {
@@ -92,12 +96,20 @@
<div class="top-menu-bar" v-if="(collapsed || mobile) && !menuOpen">
<div class="ui grid">
<div class="three wide column">
<div class="seven wide column">
<div class="ui large basic compact icon button" v-on:click="collapseMenu">
<i class="green bars icon"></i>
</div>
<router-link class="ui large basic compact icon button" to="/notes" v-on:click.native="emitReloadEvent()">
<i class="green home icon"></i>
</router-link>
<div v-on:click="toggleNightMode" class="ui large basic compact icon button">
<i class="green moon outline icon"></i>
</div>
</div>
<div class="ten wide center aligned column">
<div class="six wide center aligned column">
<img v-if="!loggedIn" src="/api/static/assets/favicon.ico" alt="logo" />
<search-input v-if="loggedIn"></search-input>
</div>
@@ -126,7 +138,7 @@
<div class="menu-section">
<div class="menu-item menu-button" v-on:click="collapseMenu">
<i class="caret square left outline icon"></i>
<i class="angle left icon"></i>
</div>
</div>
@@ -144,16 +156,22 @@
<i class="file icon"></i>Notes
</router-link>
<div>
<div class="menu-item sub">Show Only <i class="caret down icon"></i></div>
<div v-on:click="updateFastFilters(0)" class="menu-item menu-button sub"><i class="grey linkify icon"></i>Links</div>
<div v-on:click="updateFastFilters(1)" class="menu-item menu-button sub"><i class="grey tags icon"></i>Tags</div>
<div v-on:click="updateFastFilters(2)" class="menu-item menu-button sub"><i class="grey archive icon"></i>Archived</div>
<!-- <div class="menu-item sub">Show Only <i class="caret down icon"></i></div> -->
<!-- <div v-on:click="updateFastFilters(0)" class="menu-item menu-button sub"><i class="grey linkify icon"></i>Links</div> -->
<!-- <div v-on:click="updateFastFilters(1)" class="menu-item menu-button sub"><i class="grey tags icon"></i>Tags</div> -->
</div>
</div>
<div class="menu-section" v-if="loggedIn">
<div v-on:click="updateFastFilters(2)" class="menu-item menu-button">
<i class="archive icon"></i>Archived
</div>
</div>
<div class="menu-section" v-if="loggedIn">
<router-link class="menu-item menu-button" exact-active-class="active" to="/attachments">
<i class="folder icon"></i>Attachments
<i class="folder icon"></i>Files
</router-link>
</div>
@@ -176,9 +194,9 @@
<div class="menu-section">
<div v-on:click="toggleNightMode" class="menu-item menu-button">
<span v-if="$store.getters.getIsNightMode">
<i class="toggle on icon"></i>Dark Theme</span>
<i class="moon outline icon"></i>Light Theme</span>
<span v-else>
<i class="toggle off icon"></i>Dark Theme</span>
<i class="moon outline icon"></i>Dark Theme</span>
</div>
</div>
@@ -266,7 +284,6 @@
},
toggleNightMode(){
this.$store.commit('toggleNightMode')
this.$bus.$emit('toggle_night_mode')
},
ucWords(str){
return (str + '')

View File

@@ -4,7 +4,7 @@
id="InputNotes"
class="master-note-edit"
@keyup.esc="close"
:class="[{'size-down':(sizeDown == true)}, 'position-'+position ]"
:class="[{'size-down':(sizeDown == true), 'padded-bottom':extraToolbarsVisible }, 'position-'+position ]"
:style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}"
>
@@ -15,64 +15,83 @@
</div>
</div>
<span class="note-status-indicator" v-on:click="save()">{{statusText}}</span>
<div class="note-menu">
<div class="nm-button" v-on:click="close">
<i class="close icon"></i>
</div>
<div class="nm-button" v-on:click="toggleList('ol')">
<i class="list ol icon"></i>
</div>
<div class="nm-button" v-on:click="toggleList('ul')">
<i class="tasks icon"></i>
</div>
<div class="nm-button" v-on:click="toggleBold()">
<i class="bold icon"></i>
</div>
<div class="nm-button" v-on:click="toggleItalic()">
<i class="quote left icon"></i>
</div>
<div class="nm-button" v-on:click="modifyFont('2.286em') ">
<i class="text height icon"></i>
</div>
<div v-if="usersOnNote > 1" class="nm-button">
<i class="green user circle icon"></i>{{usersOnNote}}
</div>
<div class="tinymce-container">
<textarea :id="noteid+'-tinymce-editor'">{{noteText}}</textarea>
</div>
<div id="squire-id" class="squire-box" ref="squirebox"></div>
<!-- && this.$store.getters.getIsUserOnMobile -->
<span class="note-status-indicator" v-on:click="save()" v-if="statusText != 'Saved' && $store.getters.getIsUserOnMobile">
<div class="ui green button">{{statusText}}</div>
</span>
<color-picker
v-if="colorPickerVisible"
:location="colorPickerLocation"
@changeColor="onChangeColor"
:style-object="styleObject"
@changeColor="onChangeColor"
@close="onCloseColorChanger"
:style-object="styleObject"
/>
<div v-if="$store.getters.getIsNoteSettingsOpen">
<div class="all-settings">
<div class="ui grid">
<div class="row">
<div class="sixteen wide column">
<!-- Note options on the bottom of note -->
<div class="all-settings" :class="{ 'low-settings':!extraToolbarsVisible }">
<note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/><br>
<div class="note-menu">
<!-- close button -->
<div class="ui small compact basic icon button" v-on:click="$store.commit('toggleNoteSettingsPane')">
<i class="close icon"></i> Close Settings
</div>
<!-- Pin Button -->
<div @click="onToggleArchived" class="ui small compact basic icon button">
<i class="archive icon" :class="{green:(archived == 1)}"></i>
{{(archived == 1)?'Archived':'Archive'}}
</div>
<!-- archive button -->
<div @click="onTogglePinned" class="ui small compact basic icon button">
<i class="pin icon" :class="{green:(pinned == 1)}"></i>
{{(pinned == 1)?'Pinned':'Pin'}}
</div>
<!-- colors button -->
<span class="relative" v-on:click="showColorPicker">
<span class="ui small compact basic icon button">
<i class="paint brush icon"></i>
Colors
</span>
</span>
<!-- attachment button -->
<div v-if="attachmentCount > 0" class="ui small compact basic icon button" v-on:click="openEditAttachment">
<i class="folder icon"></i> Attachments
</div>
<!-- file upload button -->
<file-upload-button :noteId="noteid" />
</div>
</div>
<!-- <note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/><br> -->
<!-- Pin Button -->
<div @click="onToggleArchived" class="nm-button">
<i class="archive icon" :class="{green:(archived == 1)}"></i>
{{(archived == 1)?'Archived':'Archive'}}
</div>
<!-- archive button -->
<div @click="onTogglePinned" class="nm-button">
<i class="pin icon" :class="{green:(pinned == 1)}"></i>
{{(pinned == 1)?'Pinned':'Pin'}}
</div>
<!-- colors button -->
<span class="nm-button" v-on:click="showColorPicker">
<i class="paint brush icon"></i>
Colors
</span>
<!-- attachment button -->
<div class="nm-button" v-on:click="openEditAttachment">
<i class="folder icon"></i> Files
</div>
<!-- file upload button -->
<file-upload-button class="nm-button" :noteId="noteid" />
</div>
<!-- <div class="shade" v-on:click="showAllSettings = false"></div> -->
</div>
<!-- <div class="shade" v-on:click="showAllSettings = false"></div> -->
</div>
</template>
@@ -80,6 +99,7 @@
<script>
import axios from 'axios'
const DiffMatchPatch = require('../../../server/helpers/DiffMatchPatch')
export default {
name: 'InputNotes',
@@ -96,11 +116,13 @@
loadingMessage: 'Loading Note',
currentNoteId: 0,
noteText: '',
diffNoteText: '',
statusText: 'Saved',
lastNoteHash: null,
saveDebounce: null, //Prevent save from being called numerous times quickly
updated: 'Never',
editDebounce: null,
emitChangeDebounce: null,
keyPressesCounter: 0, //Determen keys pressed between saves
pinned: 0,
archived: 0,
@@ -116,6 +138,13 @@
//Settings vars
showAllSettings: true,
lastVisibilityState: null,
//All the squire settings
editor: null,
// pastFocusedNode: null,
usersOnNote: 0,
extraToolbarsVisible: true,
}
},
watch: {
@@ -135,136 +164,189 @@
}
},
beforeMount(){
//Load tinymce into the page only do it once
if(document.querySelectorAll('[data-mceload]').length == 0){
let tinyMceIncluder = document.createElement('script')
tinyMceIncluder.setAttribute('src', '/api/static/assets/tinymce/tinymce.min.js')
tinyMceIncluder.setAttribute('data-mceload','loaded')
document.head.appendChild(tinyMceIncluder)
}
this.$bus.$on('new_file_upload', ({noteId, imageCode}) => {
if(this.noteid == noteId && this.editor){
this.editor.moveCursorToEnd()
this.editor.insertHTML(imageCode)
}
})
},
beforeDestroy(){
document.removeEventListener('visibilitychange', this.visibiltyChangeAction)
this.$io.emit('leave_room', this.noteid)
// this.$off() // Remove all event listeners
// this.$bus.$off()
document.removeEventListener('visibilitychange', this.checkForUpdatedNote)
//Trash editor instance on close
this.tinymce.remove()
this.editor.destroy()
this.$bus.$off('new_file_upload')
},
mounted: function() {
//Change TinyMce styles on nightmored change
this.$bus.$on('toggle_night_mode', this.setEditorTextColor )
document.addEventListener('visibilitychange', this.checkForUpdatedNote)
this.$nextTick(() => {
this.loadNote(this.noteid)
//Tell server to push this note into a room
this.$io.emit('join_room', this.noteid)
this.$io.on('update_user_count', userCount => {
this.usersOnNote = userCount
})
//Server will hand deliver diffs from other notes to this one
this.$io.on('incoming_diff', incomingDiffData => {
this.patchText(incomingDiffData)
})
})
},
methods: {
initTinyMce(){
initSquire(){
//Set up squire and load note text
this.editor = new Squire( this.$refs.squirebox, {blockTag: 'p' })
this.setText(this.noteText)
// image_list: [
// {title: 'My image 1', value: 'https://www.tinymce.com/my1.gif'},
// {title: 'My image 2', value: 'http://www.moxiecode.com/my2.gif'}
// ]
//Open links when clicked in editor
this.editor.addEventListener('click', e => {
//Default plugin options for big browsers
let toolbarOptions = 'customCloseButton | mceTogglePinned | forecolor styleselect | bold italic underline | link | bullist numlist | outdent indent table | h | image'
let pluginOptions = 'paste, link, code, lists, table, hr, image'
//Link clicked in editor - open link
if(e.target.nodeName == 'A' && e.target.href){
window.open(e.target.href)
}
//Tweak doc height for mobile
let docHeight = 'calc(100vh - 1px)'
if(this.$store.getters.getIsUserOnMobile){
docHeight = 'calc(100vh - 1px)'
//List Item clicked in editor - toggle link state
if(e.target.nodeName == 'LI'){
toolbarOptions = 'customCloseButton | bullist numlist | mceTogglePinned'
pluginOptions = 'lists'
}
let el = e.target
//setup skin as dark if night mode is enabled
let skin = 'oxide'
if(this.$store.getters.getIsNightMode){
skin = 'oxide-dark'
}
//Adjust ofset by 40 px
let correction = 40
const editorId = '#'+this.noteid+'-tinymce-editor'
let vm = this
//Determine if element was clicked or area before it, before means checkbox was clicked
if (e.offsetX > e.target.offsetLeft - correction) {
//Element was clicked
} else {
//Globally defined included in index HTML
tinymce.init({
selector: editorId,
toolbar: toolbarOptions,
plugins: pluginOptions,
browser_spellcheck: true,
menubar: false,
branding: false,
statusbar: false,
height: docHeight,
skin: skin,
contextmenu: false,
init_instance_callback: this.editorInitCallback,
imagetools_toolbar: "imageoptions",
setup: editor => {
//Add custom buttons to tinymce instance
editor.ui.registry.addButton('customCloseButton', {
text: 'Close',
icon: 'close',
onAction: function (_) {
vm.close()
//Will hide keyboard if clicked, much better for mobile
this.editor.blur()
//Area before element was clicked, they clicked the checkbox
this.onKeyup()
if (el.className === 'active'){
el.className = 'inactive';
} else {
el.className = 'active';
}
})
}
//Add custom buttons to tinymce instance
editor.ui.registry.addButton('mceTogglePinned', {
text: 'Note Settings',
icon: 'settings',
onAction: function (_) {
vm.$store.commit('toggleNoteSettingsPane')
}
})
}
})
this.editor.addEventListener('keydown', event => {
//Prevent new list items from having
this.$nextTick( () => {
//Wait a moment to get item under cursor
let selection = this.editor.getSelection()
let container = selection.commonAncestorContainer
//If user hit enter on a list, make sure the next list item isn't active
if(container.nodeName == 'LI' && event.keyCode == 13 && container.classList){
container.classList.remove('active')
}
})
})
//Bind event handlers
this.editor.addEventListener('keyup', event => this.onKeyup(event) )
//Show and hide additional toolbars
this.editor.addEventListener('focus', e => {
if(this.$store.getters.getIsUserOnMobile){
this.extraToolbarsVisible = false
}
})
this.editor.addEventListener('blur', e => {
this.save()
this.extraToolbarsVisible = true
})
},
editorInitCallback(editor){
//If nothing is selected, select the entire line
selectLineIfNoSelect(){
//Select entire line if range is not set
let selection = this.editor.getSelection()
this.loading = false //Turn off loading screed when editor is loaded
this.tinymce = editor
if(selection.startOffset == selection.endOffset && selection.startContainer == selection.endContainer){
this.setEditorTextColor()
editor
.on('Change', this.onKeyup )
.on('keyup', this.onKeyup )
.on('blur', this.save )
let squireRange = this.editor.createRange(
selection.startContainer, 0,
selection.endContainer, selection.commonAncestorContainer.textContent.length)
this.editor.setSelection(squireRange)
}
},
setEditorTextColor(){
//Only Set editor text color, background is transparent and set on parent element
modifyFont(inSize){
this.selectLineIfNoSelect()
//There may be scenarios where editor has not been set up
if(this.tinymce){
//Set editor color to color from app, change with night mode
this.tinymce.getBody().style.color = getComputedStyle(document.documentElement)
.getPropertyValue('--text_color');
let fontInfo = this.editor.getFontInfo()
//Toggle font size between large and normal
if(fontInfo.size){
this.editor.setFontSize(null)
} else {
this.editor.setFontSize(inSize)
}
},
toggleList(type){
//Overwrite set color if theme is set for note.
if(this.styleObject && this.styleObject.noteText){
this.tinymce.getBody().style.color = this.styleObject.noteText
}
//Undo list if its already a lits
if(this.editor.hasFormat(type)){
this.editor.removeList()
return
}
if(type == 'ol'){
this.editor.makeOrderedList()
}
if(type == 'ul'){
this.editor.makeUnorderedList()
}
},
toggleBold(){
this.selectLineIfNoSelect()
if( this.editor.hasFormat('b') ){
this.editor.removeBold()
} else {
this.editor.bold()
}
},
toggleItalic(){
this.selectLineIfNoSelect()
if( this.editor.hasFormat('i') ){
this.editor.removeItalic()
} else {
this.editor.italic()
}
},
setText(inText){
return this.tinymce.setContent(inText)
this.editor.setHTML(inText)
this.noteText = this.editor._getHTML()
this.diffNoteText = this.editor._getHTML()
},
getText(){
//Return text from tinyMce Editor
return this.tinymce.getContent()
return this.editor.getHTML()
},
showColorPicker(event){
this.colorPickerVisible = !this.colorPickerVisible
@@ -293,13 +375,14 @@
this.lastNoteHash = 0
this.save()
},
onCloseColorChanger(){
this.colorPickerVisible = false
},
onChangeColor(newStyleObject){
//Set new style object for note, page will use some styles, styles will be saved to database
this.styleObject = newStyleObject
this.setEditorTextColor()
this.lastNoteHash = 0 //Update hash to force note update on next save
this.save()
},
@@ -320,11 +403,15 @@
//Set up local data
vm.currentNoteId = noteId
vm.noteText = response.data.text
vm.diffNoteText = response.data.text
vm.updated = response.data.updated
vm.lastNoteHash = vm.hashString(response.data.text)
//Set up note colors
if(response.data.color){
vm.styleObject = JSON.parse(response.data.color) //Load styles json from DB
vm.styleObject = JSON.parse(response.data.color)
}
if(response.data.pinned != null){
@@ -333,8 +420,11 @@
vm.archived = response.data.archived
vm.attachmentCount = response.data.attachment_count
this.loading = false
vm.$nextTick(() => {
this.initTinyMce()
// this.initTinyMce()
this.initSquire()
})
})
@@ -342,9 +432,176 @@
console.log('Could not fetch note')
}
},
onKeyup(){
diffText(){
this.statusText = 'Modified'
// dont emit to one user
if(this.usersOnNote <= 1){
return
}
//Post latest diff to server, server will emit change event to all connected clients
// clearTimeout(this.emitChangeDebounce)
this.emitChangeDebounce = setTimeout(i => {
//caldulate text diff
let oldText = this.diffNoteText
let newText = this.getText()
if(oldText == newText){
return
}
const dmp = new DiffMatchPatch.diff_match_patch()
const diff = dmp.diff_main(oldText, newText)
// dmp.diff_cleanupSemantic(diff)
const patch_list = dmp.patch_make(oldText, newText, diff);
const patch_text = dmp.patch_toText(patch_list);
var patches = dmp.patch_fromText(patch_text);
var results = dmp.patch_apply(patches, oldText);
const computedText = results[0]
//Save computed diff text
this.noteText = computedText
this.diffNoteText = computedText
if(patch_text == ''){
return
}
// console.log(patch_text)
this.$io.emit('note_diff', {
id:this.currentNoteId,
diff:patch_text
})
}, 5)
},
patchText(patch_text){
console.log(patch_text)
//
// Capture x,y of caret and position into string
//
let currentSelection = this.editor.getSelection()
let lineText = currentSelection.startContainer.textContent
console.log(lineText)
let cursorOffset = parseInt(currentSelection.startOffset) //number of characters in
let path = this.xpath(currentSelection.commonAncestorContainer.parentElement)
console.log(path)
//
//Set up text to process diff
//
let currentText = this.editor._getHTML()
const startingLines = (currentText.match(/<br>/g) || '').length + 1
console.log('1')
const dmp = new DiffMatchPatch.diff_match_patch()
var patches = dmp.patch_fromText(patch_text);
var results = dmp.patch_apply(patches, currentText);
let newText = results[0]
console.log('2')
this.noteText = newText
this.diffNoteText = newText
console.log('3')
// this.editor._setHTML(newText)
this.editor.setHTML(newText)
console.log('4')
//
// I user hasn't selected the document, we are done here
// @TODO add code to halt execution
//
const endingLines = (newText.match(/<br>/g) || '').length + 1
// if(this.pastFocusedNode != null || true){
setTimeout( ()=>{
var root = this.editor.getRoot()
//Get node under current x,y on dom (may break on scroll)
// let node = document.elementFromPoint(mouse.x, mouse.y)
let node = this.getElementByXPath(path)
if(node.firstChild){
node = node.firstChild
}
//If the number of lines changed
if(startingLines != endingLines){
//Line diff may be +1 or -1
let lineDiff = endingLines - startingLines
console.log('Line Diff => ', lineDiff)
//Pull out node number from path
var nodeNumber = path.match(/\d+/)
let modifyNode = null
if(nodeNumber.length == 1){
modifyNode = parseInt(nodeNumber[0])
}
path = path.replace(modifyNode, modifyNode + lineDiff )
console.log(path)
let maybeNext = this.getElementByXPath(path)
if(maybeNext && maybeNext.firstChild){
maybeNext = maybeNext.firstChild
}
if(maybeNext && maybeNext.textContent == lineText){
node = maybeNext
console.log('The Node Moved!')
}
}
console.log('Targeting Node')
console.log(node)
//Create and set range
let squireRange = this.editor.createRange(node, cursorOffset)
squireRange.collapse(true)
this.editor.setSelection(squireRange)
console.log('cursor set')
}, 20)
// }
},
xpath(el) {
//Skip things we can't use
if (typeof el == "string") return document.evaluate(el, document, null, 0, null)
if (!el || el.nodeType != 1) return ''
//Anchor xpath using Ids or test-ids
const testId = el.getAttribute('test-id')
if (el.id) return "//*[@id='" + el.id + "']"
//Continue to build path
const sames = [].filter.call(el.parentNode.children, function (x) { return x.tagName == el.tagName })
return this.xpath(el.parentNode) + '/' + el.tagName.toLowerCase() + (sames.length > 1 ? '['+([].indexOf.call(sames, el)+1)+']' : '')
},
getElementByXPath(xpath) {
return new XPathEvaluator()
.createExpression(xpath)
.evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE) .singleNodeValue
},
onKeyup(event){
this.statusText = 'Save'
this.diffText()
//Each note, save after 5 seconds, focus lost or 30 characters typed.
clearTimeout(this.editDebounce)
@@ -364,6 +621,8 @@
//Clear other debounced events to prevent double calling of save
// clearTimeout(this.editDebounce)
// return resolve(true)
//Don't save note if its hash doesn't change
const currentNoteText = this.getText()
if( this.lastNoteHash == this.hashString( currentNoteText )){
@@ -406,9 +665,12 @@
},
checkForUpdatedNote(){
// return
//If user leaves page then returns to page, reload the first batch
if(this.lastVisibilityState == 'hidden' && document.visibilityState == 'visible'){
console.log('Checking for changes is note data')
console.log('Checking for note updates after visibility change.')
const postData = {
noteId:this.currentNoteId,
text:this.getText(),
@@ -435,12 +697,12 @@
hashString(text){
var hash = 0;
if (text.length == 0) {
if (text == null || text.length == 0) {
return hash;
}
//Simplified for speed
return text.length
// return text.length
for (let i = 0; i < text.length; i++) {
let char = text.charCodeAt(i);
@@ -472,42 +734,67 @@
<style type="text/css" scoped>
/* squire note menu button */
.note-menu {
width: 100%;
display: block;
background: #221f2b;
color: white;
overflow: hidden;
border: 1px solid #534c68;
}
.note-menu > .nm-button {
padding: 10px 10px;
cursor: pointer;
text-align: center;
box-sizing: border-box;
font-size: 1.1em;
vertical-align: middle;
/*height: 40px;*/
display: table-cell;
}
.nm-button > i {
font-size: 1.1em;
margin: 0;
}
.nm-button:hover {
background-color: #534c68;
color: white;
}
.nm-button + .nm-button {
border-left: 1px solid #534c68;
}
/* squire styles */
/*Settings manager styles */
.all-settings {
/*border-top: 1px solid #534c68;*/
background: #221f2b;
position: absolute;
bottom: -5px;
right: 10px;
left: 10px;
bottom: 0;
right: 0;
left: 0;
z-index: 99;
border: 1px solid;
background-color: var(--background_color);
border-color: var(--border_color);
/*border: 1px solid;*/
/*background-color: var(--background_color);*/
/*border-color: var(--border_color);*/
box-sizing: border-box;
border-radius: 7px;
box-shadow: 0px 3px 7px 0px rgba(140,140,140,1);
padding: 1em;
/*border-radius: 7px;*/
/*box-shadow: 0px 3px 7px 0px rgba(140,140,140,1);*/
/*padding: 1.2em 0 0;*/
}
.low-settings {
bottom: 0px;
cursor: pointer;
height: 1.4em;
padding-top: 1.5em;
overflow: hidden;
border: 1px solid #534c68;
}
/*End Settings manager styles */
.tinymce-container {
/* Uncomment this to see the */
/*border-bottom: 2px solid green !important;*/
}
.note-top-menu {
width: 100%;
display: inline-block;
height: 37px;
border-left: 3px solid var(--border_color);
}
.note-top-menu .ui.basic.button {
border-radius: 0;
border: none;
border-right: 1px solid var(--border_color);
margin: 0px -2px;
padding-left: 15px;
padding-right: 15px;
}
/* container styles change based on mobile and number of open screens */
.master-note-edit {
@@ -520,6 +807,9 @@
z-index: 1001;
/*overflow-x: scroll;*/
}
.padded-bottom {
padding-bottom: 20px;
}
.loading-note {
position: absolute;
top: 0;

View File

@@ -13,7 +13,7 @@
<!-- Show title and snippet below it -->
<div class="top aligned row" @click.self="onClick(note.id)">
<div class="sixteen wide column overflow-hidden" @click="e => onClick(note.id, e)">
<div class="sixteen wide column overflow-hidden note-card-text" @click="e => onClick(note.id, e)">
<!-- Title display -->
<div v-if="note.title.length > 0"
@@ -190,7 +190,8 @@
border-radius: .28571429rem;
border: 1px solid;
border-color: var(--border_color);
width: calc(33.333% - 10px);
/*width: calc(33.333% - 10px);*/
width: calc(25% - 10px);
/*transition: box-shadow 0.3s;*/
box-sizing: border-box;
cursor: pointer;
@@ -204,6 +205,7 @@
}
.icon-bar {
opacity: 0.8;
margin-top: -2.2rem;
}
.hover-hide {
opacity: 0.0;

View File

@@ -1,5 +1,6 @@
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import Vuex from 'vuex'
@@ -28,7 +29,18 @@ require('./assets/themes/default/assets/fonts/outline-icons.ttf')
require('./assets/themes/default/assets/fonts/outline-icons.woff')
require('./assets/themes/default/assets/fonts/outline-icons.woff2')
require('./assets/squire.js')
//Import socket io, init using nginx configured socket path
import io from 'socket.io-client';
const socket = io({ path:'/socket' });
//integrate connected socket into vue instance
Object.defineProperties(Vue.prototype, {
$io: {
get: () => socket
}
})
@@ -46,7 +58,7 @@ import Helpers from './Helpers'
Vue.use(Vuex)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,

View File

@@ -6,22 +6,35 @@
<h2 class="ui header">
<i class="folder icon"></i>
<div class="content">
Attachments
<div class="sub header">Files and scraped web pages from notes</div>
Files
<div class="sub header">Uploaded Files and Websites from notes.</div>
</div>
</h2>
</div>
<div class="ui segment" v-if="searchParams.noteId">
Showing Attachments for note. <div class="ui button" v-on:click="clearNote">Clear</div>
<div class="sixteen wide column" v-if="searchParams.noteId">
<div class="ui green button" v-on:click="clearNote">
<i class="chevron circle left icon"></i>
Show All Attachments
</div>
<div class="ui green button" v-on:click="openNote">
<i class="file icon"></i>
Open Note
</div>
</div>
<div class="ui basic segment">
<attachment-display
v-for="item in attachments"
:item="item"
:key="item.id"
/>
<div class="sixteen wide column" v-if="searchParams['noteId'] && attachments.length == 0">
<h3>There are no attachments for this note.</h3>
<h3>Attachments are links or files added to the note.</h3>
</div>
<div class="sixteen wide column">
<attachment-display
v-for="item in attachments"
:item="item"
:key="item.id"
:search-params="searchParams"
/>
</div>
</div>
@@ -50,18 +63,30 @@
mounted: function(){
//Mount notes on load if note ID is set
if(this.$route.params && this.$route.params.id){
const inputNoteId = this.$route.params.id
this.searchParams['noteId'] = inputNoteId
}
this.openNoteAttachments()
this.searchAttachments()
},
watch:{
$route (to, from){
//Open or close notes on route change
this.openNoteAttachments()
this.searchAttachments()
}
},
methods: {
openNoteAttachments(){
if(this.$route.params && this.$route.params.id){
const inputNoteId = this.$route.params.id
this.searchParams['noteId'] = inputNoteId
}
},
openNote(){
const noteId = this.searchParams['noteId']
this.$router.push('/notes/open/'+noteId)
},
clearNote(){
this.$router.push('/attachments/')
delete this.searchParams.noteId
this.searchAttachments()
},
searchAttachments (){

View File

@@ -115,6 +115,18 @@
</div>
<!-- found attachments -->
<div class="sixteen wide column" v-if="foundAttachments.length > 0">
<h4><i class="green folder icon"></i> Found in Files ({{ foundAttachments.length }})</h4>
<attachment-display
v-for="item in foundAttachments"
:item="item"
:key="item.id"
:search-params="{}"
/>
</div>
</div>
@@ -135,6 +147,7 @@
'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,
},
data () {
return {
@@ -176,20 +189,14 @@
lastVisibilityState: null,
foundAttachments: []
}
},
beforeMount(){
this.$parent.loginGateway()
//Load tinymce into the page only do it once
if(document.querySelectorAll('[data-mceload]').length == 0){
let tinyMceIncluder = document.createElement('script')
tinyMceIncluder.setAttribute('src', '/api/static/assets/tinymce/tinymce.min.js')
tinyMceIncluder.setAttribute('data-mceload','loaded')
document.head.appendChild(tinyMceIncluder)
}
this.$bus.$on('close_active_note', ({position, noteId}) => {
this.closeNote(position)
this.updateSingleNote(noteId)
@@ -220,6 +227,9 @@
this.searchTerm = sentInSearchTerm
this.search(true, this.batchSize)
.then( () => {
this.searchAttachments()
return this.fetchUserTags()
})
})
@@ -244,7 +254,7 @@
//Close notes when back button is pressed
window.addEventListener('hashchange', this.hashChangeAction)
//update note on visibility change
document.addEventListener('visibilitychange', this.visibiltyChangeAction);
},
@@ -475,6 +485,14 @@
}
})
},
searchAttachments(){
axios.post('/api/attachment/textsearch', {'searchTerm':this.searchTerm})
.then(results => {
console.log('Attachment Results')
console.log(results.data)
this.foundAttachments = results.data
})
},
search(showLoading = true, notesInNextLoad = null, mergeExisting = false){
return new Promise((resolve, reject) => {
@@ -595,6 +613,7 @@
this.searchTerm = ''
this.searchTags = []
this.fastFilters = {}
this.foundAttachments = [] //Remove all attachments
this.$bus.$emit('reset_fast_filters')
//Load initial batch, then tags, then other batch

View File

@@ -83,7 +83,7 @@
checkKeyup(event){
let element = event.target
let padding = 22
let padding = 0
element.style.height = 'auto';
element.style.height = (element.scrollHeight + padding) +'px';

View File

@@ -11,6 +11,7 @@ export default new Vuex.Store({
nightMode: false,
isUserOnMobile: false,
isNoteSettingsOpen: false, //Little note settings pane
socket: null,
},
mutations: {
setLoginToken(state, userData){
@@ -81,7 +82,14 @@ export default new Vuex.Store({
},
toggleNoteSettingsPane(state){
state.isNoteSettingsOpen = !state.isNoteSettingsOpen
},
setSocketIoSocket(state, socket){
//Put socket id in axios headers
axios.defaults.headers.common['socketId'] = socket
state.socket = socket
}
},
getters: {
getUsername: state => {
@@ -103,5 +111,8 @@ export default new Vuex.Store({
getIsNoteSettingsOpen: state => {
return state.isNoteSettingsOpen
},
getSocket: state => {
return state.socket
},
}
})