* Added an auth screen that isn't integrated at all or working

* Force logout of user with authorization error
* Wrong site blocker doesn't trigger on the solid scribe domain
* Added log out button to main side bar making it easier to find
* Improved icon set for notes
* Colored notes display better on mobile, fixed text color based on color brightness
* Moved terms of use link to the bottom of a few pages
* Updated feature sections on home page, make them clearer and easier to process
* Tweaked color themes
* Deleted links no longer show up in search
* Updated search to use multiple key words
* Updated tests to do a multi word search
* Tweaked a bunch of styles to look better on chrome and browsers
This commit is contained in:
Max G 2020-08-03 02:40:27 +00:00
parent b34a62e114
commit b0c487404c
17 changed files with 399 additions and 193 deletions

View File

@ -13,6 +13,37 @@
</div>
</div>
<div class="auth-block" v-if="requireAuth">
<div class="ui raised inverted segment">
<div class="ui centered header">
Authentication Required
</div>
<div class="ui small inverted centered header" v-if="$store.getters.getUsername">
<i class="green user outline icon"></i>
{{ $store.getters.getUsername }}
</div>
<div class="ui large form">
<div class="field">
<div class="ui small inverted header">Password</div>
<div class="ui input">
<input type="password" v-model="password" placeholder="Password">
</div>
</div>
<div class="field">
<div class="ui small inverted header">One Time Password</div>
<div class="ui input">
<input type="password" v-model="otp" placeholder="One Time Password">
</div>
</div>
<div class="ui fluid inverted black button">
<i class="unlock icon"></i>
Submit</div>
</div>
</div>
</div>
<global-site-menu v-if="!showFakeSite" />
@ -31,14 +62,18 @@ import axios from 'axios'
export default {
components: {
'global-site-menu': require('@/components/GlobalSiteMenu.vue').default,
'global-notification':require('@/components/GlobalNotificationComponent.vue').default
'global-notification':require('@/components/GlobalNotificationComponent.vue').default,
},
data: function(){
return {
showFakeSite:false, //Incorrect domain detection
redirectSeconds: 15,
fetchingInProgress: false, //Prevent start getting token while fetch is in progress
blockUntilNextRequest: false //If token was just renewed, don't fetch more until next request
blockUntilNextRequest: false, //If token was just renewed, don't fetch more until next request
requireAuth: false,
password: '',
otp: '',
}
},
@ -81,6 +116,16 @@ export default {
return response
},
(error) => {
//Catch all authorization errors, log user out if we encounter one
if(error.response && error.response.status == 401){
this.$router.push('/')
this.$store.commit('destroyLoginToken')
this.$bus.$emit('notification', 'Error: You have been logged out.')
}
return Promise.reject(error)
}
)
@ -127,7 +172,7 @@ export default {
mounted: function(){
const isDev = process.env['NODE_ENV'] == 'development'
if(window.location.hostname.toLowerCase() != "www.solidscribe.com" && !isDev){
if(window.location.hostname.toLowerCase().replace('www.','') != "solidscribe.com" && !isDev){
this.showFakeSite = true
setInterval(() => {
this.redirectSeconds--
@ -162,16 +207,23 @@ export default {
}
},
methods: {
destroyLoginToken() {
this.$store.commit('destroyLoginToken')
},
loginGateway() {
if(!this.loggedIn){
console.log('This user is not logged in')
this.$router.push({'path':'/login'})
return
}
}
},
logout() {
this.$router.push('/')
axios.post('/api/user/logout')
setTimeout(() => {
this.$store.commit('destroyLoginToken')
this.$bus.$emit('notification', 'Logged Out')
}, 200)
},
}
}
</script>

View File

@ -39,6 +39,10 @@
html {
/*scrollbar-width: none;*/
width: 100%;
height:100%;
padding: 0;
margin: 0;
}
a:hover {
text-decoration: underline;
@ -47,6 +51,15 @@ div.ui.basic.segment.no-fluf-segment {
margin-top: 0px;
}
.page-container {
/*width: 100%;*/
display: block;
margin: 0;
padding: 0.5rem;
box-sizing: border-box;
overflow: hidden;
}
/* Night mode modifiers */
/*Make images sepia in night mode */
@ -268,7 +281,7 @@ i.green.icon.icon.icon.icon {
/*height: calc(100% - 69px);*/
min-height: 500px;
background-color: rgba(255,200,0,0.0);
background-color: var(--small_element_bg_color);
/*margin-bottom: 15px;*/
box-sizing: border-box;
@ -282,6 +295,10 @@ i.green.icon.icon.icon.icon {
scrollbar-width: none;
scrollbar-color: transparent transparent;
caret-color: var(--main-accent);
margin-left: auto;
margin-right: auto;
max-width: 1100px;
}
.squire-box::selection,
.squire-box::-moz-selection {
@ -469,7 +486,7 @@ i.green.icon.icon.icon.icon {
@media only screen and (max-width: 740px) {
.squire-box {
min-height: calc(100vh - 120px);
min-height: calc(100vh - 122px);
}
.ui.button.shrinking {
@ -549,6 +566,27 @@ i.green.icon.icon.icon.icon {
animation: fade-in-fwd 0.8s both;
}
/* div that comes up, blocking interaction annd requiring authentication */
.auth-block {
background-color: rgba(0,0,0,0.9);
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 100vh;
width: 100%;
z-index: 200;
}
.auth-block > div {
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -40%);
width: 300px;
}
/**
* ----------------------------------------
* animation fade-in-fwd

View File

@ -16,7 +16,7 @@
<div class="sixteen wide column">
<br>
<p>Note Color</p>
<div v-for="color in getReducedColors()"
<div v-for="color in colors"
class="color-button"
:style="{ backgroundColor:color }"
v-on:click="chosenColor(color)"
@ -66,7 +66,7 @@
blankStyle:{ 'noteText':null,'noteBackground':null, 'noteIcon':null, 'iconColor':null },
colors: [null,
'rgb(67,67,67)','rgb(102,102,102)','rgb(153,153,153)','rgb(183,183,183)','rgb(204,204,204)','rgb(217,217,217)','rgb(239,239,239)','rgb(243,243,243)','rgb(255,255,255)','rgb(152,0,0)','rgb(255,0,0)','rgb(255,153,0)','rgb(255,255,0)','rgb(0,255,0)','rgb(0,255,255)','rgb(74,134,232)','rgb(0,0,255)','rgb(153,0,255)','rgb(255,0,255)','rgb(230,184,175)','rgb(244,204,204)','rgb(252,229,205)','rgb(255,242,204)','rgb(217,234,211)','rgb(208,224,227)','rgb(201,218,248)','rgb(207,226,243)','rgb(217,210,233)','rgb(234,209,220)','rgb(221,126,107)','rgb(234,153,153)','rgb(249,203,156)','rgb(255,229,153)','rgb(182,215,168)','rgb(162,196,201)','rgb(164,194,244)','rgb(159,197,232)','rgb(180,167,214)','rgb(213,166,189)','rgb(204,65,37)','rgb(224,102,102)','rgb(246,178,107)','rgb(255,217,102)','rgb(147,196,125)','rgb(118,165,175)','rgb(109,158,235)','rgb(111,168,220)','rgb(142,124,195)','rgb(194,123,160)','rgb(166,28,0)','rgb(204,0,0)','rgb(230,145,56)','rgb(241,194,50)','rgb(106,168,79)','rgb(69,129,142)','rgb(60,120,216)','rgb(61,133,198)','rgb(103,78,167)','rgb(166,77,121)','rgb(133,32,12)','rgb(153,0,0)','rgb(180,95,6)','rgb(191,144,0)','rgb(56,118,29)','rgb(19,79,92)','rgb(17,85,204)','rgb(11,83,148)','rgb(53,28,117)','rgb(116,27,71)','rgb(91,15,0)','rgb(102,0,0)','rgb(120,63,4)','rgb(127,96,0)','rgb(39,78,19)','rgb(12,52,61)','rgb(28,69,135)','rgb(7,55,99)','rgb(32,18,77)','rgb(76,17,48)'],
icons: ['ambulance','anchor','balance scale','bath','bed','beer','bell','bell slash','bell slash outline','bicycle','binoculars','birthday cake','blind','bomb','book','bookmark','briefcase','building','car','coffee','crosshairs','dollar sign','eye','eye slash','fighter jet','fire','fire extinguisher','flag','flag checkered','flask','gamepad','gavel','gift','glass martini','globe','graduation cap','h square','heart','heart outline','heartbeat','home','hospital','hospital outline','image','image outline','images','images outline','industry','info','info circle','key','leaf','lemon','lemon outline','life ring','life ring outline','lightbulb','lightbulb outline','location arrow','low vision','magnet','male','map','map outline','map marker','map marker alternate','map pin','map signs','medkit','money bill alternate','money bill alternate outline','motorcycle','music','newspaper','newspaper outline','paw','phone','phone square','phone volume','plane','plug','plus','plus square','plus square outline','print','recycle','road','rocket','search','search minus','search plus','ship','shopping bag','shopping basket','shopping cart','shower','street view','subway','suitcase','tag','tags','taxi','thumbtack','ticket alternate','tint','train','tree','trophy','truck','tty','umbrella','university','utensil spoon','utensils','wheelchair','wifi','wrench']
icons: ['cat','crow','dog','dove','dragon','feather','feather alternate','fish','frog','hippo','horse','horse head','kiwi bird','otter','paw','spider','video','headphones','motorcycle','truck','monster truck','campground','cloud sun','drumstick bite','football ball','fruit-apple','hiking','mountain','tractor','tree','wind','wine bottle','coffee','flask','glass cheers','glass martini','beer','toilet paper','gift','globe','hand holding heart','comment','graduation cap','hat cowboy','hat wizard','mitten','user tie','laptop code','microchip','shield alternate','mouse','plug','power off','satellite','hammer','wrench','bell','eye','marker','paperclip','atom','award','theater masks','music','grin alternate','grin tongue squint outline','laugh wink','fire','fire alternate','poop','sun','money bill alternate','piggy bank','heart outline','heartbeat','running','walking','bacon','bone','bread slice','candy cane','carrot','cheese','cloud meatball','cookie','egg','hamburger','hotdog','ice cream','lemon','lemon outline','pepper hot','pizza slice','seedling','stroopwafel','leaf','book dead','broom','cloud moon','ghost','mask','skull crossbones','certificate','check','check circle','joint','cannabis','bong','gem','futbol','brain','dna','hand spock','hand spock outline','meteor','moon','moon outline','robot','rocket','satellite dish','space shuttle','user astronaut','fingerprint','thumbs up','thumbs down']
}
},
watch:{
@ -83,17 +83,11 @@
let reduced = []
this.colors.forEach((color,i) => {
let mod = (i % 10)+1 //1 - 10
let lines = [3, 5, 8, 9, 10]
// if(lines.includes(mod)){
if(i < 20 || i > 69){
reduced.push(color)
// }
}
})
reduced.push("#000")
return reduced
},
clearStyles(){
@ -114,12 +108,17 @@
//Automatically select note text color
//If you are using hex colors, use this
// Convert hex color to RGB - http://gist.github.com/983661
let color = +("0x" + inColor.slice(1).replace(inColor.length < 5 && /./g, '$&$&'));
// let color = +("0x" + inColor.slice(1).replace(inColor.length < 5 && /./g, '$&$&'));
// let r = color >> 16;
// let g = color >> 8 & 255;
// let b = color & 255;
let r = color >> 16;
let g = color >> 8 & 255;
let b = color & 255;
const set = inColor.match(/\d+/g)
const r = parseInt(set[0])
const g = parseInt(set[1])
const b = parseInt(set[2])
//Convert RGB to HSP
const hsp = Math.sqrt( 0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b) );

View File

@ -83,6 +83,7 @@
/*padding: 5px 1rem 5px;*/
display: flex;
justify-content: space-around;
width: 100vw;
}
.place-holder {
width: 100%;
@ -277,20 +278,18 @@
</router-link>
</div>
<div class="menu-section">
<router-link class="menu-item menu-button" exact-active-class="active" to="/terms">
<i class="info circle icon"></i>Terms
<div class="menu-section" v-if="loggedIn">
<router-link class="menu-item menu-button" exact-active-class="active" to="/settings">
<i class="cog icon"></i>Settings
</router-link>
</div>
<div class="menu-section" v-if="loggedIn">
<router-link class="menu-item menu-button" exact-active-class="active" to="/settings">
<i v-if="userIcon" class="cog icon"></i>{{ usernameDisplay }}
</router-link>
<div class="menu-item menu-button" v-on:click="logout()">
<i class="log out icon"></i>Log Out
</div>
</div>
<div v-on:click="reloadPage" class="version-display" v-if="version != 0" >
<i :class="`${getVersionIcon()} icon`"></i> {{ version }}
</div>
@ -348,24 +347,18 @@
//Map logged in from state
return this.$store.getters.getLoggedIn
},
usernameDisplay() {
//Remove Emails from username, limit length to 16 chars
let name = this.$store.getters.getUsername
let splitName = name.split('@')
if(splitName.length > 1){
name = splitName.shift()
this.userIcon = false
}
if(name.length > 16){
this.userIcon = false
}
return this.ucWords(name.substring(0, 16))
},
},
methods: {
logout() {
this.$router.push('/')
axios.post('/api/user/logout')
setTimeout(() => {
this.$store.commit('destroyLoginToken')
this.$bus.$emit('notification', 'Logged Out')
}, 200)
},
newQuickNote(){
axios.post('/api/quick-note/get')

View File

@ -73,8 +73,10 @@
<style type="text/css" scoped>
.darken-accent {
filter: brightness(62%);
-webkit-filter: brightness(62%);
}
.brighten-accent {
filter: saturate(145%);
-webkit-filter: saturate(145%);
}
</style>

View File

@ -133,10 +133,11 @@
<div class="bottom-edit-menu"></div>
<div class="input-container-wrapper" :class="{ 'side-menu-open':sideMenuOpen, 'size-down':(sizeDown == true),}">
<div class="input-container-wrapper"
:class="{ 'side-menu-open':sideMenuOpen, 'size-down':(sizeDown == true),}">
<!-- Squire box grows -->
<div id="text-box-container" class="note-wrapper" :style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}">
<div id="text-box-container" class="note-wrapper">
<!-- Loading indicator -->
<transition name="fade">
@ -154,12 +155,17 @@
v-on:keydown="titleResize"
@keydown.enter.exact.prevent="editor.focus(); editor.moveCursorToEnd()"
rows="1"
:style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}"
:style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText'] }"
v-on:blur="save" type="text" v-model="noteTitle" placeholder="Title" class="stealth-input">
</textarea>
<!-- Squire Box -->
<div id="squire-id" class="squire-box" ref="squirebox" placeholder="Type Note Here"></div>
<div
id="squire-id"
class="squire-box"
ref="squirebox"
:style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText'] }"
placeholder="Type Note Here"></div>
</div>
@ -1248,14 +1254,20 @@
.stealth-input {
width: 100%;
padding: 10px 15px 5px;
background-color: rgba(255,255,255,0.1);
background-color: var(--small_element_bg_color );
border: none;
font-size: 1.7em;
color: var(--text_color);
caret-color: var(--main-accent);
resize: none;
overflow: hidden;
margin: 0;
/*margin: 0;*/
outline: none;
display: block;
margin-left: auto;
margin-right: auto;
max-width: 1100px;
}

View File

@ -26,6 +26,12 @@
v-html="note.subtext"></span>
<!-- Not indexed warning -->
<!-- <span v-if="note.indexed != 1">
<span class="green label">Not Indexed</span>
</span> -->
<div class="ui fluid basic button" v-if="note.encrypted == 1">
<i class="green lock icon"></i>
Locked
@ -410,6 +416,7 @@
background-color: var(--small_element_bg_color);
/*The subtle shadow*/
/*box-shadow: 0px 1px 2px 1px rgba(210, 211, 211, 0.46);*/
box-shadow: 2px 2px 6px 0 rgba(0,0,0,.15);
transition: box-shadow ease 0.5s, transform linear 0.1s;
margin: 5px;
/*padding: 0.7em 1em;*/
@ -562,7 +569,7 @@
@media only screen and (max-width: 700px) {
.note-title-display-card {
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) {

View File

@ -1,6 +1,6 @@
<style type="text/css" scoped>
.colors {
position: absolute;
position: fixed;
z-index: 1023;
top: 5px;
/*height: 100px;*/

File diff suppressed because one or more lines are too long

View File

@ -167,8 +167,10 @@
<!-- Overview -->
<div class="middle aligned centered row">
<div class="six wide column">
<h2>Solid Scribe focuses on powerful text editing and user privacy</h2>
<h3>Tools to organize and collaborate on thousands of notes while maintaining security and respecting your privacy.</h3>
<h2>Powerful text editing and privacy</h2>
<h3>Easily edit, share and organize thousands of notes.</h3>
<h3>Feel safe knowing no one can read your notes but you.</h3>
<!-- <h3>Tools to organize and collaborate on thousands of notes while maintaining security and respecting your privacy.</h3> -->
</div>
<div class="four wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/idea.svg" alt="Explosion of New Ideas">
@ -176,52 +178,21 @@
</div>
<!-- features list -->
<div class="middle aligned centered row">
<div class="sixteen wide column">
<h1 class="ui center aligned header"><i class="sliders horizontal icon"></i>Features</h1>
</div>
<div class="top aligned centered row">
<!-- note features -->
<div class="six wide column">
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey lock icon"></i>
<i class="bottom left corner yellow key icon"></i>
</i>
Privacy Focused
<div class="sub header">All note text is encrypted. No one can read your notes. None of your data is shared.</div>
</div>
</h2>
<h1 class="ui center aligned header"><i class="sliders horizontal icon"></i>App Features</h1>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey list icon"></i>
<i class="bottom left corner green check icon"></i>
<i class="grey sticky note icon"></i>
<i class="bottom left corner teal plus icon"></i>
</i>
To Do Lists
<div class="sub header">Create To Do lists that are always synced, work on mobile and can be sorted.</div>
</div>
</h2>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey file icon"></i>
<i class="bottom left corner blue pen icon"></i>
</i>
Document Editing Tools
<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 images icon"></i>
<i class="bottom left corner teal paperclip icon"></i>
</i>
Add Images
<div class="sub header">Upload images to notes, add search text to the images to find them later.</div>
Create as many notes as you want
<div class="sub header">Create unlimited notes up to 5,000,000 characters long.</div>
</div>
</h2>
@ -232,40 +203,7 @@
<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">
<div class="content">
<i class="icons">
<i class="grey cloud moon icon"></i>
<i class="bottom left corner red eye icon"></i>
</i>
Night Mode
<div class="sub header">Pure black night theme with an even darker flux theme.</div>
</div>
</h2>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey share alternate icon"></i>
<i class="bottom left corner share icon"></i>
</i>
Share Encrypted Notes
<div class="sub header">Share encrypted notes with friends without compromising security.</div>
</div>
</h2>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey users icon"></i>
<i class="bottom left corner olive exchange icon"></i>
</i>
Collaborative Note Editing
<div class="sub header">Notes instantly update in real time everywhere the note is open.</div>
<div class="sub header">Easily add and edit tags on notes then search or sort by tag.</div>
</div>
</h2>
@ -275,8 +213,8 @@
<i class="grey search icon"></i>
<i class="bottom left corner orange font icon"></i>
</i>
Keyword Search
<div class="sub header">Easily search all notes. Encrypted search index ensures privacy and convenience.</div>
Search Note Text
<div class="sub header">Easily search all notes, files, links and tags.</div>
</div>
</h2>
@ -291,6 +229,109 @@
</div>
</h2>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey cloud moon icon"></i>
<i class="bottom left corner red eye icon"></i>
</i>
Night Mode
<div class="sub header">Pure black night theme with an even darker flux theme.</div>
</div>
</h2>
</div>
<!-- editing features -->
<div class="six wide column">
<h1 class="ui center aligned header"><i class="sliders horizontal icon"></i>Editing Features</h1>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey list icon"></i>
<i class="bottom left corner green check icon"></i>
</i>
Create To Do Lists
<div class="sub header">Create To Do lists that are always synced, work on mobile and can be sorted.</div>
</div>
</h2>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey file icon"></i>
<i class="bottom left corner blue pen icon"></i>
</i>
Formatting Tools
<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 file icon"></i>
<i class="bottom left corner orange paint brush icon"></i>
</i>
Customized Colorful Notes
<div class="sub header">Color the background of notes and add colored icons to make them stand out.</div>
</div>
</h2>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey images icon"></i>
<i class="bottom left corner teal paperclip icon"></i>
</i>
Add Images
<div class="sub header">Upload images to notes, add search text to the images to find them later.</div>
</div>
</h2>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey users icon"></i>
<i class="bottom left corner olive exchange icon"></i>
</i>
Collaborative Note Editing
<div class="sub header">Notes instantly update in real time everywhere its open and anywhere its shared.</div>
</div>
</h2>
</div>
</div>
<div class="middle aligned centered row">
<!-- privacy features -->
<div class="six wide column">
<h1 class="ui center aligned header"><i class="sliders horizontal icon"></i>Privacy Features</h1>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey lock icon"></i>
<i class="bottom left corner yellow key icon"></i>
</i>
All Note Text is Encrypted
<div class="sub header">All note text is encrypted. No one can read your notes. None of your data is shared.</div>
</div>
</h2>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey search icon"></i>
<i class="bottom left corner orange font icon"></i>
</i>
Note Search is Encrypted
<div class="sub header">Easily search the contents of all your notes without compromising security.</div>
</div>
</h2>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
<i class="grey share alternate icon"></i>
<i class="bottom left corner share icon"></i>
</i>
Encrypted Note Sharing
<div class="sub header">Shared notes are still encrypted, only readable by you and the shared users.</div>
</div>
</h2>
<h2 class="ui dividing header">
<div class="content">
<i class="icons">
@ -301,11 +342,12 @@
<div class="sub header">Enable two factor authentication for added peace of mind.</div>
</div>
</h2>
</div>
<div class="four wide column">
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/onboarding.svg" alt="">
</div>
</div>
@ -347,7 +389,7 @@
<h2>Leave your Ad Blockers turned on</h2>
<h3>SolidScribe doesn't load any trackers or ads. It was designed to run on
<a href="https://www.mozilla.org/en-US/firefox/new/" target="_blank">Firefox</a>, with
<a href="https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/" target="_blank">uBlock Origin</a> and a
<a href="https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/" target="_blank">an Ad Blocker</a> turned on. It even works with a
<a href="https://pi-hole.net/" target="_blank">Pi-hole</a> on the network.</h3>
</div>
<div class="four wide column">
@ -423,6 +465,10 @@
</div>
</div>
<div class="center aligned sixteen wide column">
<router-link to="/terms"></i>Solid Scribe Terms of Use</router-link>
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
<template>
<div class="ui basic segment no-fluf-segment">
<div class="page-container">
<div class="ui grid" ref="content">

View File

@ -1,10 +1,10 @@
<template>
<div class="squire-box">
<div class="">
<div>
<h3 class="ui dividing header">
<i class="green cog icon"></i>
Settings
<i class="inline green cog icon"></i>
Settings for {{ $store.getters.getUsername }}
</h3>
<h4>New Scratch Pad</h4>
@ -113,19 +113,14 @@
</div>
<!-- log out -->
<h4>Log Out</h4>
<h4>Revoke Other Active Sessions</h4>
<div class="ui segment">
<div class="ui stackable grid">
<div class="eight wide column">
<div class="ui button" v-on:click="logout()">
<i class="power off icon"></i>
Log Out on this browser
</div>
</div>
<div class="eight wide column">
<div class="sixteen wide column">
<p>Revoke access on any logged in device, except for the one you are currently using.<br><br></p>
<div class="ui button" v-on:click="revokeAllSessions()">
<i class="sign out icon"></i>
Log Out all other browsers
Log Out all other devices
</div>
</div>
</div>
@ -155,6 +150,12 @@
</div>
</div>
<div class="ui grid">
<div class="center aligned sixteen wide column">
<router-link to="/terms"></i>Solid Scribe Terms of Use</router-link>
</div>
</div>
</div>
</div>
</template>
@ -208,14 +209,6 @@
})
},
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

File diff suppressed because one or more lines are too long

View File

@ -26,24 +26,25 @@ export default new Vuex.Store({
localStorage.removeItem('currentVersion')
delete axios.defaults.headers.common['authorizationtoken']
state.username = null
state.userTotals = null
},
toggleNightMode(state, pastTheme){
const themes = {
'white':{
'body_bg_color': '#f5f6f7',
'body_bg_color': '#f1f1f1',//'#f5f6f7',
'small_element_bg_color': '#fff',
'text_color': '#3d3d3d',
'dark_border_color': '#DFE1E6',
'dark_border_color': '#d9d9d9',//'#DFE1E6',
'border_color': '#DFE1E6',
'menu-accent': '#cecece',
'menu-text': '#5e6268',
},
'black':{
'body_bg_color': '#000',
'body_bg_color': '#0f0f0f',//'#000',
'small_element_bg_color': '#000',
'text_color': '#FFF',
'dark_border_color': '#ACACAC', //Lighter color to accent elemnts user can interact with
'dark_border_color': '#555',//'#ACACAC', //Lighter color to accent elemnts user can interact with
'border_color': '#555',
'menu-accent': '#626262',
'menu-text': '#d9d9d9',
@ -52,7 +53,7 @@ export default new Vuex.Store({
'body_bg_color': '#000',
'small_element_bg_color': '#000',
'text_color': '#a98457',
'dark_border_color': '#a98457',
'dark_border_color': '#555',
'border_color': '#555',
'menu-accent': '#626262',
'menu-text': '#a69682',
@ -98,10 +99,6 @@ export default new Vuex.Store({
}
})(navigator.userAgent||navigator.vendor||window.opera, state);
},
toggleNoteSettingsPane(state){
state.isNoteSettingsOpen = !state.isNoteSettingsOpen
},
setSocketIoSocket(state, socket){
//Put socket id in axios headers

View File

@ -259,7 +259,7 @@ let NoteTest = require('@models/Note')
let AuthTest = require('@helpers/Auth')
Auth.test()
UserTest.keyPairTest('genMan25', '1', printResults)
UserTest.keyPairTest('genMan30', '1', printResults)
.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults))
.then( message => {
if(printResults) console.log(message)

View File

@ -32,6 +32,7 @@ Attachment.textSearch = (userId, searchTerm) => {
) as snippet
FROM attachment
WHERE user_id = ?
AND visible != 0
AND MATCH(text)
AGAINST(? IN NATURAL LANGUAGE MODE)
LIMIT 1000`

View File

@ -42,7 +42,7 @@ Note.test = (userId, masterKey, printResults) => {
testNoteId = newNoteId
return Note.update
(userId, testNoteId, 'Note text', 'Test Note beans Title', 0, 0, 0, 'hash', masterKey)
(userId, testNoteId, 'Note text', 'Test Note beans barns Title', 0, 0, 0, 'hash', masterKey)
})
.then(() => {
@ -63,14 +63,14 @@ Note.test = (userId, masterKey, printResults) => {
if(printResults) console.log('Test: Reindex normal note - Pass')
return Note.encryptedIndexSearch(userId, 'beans', null, masterKey)
return Note.encryptedIndexSearch(userId, 'beans barns', null, masterKey)
})
.then(textSearchResults => {
if(textSearchResults['ids'] && textSearchResults['ids'].length >= 1){
if(printResults) console.log('Test: Normal Note Search Index - Pass')
} else { console.log('Test: Search Index - Fail') }
} else { console.log('Test: Search Index - Fail-------------> 🥱') }
return ShareNote.addUserToSharedNote(userId, testNoteId, shareUserId, masterKey)
})
@ -868,39 +868,98 @@ Note.encryptedIndexSearch = (userId, searchQuery, searchTags, masterKey) => {
const decipheredSearchIndex = cs.decrypt(masterKey, row.salt, row.index)
const searchIndex = JSON.parse(decipheredSearchIndex)
//Clean up search word
const word = searchQuery.toLowerCase().replace(/[^a-z0-9]/g, '')
//Clean up search word, leave in spaces, split to array
const words = searchQuery.toLowerCase().replace(/[^a-z0-9 ]/g, '').split(' ')
let noteIds = []
let partials = []
Object.keys(searchIndex).forEach(wordIndex => {
if( wordIndex.indexOf(word) != -1 && wordIndex != word){
partials.push(wordIndex)
noteIds.push(...searchIndex[wordIndex])
let wordSearchCount = 0;
let partialWords = [] //For debugging
let exactWords = [] //For debugging
let exactWordIdSets = []
let partialMatchNoteIds = []
words.forEach(word => {
//Skip short words
if(word.length <= 2){
return
}
//count all words being searched
wordSearchCount++
//Save all exact match sets if found
if(searchIndex[word]){
// exactWords.push(word) //Words for debugging
exactWordIdSets.push(...searchIndex[word])
}
//Find all partial word matches in index
Object.keys(searchIndex).forEach(wordIndex => {
if( wordIndex.indexOf(word) != -1 && wordIndex != word){
// partialWords.push(wordIndex) //partialWords for debugging
partialMatchNoteIds.push(...searchIndex[wordIndex])
}
})
})
const exactArray = searchIndex[word] ? searchIndex[word] : []
//If more than one work was searched, remove notes that don't contain both
if(words.length > 1 && exactWordIdSets.length > 0){
let searchData = {
'word':word,
'exact': exactArray,
'partials': partials,
'partial': [...new Set(noteIds) ],
//Find ids that appear more than once, this means there was an exact match in more than one note
let overlappingIds = exactWordIdSets.filter((e, i, a) => a.indexOf(e) !== i)
overlappingIds = [...new Set(overlappingIds)]
//If there are notes that appear
if(overlappingIds.length > 0){
exactWordIdSets = overlappingIds
}
//If note appears in partial and exact, show only that set
const partialIntersect = exactWordIdSets.filter(x => partialMatchNoteIds.includes(x))
if(partialIntersect.length > 0){
exactWordIdSets = partialIntersect
partialMatchNoteIds = []
}
}
//Remove duplicates from final id sets
let finalExact = [ ...new Set(exactWordIdSets) ]
let finalPartial = [ ...new Set(partialMatchNoteIds) ]
//Remove exact matches from partials set if there is overlap
if(searchData['exact'].length > 0 && searchData['partial'].length > 0){
searchData['partial'] = searchData['partial']
.filter( ( el ) => !searchData['exact'].includes( el ) )
if(finalExact.length > 0 && finalPartial.length > 0){
finalPartial = finalPartial
.filter( ( el ) => !finalExact.includes( el ) )
}
searchData['ids'] = searchData['exact'].concat(searchData['partial'])
searchData['total'] = searchData['ids'].length
//Combine the two filtered sets
let finalIdSearchSet = finalExact.concat(finalPartial)
// console.log(searchData['total'])
// let searchData = {
// 'query':searchQuery,
// 'words_count': words.length,
// 'exact_matches': exactWordIdSets.length,
// 'word_search_count': wordSearchCount,
// 'exactWords': exactWords,
// 'exact': finalExact,
// 'partialWords': partialWords,
// 'partial': finalPartial,
// }
return resolve({ 'ids':searchData['ids'] })
// //Lump all found note ids into one array
// searchData['ids'] = finalIdSearchSet
// searchData['total'] = searchData['ids'].length
// console.log('-----------------')
// console.log(searchData)
// console.log('-----------------')
return resolve({ 'ids':finalIdSearchSet })
} else {
@ -971,7 +1030,8 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => {
GROUP_CONCAT(DISTINCT attachment.file_location) as thumbs,
shareUser.username as shareUsername,
note.shared,
note.encrypted_share_password_key
note.encrypted_share_password_key,
note.indexed
FROM note
JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id)
LEFT JOIN note_tag ON (note.id = note_tag.note_id)