I need to get back into using git. The hell is wrong with me!?

This commit is contained in:
Max G
2019-12-20 05:50:50 +00:00
parent 7b77bd37f3
commit 6fe39406b7
135 changed files with 53273 additions and 699 deletions

View File

@@ -0,0 +1,83 @@
<template>
<div class="ui basic segment no-fluf-segment">
<div class="ui grid">
<div class="ui sixteen wide column">
<h2 class="ui header">
<i class="folder icon"></i>
<div class="content">
Attachments
<div class="sub header">Files and scraped web pages from notes</div>
</div>
</h2>
</div>
<div class="ui segment" v-if="searchParams.noteId">
Showing Attachments for note. <div class="ui button" v-on:click="clearNote">Clear</div>
</div>
<div class="ui basic segment">
<attachment-display
v-for="item in attachments"
:item="item"
:key="item.id"
/>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
components: {
'attachment-display': require('@/components/AttachmentDisplayCard').default,
},
data: function(){
return {
attachments: [],
searchParams: {}
}
},
beforeCreate: function(){
//
// Perform Login check
//
this.$parent.loginGateway()
},
mounted: function(){
//Mount notes on load if note ID is set
if(this.$route.params && this.$route.params.id){
const inputNoteId = this.$route.params.id
this.searchParams['noteId'] = inputNoteId
}
this.searchAttachments()
},
methods: {
clearNote(){
this.$router.push('/attachments/')
delete this.searchParams.noteId
this.searchAttachments()
},
searchAttachments (){
axios.post('/api/attachment/search', this.searchParams)
.then( results => {
this.attachments = results.data
})
},
}
}
</script>
<style type="text/css" scoped>
.attachment-display-area {
width: 100%;
margin-top: 15px;
box-sizing: border-box;
padding: 0 5%;
}
</style>

View File

@@ -1,13 +1,310 @@
<style type="text/css" scoped>
.hero {
background-size: 50%;
background-color: #0a2f13;
background: linear-gradient(270deg, #21ba45, #3710a4);
background-size: 400% 400%;
overflow: hidden;
-webkit-animation: fadeorama 16s ease infinite;
-moz-animation: fadeorama 16s ease infinite;
animation: fadeorama 16s ease infinite;
}
.lightly-padded {
margin-top: 10px;
}
.massive-text {
color: white;
font-size: 4rem;
}
.blinking {
animation:blinkingText 1.5s linear infinite;
}
@keyframes blinkingText{
0%{ opacity: 0.9; }
50%{ opacity: 0; }
100%{ opacity: 0.9; }
}
.subtext {
border-bottom: 1px solid white;
border-right: 1px solid white;
color: white;
font-size: 1.5rem;
padding: 0 0 0 10px;
}
.stand-out {
color: white;
text-shadow:
2px 2px 1px black,
-2px -2px 1px black,
-2px 2px 1px black,
2px -2px 1px black;
}
h2, h3 {
font-weight: normal;
}
@-webkit-keyframes fadeorama {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
@-moz-keyframes fadeorama {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
@keyframes fadeorama {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
</style>
<template>
<div class="ui basic segment">
<div class="ui container">
<h1>Welcome</h1>
<div class="lightly-padded">
<div class="ui centered vertically divided stackable grid">
<div class="row hero fadeBg" :style="{ 'height':(height+'px') }">
<!-- All marketing images if you need to review -->
<div v-if="false" class="sixteen wide column">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/add.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/gardening.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/growth.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/icecream.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/investing.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/onboarding.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/robot.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/solution.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/watching.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/cloud.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/grandma.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/hamburger.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/idea.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/notebook.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/plan.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/secure.svg" alt="">
<img loading="lazy" width="10%" src="/api/static/assets/marketing/void.svg" alt="">
</div>
<div class="one wide large screen only column"></div>
<!-- desktop column - large screen only -->
<div class="seven wide middle aligned left aligned column">
<h2 class="massive-text">Take Notes, <br>Like Never Before</h2>
<h3 class="subtext">
Using an online note application <i class="i cursor icon blinking"></i>
</h3>
<br>
<i class="huge inverted chevron circle down icon"></i>
</div>
<div class="eight wide middle aligned left aligned column">
<img loading="lazy" width="90%" src="/api/static/assets/marketing/notebook.svg" alt="The Venus fly laptop about to capture another victim">
</div>
</div>
<!-- set -->
<div class="middle aligned centered row">
<div class="six wide column">
<h2>Everyone has knowledge that need to be expressed</h2>
<h3>Utilize action potential to create notes by encoding raw brainwaves converted to written language</h3>
</div>
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/idea.svg" alt="Explosion of New Ideas">
</div>
</div>
<div class="middle aligned centered row">
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/gardening.svg" alt="Pruning the mind garden">
</div>
<div class="six wide column">
<h2>Dream it, then do it</h2>
<h3>Easily record your unlimited imagination. Ideas, stories, notes, plays, poems anything, that can reasonably be put into text</h3>
</div>
</div>
<!-- set -->
<div class="middle aligned centered green row">
<div class="six wide column">
<h2>Unbridled Input</h2>
<h3>Revolutionary technology allows the use of any keyboard with up to 395 keys</h3>
</div>
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/add.svg" alt="A shpere of newness">
</div>
</div>
<div class="middle aligned centered row">
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/solution.svg" alt="Hypercube of Solutions">
</div>
<div class="six wide column">
<h2>Solutions with the Internet</h2>
<h3>With the power to save any combination of letters, you can easily inscribe thoughts</h3>
</div>
</div>
<!-- set -->
<div class="middle aligned centered row">
<div class="six wide column">
<h2>Search your data</h2>
<h3>Type in a word and find that same word but somewhere else</h3>
</div>
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/cloud.svg" alt="Girl falling into the spiral of digital chaos">
</div>
</div>
<div class="middle aligned centered row">
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/plan.svg" alt="Scheme for planetary destruction">
</div>
<div class="six wide column">
<h2>Embrace the Void</h2>
<h3>Remove unnecessary clutter for your brain and save it to the cloud, allowing you to easily embrace the gaping abyss</h3>
</div>
</div>
<!-- set -->
<div class="middle aligned centered row">
<div class="six wide column">
<h2>Space for Growth</h2>
<h3>Groom a clear path for new expressions and innovations. Elevate your being and lower your cholesterol</h3>
</div>
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/growth.svg" alt="Endless progress at the cost of sanity and health">
</div>
</div>
<div class="middle aligned centered row">
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/onboarding.svg" alt="Shrunken man near giant tablet">
</div>
<div class="six wide column">
<h2>Become your Data</h2>
<h3>We exist as electrical impulses, no different from data on a computer</h3>
</div>
</div>
<!-- set -->
<div class="middle aligned centered row">
<div class="six wide column">
<h2>Ice Cream</h2>
<h3>Get excited without all the screaming</h3>
</div>
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/icecream.svg" alt="Emergence of a 4th dimensional being perceived as a large ice cream ">
</div>
</div>
<div class="middle aligned centered row">
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/secure.svg" alt="marketing mumbo jumbo">
</div>
<div class="six wide column">
<h2>Data Backups</h2>
<h3>Nothing you do will be forgotten.<br>You can never take back what you have done</h3>
</div>
</div>
<div class="middle aligned centered row">
<div class="six wide column">
<h2>Freedom to unleash yourself</h2>
<h3>Imagine an awakening of what could be</h3>
</div>
<div class="six wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/grandma.svg" alt="Drinking the blood of the elderly">
</div>
</div>
<!-- final slide -->
<div class="middle aligned centered green row">
<div class="twelve wide center aligned column">
<br>
<br>
<br>
<br>
<h2>What are you waiting for?<br>Sign up now.</h2>
<br>
<router-link class="ui huge white labeled icon button" to="/login">
<i class="plug icon"></i>Sign Me Up!
</router-link>
<br>
<br>
<br>
OR
<br>
<br>
<br>
<span class="ui button" v-on:click="showRealInformation">View real information about this site</span>
</div>
</div>
<div v-if="realInformation" class="middle aligned centered row" ref="real">
<div class="six wide column">
<h2 class="ui center aligned">
What is this really?
</h2>
<h3>Its just a little web app for taking notes. This page is mocking the "over the top" marketing sites use to sell their products.</h3>
<p>
This App exists because I was tired of all my data being owned by big companies, having it farmed out for marketing, and leaving the contents of my life exposed to corporations.
</p>
<p>
If you want to give it a shot, feel free to make an account. There are no ads. None of this data is shared or public. I don't make any money.
</p>
<p>
If you see anything broken or want to see a feature implemented, I'm open to suggestions. <i class="thumbs up icon"></i>
</p>
<p>Hero Slide Photo Credit - <a target="_blank" href="https://unsplash.com/@tkaslik14">https://unsplash.com/@tkaslik14</a></p>
<p>Generic Marketing Images - <a target="_blank" href="https://undraw.co/">https://unDraw.co/</a></p>
</div>
<div class="four wide column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/watching.svg" alt="Drinking the blood of the elderly">
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'WelcomePage'
name: 'WelcomePage',
data(){
return {
height: null,
realInformation: false,
}
},
beforeMount(){
//Don't change hero banner on mobile
if(!this.$store.getters.getIsUserOnMobile){
let windowHeight = window.innerHeight
this.height = windowHeight - (windowHeight * 0.10)
}
},
methods: {
showRealInformation(){
this.realInformation = !this.realInformation
if(this.realInformation){
this.$nextTick(() => {
this.$refs.real.scrollIntoView({'behavior':'smooth'})
})
}
}
}
}
</script>

View File

@@ -1,41 +1,41 @@
<template>
<div class="ui container">
<h3>Login</h3>
<p>Begin the login process by typing your username or email.</p>
<p>To create an account, type in the username you want to use followed by the password.</p>
<p>You will remain logged in on this browser, until you log out.</p>
<div class="ui segment" v-on:keyup.enter="submit">
<div class="ui large form">
<div class="field">
<div class="ui input">
<input v-model="username" type="text" name="email" placeholder="Username or E-mail address" autofocus>
</div>
</div>
<div class="field" v-if="username.length > 0">
<div class="ui input">
<input v-model="password" type="password" name="password" placeholder="Password">
</div>
</div>
<div :class="{ 'disabled':(username.length == 0 || password.length == 0)}" v-on:click="submit" class="ui massive compact fluid green submit button">Login</div>
</div>
</div>
<div class="ui basic segment no-fluf-segment">
<div class="ui grid">
<div class="ui sixteen wide column">
<p><b>Create an account:</b> type in the username you want to use followed by the password.</p>
<div class="ui segment" v-on:keyup.enter="submit">
<div class="ui large form">
<div class="field">
<div class="ui input">
<input v-model="username" type="text" name="email" placeholder="Username or E-mail address" autofocus>
</div>
</div>
<div class="field">
<div class="ui input">
<input v-model="password" type="password" name="password" placeholder="Password">
</div>
</div>
<div :class="{ 'disabled':(username.length == 0 || password.length == 0)}" v-on:click="submit" class="ui massive compact fluid green submit button">Login</div>
</div>
</div>
<p>You will remain logged in on this browser, until you log out.</p>
</div>
</div>
</div>
</template>
<script>
//ajax calls
import axios from 'axios';
import { mapGetters } from 'vuex'
export default {
name: 'Login',
data () {
return {
message: 'Login stuff',
enabled: false,
username: '',
password: ''
@@ -61,26 +61,23 @@
axios.post('/api/user/login', data)
.then(response => {
console.log(response)
if(response.data.success){
const token = response.data.token
const username = response.data.username
vm.$store.commit('setLoginToken', {token, username})
//Redirect user to notes section after login
this.$router.push('/notes')
vm.$router.push('/notes')
} else {
vm.$store.commit('destroyLoginToken')
}
})
.catch(error => {
console.log('There was an error with log in request')
})
}
},
computed: {
...mapGetters([
'getRudeMessage'
])
}
}
</script>

View File

@@ -1,120 +1,132 @@
<template>
<div class="ui basic segment">
<div class="ui basic segment no-fluf-segment">
<div class="ui equal width grid">
<div class="ui grid" :class="{ 'mush-it-up':showOneColumn() }" ref="content">
<!-- <div class="ui row">{{ $store.getters.getIsUserOnMobile ? 'Mobile Device':'Normal Browser' }}</div> -->
<!-- mobile search menu -->
<div class="ui mobile only row">
<!-- Small screen new note button -->
<div class="ui four wide column">
<div @click="createNote" class="ui fluid green icon button">
<i class="plus icon"></i>
</div>
</div>
<div class="ui twelve wide column">
<div class="ui form">
<input v-model="searchTerm" @keyup="searchKeyUp" @:keyup.enter="search" placeholder="Search Notes" />
</div>
</div>
</div>
<!-- search menu -->
<div class="ui large screen only row">
<div class="ui two wide column">
<div @click="createNote" class="ui fluid green button">
<i class="plus icon"></i>
New Note
</div>
</div>
<!-- Note filter options -->
<div class="row">
<div class="ui five wide column">
<div
:class="{ 'sixteen wide column':showOneColumn(), 'eight wide column':!showOneColumn() }"
>
<div class="ui form">
<input v-model="searchTerm" @keyup="searchKeyUp" @:keyup.enter="search" placeholder="Search Notes" />
</div>
</div>
<div class="ui nine wide column">
<router-link class="ui basic button" to="/help">Help</router-link>
<div v-on:click="toggleNightMode" class="ui basic icon button">
<i class="eye icon"></i>&nbsp;Dark Theme:
<span v-if="$store.getters.getIsNightMode">On</span>
<span v-else>Off</span>
</div>
<div v-on:click="toggleArchivedVisible" class="ui basic icon button">
<i class="archive icon"></i>&nbsp;Archived:
<span v-if="showArchived == 1">Visible</span>
<span v-else>Hidden</span>
</div>
<div class="ui right floated basic button"
data-tooltip="Log Out" data-position="left center"
v-on:click="destroyLoginToken"><i class="user icon"></i> {{username}}</div>
</div>
</div>
<div class="ui row">
<!-- tags display -->
<div class="ui two wide large screen only column" v-if="activeNoteId1 == null && activeNoteId2 == null">
<div class="ui small basic fluid button" @click="reset">
<i class="undo icon"></i>Reset Filters
</div>
<div class="ui divider"></div>
<div class="ui section list">
<div class="item" v-for="tag in commonTags" @click="toggleTagFilter(tag.id)">
<div class="ui clickable basic fluid large label" :class="{ 'green':(searchTags.includes(tag.id)) }">
{{ucWords(tag.text)}} <div class="detail">{{tag.usages}}</div>
<div class="fields">
<div class="ten wide field">
<input v-model="searchTerm" @keyup="searchKeyUp" @:keyup.enter="search" placeholder="Search Notes" />
</div>
<div class="six wide field">
<span class="ui fluid green button"
v-if="showClear"
@click="reset">
<i class="undo icon"></i>Reset Filters
</span>
</div>
</div>
</div>
</div>
<!-- Note title cards -->
<div class="ui fourteen wide computer sixteen wide mobile column">
<h2>
({{notes.length}}) <fast-filters />
<div
:class="{ 'sixteen wide column':showOneColumn(), 'eight wide column':!showOneColumn() }"
>
<h2 class="ui right floated">
<fast-filters />
</h2>
<h3 v-if="searchTerm.length > 0 && notes.length == 0">No notes found. Check your spelling, try completing the word or using a different phrase.</h3>
<h3 v-if="searchTerm.length == 0 && notes.length == 0">Create your first note. Click the "New Note" button.</h3>
<div v-if="working"><div class="ui active inline loader"></div> Working...</div>
<div v-if="notes !== null && !working"
class="note-card-display-area"
:class="{'one-column':(activeNoteId1 != null || activeNoteId2 != null )}
">
<note-title-display-card
v-for="note in notes"
:onClick="openNote"
:data="note"
:currently-open="(activeNoteId1 == note.id || activeNoteId2 == note.id)"
:key="note.id + note.color + note.note_highlights.length + note.attachment_highlights.length + ' -' + note.tag_highlights.length + '-' +note.title.length + '-' +note.subtext.length"
/>
</div>
</div>
</div>
<div v-if="commonTags.length > 0" class="sixteen wide column">
<span v-for="tag in commonTags" @click="toggleTagFilter(tag.id)">
<span class="ui clickable basic label" :class="{ 'green':(searchTags.includes(tag.id)) }">
{{ucWords(tag.text)}} <span class="detail">{{tag.usages}}</span>
</span>
</span>
</div>
<h2 v-if="fastFilters['withLinks'] == 1">Only showing notes containing Links</h2>
<h2 v-if="fastFilters['withTags'] == 1">Only showing notse with Tags</h2>
<h2 v-if="fastFilters['onlyArchived'] == 1">Only showing Archived notes.</h2>
<!-- Note title card display -->
<div class="sixteen wide column">
<h3 v-if="searchTerm.length > 0 && notes.length == 0">No notes found. Check your spelling, try completing the word or using a different phrase.</h3>
<h3 v-if="searchTerm.length == 0 && notes.length == 0">Create your first note. Click the "New Note" button.</h3>
<div v-if="working">
<div class="ui active inline loader"></div> Working...
</div>
<!-- Go to one wide column, do not do this on mobile interface -->
<div v-if="notes !== null && notes.length > 0"
:class="{'one-column':(
(activeNoteId1 != null || activeNoteId2 != null) &&
!$store.getters.getIsUserOnMobile
)}">
<!-- pinned notes -->
<div v-if="containsPinnednotes > 0" class="note-card-section">
<h4><i class="green pin icon"></i>Pinned <span v-if="!working">({{containsPinnednotes}})</span></h4>
<div class="note-card-display-area">
<note-title-display-card
v-for="note in notes"
v-if="note.pinned"
:onClick="openNote"
:data="note"
:currently-open="(activeNoteId1 == note.id || activeNoteId2 == note.id)"
:key="note.id + note.color + note.note_highlights.length + note.attachment_highlights.length + ' -' + note.tag_highlights.length + '-' +note.title.length + '-' +note.subtext.length"
/>
</div>
</div>
<!-- normal notes -->
<div v-if="containsNormalNotes > 0" class="note-card-section">
<h4>Notes <span v-if="!working">({{containsNormalNotes}})</span></h4>
<div class="note-card-display-area">
<note-title-display-card
v-for="note in notes"
v-if="note.note_highlights && !note.pinned"
:onClick="openNote"
:data="note"
:currently-open="(activeNoteId1 == note.id || activeNoteId2 == note.id)"
:key="note.id + note.color + note.note_highlights.length + note.attachment_highlights.length + ' -' + note.tag_highlights.length + '-' +note.title.length + '-' +note.subtext.length"
/>
</div>
</div>
<!-- found in text -->
<div v-if="containsTextResults" class="note-card-section">
<h4><i class="green paragraph icon"></i> Found in Text ({{containsTextResults}})</h4>
<div class="note-card-display-area">
<note-title-display-card
v-for="note in notes"
v-if="note.note_highlights && note.note_highlights.length"
:textResults="true"
:onClick="openNote"
:data="note"
:currently-open="(activeNoteId1 == note.id || activeNoteId2 == note.id)"
:key="note.id + note.color + note.note_highlights.length + note.attachment_highlights.length + ' -' + note.tag_highlights.length + '-' +note.title.length + '-' +note.subtext.length"
/>
</div>
</div>
</div>
</div>
</div>
<input-notes v-if="activeNoteId1 != null" :noteid="activeNoteId1" :position="activeNote1Position" />
<input-notes v-if="activeNoteId2 != null" :noteid="activeNoteId2" :position="activeNote2Position" />
<div v-if="openAttachmentEdit">
<edit-attachment :note-id="editAttchmentId" :key="editAttchmentId" />
</div>
</div>
</template>
<script>
import axios from 'axios';
import axios from 'axios'
export default {
name: 'SearchBar',
@@ -122,32 +134,62 @@
'input-notes': require('@/components/NoteInputPanel.vue').default,
'note-title-display-card': require('@/components/NoteTitleDisplayCard.vue').default,
'fast-filters': require('@/components/FastFilters.vue').default,
'edit-attachment': require('@/components/AttachmentEditor.vue').default,
},
data () {
return {
username:'',
initComponent: true,
commonTags: [],
searchTerm: '',
searchTags: [],
notes: [],
highlights: [],
searchDebounce: null,
fastFilters: {},
showArchived: 0,
working: false,
//Load up notes in batches
firstLoadBatchSize: 30, //First set of rapidly loaded notes
batchSize: 100, //Size of batch loaded when user scrolls through current batch
batchOffset: 0, //Tracks the current batch that has been loaded
loadingBatchTimeout: null, //Limit how quickly batches can be loaded
loadingInProgress: false,
//Clear button is not visible
showClear: false,
initialPostData: null,
currentPostData: null,
containsNormalNotes: 0,
containsPinnednotes: 0,
containsTextResults: 0,
containsTagResults: 0,
containsAttachmentResults: 0,
//Currently open notes in app
activeNoteId1: null,
activeNoteId2: null,
//Position determines how note is Positioned
activeNote1Position: 0,
activeNote2Position: 0,
//Attchment to edit. Only 1 for now
openAttachmentEdit: false,
editAttchmentId: null,
}
},
beforeMount(){
let username = this.$store.getters.getUsername
this.username = this.ucWords(username)
this.$parent.loginGateway()
this.$bus.$on('open_edit_attachment', noteId => {
this.openAttachmentEdit = true
this.editAttchmentId = noteId
})
this.$bus.$on('close_edit_attachment', noteId => {
this.openAttachmentEdit = false
this.editAttchmentId = null
})
this.$bus.$on('close_active_note', position => {
this.closeNote(position)
@@ -157,24 +199,51 @@
})
this.$bus.$on('update_fast_filters', newFilter => {
this.fastFilters = newFilter
this.search()
this.search(true, this.batchSize, false)
})
//New note button pushes open note event
this.$bus.$on('open_note', noteId => {
this.openNote(noteId)
})
//Mount notes on load if note ID is set
if(this.$route.params && this.$route.params.id){
const id = this.$route.params.id
console.log('About to load note ', id)
this.openNote(id)
}
window.addEventListener('scroll', this.onScroll)
//Load tinymce into the page only do it once
if(document.querySelectorAll('[data-mceload]').length == 0){
let tinyMceIncluder = document.createElement('script')
tinyMceIncluder.setAttribute('src', '/api/static/assets/tinymce/tinymce.min.js')
tinyMceIncluder.setAttribute('data-mceload','loaded')
document.head.appendChild(tinyMceIncluder)
}
},
beforeDestroy(){
window.removeEventListener('scroll', this.onScroll)
},
mounted() {
this.search()
//Load a super fast batch
this.search(true, this.firstLoadBatchSize, 0)
.then( () => {
//Load a larger batch once first batch has loaded
this.search(false, this.batchSize, true).then( () => {
})
})
},
methods: {
showOneColumn(){
//If note 1 or 2 is open, show one column. Or if the user is on mobile
return (this.activeNoteId1 != null || this.activeNoteId2 != null) &&
!this.$store.getters.getIsUserOnMobile
},
openNote(id){
//Do not open same note twice
@@ -196,7 +265,6 @@
this.activeNote2Position = 2 //Left side of page
return
}
//2 notes open
if(this.activeNoteId2 != null && this.activeNoteId1 == null){
this.activeNoteId1 = id
@@ -236,33 +304,116 @@
this.search()
},
search(showLoading = true){
onScroll(e){
//Add archived to fast filters
this.fastFilters['archived'] = 0
if(this.showArchived == 1){
this.fastFilters['archived'] = 1
}
clearTimeout(this.loadingBatchTimeout)
this.loadingBatchTimeout = setTimeout(() => {
let postData = {
searchQuery: this.searchTerm,
searchTags: this.searchTags,
fastFilters: this.fastFilters,
}
//Distance to bottom of page
const bottomOfWindow =
Math.max(window.pageYOffset, document.documentElement.scrollTop, document.body.scrollTop)
+ window.innerHeight
//height of page
const offsetHeight = this.$refs.content.clientHeight
//Determine percentage down the page
const percentageDown = Math.round( (bottomOfWindow/offsetHeight)*100 )
// console.log( percentageDown + '%' )
//If greater than 80 of the way down the page, load the next batch
if(percentageDown >= 80){
console.log('loading batch')
this.search(false, this.batchSize, true)
}
}, 50)
if(showLoading){
this.working = true
}
return
},
search(showLoading = true, notesInNextLoad = null, mergeExisting = false){
return new Promise((resolve, reject) => {
//Perform search
let vm = this
axios.post('/api/note/search', postData).
then(response => {
vm.commonTags = response.data.tags
vm.notes = response.data.notes
vm.highlights = response.data.highlights
this.working = false
if(this.loadingInProgress){
return resolve()
}
//Remove all filter limits
delete this.fastFilters.limitSize
delete this.fastFilters.limitOffset
let postData = {
searchQuery: this.searchTerm,
searchTags: this.searchTags,
fastFilters: this.fastFilters,
}
if(showLoading){
this.working = true
}
//Save initial post data on first load
if(this.initialPostData == null){
this.initialPostData = JSON.stringify(postData)
}
//If post data is not the same as initial, show clear button
if(JSON.stringify(postData) != this.initialPostData){
this.showClear = true
}
if(notesInNextLoad && notesInNextLoad > 0){
//Create limit based off of the number of notes already loaded
postData.fastFilters.limitSize = notesInNextLoad
postData.fastFilters.limitOffset = this.batchOffset
}
//Perform search - or die
this.loadingInProgress = true
axios.post('/api/note/search', postData).
then(response => {
if(!mergeExisting){
this.containsNormalNotes = 0
this.containsPinnednotes = 0
this.containsTextResults = 0
this.batchOffset = 0 // Reset batch offset if we are not merging note batches
this.commonTags = []
this.notes = []
}
//Save the number of notes just loaded
this.batchOffset += response.data.notes.length
//Mush the two new sets of data together
this.commonTags = this.commonTags.concat(response.data.tags)
this.notes = this.notes.concat(response.data.notes)
//Go through each note and see which section to display
response.data.notes.forEach(note => {
if(note.note_highlights.length > 0){
this.containsTextResults++
return
}
if(note.pinned == 1){
this.containsPinnednotes++
return
}
this.containsNormalNotes++
})
this.working = false
this.loadingInProgress = false
return resolve(true)
})
})
},
searchKeyUp(){
@@ -270,19 +421,7 @@
clearTimeout(vm.searchDebounce)
vm.searchDebounce = setTimeout(() => {
vm.search()
}, 300)
},
createNote(event){
const title = ''
let vm = this
axios.post('/api/note/create', {title})
.then(response => {
if(response.data && response.data.id){
vm.openNote(response.data.id)
}
})
}, 500)
},
ucWords(str){
return (str + '')
@@ -291,31 +430,20 @@
})
},
reset(){
this.showClear = false
this.searchTerm = ''
this.searchTags = []
this.fastFilters = {}
this.$bus.$emit('reset_fast_filters')
this.search()
},
destroyLoginToken() {
this.$store.commit('destroyLoginToken')
this.$router.push('/')
},
toggleNightMode(){
this.$store.commit('toggleNightMode')
},
toggleArchivedVisible(){
if(this.showArchived == 0){
this.showArchived = 1
} else {
this.showArchived = 0
}
this.search()
this.search(true, this.firstLoadBatchSize, 0)
}
}
}
</script>
<style type="text/css" scoped>
.mush-it-up {
width: calc(50% - 130px);
}
.detail {
float: right;
}
@@ -323,4 +451,14 @@
display: flex;
flex-wrap: wrap;
}
.display-area-title {
width: 100%;
display: inline-block;
}
.note-card-section {
/*padding-bottom: 15px;*/
}
.note-card-section + .note-card-section {
padding: 15px 0 0;
}
</style>

View File

@@ -0,0 +1,120 @@
<template>
<div class="ui basic segment no-fluf-segment">
<div class="ui grid">
<div class="ui sixteen wide column">
<h2 class="ui header">
<i class="coffee icon"></i>
<div class="content">
Quick
<div class="sub header">Add new information with great speed</div>
</div>
</h2>
</div>
<div class="ui sixteen wide column">
<div class="ui form">
<div class="field">
<textarea
class="quick-note-input"
rows="1"
ref="fastInput"
v-model="newText"
v-on:keydown="checkKeyup"
placeholder="Push to the top of the quick note."
></textarea>
</div>
<div class="field">
<div v-on:click="appendQuickNote" class="ui green button">Save (CRTL + Enter)</div>
<div v-if="quickNoteId" v-on:click="openNoteEdit" class="ui right floated basic button">Edit</div>
</div>
</div>
</div>
<div class="fun" v-html="savedQuickNoteText"></div>
</div>
</div>
</template>
<style type="text/css" scoped>
.quick-note-input {
box-sizing: border-box !important;
resize: none !important;
}
</style>
<script>
import axios from 'axios'
export default {
data: function(){
return {
newText: '',
savedQuickNoteText: '',
quickNoteId: null
}
},
beforeCreate: function(){
//
// Perform Login check
//
this.$parent.loginGateway()
},
mounted: function(){
if(this.$refs.fastInput){
//Load up note text
this.getQuickNote()
//Set focus to input pane
this.$nextTick(() => {
this.$refs.fastInput.focus()
})
}
},
computed: {
loggedIn () {
return this.$store.getters.getLoggedIn
}
},
methods: {
openNoteEdit(){
this.$router.push({'path':'/notes/open/'+this.quickNoteId})
},
checkKeyup(event){
let element = event.target
let padding = 22
element.style.height = 'auto';
element.style.height = (element.scrollHeight + padding) +'px';
//If command+enter or control+enter is pressed, submit
if((event.metaKey || event.ctrlKey) && [13].includes(event.keyCode)){
this.appendQuickNote()
}
},
appendQuickNote(){
//Don't submit empty note
if(this.newText.trim() == ''){ return }
axios.post('/api/quick-note/update', { 'pushText':this.newText } )
.then( results => {
this.newText = '' //Clear text area
this.$refs.fastInput.style.height = 'auto' //Back to normal size
this.savedQuickNoteText = results.data.text
this.quickNoteId = results.data.id
})
},
getQuickNote (){
axios.post('/api/quick-note/get')
.then( results => {
this.savedQuickNoteText = results.data.text
this.quickNoteId = results.data.id
})
},
}
}
</script>

View File

@@ -0,0 +1,49 @@
<template>
<div class="ui basic segment">
<div class="ui container">
<div class="fun" :style="{'color':color}" v-if="noteText" v-html="noteText"></div>
</div>
<div class="ui basic segment"></div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'SharePage',
data(){
return {
noteText: null,
color: '#000'
}
},
beforeMount(){
//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)
}
},
methods:{
openNote(noteId){
axios.post('/api/public/note', {'noteId': noteId})
.then( response => {
let colors = JSON.parse(response.data.color)
if(colors && colors.noteBackground){
document.body.style.background = colors.noteBackground
}
if(colors && colors.noteText){
this.color = colors.noteText
}
this.noteText = response.data.text
})
}
}
}
</script>