Tons of littele interface changes and cleanups

Massive update to image scraper with much better image getter
Lots of little ui updates for mobile
This commit is contained in:
Max G 2022-01-27 04:48:19 +00:00
parent d4be0d6471
commit 848c86327a
17 changed files with 400 additions and 273 deletions

View File

@ -53,7 +53,7 @@ helpers.timeAgo = (time) => {
if (typeof format[2] == 'string') {
return format[list_choice]
} else {
return Math.floor(seconds / format[2]) + ' ' + format[1]// + ' ' + token
return Math.floor(seconds / format[2]) + ' ' + format[1] + ' ' + token
}
}
}

View File

@ -43,7 +43,7 @@ html {
height:100%;
padding: 0;
margin: 0;
background: none;
background: var(--body_bg_color);
}
a:hover {
text-decoration: underline;

View File

@ -2117,7 +2117,7 @@ var cleanTree = function cleanTree ( node, config, preserveWS ) {
break;
}
}
data = data.replace( /^[ \t\r\n]+/g, sibling ? ' ' : '' );
data = data.replace( /^[ \r\n]+/g, sibling ? ' ' : '' );
}
if ( endsWithWS ) {
walker.currentNode = child;
@ -2132,7 +2132,7 @@ var cleanTree = function cleanTree ( node, config, preserveWS ) {
break;
}
}
data = data.replace( /[ \t\r\n]+$/g, sibling ? ' ' : '' );
data = data.replace( /[ \r\n]+$/g, sibling ? ' ' : '' );
}
if ( data ) {
child.data = data;
@ -2693,7 +2693,8 @@ var sanitizeToDOMFragment = function ( html, isPaste, self ) {
ALLOW_UNKNOWN_PROTOCOLS: true,
WHOLE_DOCUMENT: false,
RETURN_DOM: true,
RETURN_DOM_FRAGMENT: true
RETURN_DOM_FRAGMENT: true,
FORCE_BODY: false
}) : null;
return frag ? doc.importNode( frag, true ) : doc.createDocumentFragment();
};
@ -5011,4 +5012,4 @@ if ( typeof exports === 'object' ) {
}
}
}( document ) );
}( document ) );

View File

@ -1,54 +1,59 @@
<template>
<div :style="{ 'background-color':allStyles['noteBackground'], 'color':allStyles['noteText']}">
<div class="ui basic segment">
<div>
<div class="ui grid">
<div class="ui sixteen wide center aligned column">
<div class="ui fluid button" v-on:click="clearStyles">
<div class="ui sixteen wide column">
<div class="ui dividing header">
Reset Background Color and Icon
</div>
<div class="ui labeled basic icon button" v-on:click="clearStyles">
<i class="refresh icon"></i>
Clear All Styles
Reset
</div>
</div>
<div class="row">
<div class="sixteen wide column">
<br>
<p>Note Color</p>
<div v-for="color in colors"
class="color-button"
:style="{ backgroundColor:color }"
v-on:click="chosenColor(color)"
></div>
<div class="sixteen wide column rounded" :style="{ 'background-color':allStyles['noteBackground'], 'color':allStyles['noteText']}">
<div class="ui dividing header" :style="{ 'color':allStyles['noteText']}">
<i class="fill drip icon"></i>
Background Color
</div>
<div v-for="color in colors"
class="color-button"
:style="{ backgroundColor:color }"
v-on:click="chosenColor(color)"
></div>
</div>
<div class="sixteen wide column">
<div class="ui dividing header">
<span v-if="allStyles.noteIcon" >
<i :class="`large ${allStyles.noteIcon} icon`" :style="{ 'color':allStyles.iconColor }"></i>
</span>
Note Icon
</div>
<div v-for="icon in icons" class="icon-button" v-on:click="chosenIcon(icon)" >
<i :class="`large ${icon} icon`"></i>
</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 class="sixteen wide column">
<div class="ui dividing header">
<span v-if="allStyles.noteIcon" >
<i :class="`large ${allStyles.noteIcon} icon`" :style="{ 'color':allStyles.iconColor }"></i>
</span>
Icon Color
</div>
<div v-for="color in getReducedColors()"
class="color-button"
:style="{ backgroundColor:color }"
v-on:click="chooseIconColor(color)"
>
</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>
@ -147,20 +152,20 @@
}
</script>
<style type="text/css" scoped>
.icon-button {
.icon-button, .color-button {
height: 40px;
width: calc(10% - 7px);
display: inline-block;
cursor: pointer;
font-size: 1.3em;
}
.color-button {
display: inline-block;
width: calc(10% - 7px);
height: 30px;
border-radius: 30px;
border: 1px solid grey;
text-align: center;
padding: 5px 0 0;
border-radius: 4px;
box-shadow: 0px 1px 3px 0px #3e3e3e;
margin: 7px 7px 0 0;
cursor: pointer;
}
.rounded {
border-radius: 5px;
}
</style>

View File

@ -1,9 +1,9 @@
<template>
<!-- change class to .master-note-edit to have it popup on the screen -->
<!-- change class to .master-note-edit to have it popup on the screen.
@keyup.esc="closeButtonAction()" -->
<div
id="InputNotes"
class="master-note-edit"
@keyup.esc="closeButtonAction()"
class="master-note-edit"
>
<!-- Giant Edit Note Menu -->
@ -89,9 +89,9 @@
<div class="edit-divide"></div>
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/colors`)" data-tooltip="Note Color" data-position="bottom center" :style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}">
<!-- <div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/colors`)" data-tooltip="Note Color" data-position="bottom center" :style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}">
<i class="paint brush icon"></i>
</div>
</div> -->
<!-- <div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/tags`)" data-tooltip="Tags" data-position="bottom center">
<i class="tags icon"></i>
</div> -->
@ -107,7 +107,9 @@
<div class="edit-divide"></div>
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/options`)" data-tooltip="More Options" data-position="bottom center">
&nbsp;&nbsp;
<i class="ellipsis horizontal icon"></i>
&nbsp;&nbsp;
</div>
<div class="edit-divide"></div>
@ -128,6 +130,7 @@
<div class="edit-button ui" v-on:click="closeButtonAction()" :data-tooltip="`Close\n(ESC)`" data-position="bottom center">
<i class="green close icon"></i>
<span class="ui green text">Done</span>
</div>
</div>
@ -139,7 +142,7 @@
:class="{ 'side-menu-open':sideMenuOpen, 'size-down':(sizeDown == true),}">
<!-- Squire box grows -->
<div id="text-box-container" class="note-wrapper">
<div id="text-box-container" class="note-wrapper" :style="{ 'background-color':styleObject['noteBackground']}">
<!-- Loading indicator -->
<transition name="fade">
@ -161,19 +164,22 @@
v-on:blur="save" type="text" v-model="noteTitle" placeholder="Title" class="stealth-input glint">
</textarea>
<div class="large-close-button glint" v-on:click="closeButtonAction()">
<!-- close button giant -->
<div v-if="!$store.getters.getIsUserOnMobile" class="large-close-button" v-on:click="closeButtonAction()">
<i class="fitted green close icon"></i>
</div>
<!-- little tags on the side, only show on desktop -->
<div class="note-mini-tag-area" :class="{ 'size-down':sizeDown }" v-on:click="$router.push(`/notes/open/${noteid}/menu/tags`)">
<span class="add-mini-tag" v-if="noteTags.length == 0">
<!-- tags on the side, only show on desktop -->
<div class="note-mini-tag-area" :class="{ 'size-down':sizeDown }"
v-on:click="$router.push(`/notes/open/${noteid}/menu/tags`)"
:style="{ 'background-color':styleObject['noteBackground'] }">
<span class="add-mini-tag" v-if="allTags.length == 0">
<i class="tags icon"></i>Add Tags
</span>
<span v-for="tag in allTags" class="active-mini-tag" v-if="isTagOnNote(tag.id)">
#{{ tag.text }}
<span v-for="tag in allTags" class="active-mini-tag">
#{{ tag }}
</span>
<span class="active-mini-tag" v-if="noteTags.length > 0">
<span class="active-mini-tag" v-if="allTags.length > 0">
+
</span>
@ -201,21 +207,23 @@
/>
<!-- Side slide menus for colors, tags, images and other options -->
<side-slide-menu v-if="colors" v-on:close="colors = false" name="colors">
<!-- <side-slide-menu v-if="colors" v-on:close="colors = false" name="colors">
<color-picker
@changeColor="onChangeColor"
@close="colors = false; $router.go(-1)"
:style-object="styleObject"
/>
</side-slide-menu>
</side-slide-menu> -->
<side-slide-menu v-if="tags" v-on:close="tags = false; fetchNoteTags()" name="tags" :style-object="styleObject">
<!-- tag edit menu -->
<side-slide-menu v-if="tags" v-on:close="tags = false; fetchNoteTags()" name="tags">
<div class="ui basic segment">
<note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/>
</div>
</side-slide-menu>
<side-slide-menu v-if="images" v-on:close="images = false" name="images" :style-object="styleObject">
<!-- images menu -->
<side-slide-menu v-if="images" v-on:close="images = false" name="images">
<div class="ui basic segment">
<simple-attachment-note
:note-id="noteid"
@ -224,67 +232,81 @@
</div>
</side-slide-menu>
<side-slide-menu v-if="options" v-on:close="options = false" name="note-options" :style-object="styleObject">
<side-slide-menu v-if="options" v-on:close="options = false" name="note-options">
<div class="ui basic padded segment">
<div class="ui grid">
<div class="sixteen wide column">
<h3>Note Options</h3>
</div>
<div class="eight wide column">
<div class="ui dividing header">
Note Options
</div>
<div class="ui labeled icon fluid basic button" v-on:click="onToggleArchived()">
<i class="archive icon" :class="{'green':(archived == 1)}"></i>
<span v-if="archived == 1">Un-Archive Note</span>
<span v-if="archived != 1">Archive Note</span>
</div>
</div>
<div class="eight wide column">
<div class="ui labeled icon fluid basic button" v-on:click="onTogglePinned">
<i class="pin icon" :class="{'green':(pinned == 1)}"></i>
<span v-if="pinned == 1">Un-Pin Note</span>
<span v-if="pinned != 1">Pin Note</span>
</div>
</div>
<div class="sixteen wide column">
<h3>List Options</h3>
</div>
<div class="sixteen wide column">
<div class="eight wide column">
<div class="ui dividing header">
List Options
</div>
<div class="ui labeled icon fluid basic button" v-on:click="sortList">
<i class="sort amount up icon"></i>
Sort List
Sort List (Complete to bottom)
</div>
<div class="ui labeled icon fluid basic button" v-on:click="uncheckAllListItems">
<i class="list ul icon"></i>
Uncheck All
</div>
</div>
<div class="eight wide column">
<div class="ui labeled icon fluid basic button" v-on:click="deleteCompletedListItems">
<i class="trash icon"></i>
Delete Checked
</div>
</div>
<div class="eight wide column">
<div class="ui labeled icon fluid basic button" v-on:click="uncheckAllListItems">
<i class="list ul icon"></i>
Uncheck All
<div class="ui dividing header">
Calculate Line
</div>
</div>
<div class="sixteen wide column">
<h3>Misc Options</h3>
</div>
<div class="eight wide column">
<div class="ui labeled icon fluid basic button" v-on:click="calculateMath" data-tooltip="Calculates algebra before '='">
<p>
Calculates algebra before '='
</p>
<div class="ui labeled icon fluid basic button" v-on:click="calculateMath">
<i class="calculator icon"></i>
Simple Math
</div>
</div>
<div class="eight wide column">
<!-- data-tooltip="Files on note" -->
<div v-on:click="openEditAttachment" class="ui labeled icon fluid basic button">
<i class="folder icon"></i>
Note Files
{{ attachmentCount }}
Calculate Simple Math
</div>
</div>
<div class="eight wide column">
<!-- data-tooltip="Files on note" -->
<div class="ui dividing header">
Note Attachments & Links
</div>
<p>
Attachment & Link Count {{ attachmentCount }}
</p>
<div v-on:click="openEditAttachment" class="ui labeled icon fluid basic button">
<i class="folder icon"></i>
View all Attachments & Links
</div>
</div>
<color-picker
@changeColor="onChangeColor"
@close="colors = false; $router.go(-1)"
:style-object="styleObject"
/>
<div class="sixteen wide column" v-if="rawTextId > 0">
<h3>Share Note</h3>
<div class="ui dividing header">
Share Note
</div>
<share-note-component
:note-id="noteid"
:raw-text-id="rawTextId"
@ -296,7 +318,7 @@
</side-slide-menu>
<!-- create table option -->
<side-slide-menu v-if="table" v-on:close="table = false; fetchNoteTags()" name="table" :style-object="styleObject">
<side-slide-menu v-if="table" v-on:close="table = false;" name="table" :style-object="styleObject">
<div class="ui basic segment">
<h2>Insert Table</h2>
<div class="table-tic-table">
@ -521,77 +543,15 @@
}, totalTime + 40)
},
removeTag(tagId){
this.allTags = []
let entryId = 0
//Find fucking note tag for removal
this.noteTags.forEach(noteTag => {
if(noteTag['tagId'] == tagId){
entryId = noteTag['entryId']
}
})
let postData = {
'tagId':entryId,
'noteId':this.noteid
}
axios.post('/api/tag/removefromnote', postData)
.then(response => {
this.fetchNoteTags()
})
.catch(error => { this.$bus.$emit('notification', 'Failed to Remove Tag') })
},
addTag(tagText){
this.allTags = []
let postData = {
'tagText':tagText,
'noteId':this.noteid
}
axios.post('/api/tag/addtonote', postData)
.then(response => {
this.fetchNoteTags()
})
.catch(error => { this.$bus.$emit('notification', 'Failed to Add Tag') })
},
fetchNoteTags(){
axios.post('/api/tag/get', {'noteId': this.noteid})
axios.post('/api/tag/fornote', {'noteId': this.noteid})
.then(({data}) => {
this.allTags = data.allTags
this.noteTags = data.noteTagIds
//Stick used tags at top.
if(this.noteTags.length > 0){
let frontTags = []
for (var i = this.allTags.length - 1; i >= 0; i--) {
this.noteTags.forEach(noteTag => {
if(this.allTags[i]['id'] == noteTag['tagId']){
frontTags.push(this.allTags[i])
this.allTags.splice(i,1)
}
})
}
this.allTags.unshift(...frontTags)
}
//Setup note tags from string
this.allTags = data.tags ? data.tags.split(',') : []
})
},
isTagOnNote(id){
for (let i = 0; i < this.noteTags.length; i++) {
const current = this.noteTags[i]
if(current && current['tagId'] == id){
return true
}
}
return false
},
initSquire(){
//Set up squire and load note text
@ -604,10 +564,6 @@
}
//Load tags on mobile
this.fetchNoteTags()
//Set up websockets after squire is set up
setTimeout(() => {
this.setupWebSockets()
@ -783,6 +739,9 @@
this.lastNoteHash = this.hashString( response.data.text )
// this.diffNoteText = response.data.text
//Setup note tags
this.allTags = response.data.tags ? response.data.tags.split(','):[]
//Set up note colors
if(response.data.color){
this.styleObject = JSON.parse(response.data.color)

View File

@ -21,27 +21,9 @@
class="big-text"><p>{{ note.title }}</p></span>
<span class="tags" v-if="note.tags">
<span v-for="tag in (note.tags.split(','))" class="little-tag" v-on:click="$emit('tagClick', tag.split(':')[1] )">#{{ tag.split(':')[0] }}</span>
<br>
</span>
<!-- Sub text display -->
<span v-if="note.subtext.length > 0"
class="small-text"
v-html="note.subtext"></span>
<!-- Not indexed warning -->
<!-- <span v-if="note.indexed != 1">
<span class="green label">Not Indexed</span>
</span> -->
<div class="ui fluid basic button" v-if="note.encrypted == 1">
<i class="green lock icon"></i>
Locked
</div>
<span v-for="tag in (note.tags.split(','))" class="little-tag" v-on:click="$emit('tagClick', tag.split(':')[1] )">#{{ tag.split(':')[0] }}</span>
<br>
</span>
<!-- Shared Details -->
<span class="subtext" v-if="note.shared == 2">
@ -62,23 +44,71 @@
</span>
</span>
</div>
<!-- Sub text display -->
<span v-if="note.subtext.length > 0"
class="small-text"
v-html="note.subtext"></span>
<!-- Not indexed warning -->
<!-- <span v-if="note.indexed != 1">
<span class="green label">Not Indexed</span>
</span> -->
<div v-if="titleView" class="single-line-text" @click="cardClicked">
<span class="title-line" v-if="note.title.length > 0">{{ note.title }}<br></span>
<span class="sub-line" v-if="note.subtext.length > 0">{{ removeHtml(note.subtext) }}</span>
<span v-if="note.title.length == 0 && note.title.length == 0">Empty Note</span>
<!-- <div class="ui fluid basic button" v-if="note.encrypted == 1">
<i class="green lock icon"></i>
Locked
</div> -->
</div>
<!-- slim card view -->
<div v-if="titleView" class="thin-container" @click="cardClicked">
<!-- icon -->
<span v-if="noteIcon" class="thin-icon">
<i :class="`${noteIcon} icon`" :style="{ 'color':iconColor }"></i>
</span>
<!-- title -->
<span class="thin-title" v-if="note.title.length > 0">{{ note.title }}</span>
<!-- snippet -->
<span class="thin-sub" v-if="note.subtext.length > 0">{{ removeHtml(note.subtext) }}</span>
<span v-if="note.title.length == 0 && removeHtml(note.subtext).length == 0">Empty Note</span>
<!-- tags -->
<span v-if="note.tags" class="thin-tags" >
<span v-for="tag in (note.tags.split(','))" class="little-tag" v-on:click="$emit('tagClick', tag.split(':')[1] )">#{{ tag.split(':')[0] }}
</span>
</span>
<!-- edited -->
<span class="thin-right">
{{$helpers.timeAgo( note.updated )}}
<i class="green link ellipsis vertical icon"></i>
</span>
</div>
<!-- Toolbar on the bottom -->
<div class="tool-bar" @click.self="cardClicked" v-if="!titleView">
<div class="icon-bar">
<span class="time-ago-display" :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }">
<div v-if="getThumbs.length > 0">
<div class="tiny-thumb-box" v-on:click="openEditAttachment">
<img v-for="thumb in getThumbs" class="tiny-thumb" :src="`/api/static/thumb_${thumb}`">
</div>
</div>
<div class="icon-bar" :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }">
<span class="time-ago-display">
{{$helpers.timeAgo( note.updated )}}
</span>
<span class="teeny-buttons" :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }">
<span class="teeny-buttons">
<span v-if="!note.trashed">
@ -115,19 +145,13 @@
</i>
<delete-button class="teeny-button" :note-id="note.id" />
</span>
</span>
</div>
<div v-if="getThumbs.length > 0">
<div class="tiny-thumb-box" v-on:click="openEditAttachment">
<img v-for="thumb in getThumbs" class="tiny-thumb" :src="`/api/static/thumb_${thumb}`">
</div>
</div>
</div>
<!-- tag edit menu -->
<side-slide-menu v-if="showTagSlideMenu" v-on:close="toggleTags(false)" :full-shadow="true" :skip-history="true">
<div class="ui basic segment">
<note-tag-edit :noteId="note.id" :key="'display-tags-for-note-'+note.id"/>
@ -333,13 +357,11 @@
.teeny-buttons {
float: right;
width: 65%;
text-align: right;
}
.time-ago-display {
width: 35%;
float: left;
text-align: center;
font-size: 11px;
font-weight: bold;
}
.tags {
width: 100%;
@ -364,9 +386,7 @@
/*Strict font sizes for card display*/
.small-text {
max-height: 267px;
width: 100%;
overflow: hidden;
display: inline-block;
}
.small-text, .small-text > p, .small-text > h1, .small-text > h2 {
@ -426,7 +446,7 @@
/*width: calc(33.333% - 10px);*/
width: calc(25% - 10px);
/*min-width: 190px;*/
min-height: 130px;
/*min-height: 130px;*/
/*transition: box-shadow 0.3s;*/
box-sizing: border-box;
cursor: pointer;
@ -435,7 +455,11 @@
letter-spacing: 0.05rem;
display: flex;
flex-direction: column;
align-items: stretch;
text-align: left;
min-height: 100px;
max-height: 450px;
}
.note-title-display-card:hover {
/*box-shadow: 0px 2px 2px 1px rgba(210, 211, 211, 0.8);*/
@ -446,21 +470,49 @@
width: 100%;
min-height: 20px;
max-width: none;
padding: 10px;
margin: 0;
overflow: hidden;
border-radius: 0;
border: none;
/*box-shadow: 0px 0px 1px 1px rgba(210, 211, 211, 0.46);*/
}
.single-line-text {
.title-view + .title-view {
border-top: 1px solid var(--border_color);
}
.thin-container.single-line-text {
width: calc(100% - 25px);
margin: 5px 10px;
/*margin: 5px 10px;*/
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
box-sizing: border-box;
}
.title-line {
.thin-container .thin-title {
font-weight: bold;
font-size: 1.2em;
padding: 0 20px 0 0;
}
.thin-container .thin-sub {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
opacity: 0.85;
}
.thin-container .thin-tags {
float: left;
margin-top: 3px;
}
.thin-container .thin-right {
float: right;
color: var(--dark_border_color);
}
.thin-container .thin-icon {
float: right;
}
.icon-bar {
@ -468,6 +520,7 @@
padding: 5px 10px 0;
opacity: 1;
width: 100%;
background-color: rgba(200, 200, 200, 0.2);
}
.hover-hide {
opacity: 0.0;

View File

@ -191,11 +191,24 @@
</div>
</div>
<!-- Overview -->
<div class="middle aligned centered row">
<div class="six wide column">
<h2 class="ui dividing header">Powerful text editing and privacy</h2>
<h3>Easily edit, share and organize thousands of notes.</h3>
<h3>Feel safe knowing no one can read your notes but you.</h3>
<!-- <h3>Tools to organize and collaborate on thousands of notes while maintaining security and respecting your privacy.</h3> -->
</div>
<div class="four wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/idea.svg" alt="Explosion of New Ideas">
</div>
</div>
<!-- theme selector -->
<div class="ui white row">
<div class="sixteen wide middle aligned column">
<div class="ui container">
<h2>
<h2 style="color: var(--main-accent);">
Pick your theme
</h2>
<h3 v-if="$parent.loggedIn">Go to settings to change theme</h3>
@ -211,19 +224,6 @@
</div>
</div>
<!-- Overview -->
<div class="middle aligned centered row">
<div class="six wide column">
<h2 class="ui dividing header">Powerful text editing and privacy</h2>
<h3>Easily edit, share and organize thousands of notes.</h3>
<h3>Feel safe knowing no one can read your notes but you.</h3>
<!-- <h3>Tools to organize and collaborate on thousands of notes while maintaining security and respecting your privacy.</h3> -->
</div>
<div class="four wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/idea.svg" alt="Explosion of New Ideas">
</div>
</div>
<!-- features list -->
<div class="top aligned centered row">
@ -355,7 +355,7 @@
<i class="grey lock icon"></i>
<i class="bottom left corner yellow key icon"></i>
</i>
All Note Text is Encrypted
Secure Notes
<div class="sub header">All note text is encrypted. No one can read your notes. None of your data is shared.</div>
</div>
</h2>
@ -365,7 +365,7 @@
<i class="grey search icon"></i>
<i class="bottom left corner orange font icon"></i>
</i>
Note Search is Encrypted
Private Search
<div class="sub header">Search the contents of all your notes without compromising security.</div>
</div>
</h2>
@ -375,7 +375,7 @@
<i class="grey share alternate icon"></i>
<i class="bottom left corner share icon"></i>
</i>
Encrypted Note Sharing
Encrypted Sharing
<div class="sub header">Shared notes are still encrypted, only readable by you and the shared users.</div>
</div>
</h2>

View File

@ -27,9 +27,15 @@
v-on:tagClick="tagId => toggleTagFilter(tagId)"
/>
<div class="ui basic shrinking icon button" v-on:click="toggleTitleView()" v-if="$store.getters.totals && $store.getters.totals['totalNotes'] > 0">
<i v-if="titleView" class="th icon"></i>
<i v-if="!titleView" class="bars icon"></i>
<div class="ui right floated basic shrinking icon button" v-on:click="toggleTitleView()" v-if="$store.getters.totals && $store.getters.totals['totalNotes'] > 0">
<span v-if="titleView">
<i class="th icon"></i> Tiles
</span>
<span v-if="!titleView">
<i class="list icon"></i> List
</span>
</div>
</div>
@ -223,6 +229,9 @@
this.$parent.loginGateway()
//If user is on title view,
this.titleView = this.$store.getters.getIsUserOnMobile
this.$io.on('new_note_created', noteId => {
//Do not update note if its open

View File

@ -11,4 +11,5 @@ common.js
bundle.*
client/dist*
server/public/*
client/dist*
client/dist*
*_scrape*

View File

@ -6,6 +6,7 @@ const speakeasy = require('speakeasy')
let Auth = {}
const tokenSecretKey = process.env.JSON_KEY
const sessionTokenUses = 300 //Defines number of uses each session token has before being refreshed
//Creates session token
Auth.createToken = (userId, masterKey, pastId = null, pastCreatedDate = null) => {
@ -26,7 +27,7 @@ Auth.createToken = (userId, masterKey, pastId = null, pastCreatedDate = null) =>
return db.promise().query(
'INSERT INTO user_active_session (salt, encrypted_master_password, created, uses, user_hash, session_id) VALUES (?,?,?,?,?,?)',
[salt, encryptedMasterPass, created, 40, userHash, sessionId])
[salt, encryptedMasterPass, created, sessionTokenUses, userHash, sessionId])
})
.then((r,f) => {

View File

@ -54,7 +54,7 @@ SiteScrape.getCleanUrls = (textBlock) => {
SiteScrape.getHostName = (url) => {
var hostname = 'https://'+(new URL(url)).hostname;
console.log('hostname', hostname)
// console.log('hostname', hostname)
return hostname
}
@ -63,36 +63,95 @@ SiteScrape.getDisplayImage = ($, url) => {
const hostname = SiteScrape.getHostName(url)
let metaImg = $('meta[property="og:image"]')
let shortcutIcon = $('link[rel="shortcut icon"]')
let favicon = $('link[rel="icon"]')
let metaImg = $('[property="og:image"]')
let shortcutIcon = $('[rel="shortcut icon"]')
let favicon = $('[rel="icon"]')
let randomImg = $('img')
console.log('----')
//Set of images we may want gathered from various places in source
let imagesWeWant = []
let thumbnail = ''
//Scrape metadata for page image
//Grab the first random image we find
if(randomImg && randomImg[0] && randomImg[0].attribs){
thumbnail = hostname + randomImg[0].attribs.src
console.log('random img '+thumbnail)
if(randomImg && randomImg.length > 0){
let imgSrcs = []
for (let i = 0; i < randomImg.length; i++) {
imgSrcs.push( randomImg[i].attribs.src )
}
const half = Math.ceil(imgSrcs.length / 2)
imagesWeWant = [...imgSrcs.slice(-half), ...imgSrcs.slice(0,half) ]
}
//Grab the favicon of the site
//Grab the shortcut icon
if(favicon && favicon[0] && favicon[0].attribs){
thumbnail = hostname + favicon[0].attribs.href
console.log('favicon '+thumbnail)
imagesWeWant.push(favicon[0].attribs.href)
}
//Grab the shortcut icon
if(shortcutIcon && shortcutIcon[0] && shortcutIcon[0].attribs){
thumbnail = hostname + shortcutIcon[0].attribs.href
console.log('shortcut '+thumbnail)
imagesWeWant.push(shortcutIcon[0].attribs.href)
}
//Grab the presentation image for the site
if(metaImg && metaImg[0] && metaImg[0].attribs){
thumbnail = metaImg[0].attribs.content
console.log('ogImg '+thumbnail)
imagesWeWant.unshift(metaImg[0].attribs.content)
}
// console.log(imagesWeWant)
//Remove everything that isn't an accepted file format
for (let i = imagesWeWant.length - 1; i >= 0; i--) {
let img = String(imagesWeWant[i])
if(
!img.includes('.jpg') &&
!img.includes('.jpeg') &&
!img.includes('.png') &&
!img.includes('.gif')
){
imagesWeWant.splice(i,1)
}
}
//Find if we have absolute thumbnails or not
let foundAbsolute = false
for (let i = imagesWeWant.length - 1; i >= 0; i--) {
let img = imagesWeWant[i]
//Add host name if its not included
if(String(img).includes('//') || String(img).includes('http')){
foundAbsolute = true
break
}
}
//Go through all found images. Grab the one closest to the top. Closer is better
for (let i = imagesWeWant.length - 1; i >= 0; i--) {
let img = imagesWeWant[i]
if(!String(img).includes('//') && foundAbsolute){
continue;
}
//Only add host to images if no absolute images were found
if(!String(img).includes('//') ){
if(img.indexOf('/') != 0){
img = '/' + img
}
img = hostname + img
}
if(img.indexOf('//') == 0){
img = 'https:' + img //Scrape breaks without protocol
}
thumbnail = img
}
console.log('-----')
return thumbnail
}

View File

@ -257,7 +257,6 @@ const printResults = true
let UserTest = require('@models/User')
let NoteTest = require('@models/Note')
let AuthTest = require('@helpers/Auth')
Auth.test()
UserTest.keyPairTest('genMan30', '1', printResults)
.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults))
@ -266,7 +265,6 @@ UserTest.keyPairTest('genMan30', '1', printResults)
Auth.testTwoFactor()
})
//Test
app.get('/api', (req, res) => res.send('Solidscribe API is up and running'))

View File

@ -325,14 +325,14 @@ Attachment.downloadFileFromUrl = (url) => {
return new Promise((resolve, reject) => {
if(url == null){
if(url == null || url == undefined || url == ''){
resolve(null)
}
const random = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
const extension = '.'+url.split('.').pop() //This is throwing an error
let fileName = random+'_scrape'+extension
const thumbPath = 'thumb_'+fileName
let extension = ''
let fileName = random+'_scrape'
let thumbPath = 'thumb_'+fileName
console.log('Scraping image url')
console.log(url)
@ -347,6 +347,8 @@ Attachment.downloadFileFromUrl = (url) => {
.on('response', res => {
console.log(res.statusCode)
console.log(res.headers['content-type'])
//Get mime type from header content type
// extension = '.'+String(res.headers['content-type']).split('/').pop()
})
.pipe(fs.createWriteStream(filePath+thumbPath))
.on('close', () => {
@ -354,14 +356,17 @@ Attachment.downloadFileFromUrl = (url) => {
//resize image if its real big
gm(filePath+thumbPath)
.resize(550) //Resize to width of 550 px
.quality(75) //compression level 0 - 100 (best)
.quality(85) //compression level 0 - 100 (best)
.write(filePath+thumbPath, function (err) {
if(err){ console.log(err) }
if(err){
console.log(err)
return resolve(null)
}
console.log('Saved Image')
return resolve(fileName)
})
console.log('Saved Image')
resolve(fileName)
})
})
}
@ -396,7 +401,7 @@ Attachment.processUrl = (userId, noteId, url) => {
.query(`INSERT INTO attachment
(note_id, user_id, attachment_type, text, url, last_indexed, file_location)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
[noteId, userId, 1, 'Processing...', url, created, null])
[noteId, userId, 1, url, url, created, null])
.then((rows, fields) => {
//Set two bigger variables then return request for processing
request = rp(options)

View File

@ -681,6 +681,7 @@ Note.get = (userId, noteId, masterKey) => {
note_raw_text.text,
note_raw_text.salt,
note_raw_text.updated as updated,
GROUP_CONCAT(DISTINCT(tag.text) ORDER BY tag.text DESC) AS tags,
note.id,
note.user_id,
note.created,
@ -697,7 +698,9 @@ Note.get = (userId, noteId, masterKey) => {
JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id)
LEFT JOIN attachment ON (note.id = attachment.note_id)
LEFT JOIN user as shareUser ON (note.share_user_id = shareUser.id)
WHERE note.user_id = ? AND note.id = ? LIMIT 1`, [userId, noteId])
LEFT JOIN note_tag ON (note.id = note_tag.note_id AND note_tag.user_id = ?)
LEFT JOIN tag ON (note_tag.tag_id = tag.id)
WHERE note.user_id = ? AND note.id = ? LIMIT 1`, [userId, userId, noteId])
})
.then((rows, fields) => {

View File

@ -138,6 +138,33 @@ Tag.get = (userId, noteId) => {
})
}
//
// Get just tag string for note
//
Tag.fornote = (userId, noteId) => {
return new Promise((resolve, reject) => {
db.promise()
.query(`SELECT GROUP_CONCAT(DISTINCT(tag.text) ORDER BY tag.text DESC) AS tags
FROM note_tag
LEFT JOIN tag ON (note_tag.tag_id = tag.id)
WHERE note_tag.note_id = ?
AND user_id = ?;
`, [noteId,userId])
.then((rows, fields) => {
//pull IDs out of returned results
// let ids = rows[0].map( item => {})
resolve( rows[0][0] ) //Return all tags found by query
})
.catch(console.log)
})
}
//
// Get all tags for a note and concatinate into a string 'all, tags, like, this'
//

View File

@ -9,7 +9,7 @@ const speakeasy = require('speakeasy')
let User = module.exports = {}
const version = '3.3.1'
const version = '3.3.3'
//Login a user, if that user does not exist create them
//Issues login token

View File

@ -50,6 +50,12 @@ router.post('/get', function (req, res) {
.then( data => res.send(data) )
})
//Get the latest notes the user has created
router.post('/fornote', function (req, res) {
Tags.fornote(userId, req.body.noteId)
.then( data => res.send(data) )
})
//Get all the tags for this user in order of usage
router.post('/usertags', function (req, res) {
Tags.userTags(userId, req.body.searchQuery, req.body.searchTags, req.body.fastFilters)