Compare commits
2 Commits
8d3762e106
...
21f606b480
Author | SHA1 | Date | |
---|---|---|---|
|
21f606b480 | ||
|
b961a69a91 |
@ -8,7 +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">
|
||||
<link rel="manifest" href="/api/static/assets/manifest.json">
|
||||
|
||||
<title>Notes</title>
|
||||
</head>
|
||||
|
@ -55,6 +55,9 @@ body {
|
||||
background-color: var(--background_color);
|
||||
border-color: var(--border_color);
|
||||
}
|
||||
.ui.icon.input > i.icon {
|
||||
color: var(--text_color);
|
||||
}
|
||||
div.ui.basic.green.label {
|
||||
background-color: var(--background_color) !important;
|
||||
}
|
||||
|
@ -98,7 +98,12 @@
|
||||
</div>
|
||||
|
||||
<div class="ten wide column">
|
||||
<textarea ref="edit" class="text" v-on:blur="saveIt()" v-on:keyup="checkKeyup" v-model="text"></textarea>
|
||||
<textarea ref="edit" class="text" v-on:blur="saveIt()" v-on:keyup="checkKeyup"
|
||||
v-model="text"
|
||||
v-on:focus="showSave = true"
|
||||
></textarea>
|
||||
|
||||
<div v-if="showSave" class="ui green button">Save</div>
|
||||
|
||||
<!-- link -->
|
||||
<a class="link" :href="linkUrl" target="_blank">{{linkText}}</a>
|
||||
@ -139,6 +144,7 @@
|
||||
|
||||
unfolded:true,
|
||||
visible: true,
|
||||
showSave: false,
|
||||
|
||||
working: false,
|
||||
}
|
||||
@ -199,6 +205,8 @@
|
||||
},
|
||||
saveIt(){
|
||||
|
||||
this.showSave = false
|
||||
|
||||
//Don't save text if it didn'th change
|
||||
if(this.item.text == this.text){
|
||||
return
|
||||
|
@ -166,7 +166,7 @@
|
||||
|
||||
<div class="menu-section" v-if="loggedIn && $store.getters.totals && $store.getters.totals['totalFiles']">
|
||||
<router-link class="menu-item menu-button" exact-active-class="active" to="/attachments">
|
||||
<i class="folder open outline icon"></i>Files
|
||||
<i class="open folder outline icon"></i>Files
|
||||
<counter class="float-right" number-id="totalFiles" />
|
||||
</router-link>
|
||||
</div>
|
||||
@ -233,11 +233,13 @@
|
||||
this.mobile = this.$store.getters.getIsUserOnMobile
|
||||
this.collapsed = this.$store.getters.getIsUserOnMobile
|
||||
|
||||
// {{ totals['totalNotes'] }}
|
||||
|
||||
if(this.mobile){
|
||||
this.menuOpen = false
|
||||
}
|
||||
|
||||
if(this.loggedIn){
|
||||
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||
}
|
||||
|
||||
},
|
||||
computed: {
|
||||
|
@ -158,12 +158,18 @@
|
||||
Uncheck all Checked items
|
||||
</div>
|
||||
</div>
|
||||
<div class="sixteen wide column">
|
||||
<div class="eight wide column">
|
||||
<div class="ui labeled icon fluid basic button" v-on:click="undoCustom">
|
||||
<i class="undo icon"></i>
|
||||
Undo last change
|
||||
</div>
|
||||
</div>
|
||||
<div class="eight wide column">
|
||||
<div class="ui labeled icon fluid basic button" v-on:click="calculateMath" data-tooltip="Calculates algebra before '='">
|
||||
<i class="calculator icon"></i>
|
||||
Simple Math
|
||||
</div>
|
||||
</div>
|
||||
<div class="sixteen wide column" v-if="rawTextId > 0">
|
||||
<share-note-component
|
||||
:note-id="noteid"
|
||||
@ -561,6 +567,59 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
calculateMath(){
|
||||
//
|
||||
// Find math in note and calculate the outcome
|
||||
//
|
||||
|
||||
//Close menu if user is on mobile, then sort list
|
||||
if(this.$store.getters.getIsUserOnMobile){
|
||||
this.showNoteOptions = false
|
||||
}
|
||||
|
||||
//Fetch the container
|
||||
let container = document.getElementById('squire-id')
|
||||
|
||||
// simple function that trys to evaluate javascript
|
||||
const shittyMath = (string) => {
|
||||
//Remove all chars but math chars
|
||||
const cleanString = String(string).replace(/[a-zA-Z\s]*/g,'')
|
||||
try {
|
||||
return Function('"use strict"; return (' + cleanString + ')')();
|
||||
} catch (error) {
|
||||
console.log('Math Error: ', string)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
//Go through each item, on first level, look for Unordered Lists
|
||||
container.childNodes.forEach( (node) => {
|
||||
|
||||
const line = node.innerText.trim()
|
||||
|
||||
// = sign exists and its the last character in the string
|
||||
if(line.indexOf('=') != -1 && (line.length-1) == line.indexOf('=')){
|
||||
|
||||
//Pull out everything before the formula and try to evaluate it
|
||||
const formula = line.split('=').shift()
|
||||
const output = shittyMath(formula)
|
||||
|
||||
//If its a number and didn't throw an error, update the line
|
||||
if(!isNaN(output) && output != null){
|
||||
|
||||
//Since there is HTML in the line, splice in the number after the = sign
|
||||
let equalLocation = node.innerHTML.indexOf('=')
|
||||
let newLine = node.innerHTML.slice(0, equalLocation+1).trim()
|
||||
newLine += ` ${output}`
|
||||
newLine += node.innerHTML.slice(equalLocation+1).trim()
|
||||
|
||||
//Slam in that new HTML with the output
|
||||
node.innerHTML = newLine
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
},
|
||||
setText(inText){
|
||||
|
||||
this.editor.setHTML(inText)
|
||||
|
@ -66,9 +66,9 @@
|
||||
<delete-button :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }" :note-id="note.id" />
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="note.thumbs">
|
||||
<div class="row" v-if="getThumbs.length > 0">
|
||||
<div class="tiny-thumb-box" v-on:click="openEditAttachment">
|
||||
<img v-for="thumb in note.thumbs.split(',').reverse()" class="tiny-thumb" :src="`/api/static/thumb_${thumb}`">
|
||||
<img v-for="thumb in getThumbs" class="tiny-thumb" :src="`/api/static/thumb_${thumb}`">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -118,6 +118,30 @@
|
||||
iconColor: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getThumbs(){
|
||||
if(!this.note.thumbs){
|
||||
return []
|
||||
}
|
||||
|
||||
let notDisplaying = []
|
||||
|
||||
//Remove images displaying in text from the thumbnails
|
||||
this.note.thumbs.forEach( path => {
|
||||
|
||||
const titleLocation = String(this.note.title).indexOf(path)
|
||||
const subtextLocation = String(this.note.subtext).indexOf(path)
|
||||
|
||||
if(titleLocation != -1 || subtextLocation != -1){
|
||||
return
|
||||
}
|
||||
|
||||
notDisplaying.push(path)
|
||||
})
|
||||
|
||||
return notDisplaying
|
||||
}
|
||||
},
|
||||
beforeMount(){
|
||||
|
||||
this.note = this.data
|
||||
|
@ -87,7 +87,8 @@
|
||||
|
||||
},
|
||||
mounted(){
|
||||
axios.post('/api/attachment/search', {'attachmentType':2})
|
||||
|
||||
axios.post('/api/attachment/search', {'attachmentType':'files', 'setSize':1000})
|
||||
.then( ({data}) => {
|
||||
|
||||
//Sort files into two categories
|
||||
|
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="ui basic segment no-fluf-segment">
|
||||
<div class="ui basic segment no-fluf-segment" ref="content">
|
||||
<div class="ui grid">
|
||||
|
||||
<div class="ui sixteen wide column">
|
||||
<div class="ui twelve wide column">
|
||||
<h2 class="ui header">
|
||||
<i class="folder open outline icon"></i>
|
||||
<div class="content">
|
||||
@ -10,13 +10,42 @@
|
||||
<div class="sub header">Uploaded Files and Websites from notes.</div>
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
<!-- subnav -->
|
||||
<router-link
|
||||
exact-active-class="green"
|
||||
class="ui basic button"
|
||||
to="/attachments">
|
||||
<i class="open folder outline icon"></i>
|
||||
All
|
||||
</router-link>
|
||||
<router-link
|
||||
v-if="$store.getters.totals && $store.getters.totals['linkFiles']"
|
||||
exact-active-class="green"
|
||||
class="ui basic button"
|
||||
to="/attachments/type/links">
|
||||
<i class="linkify icon"></i>
|
||||
Links
|
||||
</router-link>
|
||||
<router-link
|
||||
v-if="$store.getters.totals && $store.getters.totals['otherFiles']"
|
||||
exact-active-class="green"
|
||||
class="ui basic button"
|
||||
to="/attachments/type/files">
|
||||
<i class="copy icon"></i>
|
||||
Other Files
|
||||
</router-link>
|
||||
|
||||
</div>
|
||||
<div class="four wide bottom aligned column">
|
||||
<i v-if="loading" class="green sync alternate loading icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="sixteen wide column" v-if="searchParams.noteId">
|
||||
<div class="ui green button" v-on:click="clearNote">
|
||||
<router-link class="ui green button" to="/attachments">
|
||||
<i class="chevron circle left icon"></i>
|
||||
Show All Attachments
|
||||
</div>
|
||||
Back to All
|
||||
</router-link>
|
||||
<div class="ui green button" v-on:click="openNote">
|
||||
<i class="file outline icon"></i>
|
||||
Open Note
|
||||
@ -47,11 +76,16 @@
|
||||
export default {
|
||||
components: {
|
||||
'attachment-display': require('@/components/AttachmentDisplayCard').default,
|
||||
'counter':require('@/components/AnimatedCounterComponent.vue').default,
|
||||
},
|
||||
data: function(){
|
||||
return {
|
||||
loading: false,
|
||||
attachments: [],
|
||||
searchParams: {}
|
||||
searchParams: {},
|
||||
loadedAttachmentsOffset: 0,
|
||||
loadingBatchTimeout: null,
|
||||
allLoaded: false,
|
||||
}
|
||||
},
|
||||
beforeCreate: function(){
|
||||
@ -59,40 +93,107 @@
|
||||
// Perform Login check
|
||||
//
|
||||
this.$parent.loginGateway()
|
||||
|
||||
},
|
||||
mounted: function(){
|
||||
|
||||
//Load more attachments on scroll
|
||||
window.addEventListener('scroll', this.onScroll)
|
||||
|
||||
//Mount notes on load if note ID is set
|
||||
this.openNoteAttachments()
|
||||
this.searchAttachments()
|
||||
},
|
||||
beforeDestroy(){
|
||||
|
||||
//Remove scroll event on destroy
|
||||
window.removeEventListener('scroll', this.onScroll)
|
||||
},
|
||||
watch:{
|
||||
$route (to, from){
|
||||
//Open or close notes on route change
|
||||
this.openNoteAttachments()
|
||||
|
||||
//Reset everything on route change
|
||||
this.reset()
|
||||
//Params are handled by search function
|
||||
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
|
||||
onScroll(){
|
||||
|
||||
clearTimeout(this.loadingBatchTimeout)
|
||||
this.loadingBatchTimeout = setTimeout(() => {
|
||||
|
||||
//Distance to bottom of page
|
||||
const bottomOfWindow =
|
||||
Math.max(window.pageYOffset, document.documentElement.scrollTop, document.body.scrollTop)
|
||||
+ window.innerHeight
|
||||
|
||||
//height of page
|
||||
const offsetHeight = this.$refs.content.clientHeight
|
||||
|
||||
//Determine percentage down the page
|
||||
const percentageDown = Math.round( (bottomOfWindow/offsetHeight)*100 )
|
||||
|
||||
//If greater than 80 of the way down the page, load the next batch
|
||||
if(percentageDown >= 80){
|
||||
|
||||
this.searchAttachments()
|
||||
}
|
||||
|
||||
}, 50)
|
||||
},
|
||||
reset(){
|
||||
this.attachments = []
|
||||
this.loading = false
|
||||
this.allLoaded = false
|
||||
this.loadedAttachmentsOffset = 0
|
||||
},
|
||||
searchAttachments (){
|
||||
|
||||
if(this.loading || this.allLoaded){
|
||||
return
|
||||
}
|
||||
|
||||
delete this.searchParams.attachmentType
|
||||
delete this.searchParams.noteId
|
||||
|
||||
//Set attchment type if in URL
|
||||
if(this.$route.params.type){
|
||||
this.searchParams.attachmentType = this.$route.params.type
|
||||
}
|
||||
|
||||
//Set noteId in if in URL
|
||||
if(this.$route.params.id){
|
||||
this.searchParams.noteId = this.$route.params.id
|
||||
}
|
||||
|
||||
//Offset of attchments to load
|
||||
this.searchParams.offset = this.loadedAttachmentsOffset
|
||||
|
||||
if(this.allLoaded){
|
||||
return
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
axios.post('/api/attachment/search', this.searchParams)
|
||||
.then( results => {
|
||||
this.attachments = results.data
|
||||
|
||||
this.loading = false
|
||||
|
||||
if(results.data.length == 0){
|
||||
this.allLoaded = true
|
||||
return
|
||||
}
|
||||
|
||||
//Load up the results
|
||||
this.attachments.push(...results.data)
|
||||
|
||||
//Grab the next batch
|
||||
this.loadedAttachmentsOffset += results.data.length
|
||||
})
|
||||
},
|
||||
}
|
||||
|
@ -73,8 +73,14 @@ export default new Router({
|
||||
},
|
||||
{
|
||||
path: '/attachments/note/:id',
|
||||
name: 'Attachments',
|
||||
meta: {title:'Attachments'},
|
||||
name: 'Attachments for Note',
|
||||
meta: {title:'Attachments for Note'},
|
||||
component: AttachmentsPage
|
||||
},
|
||||
{
|
||||
path: '/attachments/type/:type',
|
||||
name: 'Attachments by Type',
|
||||
meta: {title:'Attachments by Type'},
|
||||
component: AttachmentsPage
|
||||
},
|
||||
]
|
||||
|
@ -40,13 +40,19 @@ ProcessText.deduceNoteTitle = (inString) => {
|
||||
|
||||
//Remove inline styles that may be added by editor
|
||||
inString = inString.replace(/style=".*?"/g,'')
|
||||
// inString = inString.replace('</a>','')
|
||||
|
||||
//Emergency ending tag if truncated. This will help regex find all the lines
|
||||
inString += '</end>'
|
||||
|
||||
//Match full line and closing tag or just closing tag
|
||||
let lines = inString.match(/[<[a-zA-Z0-9]+>(.*?)<\/[a-zA-Z0-9]+>|<\/[a-zA-Z0-9>]+?>/g)
|
||||
let lines = inString.match(/[<[a-zA-Z0-9]+>(.*?)<\/[a-zA-Z0-9]+>|<\/[a-zA-Z0-9>]+?>/gms)
|
||||
if(lines == null){ lines = [inString] }
|
||||
//.match(/[^\r\n]+/g) //Match return or newline
|
||||
|
||||
// console.log('----------------')
|
||||
// console.log(lines)
|
||||
// console.log('----------------')
|
||||
|
||||
let finalLines = []
|
||||
|
||||
@ -129,6 +135,7 @@ ProcessText.deduceNoteTitle = (inString) => {
|
||||
|
||||
//Cut the string down to character limit
|
||||
const cutString = lines[i].substring(0, lines[i].length+charLimit)
|
||||
|
||||
//Find last space and cut off everything after it
|
||||
let cleanCutString = cutString.substring(0, cutString.lastIndexOf(' '))
|
||||
|
||||
|
@ -43,7 +43,7 @@ Attachment.textSearch = (userId, searchTerm) => {
|
||||
})
|
||||
}
|
||||
|
||||
Attachment.search = (userId, noteId, attachmentType) => {
|
||||
Attachment.search = (userId, noteId, attachmentType, offset, setSize) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let params = [userId]
|
||||
@ -54,13 +54,20 @@ Attachment.search = (userId, noteId, attachmentType) => {
|
||||
params.push(noteId)
|
||||
}
|
||||
|
||||
if(Number.isInteger(attachmentType)){
|
||||
query += 'AND attachment_type = ? '
|
||||
params.push(attachmentType)
|
||||
if(attachmentType == 'links'){
|
||||
query += 'AND attachment_type = 1 '
|
||||
}
|
||||
if(attachmentType == 'files'){
|
||||
query += 'AND attachment_type > 1 '
|
||||
}
|
||||
|
||||
|
||||
query += 'ORDER BY last_indexed DESC '
|
||||
|
||||
const limitOffset = parseInt(offset, 10) || 0 //Either parse int, or use zero
|
||||
const parsedSetSize = parseInt(setSize, 10) || 20 //Either parse int, or use zero
|
||||
query += ` LIMIT ${limitOffset}, ${parsedSetSize}`
|
||||
|
||||
db.promise()
|
||||
.query(query, params)
|
||||
.then((rows, fields) => {
|
||||
|
@ -593,6 +593,15 @@ Note.search = (userId, searchQuery, searchTags, fastFilters) => {
|
||||
note.attachment_highlights = []
|
||||
note.tag_highlights = []
|
||||
|
||||
//Limit number of attachment thumbs to 4
|
||||
if(note.thumbs){
|
||||
//Convert comma delimited string to array
|
||||
let thumbArray = note.thumbs.split(',').reverse()
|
||||
//Limit array to 4 or size of array
|
||||
thumbArray.length = Math.min(thumbArray.length, 4)
|
||||
note.thumbs = thumbArray
|
||||
}
|
||||
|
||||
//Push in search highlights
|
||||
if(highlights && highlights[note.id]){
|
||||
note['note_highlights'] = [highlights[note.id]]
|
||||
|
@ -18,7 +18,7 @@ router.use(function setUserId (req, res, next) {
|
||||
})
|
||||
|
||||
router.post('/search', function (req, res) {
|
||||
Attachment.search(userId, req.body.noteId, req.body.attachmentType)
|
||||
Attachment.search(userId, req.body.noteId, req.body.attachmentType, req.body.offset, req.body.setSize)
|
||||
.then( data => res.send(data) )
|
||||
})
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user