Added privacy policy
Updated marketing Added some keyboard shortcuts Added settings page Added accent theming Added beta 2FA
This commit is contained in:
1 parent
2ae84ab73e
commit
06b8f0ad6a
29 files changed
+1428
-362
No files matched your search
@@ -97,6 +97,12 @@ export default {
|
|||||||
//Detect if user is on a mobile browser and set a flag in store
|
//Detect if user is on a mobile browser and set a flag in store
|
||||||
this.$store.commit('detectIsUserOnMobile')
|
this.$store.commit('detectIsUserOnMobile')
|
||||||
|
|
||||||
|
//Set Main theme color
|
||||||
|
const accentColor = localStorage.getItem('main-accent')
|
||||||
|
if(accentColor){
|
||||||
|
document.documentElement.style.setProperty('--main-accent', accentColor)
|
||||||
|
}
|
||||||
|
|
||||||
//Set color theme based on local storage
|
//Set color theme based on local storage
|
||||||
const themeNumber = localStorage.getItem('nightMode')
|
const themeNumber = localStorage.getItem('nightMode')
|
||||||
if(themeNumber != null){
|
if(themeNumber != null){
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ const helpers = {}
|
|||||||
|
|
||||||
helpers.timeAgo = (time) => {
|
helpers.timeAgo = (time) => {
|
||||||
|
|
||||||
|
if(time == null){
|
||||||
|
time = Math.round(time/1000)
|
||||||
|
}
|
||||||
|
|
||||||
if(time.toString().length >= 13){
|
if(time.toString().length >= 13){
|
||||||
time = Math.round(time/1000)
|
time = Math.round(time/1000)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
|
||||||
--main-accent: #16ab39;
|
/*main accent for all buttons, icons and logos*/
|
||||||
|
--main-accent: #21BA45;
|
||||||
|
|
||||||
/*theme colors */
|
/*theme colors */
|
||||||
--body_bg_color: #f5f6f7;
|
--body_bg_color: #f5f6f7;
|
||||||
@@ -105,7 +106,7 @@ div.ui.basic.green.label {
|
|||||||
background-color: var(--small_element_bg_color) !important;
|
background-color: var(--small_element_bg_color) !important;
|
||||||
}
|
}
|
||||||
.ui.basic.button, .ui.basic.buttons .button {
|
.ui.basic.button, .ui.basic.buttons .button {
|
||||||
background-color: var(--small_element_bg_color) !important;
|
background-color: var(--small_element_bg_color);
|
||||||
color: var(--text_color) !important;
|
color: var(--text_color) !important;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
border-color: var(--dark_border_color) !important;
|
border-color: var(--dark_border_color) !important;
|
||||||
@@ -125,6 +126,24 @@ div.ui.basic.green.label {
|
|||||||
color: var(--text_color) !important;
|
color: var(--text_color) !important;
|
||||||
border-color: var(--dark_border_color) !important;
|
border-color: var(--dark_border_color) !important;
|
||||||
}
|
}
|
||||||
|
/*Overwrites for modifiable theme color */
|
||||||
|
i.green.icon.icon.icon.icon {
|
||||||
|
color: var(--main-accent);
|
||||||
|
}
|
||||||
|
.ui.green.buttons, .ui.green.button, .ui.green.button:hover {
|
||||||
|
background-color: var(--main-accent);
|
||||||
|
}
|
||||||
|
.ui.basic.green.button, .ui.basic.green.buttons .button:hover, .ui.basic.green.button:hover, .ui.basic.green.button:focus {
|
||||||
|
box-shadow: var(--main-accent) 0px 0px 0px 1px inset;
|
||||||
|
}
|
||||||
|
.ui.green.labels .label, .ui.ui.ui.green.label {
|
||||||
|
background-color: var(--main-accent);
|
||||||
|
border-color: var(--main-accent);
|
||||||
|
}
|
||||||
|
.ui.grid > .green.row, .ui.grid > .green.column, .ui.grid > .row > .green.column {
|
||||||
|
background-color: var(--main-accent);
|
||||||
|
}
|
||||||
|
|
||||||
/* OVERWRITE DEFAULT SEMANTIC STYLES FOR CUSTOM/NIGHT MODES*/
|
/* OVERWRITE DEFAULT SEMANTIC STYLES FOR CUSTOM/NIGHT MODES*/
|
||||||
|
|
||||||
/*//
|
/*//
|
||||||
@@ -235,7 +254,7 @@ div.ui.basic.green.label {
|
|||||||
/*border-bottom: 1px solid #ccc;*/
|
/*border-bottom: 1px solid #ccc;*/
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
scrollbar-color: transparent transparent;
|
scrollbar-color: transparent transparent;
|
||||||
caret-color: #21BA45;
|
caret-color: var(--main-accent);
|
||||||
}
|
}
|
||||||
.squire-box::selection,
|
.squire-box::selection,
|
||||||
.squire-box::-moz-selection {
|
.squire-box::-moz-selection {
|
||||||
@@ -253,7 +272,7 @@ div.ui.basic.green.label {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.night-mode .note-card-text i:not(.icon),
|
.night-mode .note-card-text i:not(.icon),
|
||||||
.night-mode .squire-box i {
|
.night-mode .squire-box i:not(.icon) {
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,9 +341,55 @@ div.ui.basic.green.label {
|
|||||||
|
|
||||||
font-family: 'Icons';
|
font-family: 'Icons';
|
||||||
content: "\f058";
|
content: "\f058";
|
||||||
color: #21BA45;
|
color: var(--main-accent);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note-title-display-card .divide,
|
||||||
|
.squire-box .divide {
|
||||||
|
width: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
height: 2px;
|
||||||
|
background-color: var(--main-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
/* table:hover th, table:hover td {
|
||||||
|
border: 1px solid black;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 3px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-table {
|
||||||
|
width: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-table > span,
|
||||||
|
.t-table > div {
|
||||||
|
display: flex; /* aligns all child elements (flex items) in a row */
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-table > span > span,
|
||||||
|
.t-table > div > div {
|
||||||
|
flex: 1; /* distributes space on the line equally among items */
|
||||||
|
border: 1px solid #DDD;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* adjust checkboxes for mobile. Make them a little bigger, easier to click */
|
/* adjust checkboxes for mobile. Make them a little bigger, easier to click */
|
||||||
@media only screen and (max-width: 740px) {
|
@media only screen and (max-width: 740px) {
|
||||||
|
|
||||||
|
|||||||
+64
-16
@@ -1096,6 +1096,9 @@ var moveRangeBoundariesUpTree = function ( range, startMax, endMax, root ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while ( true ) {
|
while ( true ) {
|
||||||
|
if ( endContainer === endMax || endContainer === root ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if ( maySkipBR &&
|
if ( maySkipBR &&
|
||||||
endContainer.nodeType !== TEXT_NODE &&
|
endContainer.nodeType !== TEXT_NODE &&
|
||||||
endContainer.childNodes[ endOffset ] &&
|
endContainer.childNodes[ endOffset ] &&
|
||||||
@@ -1103,9 +1106,7 @@ var moveRangeBoundariesUpTree = function ( range, startMax, endMax, root ) {
|
|||||||
endOffset += 1;
|
endOffset += 1;
|
||||||
maySkipBR = false;
|
maySkipBR = false;
|
||||||
}
|
}
|
||||||
if ( endContainer === endMax ||
|
if ( endOffset !== getLength( endContainer ) ) {
|
||||||
endContainer === root ||
|
|
||||||
endOffset !== getLength( endContainer ) ) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
parent = endContainer.parentNode;
|
parent = endContainer.parentNode;
|
||||||
@@ -1117,6 +1118,20 @@ var moveRangeBoundariesUpTree = function ( range, startMax, endMax, root ) {
|
|||||||
range.setEnd( endContainer, endOffset );
|
range.setEnd( endContainer, endOffset );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var moveRangeBoundaryOutOf = function ( range, nodeName, root ) {
|
||||||
|
var parent = getNearest( range.endContainer, root, 'A' );
|
||||||
|
if ( parent ) {
|
||||||
|
var clone = range.cloneRange();
|
||||||
|
parent = parent.parentNode;
|
||||||
|
moveRangeBoundariesUpTree( clone, parent, parent, root );
|
||||||
|
if ( clone.endContainer === parent ) {
|
||||||
|
range.setStart( clone.endContainer, clone.endOffset );
|
||||||
|
range.setEnd( clone.endContainer, clone.endOffset );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
};
|
||||||
|
|
||||||
// Returns the first block at least partially contained by the range,
|
// Returns the first block at least partially contained by the range,
|
||||||
// or null if no block is contained by the range.
|
// or null if no block is contained by the range.
|
||||||
var getStartBlockOfRange = function ( range, root ) {
|
var getStartBlockOfRange = function ( range, root ) {
|
||||||
@@ -1285,10 +1300,13 @@ var onKey = function ( event ) {
|
|||||||
if ( event.altKey ) { modifiers += 'alt-'; }
|
if ( event.altKey ) { modifiers += 'alt-'; }
|
||||||
if ( event.ctrlKey ) { modifiers += 'ctrl-'; }
|
if ( event.ctrlKey ) { modifiers += 'ctrl-'; }
|
||||||
if ( event.metaKey ) { modifiers += 'meta-'; }
|
if ( event.metaKey ) { modifiers += 'meta-'; }
|
||||||
|
if ( event.shiftKey ) { modifiers += 'shift-'; }
|
||||||
}
|
}
|
||||||
// However, on Windows, shift-delete is apparently "cut" (WTF right?), so
|
// However, on Windows, shift-delete is apparently "cut" (WTF right?), so
|
||||||
// we want to let the browser handle shift-delete.
|
// we want to let the browser handle shift-delete in this situation.
|
||||||
if ( event.shiftKey ) { modifiers += 'shift-'; }
|
if ( isWin && event.shiftKey && key === 'delete' ) {
|
||||||
|
modifiers += 'shift-';
|
||||||
|
}
|
||||||
|
|
||||||
key = modifiers + key;
|
key = modifiers + key;
|
||||||
|
|
||||||
@@ -1465,12 +1483,7 @@ var handleEnter = function ( self, shiftKey, range ) {
|
|||||||
// just play it safe and insert a <br>.
|
// just play it safe and insert a <br>.
|
||||||
if ( !block || shiftKey || /^T[HD]$/.test( block.nodeName ) ) {
|
if ( !block || shiftKey || /^T[HD]$/.test( block.nodeName ) ) {
|
||||||
// If inside an <a>, move focus out
|
// If inside an <a>, move focus out
|
||||||
parent = getNearest( range.endContainer, root, 'A' );
|
moveRangeBoundaryOutOf( range, 'A', root );
|
||||||
if ( parent ) {
|
|
||||||
parent = parent.parentNode;
|
|
||||||
moveRangeBoundariesUpTree( range, parent, parent, root );
|
|
||||||
range.collapse( false );
|
|
||||||
}
|
|
||||||
insertNodeInRange( range, self.createElement( 'BR' ) );
|
insertNodeInRange( range, self.createElement( 'BR' ) );
|
||||||
range.collapse( false );
|
range.collapse( false );
|
||||||
self.setSelection( range );
|
self.setSelection( range );
|
||||||
@@ -1821,16 +1834,45 @@ if ( !isMac ) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeIndentationLevel = function ( methodIfInQuote, methodIfInList ) {
|
||||||
|
return function ( self, event ) {
|
||||||
|
event.preventDefault();
|
||||||
|
var path = self.getPath();
|
||||||
|
if ( /(?:^|>)BLOCKQUOTE/.test( path ) ||
|
||||||
|
!/(?:^|>)[OU]L/.test( path ) ) {
|
||||||
|
self[ methodIfInQuote ]();
|
||||||
|
} else {
|
||||||
|
self[ methodIfInList ]();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleList = function ( listRegex, methodIfNotInList ) {
|
||||||
|
return function ( self, event ) {
|
||||||
|
event.preventDefault();
|
||||||
|
var path = self.getPath();
|
||||||
|
if ( !listRegex.test( path ) ) {
|
||||||
|
self[ methodIfNotInList ]();
|
||||||
|
} else {
|
||||||
|
self.removeList();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
keyHandlers[ ctrlKey + 'b' ] = mapKeyToFormat( 'B' );
|
keyHandlers[ ctrlKey + 'b' ] = mapKeyToFormat( 'B' );
|
||||||
keyHandlers[ ctrlKey + 'i' ] = mapKeyToFormat( 'I' );
|
keyHandlers[ ctrlKey + 'i' ] = mapKeyToFormat( 'I' );
|
||||||
keyHandlers[ ctrlKey + 'u' ] = mapKeyToFormat( 'U' );
|
keyHandlers[ ctrlKey + 'u' ] = mapKeyToFormat( 'U' );
|
||||||
keyHandlers[ ctrlKey + 'shift-7' ] = mapKeyToFormat( 'S' );
|
keyHandlers[ ctrlKey + 'shift-7' ] = mapKeyToFormat( 'S' );
|
||||||
keyHandlers[ ctrlKey + 'shift-5' ] = mapKeyToFormat( 'SUB', { tag: 'SUP' } );
|
keyHandlers[ ctrlKey + 'shift-5' ] = mapKeyToFormat( 'SUB', { tag: 'SUP' } );
|
||||||
keyHandlers[ ctrlKey + 'shift-6' ] = mapKeyToFormat( 'SUP', { tag: 'SUB' } );
|
keyHandlers[ ctrlKey + 'shift-6' ] = mapKeyToFormat( 'SUP', { tag: 'SUB' } );
|
||||||
keyHandlers[ ctrlKey + 'shift-8' ] = mapKeyTo( 'makeUnorderedList' );
|
keyHandlers[ ctrlKey + 'shift-8' ] =
|
||||||
keyHandlers[ ctrlKey + 'shift-9' ] = mapKeyTo( 'makeOrderedList' );
|
toggleList( /(?:^|>)UL/, 'makeUnorderedList' );
|
||||||
keyHandlers[ ctrlKey + '[' ] = mapKeyTo( 'decreaseQuoteLevel' );
|
keyHandlers[ ctrlKey + 'shift-9' ] =
|
||||||
keyHandlers[ ctrlKey + ']' ] = mapKeyTo( 'increaseQuoteLevel' );
|
toggleList( /(?:^|>)OL/, 'makeOrderedList' );
|
||||||
|
keyHandlers[ ctrlKey + '[' ] =
|
||||||
|
changeIndentationLevel( 'decreaseQuoteLevel', 'decreaseListLevel' );
|
||||||
|
keyHandlers[ ctrlKey + ']' ] =
|
||||||
|
changeIndentationLevel( 'increaseQuoteLevel', 'increaseListLevel' );
|
||||||
keyHandlers[ ctrlKey + 'd' ] = mapKeyTo( 'toggleCode' );
|
keyHandlers[ ctrlKey + 'd' ] = mapKeyTo( 'toggleCode' );
|
||||||
keyHandlers[ ctrlKey + 'y' ] = mapKeyTo( 'redo' );
|
keyHandlers[ ctrlKey + 'y' ] = mapKeyTo( 'redo' );
|
||||||
keyHandlers[ ctrlKey + 'z' ] = mapKeyTo( 'undo' );
|
keyHandlers[ ctrlKey + 'z' ] = mapKeyTo( 'undo' );
|
||||||
@@ -4417,6 +4459,12 @@ proto.insertHTML = function ( html, isPaste ) {
|
|||||||
this._docWasChanged();
|
this._docWasChanged();
|
||||||
}
|
}
|
||||||
range.collapse( false );
|
range.collapse( false );
|
||||||
|
|
||||||
|
// After inserting the fragment, check whether the cursor is inside
|
||||||
|
// an <a> element and if so if there is an equivalent cursor
|
||||||
|
// position after the <a> element. If there is, move it there.
|
||||||
|
moveRangeBoundaryOutOf( range, 'A', root );
|
||||||
|
|
||||||
this._ensureBottomLine();
|
this._ensureBottomLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4947,4 +4995,4 @@ if ( typeof exports === 'object' ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}( document ) );
|
}( document ) );
|
||||||
@@ -182,7 +182,6 @@
|
|||||||
openNote(){
|
openNote(){
|
||||||
const noteId = this.item.note_id
|
const noteId = this.item.note_id
|
||||||
this.$router.push('/notes/open/'+noteId)
|
this.$router.push('/notes/open/'+noteId)
|
||||||
this.$bus.$emit('open_note', noteId)
|
|
||||||
},
|
},
|
||||||
openEditAttachments(){
|
openEditAttachments(){
|
||||||
const noteId = this.item.note_id
|
const noteId = this.item.note_id
|
||||||
|
|||||||
@@ -24,9 +24,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeMount(){
|
beforeMount(){
|
||||||
this.$bus.$on('reset_fast_filters', () => {
|
// this.$bus.$on('reset_fast_filters', () => {
|
||||||
this.orderString = 'Order by Last Edited'
|
// this.orderString = 'Order by Last Edited'
|
||||||
})
|
// })
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
displayString(){
|
displayString(){
|
||||||
|
|||||||
@@ -19,9 +19,10 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
.menu-logo-display {
|
.menu-logo-display {
|
||||||
width: 25px;
|
width: 27px;
|
||||||
margin: 5px 0 0 42px;
|
margin: 5px 0 0 41px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item {
|
.menu-item {
|
||||||
@@ -79,15 +80,17 @@
|
|||||||
background-color: var(--small_element_bg_color);
|
background-color: var(--small_element_bg_color);
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
border-color: var(--border_color);
|
border-color: var(--border_color);
|
||||||
padding: 5px 1rem 5px;
|
/*padding: 5px 1rem 5px;*/
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
.place-holder {
|
.place-holder {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
.top-menu-bar img {
|
.logo-display {
|
||||||
width: 30px;
|
width: 27px;
|
||||||
height: 30px;
|
height: auto;
|
||||||
}
|
}
|
||||||
.version-display {
|
.version-display {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -101,6 +104,19 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mobile-button {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 2em;
|
||||||
|
padding: 6px 3px 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.mobile-button.active {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.mobile-button i {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -110,45 +126,37 @@
|
|||||||
|
|
||||||
<!-- collapsed menu, appears as a bar -->
|
<!-- collapsed menu, appears as a bar -->
|
||||||
<div class="top-menu-bar" v-if="(collapsed || mobile) && !menuOpen">
|
<div class="top-menu-bar" v-if="(collapsed || mobile) && !menuOpen">
|
||||||
<div class="ui grid">
|
|
||||||
|
|
||||||
<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 v-if="loggedIn" class="ui large basic compact icon button" to="/notes" v-on:click.native="emitReloadEvent()">
|
|
||||||
<i class="green home icon"></i>
|
|
||||||
</router-link> -->
|
|
||||||
|
|
||||||
<router-link v-if="loggedIn" class="ui basic icon button" exact-active-class="active" to="/attachments">
|
|
||||||
<i class="open folder outline icon"></i>
|
|
||||||
</router-link>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="two wide center aligned bottom aligned column">
|
|
||||||
<img loading="lazy" src="/api/static/assets/logo.svg" alt="Solid Scribe Logo">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="seven wide right aligned column">
|
|
||||||
|
|
||||||
<div v-on:click="toggleNightMode" class="ui large basic compact icon button">
|
|
||||||
<i class="green moon outline icon"></i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- mobile create note button -->
|
|
||||||
<span v-if="loggedIn">
|
|
||||||
<span v-if="!disableNewNote" @click="createNote" class="ui large green compact icon button">
|
|
||||||
<i class="plus icon"></i>
|
|
||||||
</span>
|
|
||||||
<span v-if="disableNewNote" class="ui large basic compact icon button">
|
|
||||||
<i class="grey plus icon"></i>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="mobile-button">
|
||||||
|
<i class="green link bars icon" v-on:click="collapseMenu"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mobile-button"></div>
|
||||||
|
|
||||||
|
<router-link v-if="loggedIn" to="/quick" class="mobile-button" exact-active-class="active">
|
||||||
|
<i class="green sticky note outline icon"></i>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<router-link v-if="loggedIn" class="mobile-button" exact-active-class="active" to="/notes" v-on:click.native="emitReloadEvent()">
|
||||||
|
<logo class="logo-display" color="var(--main-accent)" />
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<router-link v-if="loggedIn" class="mobile-button" exact-active-class="active" to="/attachments">
|
||||||
|
<i class="green open folder outline icon"></i>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<div class="mobile-button"></div>
|
||||||
|
|
||||||
|
<!-- mobile create note button -->
|
||||||
|
<span v-if="loggedIn">
|
||||||
|
<span v-if="!disableNewNote" @click="createNote" class="mobile-button">
|
||||||
|
<i class="green plus icon"></i>
|
||||||
|
</span>
|
||||||
|
<span v-if="disableNewNote" class="mobile-button">
|
||||||
|
<i class="grey plus icon"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="shade" v-if="mobile && !collapsed" v-on:click="collapseMenu"></div>
|
<div class="shade" v-if="mobile && !collapsed" v-on:click="collapseMenu"></div>
|
||||||
@@ -161,7 +169,7 @@
|
|||||||
<div class="menu-section" v-on:click="collapseMenu">
|
<div class="menu-section" v-on:click="collapseMenu">
|
||||||
<!-- <div class="menu-item menu-button" > -->
|
<!-- <div class="menu-item menu-button" > -->
|
||||||
<i class="white angle left icon"></i>
|
<i class="white angle left icon"></i>
|
||||||
<img class="menu-logo-display" loading="lazy" src="/api/static/assets/logo.svg" alt="Solid Scribe Logo">
|
<logo class="menu-logo-display" color="var(--main-accent)" />
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -236,14 +244,14 @@
|
|||||||
|
|
||||||
<div class="menu-section">
|
<div class="menu-section">
|
||||||
<router-link class="menu-item menu-button" exact-active-class="active" to="/help">
|
<router-link class="menu-item menu-button" exact-active-class="active" to="/help">
|
||||||
<i class="question circle outline icon"></i>Help
|
<i class="question circle outline icon"></i>Help / Terms
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="menu-section" v-if="loggedIn" :data-tooltip="`Logout ${this.$store.getters.getUsername}`" data-inverted="" data-position="right center">
|
<div class="menu-section" v-if="loggedIn" :data-tooltip="`Settings for ${this.$store.getters.getUsername}`" data-inverted="" data-position="right center">
|
||||||
<div v-on:click="destroyLoginToken" class="menu-item menu-button">
|
<router-link class="menu-item menu-button" exact-active-class="active" to="/settings">
|
||||||
<i v-if="userIcon" class="user outline icon"></i>{{ usernameDisplay }}
|
<i v-if="userIcon" class="cog icon"></i>{{ usernameDisplay }}
|
||||||
</div>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -264,6 +272,7 @@
|
|||||||
components: {
|
components: {
|
||||||
'search-input': require('@/components/SearchInput.vue').default,
|
'search-input': require('@/components/SearchInput.vue').default,
|
||||||
'counter':require('@/components/AnimatedCounterComponent.vue').default,
|
'counter':require('@/components/AnimatedCounterComponent.vue').default,
|
||||||
|
'logo':require('@/components/LogoComponent.vue').default,
|
||||||
},
|
},
|
||||||
data: function(){
|
data: function(){
|
||||||
return {
|
return {
|
||||||
@@ -274,10 +283,14 @@
|
|||||||
disableNewNote: false,
|
disableNewNote: false,
|
||||||
menuOpen: true,
|
menuOpen: true,
|
||||||
userIcon: true,
|
userIcon: true,
|
||||||
|
resizeDebounce: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeCreate: function(){
|
beforeMount(){
|
||||||
|
window.addEventListener('resize', this.resizeEventHandler)
|
||||||
|
},
|
||||||
|
beforeDestroy(){
|
||||||
|
window.removeEventListener('resize', this.resizeEventHandler)
|
||||||
},
|
},
|
||||||
mounted: function(){
|
mounted: function(){
|
||||||
this.mobile = this.$store.getters.getIsUserOnMobile
|
this.mobile = this.$store.getters.getIsUserOnMobile
|
||||||
@@ -316,6 +329,19 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
resizeEventHandler(e) {
|
||||||
|
clearTimeout(this.resizeDebounce)
|
||||||
|
this.resizeDebounce = setTimeout(() => {
|
||||||
|
|
||||||
|
this.menuOpen = false
|
||||||
|
this.collapsed = false
|
||||||
|
|
||||||
|
if(window.innerWidth < 700){
|
||||||
|
|
||||||
|
this.collapsed = true
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
},
|
||||||
menuClicked(){
|
menuClicked(){
|
||||||
//Collapse menu when item is clicked in mobile
|
//Collapse menu when item is clicked in mobile
|
||||||
if(this.mobile && !this.collapsed){
|
if(this.mobile && !this.collapsed){
|
||||||
@@ -334,28 +360,22 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
createNote(event){
|
createNote(event){
|
||||||
const title = ''
|
|
||||||
this.disableNewNote = true
|
this.disableNewNote = true
|
||||||
|
|
||||||
axios.post('/api/note/create', {title})
|
axios.post('/api/note/create', {title:''})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
||||||
if(response.data && response.data.id){
|
if(response.data && response.data.id){
|
||||||
//Redirect to note page if user is not on it
|
|
||||||
this.$bus.$emit('open_note', response.data.id)
|
//Push new note to url and it will open
|
||||||
|
this.$router.push('/notes/open/'+response.data.id)
|
||||||
|
|
||||||
this.disableNewNote = false
|
this.disableNewNote = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => { this.$bus.$emit('notification', 'Failed to create note') })
|
.catch(error => { this.$bus.$emit('notification', 'Failed to create note') })
|
||||||
},
|
},
|
||||||
destroyLoginToken() {
|
|
||||||
axios.post('/api/user/logout')
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$bus.$emit('notification', 'Logged Out')
|
|
||||||
this.$store.commit('destroyLoginToken')
|
|
||||||
this.$router.push('/')
|
|
||||||
}, 200)
|
|
||||||
},
|
|
||||||
toggleNightMode(){
|
toggleNightMode(){
|
||||||
this.$store.commit('toggleNightMode')
|
this.$store.commit('toggleNightMode')
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="loading-container">
|
<div class="loading-container">
|
||||||
<svg version="1.1" id="L6" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
|
<svg version="1.1" id="L6" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
|
||||||
<rect fill="none" :stroke="$store.getters.getIsNightMode > 0 ? '#FFF':'#16ab39'" stroke-width="4" x="25" y="25" width="50" height="50" rx="5">
|
<rect fill="none" :stroke="$store.getters.getIsNightMode > 0 ? '#FFF':'var(--main-accent)'" stroke-width="4" x="25" y="25" width="50" height="50" rx="5">
|
||||||
<animateTransform
|
<animateTransform
|
||||||
attributeName="transform"
|
attributeName="transform"
|
||||||
dur="0.5s"
|
dur="0.5s"
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
attributeType="XML"
|
attributeType="XML"
|
||||||
begin="rectBox.end"/>
|
begin="rectBox.end"/>
|
||||||
</rect>
|
</rect>
|
||||||
<rect x="25" y="25" :fill="$store.getters.getIsNightMode > 0 ? '#FFF':'#16ab39'" width="50" height="50">
|
<rect x="25" y="25" :fill="$store.getters.getIsNightMode > 0 ? '#FFF':'var(--main-accent)'" width="50" height="50">
|
||||||
<animate
|
<animate
|
||||||
attributeName="height"
|
attributeName="height"
|
||||||
dur="1.3s"
|
dur="1.3s"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div v-on:keyup.enter="login()">
|
<div v-on:keyup.enter="login()">
|
||||||
@@ -14,6 +15,11 @@
|
|||||||
<input v-model="password" type="password" name="password" placeholder="Password">
|
<input v-model="password" type="password" name="password" placeholder="Password">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field" v-if="require2FA">
|
||||||
|
<div class="ui input">
|
||||||
|
<input v-model="authToken" ref="authForm" type="text" name="authToken" placeholder="Authorization Token">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="sixteen wide field">
|
<div class="sixteen wide field">
|
||||||
<div class="ui fluid buttons">
|
<div class="ui fluid buttons">
|
||||||
<div :class="{ 'disabled':(username.length == 0 || password.length == 0)}" v-on:click="login()" class="ui green button">
|
<div :class="{ 'disabled':(username.length == 0 || password.length == 0)}" v-on:click="login()" class="ui green button">
|
||||||
@@ -27,34 +33,54 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sixteen wide column">
|
||||||
|
<span class="small-terms">
|
||||||
|
By signing up you agree to Solid Scribe's
|
||||||
|
<router-link to="/help">
|
||||||
|
Terms of Use
|
||||||
|
</router-link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Thin form display -->
|
<!-- Thin form display -->
|
||||||
<div v-if="thin" class="ui small form">
|
<div v-if="thin" class="ui small form">
|
||||||
<div class="fields">
|
<div class="equal width fields">
|
||||||
<div class="four wide field">
|
<div class="field">
|
||||||
<div class="ui input">
|
<div class="ui input">
|
||||||
<input ref="nameForm" v-model="username" type="text" name="email" placeholder="Username or E-mail">
|
<input ref="nameForm" v-model="username" type="text" name="email" placeholder="Username or E-mail">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="four wide field">
|
<div class="field">
|
||||||
<div class="ui input">
|
<div class="ui input">
|
||||||
<input v-model="password" type="password" name="password" placeholder="Password">
|
<input v-model="password" type="password" name="password" placeholder="Password">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="four wide field">
|
<div class="field" v-if="require2FA">
|
||||||
|
<div class="ui input">
|
||||||
|
<input v-model="authToken" ref="authForm" type="text" name="authToken" placeholder="Authorization Token">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- hide this field if someone is logging in with 2FA -->
|
||||||
|
<div class="field" v-if="!require2FA">
|
||||||
<div v-on:click="register()" class="ui fluid green button">
|
<div v-on:click="register()" class="ui fluid green button">
|
||||||
<i class="plug icon"></i>
|
<i class="plug icon"></i>
|
||||||
Sign Up
|
Sign Up
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="four wide field">
|
<div class="field">
|
||||||
<div v-on:click="login()" class="ui fluid button">
|
<div v-on:click="login()" class="ui fluid button">
|
||||||
<i class="power icon"></i>
|
<i class="power icon"></i>
|
||||||
Login
|
Login
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="small-terms">
|
||||||
|
By signing up you agree to Solid Scribe's
|
||||||
|
<router-link to="/help">
|
||||||
|
Terms of Use
|
||||||
|
</router-link>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -83,7 +109,9 @@
|
|||||||
return {
|
return {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
username: '',
|
username: '',
|
||||||
password: ''
|
password: '',
|
||||||
|
authToken: '',
|
||||||
|
require2FA: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -139,14 +167,30 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.post('/api/user/login', {'username': this.username, 'password': this.password})
|
axios.post('/api/user/login', {'username': this.username, 'password': this.password, 'authToken':this.authToken })
|
||||||
.then(({data}) => {
|
.then(({data}) => {
|
||||||
|
|
||||||
if(data == false){
|
//Enable 2FA on form
|
||||||
this.$bus.$emit('notification', 'Unable to Login - Incorrect Username or Password')
|
if(data.success == false && data.verificationRequired == true && this.require2FA == false){
|
||||||
|
this.$bus.$emit('notification', data.message)
|
||||||
|
this.require2FA = true
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.authForm.focus()
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.finalizeLogin(data)
|
if(data.success == false){
|
||||||
|
this.$bus.$emit('notification', data.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data.success){
|
||||||
|
this.finalizeLogin(data)
|
||||||
|
return
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.$bus.$emit('notification', 'Unable to Login - Incorrect Username or Password')
|
this.$bus.$emit('notification', 'Unable to Login - Incorrect Username or Password')
|
||||||
@@ -154,4 +198,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style type="text/css" scoped="true">
|
||||||
|
.small-terms {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: right;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
id="svg8"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 132.29166 132.29167"
|
||||||
|
height="500"
|
||||||
|
width="500">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
style="display:inline"
|
||||||
|
transform="translate(0,-164.70832)"
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
class="darken-accent"
|
||||||
|
id="path3813-4"
|
||||||
|
d="m 56.22733,165.36641 -55.56249926,15.875 8e-7,63.5 47.62499846,11.90625 v 27.78125 l -47.76066333,-13.9757 0.13566407,10.00695 55.56249926,15.875 v -47.625 l -47.6249985,-11.90625 -8e-7,-47.625 47.7606633,-13.94121 c 0.135664,-2.30629 -0.135664,-9.87129 -0.135664,-9.87129 z"
|
||||||
|
:style="`fill:${displayColor};fill-opacity:1;stroke:none;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1`" />
|
||||||
|
<path
|
||||||
|
class="brighten-accent"
|
||||||
|
id="path4563"
|
||||||
|
d="m 20.508581,220.92891 c 15.265814,-14.23899 27.809717,-7.68002 39.687499,3.96875 v -7.9375 C 51.75093,200.8366 37.512584,206.01499 20.508581,205.05391 Z"
|
||||||
|
:style="`fill:${displayColor};fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1`" />
|
||||||
|
<path
|
||||||
|
class="brighten-accent"
|
||||||
|
id="path4563-6"
|
||||||
|
d="m 111.78985,220.92891 c -15.265834,-14.23899 -27.809737,-7.68002 -39.68752,3.96875 v -7.9375 c 8.445151,-16.12356 22.683497,-10.94517 39.68752,-11.90625 z"
|
||||||
|
:style="`display:inline;fill:${displayColor};fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1`" />
|
||||||
|
<path
|
||||||
|
class="brighten-accent"
|
||||||
|
id="path3813-4-2"
|
||||||
|
d="m 76.07108,165.36641 55.5625,15.875 v 63.5 l -47.625,11.90625 v 27.78125 l 47.76067,-13.9757 -0.13567,10.00695 -55.5625,15.875 v -47.625 l 47.625,-11.90626 V 189.17891 L 75.93542,175.2377 c -0.13567,-2.30629 0.13566,-9.87129 0.13566,-9.87129 z"
|
||||||
|
:style="`display:inline;fill:${displayColor};fill-opacity:1;stroke:none;stroke-width:0.52916676;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1`" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'LoadingIcon',
|
||||||
|
props:[ 'color' ],
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
displayColor: '#21BA45', //Default green color
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
//Set color if passed
|
||||||
|
if(this.color){
|
||||||
|
this.displayColor = this.color
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style type="text/css" scoped>
|
||||||
|
.darken-accent {
|
||||||
|
filter: brightness(62%);
|
||||||
|
}
|
||||||
|
.brighten-accent {
|
||||||
|
filter: saturate(145%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,9 +2,8 @@
|
|||||||
<!-- 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 -->
|
||||||
<div
|
<div
|
||||||
id="InputNotes"
|
id="InputNotes"
|
||||||
class="master-note-edit full-focus"
|
class="master-note-edit full-focus position-0"
|
||||||
@keyup.esc="close()"
|
@keyup.esc="closeButtonAction()"
|
||||||
:class="[ 'position-'+position ]"
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<!-- Giant Edit Note Menu -->
|
<!-- Giant Edit Note Menu -->
|
||||||
@@ -14,7 +13,7 @@
|
|||||||
<div class="edit-spacer"></div>
|
<div class="edit-spacer"></div>
|
||||||
|
|
||||||
<div class="menu-top-half">
|
<div class="menu-top-half">
|
||||||
<div class="edit-button" v-on:click="close()" data-tooltip="Close" data-position="bottom center" data-inverted>
|
<div class="edit-button" v-on:click="closeButtonAction()" data-tooltip="Close" data-position="bottom center" data-inverted>
|
||||||
<i class="close icon"></i>
|
<i class="close icon"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -61,7 +60,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="menu-bottom-half">
|
<div class="menu-bottom-half">
|
||||||
|
|
||||||
|
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/table`)" data-tooltip="Insert Table" data-position="bottom center" data-inverted>
|
||||||
|
<i class="border all icon"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-button" v-on:click="insertDivide()" data-tooltip="Insert Divide" data-position="bottom center" data-inverted>
|
||||||
|
<i class="grip lines icon"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-button" v-on:click="insertTable(4,4)" data-tooltip="Insert Table" data-position="bottom center" data-inverted>
|
||||||
|
<i class="book dead icon"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="edit-button" v-on:click="removeFormatting()" data-tooltip="Remove Formatting" data-position="bottom center" data-inverted>
|
<div class="edit-button" v-on:click="removeFormatting()" data-tooltip="Remove Formatting" data-position="bottom center" data-inverted>
|
||||||
<i class="remove format icon"></i>
|
<i class="remove format icon"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -95,7 +106,8 @@
|
|||||||
|
|
||||||
<div class="edit-divide"></div>
|
<div class="edit-divide"></div>
|
||||||
|
|
||||||
<!-- <div class="edit-button" v-on:click="onToggleArchived()" :data-tooltip="archived == 1?'Move to main list':'Move to Archive'" data-position="bottom center" data-inverted>
|
<!--
|
||||||
|
<div class="edit-button" v-on:click="onToggleArchived()" :data-tooltip="archived == 1?'Move to main list':'Move to Archive'" data-position="bottom center" data-inverted>
|
||||||
<span v-if="archived == 1"><i class="green archive icon"></i></span>
|
<span v-if="archived == 1"><i class="green archive icon"></i></span>
|
||||||
<span v-if="archived != 1"><i class="archive icon"></i></span>
|
<span v-if="archived != 1"><i class="archive icon"></i></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -105,17 +117,13 @@
|
|||||||
<span v-if="pinned != 1"><i class="pin icon"></i></span>
|
<span v-if="pinned != 1"><i class="pin icon"></i></span>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<!-- data-tooltip="Files on note" -->
|
|
||||||
<!-- <div v-if="attachmentCount > 0" class="edit-button" v-on:click="openEditAttachment" data-tooltip="Files" data-position="bottom center" data-inverted>
|
|
||||||
<i class="folder icon"></i>
|
|
||||||
{{ attachmentCount }}
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<div class="edit-button" v-if="usersOnNote > 1">
|
<div class="edit-button" v-if="usersOnNote > 1">
|
||||||
<i class="green eye icon"></i> {{ usersOnNote }}
|
<i class="green eye icon"></i> {{ usersOnNote }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <div class="edit-button" v-on:click="simulateTyping()">
|
<!--
|
||||||
|
<div class="edit-button" v-on:click="simulateTyping()">
|
||||||
<i class="purple bolt icon"></i>
|
<i class="purple bolt icon"></i>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
@@ -132,7 +140,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bottom-edit-menu"></div>
|
<div class="bottom-edit-menu"></div>
|
||||||
@@ -163,7 +170,7 @@
|
|||||||
</textarea>
|
</textarea>
|
||||||
|
|
||||||
<!-- Squire Box -->
|
<!-- Squire Box -->
|
||||||
<div id="squire-id" class="squire-box" ref="squirebox" placeholder="Note Text"></div>
|
<div id="squire-id" class="squire-box" ref="squirebox" placeholder="Type Note Here"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -224,19 +231,19 @@
|
|||||||
<div class="sixteen wide column">
|
<div class="sixteen wide column">
|
||||||
<div class="ui labeled icon fluid basic button" v-on:click="sortList">
|
<div class="ui labeled icon fluid basic button" v-on:click="sortList">
|
||||||
<i class="sort amount up icon"></i>
|
<i class="sort amount up icon"></i>
|
||||||
Sort List items (Move checked to bottom)
|
Sort List
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="eight wide column">
|
<div class="eight wide column">
|
||||||
<div class="ui labeled icon fluid basic button" v-on:click="deleteCompletedListItems">
|
<div class="ui labeled icon fluid basic button" v-on:click="deleteCompletedListItems">
|
||||||
<i class="trash icon"></i>
|
<i class="trash icon"></i>
|
||||||
Delete Checked Items
|
Delete Checked
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="eight wide column">
|
<div class="eight wide column">
|
||||||
<div class="ui labeled icon fluid basic button" v-on:click="uncheckAllListItems">
|
<div class="ui labeled icon fluid basic button" v-on:click="uncheckAllListItems">
|
||||||
<i class="list ul icon"></i>
|
<i class="list ul icon"></i>
|
||||||
Uncheck all Checked items
|
Uncheck All
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="eight wide column">
|
<div class="eight wide column">
|
||||||
@@ -245,6 +252,15 @@
|
|||||||
Simple Math
|
Simple Math
|
||||||
</div>
|
</div>
|
||||||
</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 }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="sixteen wide column" v-if="rawTextId > 0">
|
<div class="sixteen wide column" v-if="rawTextId > 0">
|
||||||
<h2>Share Note</h2>
|
<h2>Share Note</h2>
|
||||||
<share-note-component
|
<share-note-component
|
||||||
@@ -257,13 +273,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</side-slide-menu>
|
</side-slide-menu>
|
||||||
|
|
||||||
|
<!-- create table option -->
|
||||||
|
<side-slide-menu v-if="table" v-on:close="table = false; fetchNoteTags()" name="table" :style-object="styleObject">
|
||||||
|
<div class="ui basic segment">
|
||||||
|
Create a table
|
||||||
|
</div>
|
||||||
|
</side-slide-menu>
|
||||||
|
|
||||||
<!-- Show side shades if user is on desktop only -->
|
<!-- Show side shades if user is on desktop only -->
|
||||||
<div class="full-focus-shade shade1"
|
<div class="full-focus-shade shade1"
|
||||||
:class="{ 'slide-out-left':sizeDown }"
|
:class="{ 'slide-out-left':sizeDown }"
|
||||||
v-on:click="close()"></div>
|
v-on:click="closeButtonAction()"></div>
|
||||||
<div class="full-focus-shade shade2"
|
<div class="full-focus-shade shade2"
|
||||||
:class="{ 'slide-out-right':sizeDown }"
|
:class="{ 'slide-out-right':sizeDown }"
|
||||||
v-on:click="close()"></div>
|
v-on:click="closeButtonAction()"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -335,6 +358,7 @@
|
|||||||
images: false,
|
images: false,
|
||||||
options: false,
|
options: false,
|
||||||
colorpicker: false,
|
colorpicker: false,
|
||||||
|
table: false,
|
||||||
|
|
||||||
//Diff text/sync text variables
|
//Diff text/sync text variables
|
||||||
diffTextTimeout: null,
|
diffTextTimeout: null,
|
||||||
@@ -355,7 +379,7 @@
|
|||||||
//Handle changes in URL to
|
//Handle changes in URL to
|
||||||
|
|
||||||
if(newVal.id == undefined || newVal.id != this.noteid){
|
if(newVal.id == undefined || newVal.id != this.noteid){
|
||||||
this.close()
|
// this.closeButtonAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
//Reset all note menus on URL change
|
//Reset all note menus on URL change
|
||||||
@@ -364,6 +388,7 @@
|
|||||||
this.tags = false
|
this.tags = false
|
||||||
this.options = false
|
this.options = false
|
||||||
this.images = false
|
this.images = false
|
||||||
|
this.table = false
|
||||||
|
|
||||||
//If a menu value is set, open it
|
//If a menu value is set, open it
|
||||||
if(newVal.openMenu){
|
if(newVal.openMenu){
|
||||||
@@ -394,18 +419,20 @@
|
|||||||
|
|
||||||
document.removeEventListener('visibilitychange', this.checkForUpdatedNote)
|
document.removeEventListener('visibilitychange', this.checkForUpdatedNote)
|
||||||
|
|
||||||
// if(this.editor){
|
//Obliterate squire instance
|
||||||
this.editor.destroy()
|
this.editor.destroy()
|
||||||
// }
|
|
||||||
|
|
||||||
|
this.close()
|
||||||
|
|
||||||
},
|
},
|
||||||
mounted: function() {
|
mounted: function() {
|
||||||
|
|
||||||
|
//Show loading for a minimum time
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
this.forceShowLoading = false
|
this.forceShowLoading = false
|
||||||
}, 500)
|
}, 500)
|
||||||
|
|
||||||
document.addEventListener('visibilitychange', this.checkForUpdatedNote)
|
// document.addEventListener('visibilitychange', this.checkForUpdatedNote)
|
||||||
|
|
||||||
//Init squire as early as possible
|
//Init squire as early as possible
|
||||||
this.editor = new Squire( this.$refs.squirebox, {blockTag: 'p' })
|
this.editor = new Squire( this.$refs.squirebox, {blockTag: 'p' })
|
||||||
@@ -599,6 +626,30 @@
|
|||||||
|
|
||||||
this.editor.addEventListener('keydown', event => {
|
this.editor.addEventListener('keydown', event => {
|
||||||
|
|
||||||
|
//Tab to increase quote level, tab + shigt to decrease quote level
|
||||||
|
const keyCode = event.key
|
||||||
|
if(keyCode == 'Tab'){
|
||||||
|
|
||||||
|
if(event.shiftKey){
|
||||||
|
this.editor.decreaseQuoteLevel()
|
||||||
|
} else {
|
||||||
|
this.editor.increaseQuoteLevel()
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//Save on pressing CTRL/CMD + S
|
||||||
|
if(keyCode == 's' && (event.ctrlKey || event.metaKey) ){
|
||||||
|
|
||||||
|
this.$bus.$emit('notification', 'Note Saved')
|
||||||
|
this.save()
|
||||||
|
|
||||||
|
event.preventDefault()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
//Prevent new list items from having
|
//Prevent new list items from having
|
||||||
this.$nextTick( () => {
|
this.$nextTick( () => {
|
||||||
//Wait a moment to get item under cursor
|
//Wait a moment to get item under cursor
|
||||||
@@ -612,10 +663,6 @@
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
this.editor.addEventListener('keydown', event => {
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
//Bind event handlers
|
//Bind event handlers
|
||||||
this.editor.addEventListener('keyup', event => {
|
this.editor.addEventListener('keyup', event => {
|
||||||
|
|
||||||
@@ -682,7 +729,7 @@
|
|||||||
//Block notes you don't have access to from opening
|
//Block notes you don't have access to from opening
|
||||||
if(response.data === false){
|
if(response.data === false){
|
||||||
this.$bus.$emit('notification', 'Error opening Note')
|
this.$bus.$emit('notification', 'Error opening Note')
|
||||||
this.close(true)
|
this.close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -915,18 +962,21 @@
|
|||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
},
|
},
|
||||||
close(force = false){
|
closeButtonAction(){
|
||||||
|
this.sizeDown = true
|
||||||
|
//This timeout allows animation to play before closing
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$router.push('/notes')
|
||||||
|
}, 300)
|
||||||
|
},
|
||||||
|
close(){
|
||||||
|
|
||||||
// force = true
|
// force = true
|
||||||
|
|
||||||
// console.log(`Close Note ${this.noteid} -> force: ${force}, modified: ${this.modified}`)
|
// console.log(`Close Note ${this.noteid} -> force: ${force}, modified: ${this.modified}`)
|
||||||
|
|
||||||
if(force){
|
//Skip everything if foce close is true. Note will just die.
|
||||||
this.$bus.$emit('close_active_note', {
|
if(this.currentNoteId == 0){ return }
|
||||||
noteId: this.noteid, modified: this.modified
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingMessage = 'Saving...'
|
this.loadingMessage = 'Saving...'
|
||||||
this.loading = true
|
this.loading = true
|
||||||
@@ -938,14 +988,10 @@
|
|||||||
axios.post('/api/note/reindex')
|
axios.post('/api/note/reindex')
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sizeDown = true
|
this.$bus.$emit('close_active_note', {
|
||||||
//This timeout allows animation to play before closing
|
noteId: this.noteid, modified: this.modified
|
||||||
setTimeout(() => {
|
})
|
||||||
this.$bus.$emit('close_active_note', {
|
return
|
||||||
noteId: this.noteid, modified: this.modified
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}, 300)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
destroyWebSockets(){
|
destroyWebSockets(){
|
||||||
@@ -1005,8 +1051,8 @@
|
|||||||
let element = this.$refs.titleTextarea
|
let element = this.$refs.titleTextarea
|
||||||
let padding = 0
|
let padding = 0
|
||||||
|
|
||||||
element.style.height = 'auto';
|
element.style.height = 'auto'
|
||||||
element.style.height = (element.scrollHeight + padding) +'px';
|
element.style.height = (element.scrollHeight + padding) +'px'
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1152,7 +1198,7 @@
|
|||||||
background-color: var(--menu-accent);
|
background-color: var(--menu-accent);
|
||||||
}
|
}
|
||||||
.edit-active {
|
.edit-active {
|
||||||
background-color: #21BA45;
|
background-color: var(--main-accent);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
.edit-divide {
|
.edit-divide {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="time-ago-display" :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }">
|
<span class="time-ago-display" :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }">
|
||||||
{{$helpers.timeAgo(note.updated)}}
|
{{$helpers.timeAgo( note.updated )}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="teeny-buttons" :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }">
|
<span class="teeny-buttons" :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }">
|
||||||
@@ -234,12 +234,12 @@
|
|||||||
},
|
},
|
||||||
justClosed(){
|
justClosed(){
|
||||||
|
|
||||||
//Scroll note into view
|
// Scroll note into view
|
||||||
// this.$el.scrollIntoView({
|
this.$el.scrollIntoView({
|
||||||
// behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
// block: 'center',
|
block: 'center',
|
||||||
// inline: 'center'
|
inline: 'center'
|
||||||
// })
|
})
|
||||||
|
|
||||||
//After scroll, trigger green outline animation
|
//After scroll, trigger green outline animation
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -356,13 +356,14 @@
|
|||||||
|
|
||||||
/*Strict font sizes for card display*/
|
/*Strict font sizes for card display*/
|
||||||
.small-text {
|
.small-text {
|
||||||
max-height: 261px;
|
max-height: 267px;
|
||||||
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.small-text, .small-text > p, .small-text > h1, .small-text > h2 {
|
.small-text, .small-text > p, .small-text > h1, .small-text > h2 {
|
||||||
/*font-size: 1.0em !important;*/
|
/*font-size: 1.0em !important;*/
|
||||||
font-size: 16px !important;
|
font-size: 14px !important;
|
||||||
}
|
}
|
||||||
.small-text > p, , .small-text > h1, .small-text > h2 {
|
.small-text > p, , .small-text > h1, .small-text > h2 {
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
@@ -370,7 +371,7 @@
|
|||||||
.big-text > p:first-child,
|
.big-text > p:first-child,
|
||||||
.big-text > h1, .big-text > h2 {
|
.big-text > h1, .big-text > h2 {
|
||||||
/*font-size: 1.3em !important;*/
|
/*font-size: 1.3em !important;*/
|
||||||
font-size: 17px !important;
|
font-size: 20px !important;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
@@ -415,14 +416,14 @@
|
|||||||
border-color: var(--border_color);
|
border-color: var(--border_color);
|
||||||
/*width: calc(33.333% - 10px);*/
|
/*width: calc(33.333% - 10px);*/
|
||||||
width: calc(25% - 10px);
|
width: calc(25% - 10px);
|
||||||
min-width: 190px;
|
/*min-width: 190px;*/
|
||||||
min-height: 130px;
|
min-height: 130px;
|
||||||
/*transition: box-shadow 0.3s;*/
|
/*transition: box-shadow 0.3s;*/
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
line-height: 1.8rem;
|
line-height: 1.8rem;
|
||||||
letter-spacing: 0.02rem;
|
letter-spacing: 0.05rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -555,27 +556,50 @@
|
|||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tweak mobile display to show only one column */
|
/* Break points determine when display cards shrink */
|
||||||
@media only screen and (min-width: 1500px) {
|
@media only screen and (max-width: 700px) {
|
||||||
.note-title-display-card {
|
|
||||||
width: calc(20% - 10px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 740px) {
|
|
||||||
.note-title-display-card {
|
.note-title-display-card {
|
||||||
width: calc(100% + 10px);
|
width: calc(100% + 10px);
|
||||||
margin: 0px -5px 10px -5px;
|
margin: 0px -5px 10px -5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media only screen and (min-width: 700px) and (max-width: 900px) {
|
||||||
|
.note-title-display-card {
|
||||||
|
width: calc(50% - 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (min-width: 900px) and (max-width: 1100px) {
|
||||||
|
.note-title-display-card {
|
||||||
|
width: calc(33.33333% - 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (min-width: 1100px) and (max-width: 1300px) {
|
||||||
|
.note-title-display-card {
|
||||||
|
width: calc(25% - 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (min-width: 1300px) and (max-width: 1800px) {
|
||||||
|
.note-title-display-card {
|
||||||
|
width: calc(20% - 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (min-width: 1800px) {
|
||||||
|
.note-title-display-card {
|
||||||
|
width: calc(16.66666% - 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*Animations for cool border effects*/
|
/*Animations for cool border effects*/
|
||||||
@keyframes bgin {
|
@keyframes bgin {
|
||||||
0% {
|
0% {
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(to right, #21BA45 50%, #21BA45 100%), /* TopLeft to Right */
|
linear-gradient(to right, var(--main-accent) 50%, var(--main-accent) 100%), /* TopLeft to Right */
|
||||||
linear-gradient(to bottom, #21BA45 50%, #21BA45 100%), /* TopRight to Bottom */
|
linear-gradient(to bottom, var(--main-accent) 50%, var(--main-accent) 100%), /* TopRight to Bottom */
|
||||||
linear-gradient(to right, #21BA45 50%, #21BA45 100%), /* BottomLeft to Right*/
|
linear-gradient(to right, var(--main-accent) 50%, var(--main-accent) 100%), /* BottomLeft to Right*/
|
||||||
linear-gradient(to bottom, #21BA45 50%, #21BA45 100%); /* TopLeft to Bottom */
|
linear-gradient(to bottom, var(--main-accent) 50%, var(--main-accent) 100%); /* TopLeft to Bottom */
|
||||||
/*Initial state, no BG*/
|
/*Initial state, no BG*/
|
||||||
background-size: 0 4px, 4px 0, 0 4px, 4px 0;
|
background-size: 0 4px, 4px 0, 0 4px, 4px 0;
|
||||||
}
|
}
|
||||||
@@ -590,10 +614,10 @@
|
|||||||
30% {
|
30% {
|
||||||
background-size: 100% 4px, 4px 100%, 100% 4px, 4px 100%;
|
background-size: 100% 4px, 4px 100%, 100% 4px, 4px 100%;
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(to right, #21BA45 50%, #21BA45 100%), /* TopLeft to Right */
|
linear-gradient(to right, var(--main-accent) 50%, var(--main-accent) 100%), /* TopLeft to Right */
|
||||||
linear-gradient(to bottom, #21BA45 50%, #21BA45 100%), /* TopRight to Bottom */
|
linear-gradient(to bottom, var(--main-accent) 50%, var(--main-accent) 100%), /* TopRight to Bottom */
|
||||||
linear-gradient(to right, #21BA45 50%, #21BA45 100%), /* BottomLeft to Right*/
|
linear-gradient(to right, var(--main-accent) 50%, var(--main-accent) 100%), /* BottomLeft to Right*/
|
||||||
linear-gradient(to bottom, #21BA45 50%, #21BA45 100%); /* TopLeft to Bottom */
|
linear-gradient(to bottom, var(--main-accent) 50%, var(--main-accent) 100%); /* TopLeft to Bottom */
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
background-image:
|
background-image:
|
||||||
|
|||||||
@@ -98,13 +98,17 @@
|
|||||||
},
|
},
|
||||||
beforeCreate: function(){
|
beforeCreate: function(){
|
||||||
},
|
},
|
||||||
mounted: function(){
|
beforeMount(){
|
||||||
|
|
||||||
//search clear
|
//search clear
|
||||||
this.$bus.$on('reset_fast_filters', () => {
|
this.$bus.$on('reset_fast_filters', () => {
|
||||||
this.searchTerm = ''
|
this.searchTerm = ''
|
||||||
this.tagSuggestions = []
|
this.tagSuggestions = []
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
beforeDestroy(){
|
||||||
|
this.$bus.$off('reset_fast_filters')
|
||||||
|
},
|
||||||
|
mounted: function(){
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -148,7 +152,6 @@
|
|||||||
|
|
||||||
if(response.data && response.data.id){
|
if(response.data && response.data.id){
|
||||||
this.$router.push('/notes/open/'+response.data.id)
|
this.$router.push('/notes/open/'+response.data.id)
|
||||||
this.$bus.$emit('open_note', response.data.id)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => { this.$bus.$emit('notification', 'Failed to create note') })
|
.catch(error => { this.$bus.$emit('notification', 'Failed to create note') })
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const SquireButtonFunctions = {
|
|||||||
//
|
//
|
||||||
|
|
||||||
pathChangeEvent(e){
|
pathChangeEvent(e){
|
||||||
|
|
||||||
//Reset all button states
|
//Reset all button states
|
||||||
this.activeBold = false
|
this.activeBold = false
|
||||||
this.activeTitle = false
|
this.activeTitle = false
|
||||||
@@ -143,28 +144,23 @@ const SquireButtonFunctions = {
|
|||||||
// Uncheck All List Items
|
// Uncheck All List Items
|
||||||
//
|
//
|
||||||
|
|
||||||
//Close menu if user is on mobile, then sort list
|
|
||||||
if(this.$store.getters.getIsUserOnMobile){
|
|
||||||
this.options = false
|
|
||||||
}
|
|
||||||
|
|
||||||
//Fetch the container
|
//Fetch the container
|
||||||
let container = document.getElementById('squire-id')
|
let container = document.getElementById('squire-id')
|
||||||
|
|
||||||
Array.from( container.getElementsByClassName('active') ).forEach(item => {
|
Array.from( container.getElementsByClassName('active') ).forEach(item => {
|
||||||
item.classList.remove('active');
|
item.classList.remove('active');
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//Close menu if user is on mobile, then sort list
|
||||||
|
if(this.$store.getters.getIsUserOnMobile){
|
||||||
|
this.$router.go(-1)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
deleteCompletedListItems(){
|
deleteCompletedListItems(){
|
||||||
//
|
//
|
||||||
// Delete Completed List Items
|
// Delete Completed List Items
|
||||||
//
|
//
|
||||||
|
|
||||||
//Close menu if user is on mobile, then sort list
|
|
||||||
if(this.$store.getters.getIsUserOnMobile){
|
|
||||||
this.options = false
|
|
||||||
}
|
|
||||||
|
|
||||||
//Fetch the container
|
//Fetch the container
|
||||||
let container = document.getElementById('squire-id')
|
let container = document.getElementById('squire-id')
|
||||||
|
|
||||||
@@ -210,17 +206,17 @@ const SquireButtonFunctions = {
|
|||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//Close menu if user is on mobile, then sort list
|
||||||
|
if(this.$store.getters.getIsUserOnMobile){
|
||||||
|
this.$router.go(-1)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
sortList(){
|
sortList(){
|
||||||
//
|
//
|
||||||
// Sort list, checked at the bottom, unchecked at the top
|
// Sort list, checked at the bottom, unchecked at the top
|
||||||
//
|
//
|
||||||
|
|
||||||
//Close menu if user is on mobile, then sort list
|
|
||||||
if(this.$store.getters.getIsUserOnMobile){
|
|
||||||
this.options = false
|
|
||||||
}
|
|
||||||
|
|
||||||
//Fetch the container
|
//Fetch the container
|
||||||
let container = document.getElementById('squire-id')
|
let container = document.getElementById('squire-id')
|
||||||
|
|
||||||
@@ -274,17 +270,17 @@ const SquireButtonFunctions = {
|
|||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//Close menu if user is on mobile
|
||||||
|
if(this.$store.getters.getIsUserOnMobile){
|
||||||
|
this.$router.go(-1)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
calculateMath(){
|
calculateMath(){
|
||||||
//
|
//
|
||||||
// Find math in note and calculate the outcome
|
// Find math in note and calculate the outcome
|
||||||
//
|
//
|
||||||
|
|
||||||
//Close menu if user is on mobile, then sort list
|
|
||||||
if(this.$store.getters.getIsUserOnMobile){
|
|
||||||
this.options = false
|
|
||||||
}
|
|
||||||
|
|
||||||
//Fetch the container
|
//Fetch the container
|
||||||
let container = document.getElementById('squire-id')
|
let container = document.getElementById('squire-id')
|
||||||
|
|
||||||
@@ -327,6 +323,11 @@ const SquireButtonFunctions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//Close menu if user is on mobile, then sort list
|
||||||
|
if(this.$store.getters.getIsUserOnMobile){
|
||||||
|
this.$router.go(-1)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setText(inText){
|
setText(inText){
|
||||||
|
|
||||||
@@ -338,6 +339,69 @@ const SquireButtonFunctions = {
|
|||||||
|
|
||||||
return this.editor.getHTML()
|
return this.editor.getHTML()
|
||||||
},
|
},
|
||||||
|
insertDivide(){
|
||||||
|
|
||||||
|
this.editor.insertHTML(`<p><div class='divide'></div><br></p>`)
|
||||||
|
this.editor.focus()
|
||||||
|
this.editor.moveCursorToEnd()
|
||||||
|
},
|
||||||
|
insertTable(wide, tall){
|
||||||
|
console.log('Insert a table')
|
||||||
|
|
||||||
|
let tableSyntax = `
|
||||||
|
<div>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th><p><br></p></th>
|
||||||
|
<th><p><br></p></th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><p><br></p></td>
|
||||||
|
<td><p><br></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><p><br></p></td>
|
||||||
|
<td><p><br></p></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
tableSyntax = `
|
||||||
|
<span class="t-table">
|
||||||
|
<span>
|
||||||
|
<span>
|
||||||
|
<p><br></p>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<p><br></p>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<span>
|
||||||
|
<p><br></p>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<p><br></p>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<p><br></p>
|
||||||
|
`
|
||||||
|
tableSyntax = ''
|
||||||
|
tableSyntax += '<span class="t-table">'
|
||||||
|
for (let i = 0; i < tall; i++) {
|
||||||
|
|
||||||
|
for (let j = 0; j < wide; j++) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableSyntax += '</span><p><br></p>'
|
||||||
|
|
||||||
|
this.editor.insertHTML(tableSyntax)
|
||||||
|
this.editor.focus()
|
||||||
|
this.editor.moveCursorToEnd()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ui basic segment">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
<div class="sixteen wide column">
|
<div class="sixteen wide column"></div>
|
||||||
<div class="ui text container">
|
<div class="sixteen wide column squire-box">
|
||||||
<div class="ui raised segment">
|
|
||||||
|
|
||||||
|
|
||||||
<h2 class="ui dividing header">
|
<h2 class="ui dividing header">
|
||||||
<i class="green question circle outline icon"></i>
|
|
||||||
Help
|
Help
|
||||||
</h2>
|
</h2>
|
||||||
|
<div class="ui segment">
|
||||||
<!-- Content copied from note -->
|
|
||||||
<!-- https://www.solidscribe.com/#/notes/open/552 -->
|
|
||||||
|
|
||||||
<p><b>Only Note Text is Encrypted</b><br></p><blockquote><p>Only you can read your notes. Encryption is the transformation of data into a form unreadable by anyone without the password. Its purpose is to ensure privacy by keeping the information hidden from anyone for whom it is not intended, even those who can see the encrypted data. Even if every note in the database was leaked, nothing would be readable. If the government asked for your notes, it would all be gibberish. <br></p></blockquote><p><br></p><p><b>Some Data is not encrypted</b><br></p><blockquote><p>Everything isn't encrypted, to keep up ease of use. Files, Tags and Attachments are not encrypted.<br></p></blockquote><p><br></p><p><b>Searching is somewhat limited</b><br></p><blockquote><p>Since every note is encrypted, searching is limited. To maintain security, only single words can be searched. Your search index is private and Encrypted.<br></p></blockquote><p><br></p><p><b>The Scrat</b><b></b><b>ch Pad</b><b></b><br></p><blockquote><p>The Scratch Pad was designed to allow rapid input to a single note. Rather than junking up all your notes with random links, numbers or haikus, you can put them all in one place. <br></p><ul><li>All data pushed to the quick note can still be edited like a normal note.<br></li><li>Creating a new quick note will not erase your old <br></li></ul></blockquote><p><br></p><p><b>Flux Theme</b><br></p><blockquote><p>Flux theme limits the amount of blue emitted by your screen. Most things turn sepia and a filter is applied to images to make them more sepia. Less blue light at night is supposed to be helpful for falling asleep.<br></p><p>Here is some good research on the topic: <a href="https://justgetflux.com/research.html">https://justgetflux.com/research.html</a><br></p></blockquote><p><br></p><p><b>Text Editor Keyboard Shor</b><b>tcuts</b><br></p><blockquote><p>Number List - CTRL + SHIFT + 9<br></p><p>Todo List - CTRL + SHIFT + 8<br></p><p>Underline CTRL + u<br></p><p>Bold - CTRL + b<br></p><p>Quote - CRTL + i<br></p><p>Indent - CTL + ]<br></p><p>Outdent - CRTL + [<br></p><p>Undo - CTRL + z<br></p><p>Redo - CTRL + y<br></p></blockquote><p><br></p><p><b>Shared Notes and Security</b><br></p><blockquote><p>Shared notes still respect privacy but use a different security scheme. Instead of encrypting the note with your password, a shared password is created. This note then uses <a href="https://en.wikipedia.org/wiki/Public-key_cryptography">public-key cryptography</a> to share the note with other users, while still making it unreadable to anyone else. <br></p><ul><li>You can revoke any users access to a shared note at any time.<br></li><li>Notes you share can not be shared by other people.<br></li><li>If you turn off sharing, all users lose access and the note is reverted to the default security scheme.<br></li></ul></blockquote><p><b>Public Links for shared notes</b><br></p><blockquote><p>It is possible to generate a public URL for shared notes. This is a huge security risk and exposes the Encryption Key for the note. The encryption key to a shared note can easily be changed by turning off sharing, then turning it back on. <br></p><p><br></p></blockquote><p><b>Links in notes</b><br></p><blockquote><p>Links put into notes are automatically scraped. This means the data from the link will be scanned to get an image and some text from the website to help make that link more accessible in the future. You can edit the text of scarped links and any time and search for it later. <br></p></blockquote><p><br></p><p><b>Files in notes</b><br></p><blockquote><p>Files can be uploaded to notes. If its an image, the picture will be put into the note.<br></p><p>Images added to notes will have text scanned, so it can be searched (This isn't super accurate so don't rely to heavily on it.) The text can be updated at any time.<br></p></blockquote><p><br></p><p><b>Deleting notes</b><br></p><blockquote><p>When<b> </b>notes are deleted, none of the files related to the note are deleted. <br></p></blockquote><p><br></p><p><b>Daily Backups</b><br></p><blockquote><p>All notes are backed up, every night, at midnight. If there is data loss, it can be restored from a backup. If you experience some sort of cataclysmic data loss please contact the system administrator for a copy of your data or a restoration procedure. <br></p></blockquote><p><br></p>
|
<!-- Content copied from note -->
|
||||||
|
<!-- https://www.solidscribe.com/#/notes/open/552 -->
|
||||||
|
<p><b>Only Note Text is Encrypted</b><br></p><blockquote><p>Only you can read your notes. Encryption is the transformation of data into a form unreadable by anyone without the password. Its purpose is to ensure privacy by keeping the information hidden from anyone for whom it is not intended, even those who can see the encrypted data. Even if every note in the database was leaked, nothing would be readable. If the government asked for your notes, it would all be gibberish. <br></p></blockquote><p><br></p><p><b>Some Data is not encrypted</b><br></p><blockquote><p>Everything isn't encrypted, to keep up ease of use. Files, Tags and Attachments are not encrypted.<br></p></blockquote><p><br></p><p><b>Searching is somewhat limited</b><br></p><blockquote><p>Since every note is encrypted, searching is limited. To maintain security, only single words can be searched. Your search index is private and Encrypted.<br></p></blockquote><p><br></p><p><b>The Scrat</b><b></b><b>ch Pad</b><b></b><br></p><blockquote><p>The Scratch Pad was designed to allow rapid input to a single note. Rather than junking up all your notes with random links, numbers or haikus, you can put them all in one place. <br></p><ul><li>All data pushed to the quick note can still be edited like a normal note.<br></li><li>Creating a new quick note will not erase your old <br></li></ul></blockquote><p><br></p><p><b>Flux Theme</b><br></p><blockquote><p>Flux theme limits the amount of blue emitted by your screen. Most things turn sepia and a filter is applied to images to make them more sepia. Less blue light at night is supposed to be helpful for falling asleep.<br></p><p>Here is some good research on the topic: <a href="https://justgetflux.com/research.html">https://justgetflux.com/research.html</a><br></p></blockquote><p><br></p><p><b>Text Editor Keyboard Shor</b><b>tcuts</b><br></p><blockquote><p>Number List - CTRL + SHIFT + 9<br></p><p>Todo List - CTRL + SHIFT + 8<br></p><p>Underline CTRL + u<br></p><p>Bold - CTRL + b<br></p><p>Quote - CRTL + i<br></p><p>Indent - CTL + ]<br></p><p>Outdent - CRTL + [<br></p><p>Undo - CTRL + z<br></p><p>Redo - CTRL + y<br></p></blockquote><p><br></p><p><b>Shared Notes and Security</b><br></p><blockquote><p>Shared notes still respect privacy but use a different security scheme. Instead of encrypting the note with your password, a shared password is created. This note then uses <a href="https://en.wikipedia.org/wiki/Public-key_cryptography">public-key cryptography</a> to share the note with other users, while still making it unreadable to anyone else. <br></p><ul><li>You can revoke any users access to a shared note at any time.<br></li><li>Notes you share can not be shared by other people.<br></li><li>If you turn off sharing, all users lose access and the note is reverted to the default security scheme.<br></li></ul></blockquote><p><b>Public Links for shared notes</b><br></p><blockquote><p>It is possible to generate a public URL for shared notes. This is a huge security risk and exposes the Encryption Key for the note. The encryption key to a shared note can easily be changed by turning off sharing, then turning it back on. <br></p><p><br></p></blockquote><p><b>Links in notes</b><br></p><blockquote><p>Links put into notes are automatically scraped. This means the data from the link will be scanned to get an image and some text from the website to help make that link more accessible in the future. You can edit the text of scarped links and any time and search for it later. <br></p></blockquote><p><br></p><p><b>Files in notes</b><br></p><blockquote><p>Files can be uploaded to notes. If its an image, the picture will be put into the note.<br></p><p>Images added to notes will have text scanned, so it can be searched (This isn't super accurate so don't rely to heavily on it.) The text can be updated at any time.<br></p></blockquote><p><br></p><p><b>Deleting notes</b><br></p><blockquote><p>When<b> </b>notes are deleted, none of the files related to the note are deleted. <br></p></blockquote><p><br></p><p><b>Daily Backups</b><br></p><blockquote><p>All notes are backed up, every night, at midnight. If there is data loss, it can be restored from a backup. If you experience some sort of cataclysmic data loss please contact the system administrator for a copy of your data or a restoration procedure.<br></p></blockquote>
|
||||||
|
|
||||||
<!-- content copied from note -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<h2 class="ui dividing header">
|
||||||
|
Solid Scribe Terms of Use
|
||||||
|
</h2>
|
||||||
|
<div class="ui segment">
|
||||||
|
|
||||||
|
<p>Welcome to Solid Scribe’s web-based application at solidscribe.com.<br></p><p><br></p><p>Your use of the Site and/or participation in any of the Services offered through the Site, is subject to these Terms of Use.<br></p><p>By registering an an account or using the Site you agree to be bound by these Terms of Use.<br></p><p><br></p><p>The Company reserves the right to change these Terms of Use from time to time by publishing them on the Site.<br></p><p>By using the Site, you agree to the most current version of these Terms of Use as posted on the Site.<br></p><p><br></p><p>All content on the Site is provided "as is." Solid Scribe is not liable for any informational errors, incompleteness, delays or any actions taken in reliance on information contained on the Site. The Company cannot and does not guarantee that any content on the Site will be entirely accurate or up-to-date at all times as the information on the Site originates from various sources, which may or may not be within the control of the Company.<br></p><p><br></p><p>The Company is not responsible for any omission, deletion, failure to store, improper delivery or untimely delivery of any information or material. The Company is not responsible for any reliance upon or damage arising from the use of the Site, including technical damage resulting from downloading or accessing any information or material provided.<br></p><p><br></p><p>You understand and agree that the Company is not responsible for any content posted by you or third parties. You further understand that the Company does not monitor the content posted by other users.<br></p><p><br></p><p>The Company reserves the right to remove content that the Company deems inappropriate for any reason what so ever without notice. The Company also reserves the right, in the Company’s sole discretion, to remove a user's privilege to post content on the Site.<br></p><p><br></p><p>The Company does not in any manner endorse the content of its users and cannot and does not vouch for its reliability or otherwise accept liability for it.<br></p><p><br></p><p>Minors (persons under 18 years of age), persons or organizations that cannot enter into binding contracts under applicable law, and those who may be prevented by contractual obligations from entering into binding contracts with Solid Scribe are not allowed to use this Site.<br></p><p><br></p><p>The Company has the right, but not the obligation, to take any of the following actions in the Company’s sole discretion at any time and for any reason without providing you any prior notice:<br></p><p><br></p><p>1. Restrict, suspend, modify or terminate access to the Site or all or any part of the Company’s Services;<br></p><p><br></p><p>2. Change, suspend or discontinue all or any part of the Company’s Services;<br></p><p><br></p><p>3. Refuse, move, modify or remove any material submitted to the Site by any person, for any reason;<br></p><p><br></p><p>4. Refuse, move, modify or remove any content available on the Site;<br></p><p><br></p><p>5. Deactivate or delete your account and all related information and files in your account;<br></p><p><br></p><p>6. Establish, discontinue or change general practices and limits concerning use of the Site and the Company’s Services.<br></p><p><br></p><p>You agree that the Company is not liable to you or any third party for taking any of these actions. Any registration or other eligibility requirements are in the sole discretion of the Company and may be modified for any reason at any time.<br></p><p><br></p><p>YOUR INFORMATION AND CONTENT<br></p><p><br></p><p>Any information you provide to Solid Scribe or other users during your registration or use of the Site (in any public message area, through any mail feature, or otherwise generated out of your use of the Site or the Services), is subject to the following:<br></p><p><br></p><p>(a) You are solely responsible for the content of your information, postings or Notes. The Company reserves the right to take any action with respect to such information that it deems necessary or appropriate in its sole discretion for any reason, including if the Company believes information is inappropriate for the Site or the Services, may create liability for the Company.<br></p><p><br></p><p>(b) You agree that your information will not, to the best of your knowledge: (i) violate any law (including without limitation those governing export control, consumer protection, unfair competition, anti-discrimination or false advertising); or (ii) be libelous, defamatory, obscene, abusive, violate a third party's right to privacy, otherwise violate any applicable local, state, national or international law, amount to spamming or be otherwise inappropriate. You may not post content that degrades others on the basis of gender, race, class, ethnicity, national origin, religion, sexual preference, disability or other classification. Epithets and other language intended to intimidate, unlaLine truncated
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -110,7 +110,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<h3 class="subtext">
|
<h3 class="subtext">
|
||||||
A free, secure Note App<i class="i cursor icon blinking"></i>
|
A free, secure, online note taking application<i class="i cursor icon blinking"></i>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -210,18 +210,7 @@
|
|||||||
<i class="bottom left corner blue pen icon"></i>
|
<i class="bottom left corner blue pen icon"></i>
|
||||||
</i>
|
</i>
|
||||||
Document Editing Tools
|
Document Editing Tools
|
||||||
<div class="sub header">Bold, Underline, Title, Add Links, Add Tables Color Text, Color Background and more.</div>
|
<div class="sub header">Bold, Underline, Title, Add Links, Add Tables, Color Text, Color Background and more.</div>
|
||||||
</div>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<h2 class="ui dividing header">
|
|
||||||
<div class="content">
|
|
||||||
<i class="icons">
|
|
||||||
<i class="grey tags icon"></i>
|
|
||||||
<i class="bottom left corner purple plus icon"></i>
|
|
||||||
</i>
|
|
||||||
Tag Notes
|
|
||||||
<div class="sub header">Easily add and edit tags on notes then sort notes by tag.</div>
|
|
||||||
</div>
|
</div>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
@@ -236,6 +225,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
<h2 class="ui dividing header">
|
||||||
|
<div class="content">
|
||||||
|
<i class="icons">
|
||||||
|
<i class="grey tags icon"></i>
|
||||||
|
<i class="bottom left corner purple plus icon"></i>
|
||||||
|
</i>
|
||||||
|
Tag Notes
|
||||||
|
<div class="sub header">Easily add and edit tags on notes then sort notes by tag.</div>
|
||||||
|
</div>
|
||||||
|
</h2>
|
||||||
|
|
||||||
<h2 class="ui dividing header">
|
<h2 class="ui dividing header">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<i class="icons">
|
<i class="icons">
|
||||||
@@ -254,7 +254,7 @@
|
|||||||
<i class="bottom left corner share icon"></i>
|
<i class="bottom left corner share icon"></i>
|
||||||
</i>
|
</i>
|
||||||
Share Encrypted Notes
|
Share Encrypted Notes
|
||||||
<div class="sub header">Share notes with friends without compromising security. And its easy to disable sharing.</div>
|
<div class="sub header">Share encrypted notes with friends without compromising security.</div>
|
||||||
</div>
|
</div>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
@@ -290,6 +290,17 @@
|
|||||||
<div class="sub header">Add text to Images and links than can be searched.</div>
|
<div class="sub header">Add text to Images and links than can be searched.</div>
|
||||||
</div>
|
</div>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
<h2 class="ui dividing header">
|
||||||
|
<div class="content">
|
||||||
|
<i class="icons">
|
||||||
|
<i class="grey tv icon"></i>
|
||||||
|
<i class="bottom left corner blue mobile icon"></i>
|
||||||
|
</i>
|
||||||
|
Two Factor Authentication
|
||||||
|
<div class="sub header">Enable two factor authentication for added peace of mind.</div>
|
||||||
|
</div>
|
||||||
|
</h2>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="four wide column">
|
<div class="four wide column">
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>You will remain logged in on this browser, for 30 days or until you log out.</p>
|
<p>You will remain logged in on this browser, for 20 days or until you log out.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<i v-if="titleView" class="th icon"></i>
|
<i v-if="titleView" class="th icon"></i>
|
||||||
<i v-if="!titleView" class="bars icon"></i>
|
<i v-if="!titleView" class="bars icon"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="eight wide column" v-if="showClear">
|
<div class="eight wide column" v-if="showClear">
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
:onClick="openNote"
|
:onClick="openNote"
|
||||||
:data="note"
|
:data="note"
|
||||||
:title-view="titleView"
|
:title-view="titleView"
|
||||||
:currently-open="(activeNoteId1 == note.id || activeNoteId2 == note.id)"
|
:currently-open="activeNoteId1 == note.id"
|
||||||
:key="note.id + note.color + '-' +note.title.length + '-' +note.subtext.length + '-' + note.tag_count + note.updated"
|
:key="note.id + note.color + '-' +note.title.length + '-' +note.subtext.length + '-' + note.tag_count + note.updated"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -132,13 +132,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<input-notes
|
<note-input-panel
|
||||||
v-if="activeNoteId1 != null"
|
v-if="activeNoteId1 != null"
|
||||||
:key="'active_note_'+activeNoteId1"
|
:key="activeNoteId1"
|
||||||
:noteid="activeNoteId1"
|
:noteid="activeNoteId1"
|
||||||
:position="activeNote1Position"
|
|
||||||
:url-data="$route.params"
|
:url-data="$route.params"
|
||||||
ref="note1" />
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -151,7 +150,7 @@
|
|||||||
name: 'SearchBar',
|
name: 'SearchBar',
|
||||||
components: {
|
components: {
|
||||||
|
|
||||||
'input-notes': () => import(/* webpackChunkName: "NoteInputPanel" */ '@/components/NoteInputPanel.vue'),
|
'note-input-panel': () => import(/* webpackChunkName: "NoteInputPanel" */ '@/components/NoteInputPanel.vue'),
|
||||||
|
|
||||||
'note-title-display-card': require('@/components/NoteTitleDisplayCard.vue').default,
|
'note-title-display-card': require('@/components/NoteTitleDisplayCard.vue').default,
|
||||||
// 'fast-filters': require('@/components/FastFilters.vue').default,
|
// 'fast-filters': require('@/components/FastFilters.vue').default,
|
||||||
@@ -247,7 +246,7 @@
|
|||||||
|
|
||||||
//Do not update note if its open
|
//Do not update note if its open
|
||||||
if(this.activeNoteId1 != noteId){
|
if(this.activeNoteId1 != noteId){
|
||||||
this.updateSingleNote(noteId, false)
|
this.updateSingleNote(noteId, true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -264,10 +263,18 @@
|
|||||||
//Close note event
|
//Close note event
|
||||||
this.$bus.$on('close_active_note', ({noteId, modified}) => {
|
this.$bus.$on('close_active_note', ({noteId, modified}) => {
|
||||||
|
|
||||||
this.closeNote()
|
if(modified){
|
||||||
|
console.log('Just closed Note -> ' + noteId + ', modified -> ', modified)
|
||||||
|
}
|
||||||
|
|
||||||
|
//A note has been closed
|
||||||
|
if(this.$route.fullPath != '/notes'){
|
||||||
|
this.$router.push('/notes')
|
||||||
|
}
|
||||||
|
|
||||||
this.$store.dispatch('fetchAndUpdateUserTotals')
|
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||||
//Focus and animate if modified
|
//Focus and animate if modified
|
||||||
this.updateSingleNote(parseInt(noteId), modified)
|
this.updateSingleNote(noteId, modified)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.$bus.$on('note_deleted', (noteId) => {
|
this.$bus.$on('note_deleted', (noteId) => {
|
||||||
@@ -312,35 +319,25 @@
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
//New note button pushes open note event
|
//Reload page content - don't trigger if load is in progress
|
||||||
this.$bus.$on('open_note', noteId => {
|
|
||||||
this.openNote(noteId)
|
|
||||||
})
|
|
||||||
|
|
||||||
//Reload page content
|
|
||||||
this.$bus.$on('note_reload', () => {
|
this.$bus.$on('note_reload', () => {
|
||||||
this.reset()
|
if(!this.loadingInProgress){
|
||||||
|
this.reset()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
//Mount notes on load if note ID is set
|
|
||||||
if(this.$route.params && this.$route.params.id){
|
|
||||||
const id = this.$route.params.id
|
|
||||||
this.openNote(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('scroll', this.onScroll)
|
window.addEventListener('scroll', this.onScroll)
|
||||||
|
|
||||||
//Close notes when back button is pressed
|
//Close notes when back button is pressed
|
||||||
window.addEventListener('hashchange', this.hashChangeAction)
|
// window.addEventListener('hashchange', this.hashChangeAction)
|
||||||
|
|
||||||
//update note on visibility change
|
//update note on visibility change
|
||||||
document.addEventListener('visibilitychange', this.visibiltyChangeAction);
|
// document.addEventListener('visibilitychange', this.visibiltyChangeAction);
|
||||||
|
|
||||||
},
|
},
|
||||||
beforeDestroy(){
|
beforeDestroy(){
|
||||||
window.removeEventListener('scroll', this.onScroll)
|
window.removeEventListener('scroll', this.onScroll)
|
||||||
window.removeEventListener('hashchange', this.hashChangeAction)
|
// document.removeEventListener('visibilitychange', this.visibiltyChangeAction)
|
||||||
document.removeEventListener('visibilitychange', this.visibiltyChangeAction)
|
|
||||||
|
|
||||||
this.$bus.$off('note_reload')
|
this.$bus.$off('note_reload')
|
||||||
this.$bus.$off('close_active_note')
|
this.$bus.$off('close_active_note')
|
||||||
@@ -348,7 +345,6 @@
|
|||||||
this.$bus.$off('note_deleted')
|
this.$bus.$off('note_deleted')
|
||||||
this.$bus.$off('update_fast_filters')
|
this.$bus.$off('update_fast_filters')
|
||||||
this.$bus.$off('update_search_term')
|
this.$bus.$off('update_search_term')
|
||||||
this.$bus.$off('open_note')
|
|
||||||
|
|
||||||
//We want to remove event listeners, but something here is messing them up and preventing ALL event listeners from working
|
//We want to remove event listeners, but something here is messing them up and preventing ALL event listeners from working
|
||||||
// this.$off() // Remove all event listeners
|
// this.$off() // Remove all event listeners
|
||||||
@@ -356,11 +352,20 @@
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
||||||
|
//Open note on load if ID is set
|
||||||
|
if(this.$route.params.id > 1){
|
||||||
|
this.activeNoteId1 = this.$route.params.id
|
||||||
|
}
|
||||||
|
|
||||||
//Loads initial batch and tags
|
//Loads initial batch and tags
|
||||||
this.reset()
|
this.reset()
|
||||||
|
|
||||||
// this.search(true, this.firstLoadBatchSize, false)
|
},
|
||||||
// .then( r => this.search(false, this.batchSize, true))
|
watch: {
|
||||||
|
'$route.params.id': function(id){
|
||||||
|
//Open note on ID, null id will close note
|
||||||
|
this.activeNoteId1 = id
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleTitleView(){
|
toggleTitleView(){
|
||||||
@@ -382,17 +387,9 @@
|
|||||||
if(nodeClick == 'A'){ return }
|
if(nodeClick == 'A'){ return }
|
||||||
}
|
}
|
||||||
|
|
||||||
//1 note open
|
//Open note if a link was not clicked
|
||||||
if(this.activeNoteId1 == null){
|
this.$router.push('/notes/open/'+id)
|
||||||
this.activeNoteId1 = id
|
return
|
||||||
this.activeNote1Position = 0 //Middel of page
|
|
||||||
this.$router.push('/notes/open/'+this.activeNoteId1).catch(e => { console.log(e) })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closeNote(position){
|
|
||||||
this.activeNoteId1 = null
|
|
||||||
this.$router.push('/notes')
|
|
||||||
},
|
},
|
||||||
toggleTagFilter(tagId){
|
toggleTagFilter(tagId){
|
||||||
|
|
||||||
@@ -429,34 +426,6 @@
|
|||||||
|
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
//Try to close notes on URL hash change /notes/open/123 to /notes - parse 123, close note id 123
|
|
||||||
hashChangeAction(event){
|
|
||||||
|
|
||||||
//Clean up path of hash change
|
|
||||||
let path = window.location.protocol + '//' + window.location.hostname + window.location.pathname + window.location.hash
|
|
||||||
let newPath = event.newURL.replace(path,'')
|
|
||||||
let oldPath = event.oldURL.replace(path,'')
|
|
||||||
|
|
||||||
// console.log(this.$route.params)
|
|
||||||
// console.log(this.$router)
|
|
||||||
|
|
||||||
//Open note if user goes forward to a note id
|
|
||||||
if(this.$route.params && this.$route.params.id){
|
|
||||||
this.openNote(this.$route.params.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
//If we go from open note ID to no note ID, close the note
|
|
||||||
if(newPath == '' && oldPath.indexOf('/open/') != -1){
|
|
||||||
//Pull note ID out of URL
|
|
||||||
const noteIdToClose = oldPath.split('/').pop()
|
|
||||||
|
|
||||||
// console.log(noteIdToClose)
|
|
||||||
|
|
||||||
if(this.$refs.note1 && this.$refs.note1.currentNoteId == noteIdToClose){
|
|
||||||
// this.$refs.note1.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
visibiltyChangeAction(event){
|
visibiltyChangeAction(event){
|
||||||
|
|
||||||
//Fuck this shit, just use web sockets
|
//Fuck this shit, just use web sockets
|
||||||
@@ -484,8 +453,11 @@
|
|||||||
let note = null
|
let note = null
|
||||||
if(this.$refs['note-'+noteId] && this.$refs['note-'+noteId][0] && this.$refs['note-'+noteId][0].note){
|
if(this.$refs['note-'+noteId] && this.$refs['note-'+noteId][0] && this.$refs['note-'+noteId][0].note){
|
||||||
note = this.$refs['note-'+noteId][0].note
|
note = this.$refs['note-'+noteId][0].note
|
||||||
|
//Show that note is working on updating
|
||||||
|
this.$refs['note-'+noteId][0].showWorking = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Lookup one note using passed in ID
|
//Lookup one note using passed in ID
|
||||||
const postData = {
|
const postData = {
|
||||||
searchQuery: this.searchTerm,
|
searchQuery: this.searchTerm,
|
||||||
@@ -508,18 +480,13 @@
|
|||||||
|
|
||||||
if(note && newNote){
|
if(note && newNote){
|
||||||
|
|
||||||
//Don't move notes that were not changed
|
|
||||||
if(note.updated == newNote.updated){
|
|
||||||
// return
|
|
||||||
}
|
|
||||||
|
|
||||||
//go through each prop and update it with new values
|
//go through each prop and update it with new values
|
||||||
Object.keys(newNote).forEach(prop => {
|
Object.keys(newNote).forEach(prop => {
|
||||||
note[prop] = newNote[prop]
|
note[prop] = newNote[prop]
|
||||||
})
|
})
|
||||||
|
|
||||||
//Push new note to front if its modified
|
//Push new note to front if its modified or we want it to
|
||||||
if(focuseAndAnimate){
|
if( focuseAndAnimate || note.updated != newNote.updated ){
|
||||||
|
|
||||||
// Find note, in section, move to front
|
// Find note, in section, move to front
|
||||||
Object.keys(this.noteSections).forEach( key => {
|
Object.keys(this.noteSections).forEach( key => {
|
||||||
@@ -536,6 +503,7 @@
|
|||||||
this.$nextTick( () => {
|
this.$nextTick( () => {
|
||||||
//Trigger close animation on note
|
//Trigger close animation on note
|
||||||
this.$refs['note-'+noteId][0].justClosed()
|
this.$refs['note-'+noteId][0].justClosed()
|
||||||
|
this.$refs['note-'+noteId][0].showWorking = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,9 +515,14 @@
|
|||||||
//Trigger close animation on note
|
//Trigger close animation on note
|
||||||
if(this.$refs['note-'+noteId] && this.$refs['note-'+noteId][0]){
|
if(this.$refs['note-'+noteId] && this.$refs['note-'+noteId][0]){
|
||||||
this.$refs['note-'+noteId][0].justClosed()
|
this.$refs['note-'+noteId][0].justClosed()
|
||||||
|
this.$refs['note-'+noteId][0].showWorking = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(this.$refs['note-'+noteId] && this.$refs['note-'+noteId][0]){
|
||||||
|
this.$refs['note-'+noteId][0].showWorking = false
|
||||||
|
}
|
||||||
|
|
||||||
//Trigger section rebuild
|
//Trigger section rebuild
|
||||||
this.rebuildNoteCategorise()
|
this.rebuildNoteCategorise()
|
||||||
})
|
})
|
||||||
@@ -610,7 +583,6 @@
|
|||||||
|
|
||||||
//Perform search - or die
|
//Perform search - or die
|
||||||
this.loadingInProgress = true
|
this.loadingInProgress = true
|
||||||
// console.time('Fetch TitleCard Batch '+notesInNextLoad)
|
|
||||||
axios.post('/api/note/search', postData)
|
axios.post('/api/note/search', postData)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
||||||
@@ -735,7 +707,6 @@
|
|||||||
this.fastFilters = {}
|
this.fastFilters = {}
|
||||||
this.foundAttachments = [] //Remove all attachments
|
this.foundAttachments = [] //Remove all attachments
|
||||||
|
|
||||||
this.$bus.$emit('reset_fast_filters')
|
|
||||||
this.updateFastFilters(5) //This loads notes
|
this.updateFastFilters(5) //This loads notes
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,6 +14,11 @@
|
|||||||
|
|
||||||
<div class="sixteen wide middle aligned column" v-if="quickNoteId > 0">
|
<div class="sixteen wide middle aligned column" v-if="quickNoteId > 0">
|
||||||
|
|
||||||
|
<div v-if="quickNoteId" v-on:click="openNoteEdit" class="ui compact basic button">
|
||||||
|
<i class="file outline icon"></i>
|
||||||
|
Open Note
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="ui compact basic right floated button shrinking" v-if="!showNewNoteConfirm" v-on:click="showNewNoteConfirm = true">
|
<div class="ui compact basic right floated button shrinking" v-if="!showNewNoteConfirm" v-on:click="showNewNoteConfirm = true">
|
||||||
<i class="sync alternate reload icon"></i>
|
<i class="sync alternate reload icon"></i>
|
||||||
New Scratch Pad
|
New Scratch Pad
|
||||||
@@ -46,10 +51,6 @@
|
|||||||
<i class="folder open outline icon"></i>
|
<i class="folder open outline icon"></i>
|
||||||
Files
|
Files
|
||||||
</div>
|
</div>
|
||||||
<div v-if="quickNoteId" v-on:click="openNoteEdit" class="ui right floated basic button">
|
|
||||||
<i class="file outline icon"></i>
|
|
||||||
Open Note
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ui grid">
|
||||||
|
<div class="row"></div>
|
||||||
|
<!-- spacer column -->
|
||||||
|
<div class="sixteen wide column">
|
||||||
|
<h2 class="ui dividing header">
|
||||||
|
<i class="cog icon"></i>
|
||||||
|
Settings
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<!-- Accent Color -->
|
||||||
|
<div class="ui segment">
|
||||||
|
<div class="ui grid">
|
||||||
|
<div class="sixteen wide column">
|
||||||
|
<h3 class="ui header">
|
||||||
|
Accent Color
|
||||||
|
</h3>
|
||||||
|
<div
|
||||||
|
v-for="color in themeColors"
|
||||||
|
class="ui compact basic button"
|
||||||
|
:style="`background: linear-gradient(0deg, ${color} 4%, rgba(0,0,0,0) 5%);`"
|
||||||
|
v-on:click="setAccentColor(color)">
|
||||||
|
<logo style="width: 33px; height: auto;" :color="color" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Enable Two Factor -->
|
||||||
|
<div class="ui segment">
|
||||||
|
<h3>Two Factor Authentication</h3>
|
||||||
|
<div class="ui stackable grid">
|
||||||
|
<div class="four wide column">
|
||||||
|
<p>1. Enter Password and get QR</p>
|
||||||
|
<div class="ui fluid action input">
|
||||||
|
<input type="password" placeholder="Current Password" v-model="password">
|
||||||
|
|
||||||
|
<div v-if="password.length == 0" class="ui disabled button">
|
||||||
|
Get QR code
|
||||||
|
</div>
|
||||||
|
<div v-if="password.length > 0" class="ui green button" v-on:click="getQrCode()">
|
||||||
|
Get QR code
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="five wide column">
|
||||||
|
<p>2. Scan QR Code</p>
|
||||||
|
<p v-if="qrCode == ''">QR Code Will appear here.</p>
|
||||||
|
<img v-if="qrCode != ''" :src="qrCode" alt="QR Code">
|
||||||
|
</div>
|
||||||
|
<div class="four wide column">
|
||||||
|
<p>3. Verify with code</p>
|
||||||
|
<div class="ui input" v-if="qrCode != ''">
|
||||||
|
<input type="text" placeholder="Verification Code" v-model="verificationToken" v-on:keyup.enter="verifyQrCode()">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- change password -->
|
||||||
|
<div class="ui segment">
|
||||||
|
<h3>Change Password</h3>
|
||||||
|
<div class="ui grid">
|
||||||
|
<div class="five wide column">
|
||||||
|
<label>Current Password</label>
|
||||||
|
<div class="ui fluid input">
|
||||||
|
<input type="text" placeholder="Current Password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="five wide column">
|
||||||
|
<label>New Password</label>
|
||||||
|
<div class="ui fluid input">
|
||||||
|
<input type="text" placeholder="New Password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="six wide column">
|
||||||
|
<label>Rereat New Password</label>
|
||||||
|
<div class="ui fluid action input">
|
||||||
|
<input type="text" placeholder="Repeat Password">
|
||||||
|
<div class="ui green button">
|
||||||
|
Change it!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- log out -->
|
||||||
|
<div class="ui segment">
|
||||||
|
<div class="ui grid">
|
||||||
|
<div class="sixteen wide column">
|
||||||
|
<h3>Log Out</h3>
|
||||||
|
</div>
|
||||||
|
<div class="eight wide column">
|
||||||
|
<div class="ui button" v-on:click="logout()">
|
||||||
|
Log Out this browser
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="eight wide column">
|
||||||
|
<div class="ui button">
|
||||||
|
Log Out all other browsers
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SettingsPage',
|
||||||
|
components: {
|
||||||
|
'logo':require('@/components/LogoComponent.vue').default,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
password: '',
|
||||||
|
qrCode: '',
|
||||||
|
verificationToken: '',
|
||||||
|
|
||||||
|
themeColors: [
|
||||||
|
'#21BA45', //Green
|
||||||
|
'#b5cc18', //Lime
|
||||||
|
'#00b5ad', //Teal
|
||||||
|
'#2185d0', //Blue
|
||||||
|
'#7128b9', //Violet
|
||||||
|
'#a333c8', // "Purple"
|
||||||
|
'#e03997', //Pink
|
||||||
|
'#db2828', //Red
|
||||||
|
'#f2711c', //Orange
|
||||||
|
'#fbbd08', //Yellow
|
||||||
|
'#767676', //Grey
|
||||||
|
'#303030', //Black-almost
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
logout() {
|
||||||
|
this.$store.commit('destroyLoginToken')
|
||||||
|
this.$router.push('/')
|
||||||
|
axios.post('/api/user/logout')
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$bus.$emit('notification', 'Logged Out')
|
||||||
|
}, 200)
|
||||||
|
},
|
||||||
|
setAccentColor(color){
|
||||||
|
|
||||||
|
let root = document.documentElement
|
||||||
|
root.style.setProperty('--main-accent', color)
|
||||||
|
localStorage.setItem('main-accent', color)
|
||||||
|
|
||||||
|
if(!color || color == '#21BA45'){
|
||||||
|
localStorage.removeItem('main-accent')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getQrCode(){
|
||||||
|
|
||||||
|
axios.post('/api/user/twofactorsetup', { password:this.password })
|
||||||
|
.then(({data}) => {
|
||||||
|
this.qrCode = data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
verifyQrCode(){
|
||||||
|
|
||||||
|
axios.post('/api/user/verifytwofactorsetuptoken', { password:this.password, token: this.verificationToken })
|
||||||
|
.then(({data}) => {
|
||||||
|
if(data == true){
|
||||||
|
//Two FA is set up
|
||||||
|
} else {
|
||||||
|
//It failed
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -6,6 +6,7 @@ import Router from 'vue-router'
|
|||||||
const HomePage = () => import(/* webpackChunkName: "HomePage" */ '@/pages/HomePage')
|
const HomePage = () => import(/* webpackChunkName: "HomePage" */ '@/pages/HomePage')
|
||||||
const LoginPage = () => import(/* webpackChunkName: "LoginPage" */ '@/pages/LoginPage')
|
const LoginPage = () => import(/* webpackChunkName: "LoginPage" */ '@/pages/LoginPage')
|
||||||
const HelpPage = () => import(/* webpackChunkName: "HelpPage" */ '@/pages/HelpPage')
|
const HelpPage = () => import(/* webpackChunkName: "HelpPage" */ '@/pages/HelpPage')
|
||||||
|
const SettingsPage = () => import(/* webpackChunkName: "SettingsPage" */ '@/pages/SettingsPage')
|
||||||
const SharePage = () => import(/* webpackChunkName: "SharePage" */ '@/pages/SharePage')
|
const SharePage = () => import(/* webpackChunkName: "SharePage" */ '@/pages/SharePage')
|
||||||
const NotesPage = () => import(/* webpackChunkName: "NotesPage" */ '@/pages/NotesPage')
|
const NotesPage = () => import(/* webpackChunkName: "NotesPage" */ '@/pages/NotesPage')
|
||||||
const QuickPage = () => import(/* webpackChunkName: "QuickPage" */ '@/pages/QuickPage')
|
const QuickPage = () => import(/* webpackChunkName: "QuickPage" */ '@/pages/QuickPage')
|
||||||
@@ -51,6 +52,12 @@ export default new Router({
|
|||||||
name: 'Help',
|
name: 'Help',
|
||||||
meta: {title:'Help'},
|
meta: {title:'Help'},
|
||||||
component: HelpPage
|
component: HelpPage
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/settings',
|
||||||
|
name: 'Settings',
|
||||||
|
meta: {title:'Settings'},
|
||||||
|
component: SettingsPage
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/public/note/:id/:token',
|
path: '/public/note/:id/:token',
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ export default new Vuex.Store({
|
|||||||
Object.keys( themes[currentTheme] ).forEach( attribute => {
|
Object.keys( themes[currentTheme] ).forEach( attribute => {
|
||||||
root.style.setProperty('--'+attribute, themes[currentTheme][attribute])
|
root.style.setProperty('--'+attribute, themes[currentTheme][attribute])
|
||||||
})
|
})
|
||||||
|
|
||||||
},
|
},
|
||||||
detectIsUserOnMobile(state){
|
detectIsUserOnMobile(state){
|
||||||
|
|
||||||
|
|||||||
Generated
+265
@@ -49,6 +49,19 @@
|
|||||||
"uri-js": "^4.2.2"
|
"uri-js": "^4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
|
||||||
|
},
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"append-field": {
|
"append-field": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||||
@@ -112,11 +125,21 @@
|
|||||||
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||||
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||||
},
|
},
|
||||||
|
"base32.js": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-0EVzalex9sE58MffQlGKhOkbsro="
|
||||||
|
},
|
||||||
"base64-arraybuffer": {
|
"base64-arraybuffer": {
|
||||||
"version": "0.1.5",
|
"version": "0.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
|
||||||
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
|
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
|
||||||
},
|
},
|
||||||
|
"base64-js": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||||
|
},
|
||||||
"base64id": {
|
"base64id": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||||
@@ -182,11 +205,39 @@
|
|||||||
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz",
|
||||||
"integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA=="
|
"integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA=="
|
||||||
},
|
},
|
||||||
|
"buffer": {
|
||||||
|
"version": "5.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
|
||||||
|
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.0.2",
|
||||||
|
"ieee754": "^1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"buffer-alloc": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
|
||||||
|
"requires": {
|
||||||
|
"buffer-alloc-unsafe": "^1.1.0",
|
||||||
|
"buffer-fill": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"buffer-alloc-unsafe": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
|
||||||
|
},
|
||||||
"buffer-equal-constant-time": {
|
"buffer-equal-constant-time": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||||
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
|
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
|
||||||
},
|
},
|
||||||
|
"buffer-fill": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
|
||||||
|
},
|
||||||
"buffer-from": {
|
"buffer-from": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||||
@@ -229,6 +280,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
|
||||||
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
|
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
|
||||||
},
|
},
|
||||||
|
"camelcase": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
|
||||||
|
},
|
||||||
"camelize": {
|
"camelize": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
|
||||||
@@ -252,6 +308,29 @@
|
|||||||
"parse5": "^3.0.1"
|
"parse5": "^3.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cliui": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
|
||||||
|
"requires": {
|
||||||
|
"string-width": "^3.1.0",
|
||||||
|
"strip-ansi": "^5.2.0",
|
||||||
|
"wrap-ansi": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"requires": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
|
},
|
||||||
"combined-stream": {
|
"combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
@@ -394,6 +473,11 @@
|
|||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"decamelize": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
|
||||||
|
},
|
||||||
"delayed-stream": {
|
"delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
@@ -441,6 +525,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dijkstrajs": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs="
|
||||||
|
},
|
||||||
"dns-prefetch-control": {
|
"dns-prefetch-control": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz",
|
||||||
@@ -509,6 +598,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||||
},
|
},
|
||||||
|
"emoji-regex": {
|
||||||
|
"version": "7.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||||
|
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
|
||||||
|
},
|
||||||
"encodeurl": {
|
"encodeurl": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
@@ -708,6 +802,14 @@
|
|||||||
"unpipe": "~1.0.0"
|
"unpipe": "~1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"find-up": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
|
||||||
|
"requires": {
|
||||||
|
"locate-path": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"forever-agent": {
|
"forever-agent": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||||
@@ -746,6 +848,11 @@
|
|||||||
"is-property": "^1.0.2"
|
"is-property": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"get-caller-file": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
|
||||||
|
},
|
||||||
"getpass": {
|
"getpass": {
|
||||||
"version": "0.1.7",
|
"version": "0.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||||
@@ -926,6 +1033,11 @@
|
|||||||
"safer-buffer": ">= 2.1.2 < 3"
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ieee754": {
|
||||||
|
"version": "1.1.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||||
|
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||||
|
},
|
||||||
"indexof": {
|
"indexof": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
|
||||||
@@ -941,6 +1053,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
|
||||||
"integrity": "sha1-N9905DCg5HVQ/lSi3v4w2KzZX2U="
|
"integrity": "sha1-N9905DCg5HVQ/lSi3v4w2KzZX2U="
|
||||||
},
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
|
||||||
|
},
|
||||||
"is-property": {
|
"is-property": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
||||||
@@ -1040,6 +1157,15 @@
|
|||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"locate-path": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
|
||||||
|
"requires": {
|
||||||
|
"p-locate": "^3.0.0",
|
||||||
|
"path-exists": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.15",
|
"version": "4.17.15",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||||
@@ -1257,6 +1383,27 @@
|
|||||||
"ee-first": "1.1.1"
|
"ee-first": "1.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||||
|
"requires": {
|
||||||
|
"p-try": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"p-locate": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
|
||||||
|
"requires": {
|
||||||
|
"p-limit": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"p-try": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
|
||||||
|
},
|
||||||
"parse5": {
|
"parse5": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
|
||||||
@@ -1286,6 +1433,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
"integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ="
|
"integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ="
|
||||||
},
|
},
|
||||||
|
"path-exists": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
|
||||||
|
},
|
||||||
"path-to-regexp": {
|
"path-to-regexp": {
|
||||||
"version": "0.1.7",
|
"version": "0.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
@@ -1296,6 +1448,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||||
},
|
},
|
||||||
|
"pngjs": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="
|
||||||
|
},
|
||||||
"process-nextick-args": {
|
"process-nextick-args": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
@@ -1325,6 +1482,27 @@
|
|||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
"integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew="
|
"integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew="
|
||||||
},
|
},
|
||||||
|
"qrcode": {
|
||||||
|
"version": "1.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz",
|
||||||
|
"integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==",
|
||||||
|
"requires": {
|
||||||
|
"buffer": "^5.4.3",
|
||||||
|
"buffer-alloc": "^1.2.0",
|
||||||
|
"buffer-from": "^1.1.1",
|
||||||
|
"dijkstrajs": "^1.0.1",
|
||||||
|
"isarray": "^2.0.1",
|
||||||
|
"pngjs": "^3.3.0",
|
||||||
|
"yargs": "^13.2.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"isarray": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"qs": {
|
"qs": {
|
||||||
"version": "6.5.2",
|
"version": "6.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||||
@@ -1407,6 +1585,16 @@
|
|||||||
"lodash": "^4.17.15"
|
"lodash": "^4.17.15"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"require-directory": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
|
||||||
|
},
|
||||||
|
"require-main-filename": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
|
||||||
|
},
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
@@ -1465,6 +1653,11 @@
|
|||||||
"send": "0.17.1"
|
"send": "0.17.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
|
||||||
|
},
|
||||||
"setprototypeof": {
|
"setprototypeof": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||||
@@ -1599,6 +1792,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"speakeasy": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/speakeasy/-/speakeasy-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-hckaBxsJpcuGQlkNmDVmFl9XYTo=",
|
||||||
|
"requires": {
|
||||||
|
"base32.js": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"sqlstring": {
|
"sqlstring": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz",
|
||||||
@@ -1635,6 +1836,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
||||||
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
|
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
|
||||||
},
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||||
|
"requires": {
|
||||||
|
"emoji-regex": "^7.0.1",
|
||||||
|
"is-fullwidth-code-point": "^2.0.0",
|
||||||
|
"strip-ansi": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
|
||||||
@@ -1643,6 +1854,14 @@
|
|||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"to-array": {
|
"to-array": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
|
||||||
@@ -1755,6 +1974,21 @@
|
|||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"which-module": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
|
||||||
|
},
|
||||||
|
"wrap-ansi": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^3.2.0",
|
||||||
|
"string-width": "^3.0.0",
|
||||||
|
"strip-ansi": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "7.2.1",
|
"version": "7.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.1.tgz",
|
||||||
@@ -1775,11 +2009,42 @@
|
|||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
"integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q="
|
"integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q="
|
||||||
},
|
},
|
||||||
|
"y18n": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
|
||||||
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||||
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
|
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
|
||||||
},
|
},
|
||||||
|
"yargs": {
|
||||||
|
"version": "13.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
|
||||||
|
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
|
||||||
|
"requires": {
|
||||||
|
"cliui": "^5.0.0",
|
||||||
|
"find-up": "^3.0.0",
|
||||||
|
"get-caller-file": "^2.0.1",
|
||||||
|
"require-directory": "^2.1.1",
|
||||||
|
"require-main-filename": "^2.0.0",
|
||||||
|
"set-blocking": "^2.0.0",
|
||||||
|
"string-width": "^3.0.0",
|
||||||
|
"which-module": "^2.0.0",
|
||||||
|
"y18n": "^4.0.0",
|
||||||
|
"yargs-parser": "^13.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yargs-parser": {
|
||||||
|
"version": "13.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
|
||||||
|
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
|
||||||
|
"requires": {
|
||||||
|
"camelcase": "^5.0.0",
|
||||||
|
"decamelize": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"yeast": {
|
"yeast": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||||
|
|||||||
+3
-1
@@ -21,9 +21,11 @@
|
|||||||
"multer": "^1.4.2",
|
"multer": "^1.4.2",
|
||||||
"mysql2": "^1.7.0",
|
"mysql2": "^1.7.0",
|
||||||
"node-tesseract-ocr": "^1.0.0",
|
"node-tesseract-ocr": "^1.0.0",
|
||||||
|
"qrcode": "^1.4.4",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"request-promise": "^4.2.5",
|
"request-promise": "^4.2.5",
|
||||||
"socket.io": "^2.3.0"
|
"socket.io": "^2.3.0",
|
||||||
|
"speakeasy": "^2.0.0"
|
||||||
},
|
},
|
||||||
"_moduleAliases": {
|
"_moduleAliases": {
|
||||||
"@root": ".",
|
"@root": ".",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const db = require('@config/database')
|
const db = require('@config/database')
|
||||||
const jwt = require('jsonwebtoken')
|
const jwt = require('jsonwebtoken')
|
||||||
const cs = require('@helpers/CryptoString')
|
const cs = require('@helpers/CryptoString')
|
||||||
|
const speakeasy = require('speakeasy')
|
||||||
|
|
||||||
let Auth = {}
|
let Auth = {}
|
||||||
|
|
||||||
@@ -120,6 +121,7 @@ Auth.decodeToken = (token, request = null) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Auth.terminateSession = (sessionId) => {
|
Auth.terminateSession = (sessionId) => {
|
||||||
|
|
||||||
return db.promise().query('DELETE from user_active_session WHERE session_id = ?', [sessionId])
|
return db.promise().query('DELETE from user_active_session WHERE session_id = ?', [sessionId])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +132,143 @@ Auth.deletAllLoginKeys = (userId) => {
|
|||||||
return db.promise().query('DELETE FROM user_active_session WHERE user_hash = ?', [userHash])
|
return db.promise().query('DELETE FROM user_active_session WHERE user_hash = ?', [userHash])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Generate two factor secret key, if key is not verified, return a new one
|
||||||
|
//Only return QR code if user is not verified, only show unique QR code, once
|
||||||
|
Auth.generateTwoFactorSecretKey = (userId, password) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
const QRCode = require('qrcode')
|
||||||
|
|
||||||
|
const User = require('@models/User')
|
||||||
|
User.getMasterKey(userId, password)
|
||||||
|
.then(masterKey => {
|
||||||
|
return db.promise().query('SELECT username, two_fa_enabled FROM user WHERE id = ?', [userId])
|
||||||
|
})
|
||||||
|
.then((r,f) => {
|
||||||
|
|
||||||
|
const tfaEnabled = r[0][0]['two_fa_enabled'] == 1
|
||||||
|
const username = r[0][0]['username']
|
||||||
|
|
||||||
|
if(!tfaEnabled){
|
||||||
|
|
||||||
|
var secret = speakeasy.generateSecret({length: 20, name: username+' - solidscribe.com'})
|
||||||
|
const twoFaSecretToken = secret.base32
|
||||||
|
const otpauthUrl = secret.otpauth_url
|
||||||
|
|
||||||
|
//Generate test Token
|
||||||
|
var token = speakeasy.totp({
|
||||||
|
secret: twoFaSecretToken,
|
||||||
|
encoding: 'base32'
|
||||||
|
})
|
||||||
|
|
||||||
|
db.promise().query('UPDATE user SET two_fa_secret = ? WHERE id = ? LIMIT 1', [twoFaSecretToken, userId])
|
||||||
|
.then((r,f) => {
|
||||||
|
|
||||||
|
QRCode.toDataURL(otpauthUrl, function(err, qrCode) {
|
||||||
|
|
||||||
|
//Return A QR code for the user, one time use
|
||||||
|
return resolve({qrCode, token})
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return reject('Two factor already enabled for user')
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log('Key auth error')
|
||||||
|
console.log(error)
|
||||||
|
return reject(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Auth.setTwoFactorEnabled = (userId, password, token, enable) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
Auth.validateTwoFactorToken(userId, password, token)
|
||||||
|
.then(isValid => {
|
||||||
|
if(isValid){
|
||||||
|
db.promise().query('UPDATE user SET two_fa_enabled = ? WHERE id = ? LIMIT 1', [enable, userId])
|
||||||
|
.then((r, f) => {
|
||||||
|
return resolve(true)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return resolve(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Auth.validateTwoFactorToken = (userId, password, token) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
const User = require('@models/User')
|
||||||
|
User.getMasterKey(userId, password)
|
||||||
|
.then(masterKey => {
|
||||||
|
return db.promise().query('SELECT two_fa_secret FROM user WHERE id = ?', [userId])
|
||||||
|
})
|
||||||
|
.then((r,f) => {
|
||||||
|
|
||||||
|
//Verify Token
|
||||||
|
const tokenValidates = speakeasy.totp.verify({
|
||||||
|
'secret': r[0][0]['two_fa_secret'],
|
||||||
|
'encoding': 'base32',
|
||||||
|
'token': token,
|
||||||
|
'window': 6
|
||||||
|
})
|
||||||
|
|
||||||
|
return resolve(tokenValidates)
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log('Token Validation Error')
|
||||||
|
return resolve(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Auth.testTwoFactor = () => {
|
||||||
|
|
||||||
|
const userId = 93
|
||||||
|
const pass = '1'
|
||||||
|
|
||||||
|
|
||||||
|
let tfaToken = null
|
||||||
|
console.log('Test Two Factor')
|
||||||
|
|
||||||
|
Auth.generateTwoFactorSecretKey(userId, pass)
|
||||||
|
.then( ({qrCode, token}) => {
|
||||||
|
|
||||||
|
tfaToken = token
|
||||||
|
|
||||||
|
Auth.validateTwoFactorToken(userId, pass, tfaToken)
|
||||||
|
.then(validToken => {
|
||||||
|
console.log('Is Token Valid:', validToken)
|
||||||
|
})
|
||||||
|
|
||||||
|
return Auth.setTwoFactorEnabled(userId, pass, tfaToken, true)
|
||||||
|
})
|
||||||
|
.then(twoFactorEnbled => {
|
||||||
|
console.log('Was it enabled?', twoFactorEnbled)
|
||||||
|
|
||||||
|
return Auth.setTwoFactorEnabled(userId, pass, tfaToken, false)
|
||||||
|
|
||||||
|
})
|
||||||
|
.then(twoFactorEnbled => {
|
||||||
|
console.log('Was it disabled?', twoFactorEnbled)
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
Auth.test = () => {
|
Auth.test = () => {
|
||||||
|
|
||||||
const testUserId = 22
|
const testUserId = 22
|
||||||
|
|||||||
+4
-2
@@ -259,13 +259,15 @@ const printResults = true
|
|||||||
let UserTest = require('@models/User')
|
let UserTest = require('@models/User')
|
||||||
let NoteTest = require('@models/Note')
|
let NoteTest = require('@models/Note')
|
||||||
let AuthTest = require('@helpers/Auth')
|
let AuthTest = require('@helpers/Auth')
|
||||||
|
|
||||||
|
Auth.testTwoFactor()
|
||||||
|
|
||||||
Auth.test()
|
Auth.test()
|
||||||
UserTest.keyPairTest('genMan15', '1', printResults)
|
UserTest.keyPairTest('genMan16', '1', printResults)
|
||||||
.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults))
|
.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults))
|
||||||
.then( message => {
|
.then( message => {
|
||||||
if(printResults) console.log(message)
|
if(printResults) console.log(message)
|
||||||
})
|
})
|
||||||
// Test Area
|
|
||||||
|
|
||||||
|
|
||||||
//Test
|
//Test
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ Note.create = (userId, noteTitle = '', noteText = '', masterKey) => {
|
|||||||
const encryptedText = cs.encrypt(masterKey, salt, textObject)
|
const encryptedText = cs.encrypt(masterKey, salt, textObject)
|
||||||
|
|
||||||
db.promise()
|
db.promise()
|
||||||
.query(`INSERT INTO note_raw_text (text, salt, updated) VALUE (?, ?, ?)`, [encryptedText, salt, created])
|
.query(`INSERT INTO note_raw_text (text, salt, updated) VALUE (?, ?, ?)`, [encryptedText, salt, (+new Date)])
|
||||||
.then( (rows, fields) => {
|
.then( (rows, fields) => {
|
||||||
|
|
||||||
const rawTextId = rows[0].insertId
|
const rawTextId = rows[0].insertId
|
||||||
@@ -515,7 +515,7 @@ Note.setPinned = (userId, noteId, pinnedBoolean) => {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
const pinned = pinnedBoolean ? 1:0
|
const pinned = pinnedBoolean ? 1:0
|
||||||
const now = Math.round((+new Date)/1000)
|
const now = (+new Date)
|
||||||
|
|
||||||
//Update other note attributes
|
//Update other note attributes
|
||||||
return db.promise()
|
return db.promise()
|
||||||
@@ -532,7 +532,7 @@ Note.setArchived = (userId, noteId, archivedBoolead) => {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
const archived = archivedBoolead ? 1:0
|
const archived = archivedBoolead ? 1:0
|
||||||
const now = Math.round((+new Date)/1000)
|
const now = (+new Date)
|
||||||
|
|
||||||
//Update other note attributes
|
//Update other note attributes
|
||||||
return db.promise()
|
return db.promise()
|
||||||
@@ -549,7 +549,7 @@ Note.setTrashed = (userId, noteId, trashedBoolean, masterKey) => {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
const trashed = trashedBoolean ? 1:0
|
const trashed = trashedBoolean ? 1:0
|
||||||
const now = Math.round((+new Date)/1000)
|
const now = (+new Date)
|
||||||
|
|
||||||
//Update other note attributes
|
//Update other note attributes
|
||||||
return db.promise()
|
return db.promise()
|
||||||
@@ -1048,7 +1048,7 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => {
|
|||||||
// Always prioritize pinned notes in searches.
|
// Always prioritize pinned notes in searches.
|
||||||
|
|
||||||
//Default Sort, order by last updated
|
//Default Sort, order by last updated
|
||||||
let defaultOrderBy = ' ORDER BY note.pinned DESC, updated DESC, note.created DESC, note.opened DESC, id DESC'
|
let defaultOrderBy = ' ORDER BY note.pinned DESC, updated DESC, note.created DESC, note.opened DESC'
|
||||||
|
|
||||||
//Order by Last Created Date
|
//Order by Last Created Date
|
||||||
if(fastFilters.lastCreated == 1){
|
if(fastFilters.lastCreated == 1){
|
||||||
|
|||||||
+85
-28
@@ -5,22 +5,32 @@ const Note = require('@models/Note')
|
|||||||
const db = require('@config/database')
|
const db = require('@config/database')
|
||||||
const Auth = require('@helpers/Auth')
|
const Auth = require('@helpers/Auth')
|
||||||
const cs = require('@helpers/CryptoString')
|
const cs = require('@helpers/CryptoString')
|
||||||
|
const speakeasy = require('speakeasy')
|
||||||
|
|
||||||
let User = module.exports = {}
|
let User = module.exports = {}
|
||||||
|
|
||||||
const version = '3.0.1'
|
const version = '3.1.3'
|
||||||
|
|
||||||
//Login a user, if that user does not exist create them
|
//Login a user, if that user does not exist create them
|
||||||
//Issues login token
|
//Issues login token
|
||||||
User.login = (username, password) => {
|
User.login = (username, password, authToken = null) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
const lowerName = username.toLowerCase();
|
const lowerName = username.toLowerCase()
|
||||||
|
|
||||||
|
let statusObject = {
|
||||||
|
success: false,
|
||||||
|
token: null,
|
||||||
|
userId: null,
|
||||||
|
verificationRequired: false,
|
||||||
|
message: 'Incorrect Username or Password'
|
||||||
|
}
|
||||||
|
|
||||||
db.promise()
|
db.promise()
|
||||||
.query('SELECT * FROM user WHERE username = ? LIMIT 1', [lowerName])
|
.query('SELECT * FROM user WHERE username = ? LIMIT 1', [lowerName])
|
||||||
.then((rows, fields) => {
|
.then((rows, fields) => {
|
||||||
|
|
||||||
|
//
|
||||||
// Login User
|
// Login User
|
||||||
//
|
//
|
||||||
if(rows[0].length == 1){
|
if(rows[0].length == 1){
|
||||||
@@ -28,34 +38,76 @@ User.login = (username, password) => {
|
|||||||
//Pull out user data from database results
|
//Pull out user data from database results
|
||||||
const lookedUpUser = rows[0][0]
|
const lookedUpUser = rows[0][0]
|
||||||
|
|
||||||
//hash the password and check for a match
|
//Verify Token if set
|
||||||
// const salt = new Buffer(lookedUpUser.salt, 'binary')
|
const tokenValidates = speakeasy.totp.verify({
|
||||||
const salt = Buffer.from(lookedUpUser.salt, 'binary')
|
'secret': lookedUpUser['two_fa_secret'],
|
||||||
crypto.pbkdf2(password, salt, lookedUpUser.iterations, 512, 'sha512', function(err, delivered_key){
|
'encoding': 'base32',
|
||||||
if(delivered_key.toString('hex') === lookedUpUser.password){
|
'token': authToken,
|
||||||
|
'window': 2
|
||||||
|
})
|
||||||
|
|
||||||
User.generateMasterKey(lookedUpUser.id, password)
|
if(lookedUpUser.two_fa_enabled == 1 && !authToken){
|
||||||
.then( result => User.getMasterKey(lookedUpUser.id, password))
|
|
||||||
.then(masterKey => {
|
|
||||||
|
|
||||||
User.generateKeypair(lookedUpUser.id, masterKey)
|
statusObject['verificationRequired'] = true
|
||||||
.then(({publicKey, privateKey}) => {
|
statusObject['message'] = '2FA authentication required.'
|
||||||
|
|
||||||
//Passback a json web token
|
return resolve(statusObject)
|
||||||
Auth.createToken(lookedUpUser.id, masterKey)
|
}
|
||||||
.then(token => {
|
|
||||||
return resolve({ token: token, userId:lookedUpUser.id })
|
if(lookedUpUser.two_fa_enabled == 1 && !tokenValidates){
|
||||||
|
|
||||||
|
statusObject['verificationRequired'] = true
|
||||||
|
statusObject['message'] = 'Invalid Authorization Token.'
|
||||||
|
|
||||||
|
return resolve(statusObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lookedUpUser.two_fa_enabled == 0 || (lookedUpUser.two_fa_enabled == 1 && tokenValidates) ){
|
||||||
|
|
||||||
|
//hash the password and check for a match
|
||||||
|
|
||||||
|
const salt = Buffer.from(lookedUpUser.salt, 'binary')
|
||||||
|
crypto.pbkdf2(password, salt, lookedUpUser.iterations, 512, 'sha512', function(err, delivered_key){
|
||||||
|
if(delivered_key.toString('hex') === lookedUpUser.password){
|
||||||
|
|
||||||
|
User.generateMasterKey(lookedUpUser.id, password)
|
||||||
|
.then( result => User.getMasterKey(lookedUpUser.id, password))
|
||||||
|
.then(masterKey => {
|
||||||
|
|
||||||
|
User.generateKeypair(lookedUpUser.id, masterKey)
|
||||||
|
.then(({publicKey, privateKey}) => {
|
||||||
|
|
||||||
|
//Passback a json web token
|
||||||
|
Auth.createToken(lookedUpUser.id, masterKey)
|
||||||
|
.then(token => {
|
||||||
|
|
||||||
|
statusObject['token'] = token
|
||||||
|
statusObject['userId'] = lookedUpUser.id
|
||||||
|
statusObject['success'] = true
|
||||||
|
|
||||||
|
return resolve(statusObject)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
return resolve(statusObject)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
reject('Password does not match database')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
return reject('Incorrect Username or Password')
|
|
||||||
|
//If user is not found, say two factor authentication is required
|
||||||
|
statusObject['verificationRequired'] = true
|
||||||
|
statusObject['message'] = '2FA authentication required.'
|
||||||
|
|
||||||
|
//Show fake auth token message
|
||||||
|
if(authToken){
|
||||||
|
statusObject['message'] = 'Invalid Authorization Token.'
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve(statusObject)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
@@ -186,6 +238,12 @@ User.getCounts = (userId) => {
|
|||||||
|
|
||||||
Object.assign(countTotals, rows[0][0]) //combine results
|
Object.assign(countTotals, rows[0][0]) //combine results
|
||||||
|
|
||||||
|
return db.promise().query('SELECT two_fa_enabled FROM user WHERE id = ?', [userId])
|
||||||
|
|
||||||
|
}).then( (rows, fields) => {
|
||||||
|
|
||||||
|
Object.assign(countTotals, rows[0][0]) //combine results
|
||||||
|
|
||||||
//Convert everything to an int or 0
|
//Convert everything to an int or 0
|
||||||
Object.keys(countTotals).forEach( key => {
|
Object.keys(countTotals).forEach( key => {
|
||||||
const count = parseInt(countTotals[key])
|
const count = parseInt(countTotals[key])
|
||||||
@@ -253,13 +311,12 @@ User.generateMasterKey = (userId, password) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
User.getMasterKey = (userId, password) => {
|
User.getMasterKey = (userId, password) => {
|
||||||
|
|
||||||
if(!userId || !password){
|
|
||||||
reject('Need userId and password to fetch key')
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
if(!userId || !password){
|
||||||
|
reject('Need userId and password to fetch key')
|
||||||
|
}
|
||||||
|
|
||||||
db.promise().query('SELECT * FROM user_key WHERE user_id = ? LIMIT 1', [userId])
|
db.promise().query('SELECT * FROM user_key WHERE user_id = ? LIMIT 1', [userId])
|
||||||
.then((rows, fields) => {
|
.then((rows, fields) => {
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
var express = require('express')
|
var express = require('express')
|
||||||
var router = express.Router()
|
var router = express.Router()
|
||||||
|
|
||||||
let User = require('@models/User');
|
const User = require('@models/User')
|
||||||
|
const Auth = require('@helpers/Auth')
|
||||||
const cs = require('@helpers/CryptoString')
|
const cs = require('@helpers/CryptoString')
|
||||||
|
|
||||||
// middleware that is specific to this router
|
// middleware that is specific to this router
|
||||||
@@ -9,26 +10,14 @@ router.use(function timeLog (req, res, next) {
|
|||||||
// console.log('Time: ', Date.now())
|
// console.log('Time: ', Date.now())
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
// define the home page route
|
|
||||||
router.get('/', function (req, res) {
|
|
||||||
res.send('User Home Page ' + User.getUsername())
|
|
||||||
})
|
|
||||||
// define the about route
|
|
||||||
router.get('/about', function (req, res) {
|
|
||||||
User.getUsername(req.headers.userId)
|
|
||||||
.then( data => res.send(data) )
|
|
||||||
})
|
|
||||||
// Login User
|
// Login User
|
||||||
router.post('/login', function (req, res) {
|
router.post('/login', function (req, res) {
|
||||||
|
|
||||||
User.login(req.body.username, req.body.password)
|
User.login(req.body.username, req.body.password, req.body.authToken)
|
||||||
.then( returnData => {
|
.then( returnData => {
|
||||||
|
|
||||||
res.send(returnData)
|
res.send(returnData)
|
||||||
})
|
})
|
||||||
.catch(e => {
|
|
||||||
res.send(false)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
// Logout User
|
// Logout User
|
||||||
router.post('/logout', function (req, res) {
|
router.post('/logout', function (req, res) {
|
||||||
@@ -39,10 +28,7 @@ router.post('/logout', function (req, res) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Register User
|
||||||
|
|
||||||
|
|
||||||
// Login User
|
|
||||||
router.post('/register', function (req, res) {
|
router.post('/register', function (req, res) {
|
||||||
|
|
||||||
User.register(req.body.username, req.body.password)
|
User.register(req.body.username, req.body.password)
|
||||||
@@ -55,12 +41,36 @@ router.post('/register', function (req, res) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// fetch counts of users notes
|
// fetch counts of users notes
|
||||||
router.post('/totals', function (req, res) {
|
router.post('/totals', function (req, res) {
|
||||||
User.getCounts(req.headers.userId)
|
User.getCounts(req.headers.userId)
|
||||||
.then( countsObject => res.send( countsObject ))
|
.then( countsObject => res.send( countsObject ))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//
|
||||||
|
// Two Factor Auth Setup
|
||||||
|
//
|
||||||
|
router.post('/twofactorsetup', function (req, res) {
|
||||||
|
|
||||||
|
//Send QR code to user for 2FA setup
|
||||||
|
Auth.generateTwoFactorSecretKey(req.headers.userId, req.body.password)
|
||||||
|
.then( ({ qrCode }) => { res.send( qrCode ) })
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/verifytwofactorsetuptoken', function (req, res) {
|
||||||
|
|
||||||
|
//Verify Users QR code with token
|
||||||
|
Auth.setTwoFactorEnabled(req.headers.userId, req.body.password, req.body.token, true)
|
||||||
|
.then( ( results ) => { res.send( results ) })
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/validatetwofactortoken', function (req, res) {
|
||||||
|
|
||||||
|
//Verify Users QR code with token
|
||||||
|
Auth.validateTwoFactorToken(req.headers.userId, req.body.password, req.body.token)
|
||||||
|
.then( ( results ) => { res.send( results ) })
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
Reference in new issue
Block a user