Added counts to each category
Counts update on certain events and show or hide various elements Fixed various little ui element issues fixes #6
This commit is contained in:
parent
c903bcbcd1
commit
771b739c37
@ -13,6 +13,7 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
// import io from 'socket.io-client'
|
// import io from 'socket.io-client'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -50,7 +51,6 @@ export default {
|
|||||||
|
|
||||||
},
|
},
|
||||||
mounted: function(){
|
mounted: function(){
|
||||||
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
loggedIn () {
|
loggedIn () {
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
scrollbar-width: none;
|
/*scrollbar-width: none;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
div.ui.basic.segment.no-fluf-segment {
|
div.ui.basic.segment.no-fluf-segment {
|
||||||
|
@ -192,6 +192,7 @@
|
|||||||
this.unfolded = false
|
this.unfolded = false
|
||||||
setTimeout( () => {
|
setTimeout( () => {
|
||||||
this.visible = false
|
this.visible = false
|
||||||
|
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||||
}, 600)
|
}, 600)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
const imageCode = `<img alt="image" src="/api/static/thumb_${location}">`
|
const imageCode = `<img alt="image" src="/api/static/thumb_${location}">`
|
||||||
|
|
||||||
this.$bus.$emit('new_file_upload', {noteId: this.noteId, imageCode})
|
this.$bus.$emit('new_file_upload', {noteId: this.noteId, imageCode})
|
||||||
|
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(results => {
|
.catch(results => {
|
||||||
|
@ -154,6 +154,7 @@
|
|||||||
<div class="menu-section" v-if="loggedIn">
|
<div class="menu-section" v-if="loggedIn">
|
||||||
<router-link exact-active-class="active" class="menu-item menu-button" to="/notes" v-on:click.native="emitReloadEvent()">
|
<router-link exact-active-class="active" class="menu-item menu-button" to="/notes" v-on:click.native="emitReloadEvent()">
|
||||||
<i class="file outline icon"></i>Notes
|
<i class="file outline icon"></i>Notes
|
||||||
|
<!-- <span v-if="$store.getters.totals">{{ $store.getters.totals['totalNotes'] }}</span> -->
|
||||||
</router-link>
|
</router-link>
|
||||||
<div>
|
<div>
|
||||||
<!-- <div class="menu-item sub">Show Only <i class="caret down icon"></i></div> -->
|
<!-- <div class="menu-item sub">Show Only <i class="caret down icon"></i></div> -->
|
||||||
@ -163,21 +164,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="menu-section" v-if="loggedIn">
|
<div class="menu-section" v-if="loggedIn && $store.getters.totals && $store.getters.totals['totalFiles']">
|
||||||
<div v-on:click="updateFastFilters(3)" class="menu-item menu-button">
|
|
||||||
<i class="mail outline icon"></i>Inbox
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="menu-section" v-if="loggedIn">
|
|
||||||
<div v-on:click="updateFastFilters(2)" class="menu-item menu-button">
|
|
||||||
<i class="hdd outline icon"></i>Archived
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="menu-section" v-if="loggedIn">
|
|
||||||
<router-link class="menu-item menu-button" exact-active-class="active" to="/attachments">
|
<router-link class="menu-item menu-button" exact-active-class="active" to="/attachments">
|
||||||
<i class="folder open outline icon"></i>Files
|
<i class="folder open outline icon"></i>Files
|
||||||
|
<!-- <span>{{ $store.getters.totals['totalFiles'] }}</span> -->
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -242,6 +232,8 @@
|
|||||||
this.mobile = this.$store.getters.getIsUserOnMobile
|
this.mobile = this.$store.getters.getIsUserOnMobile
|
||||||
this.collapsed = this.$store.getters.getIsUserOnMobile
|
this.collapsed = this.$store.getters.getIsUserOnMobile
|
||||||
|
|
||||||
|
// {{ totals['totalNotes'] }}
|
||||||
|
|
||||||
if(this.mobile){
|
if(this.mobile){
|
||||||
this.menuOpen = false
|
this.menuOpen = false
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
axios.post('/api/note/delete', {'noteId':this.noteId}).then(response => {
|
axios.post('/api/note/delete', {'noteId':this.noteId}).then(response => {
|
||||||
if(response.data == true){
|
if(response.data == true){
|
||||||
this.$bus.$emit('note_deleted', this.noteId)
|
this.$bus.$emit('note_deleted', this.noteId)
|
||||||
|
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -202,6 +202,7 @@
|
|||||||
loading: true,
|
loading: true,
|
||||||
loadingMessage: 'Loading Note',
|
loadingMessage: 'Loading Note',
|
||||||
currentNoteId: 0,
|
currentNoteId: 0,
|
||||||
|
modified: false,
|
||||||
noteText: '',
|
noteText: '',
|
||||||
rawTextId: 0,
|
rawTextId: 0,
|
||||||
shareUsername: null,
|
shareUsername: null,
|
||||||
@ -873,21 +874,16 @@
|
|||||||
'archived':this.archived,
|
'archived':this.archived,
|
||||||
}
|
}
|
||||||
|
|
||||||
let vm = this
|
this.statusText = 'Saving'
|
||||||
//Debounce save to prevent spamming
|
|
||||||
// clearTimeout(this.saveDebounce)
|
|
||||||
// this.saveDebounce = setTimeout(() => {
|
|
||||||
//Only notify user if saving - may help with debugging in the future
|
|
||||||
vm.statusText = 'Saving'
|
|
||||||
axios.post('/api/note/update', postData).then( response => {
|
axios.post('/api/note/update', postData).then( response => {
|
||||||
vm.statusText = 'Saved'
|
this.statusText = 'Saved'
|
||||||
vm.updated = Math.round((+new Date)/1000)
|
this.updated = Math.round((+new Date)/1000)
|
||||||
|
this.modified = true
|
||||||
|
|
||||||
//Update last saved note hash
|
//Update last saved note hash
|
||||||
vm.lastNoteHash = vm.hashString( currentNoteText )
|
this.lastNoteHash = this.hashString( currentNoteText )
|
||||||
return resolve(true)
|
return resolve(true)
|
||||||
})
|
})
|
||||||
// }, 300)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
checkForUpdatedNote(){
|
checkForUpdatedNote(){
|
||||||
@ -949,7 +945,9 @@
|
|||||||
this.sizeDown = true
|
this.sizeDown = true
|
||||||
//This timeout allows animation to play before closing
|
//This timeout allows animation to play before closing
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$bus.$emit('close_active_note', {position: this.position, noteId: this.noteid})
|
this.$bus.$emit('close_active_note', {
|
||||||
|
position: this.position, noteId: this.noteid, modified: this.modified
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}, 300)
|
}, 300)
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
|
<div class="ui left icon fluid input">
|
||||||
<input v-model="searchTerm" @keyup="searchKeyUp" @:keyup.enter="search" placeholder="Search Notes and Files" />
|
<input v-model="searchTerm" @keyup="searchKeyUp" @:keyup.enter="search" placeholder="Search Notes and Files" />
|
||||||
|
<i class="search icon"></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -128,7 +128,6 @@
|
|||||||
|
|
||||||
if(this.styleObject && this.styleObject.noteBackground){
|
if(this.styleObject && this.styleObject.noteBackground){
|
||||||
this.bgColor = this.styleObject.noteBackground
|
this.bgColor = this.styleObject.noteBackground
|
||||||
console.log(this.bgColor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,21 +3,39 @@
|
|||||||
|
|
||||||
<div class="ui grid" :class="{ 'mush-it-up':showOneColumn() }" ref="content">
|
<div class="ui grid" :class="{ 'mush-it-up':showOneColumn() }" ref="content">
|
||||||
|
|
||||||
<div v-if="!$store.getters.getIsUserOnMobile"
|
<div v-if="!$store.getters.getIsUserOnMobile" class="sixteen wide column">
|
||||||
:class="{ 'sixteen wide column':showOneColumn(), 'twelve wide column':!showOneColumn() }">
|
<!-- :class="{ 'sixteen wide column':showOneColumn(), 'sixteen wide column':!showOneColumn() }" -->
|
||||||
|
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
|
|
||||||
<div class="eight wide column">
|
<div class="eight wide column">
|
||||||
<search-input></search-input>
|
<search-input></search-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="eight wide column">
|
<div class="eight wide column">
|
||||||
|
|
||||||
|
<div class="ui basic button" v-on:click="updateFastFilters(3)" v-if="$store.getters.totals && $store.getters.totals['sharedToNotes'] > 0" style="position: relative;">
|
||||||
|
<i class="green mail icon"></i>Inbox
|
||||||
|
<span class="floating ui green label" v-if="$store.getters.totals['unreadNotes'] > 0">
|
||||||
|
{{ $store.getters.totals['unreadNotes'] }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui basic button" v-on:click="updateFastFilters(2)" v-if="$store.getters.totals && $store.getters.totals['archivedNotes'] > 0">
|
||||||
|
<i class="green archive icon"></i>Archived
|
||||||
|
<!-- <span>{{ $store.getters.totals['archivedNotes'] }}</span> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="eight wide column" v-if="showClear">
|
||||||
<!-- <fast-filters /> -->
|
<!-- <fast-filters /> -->
|
||||||
<span class="ui fluid green button"
|
<span class="ui fluid green button"
|
||||||
v-if="showClear"
|
|
||||||
@click="reset">
|
@click="reset">
|
||||||
<i class="arrow circle left icon"></i>Back to All Notes
|
<i class="arrow circle left icon"></i>Back to All Notes
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -64,7 +82,7 @@
|
|||||||
<!-- pinned notes -->
|
<!-- pinned notes -->
|
||||||
<div v-if="containsPinnednotes > 0" class="note-card-section">
|
<div v-if="containsPinnednotes > 0" class="note-card-section">
|
||||||
<!-- ({{containsPinnednotes}}) -->
|
<!-- ({{containsPinnednotes}}) -->
|
||||||
<h4><i class="green pin icon"></i>Pinned <span v-if="!working"></span></h4>
|
<h4><i class="green pin icon"></i>Pinned</h4>
|
||||||
<div class="note-card-display-area">
|
<div class="note-card-display-area">
|
||||||
<note-title-display-card
|
<note-title-display-card
|
||||||
v-for="note in notes"
|
v-for="note in notes"
|
||||||
@ -80,7 +98,7 @@
|
|||||||
<!-- normal notes -->
|
<!-- normal notes -->
|
||||||
<div v-if="containsNormalNotes > 0" class="note-card-section">
|
<div v-if="containsNormalNotes > 0" class="note-card-section">
|
||||||
<!-- ({{containsNormalNotes}}) -->
|
<!-- ({{containsNormalNotes}}) -->
|
||||||
<h4><i class="green file icon"></i>Notes <span v-if="!working"></span></h4>
|
<h4><i class="green file icon"></i>Notes</h4>
|
||||||
<div class="note-card-display-area">
|
<div class="note-card-display-area">
|
||||||
<note-title-display-card
|
<note-title-display-card
|
||||||
v-for="note in notes"
|
v-for="note in notes"
|
||||||
@ -194,9 +212,16 @@
|
|||||||
|
|
||||||
this.$parent.loginGateway()
|
this.$parent.loginGateway()
|
||||||
|
|
||||||
this.$bus.$on('close_active_note', ({position, noteId}) => {
|
//Update totals for app
|
||||||
|
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||||
|
|
||||||
|
this.$bus.$on('close_active_note', ({position, noteId, modified}) => {
|
||||||
this.closeNote(position)
|
this.closeNote(position)
|
||||||
|
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||||
|
if(modified){
|
||||||
this.updateSingleNote(noteId)
|
this.updateSingleNote(noteId)
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
this.$bus.$on('note_deleted', (noteId) => {
|
this.$bus.$on('note_deleted', (noteId) => {
|
||||||
//Remove deleted note from set, its deleted
|
//Remove deleted note from set, its deleted
|
||||||
@ -265,6 +290,7 @@
|
|||||||
// this.$bus.$off()
|
// this.$bus.$off()
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
||||||
//Loads initial batch and tags
|
//Loads initial batch and tags
|
||||||
this.reset()
|
this.reset()
|
||||||
},
|
},
|
||||||
@ -634,6 +660,31 @@
|
|||||||
resolve(data)
|
resolve(data)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
updateFastFilters(index){
|
||||||
|
|
||||||
|
//clear out tags
|
||||||
|
this.searchTags = []
|
||||||
|
|
||||||
|
//A little hacky, brings user to notes page then filters on click
|
||||||
|
if(this.$route.name != 'NotesPage'){
|
||||||
|
this.$router.push('/notes')
|
||||||
|
setTimeout( () => {
|
||||||
|
this.updateFastFilters(index)
|
||||||
|
}, 500 )
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
'withLinks', // 'Only Show Notes with Links'
|
||||||
|
'withTags', // 'Only Show Notes with Tags'
|
||||||
|
'onlyArchived', //'Only Show Archived Notes'
|
||||||
|
'onlyShowSharedNotes', //Only show shared notes
|
||||||
|
]
|
||||||
|
|
||||||
|
let filter = {}
|
||||||
|
filter[options[index]] = 1
|
||||||
|
|
||||||
|
this.$bus.$emit('update_fast_filters', filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ export default new Vuex.Store({
|
|||||||
isUserOnMobile: false,
|
isUserOnMobile: false,
|
||||||
isNoteSettingsOpen: false, //Little note settings pane
|
isNoteSettingsOpen: false, //Little note settings pane
|
||||||
socket: null,
|
socket: null,
|
||||||
|
userTotals: null,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setLoginToken(state, userData){
|
setLoginToken(state, userData){
|
||||||
@ -88,6 +89,14 @@ export default new Vuex.Store({
|
|||||||
//Put socket id in axios headers
|
//Put socket id in axios headers
|
||||||
axios.defaults.headers.common['socketId'] = socket
|
axios.defaults.headers.common['socketId'] = socket
|
||||||
state.socket = socket
|
state.socket = socket
|
||||||
|
},
|
||||||
|
setUserTotals(state, totalsObject){
|
||||||
|
//Save all the totals for the user
|
||||||
|
state.userTotals = totalsObject
|
||||||
|
|
||||||
|
// Object.keys(totalsObject).forEach( key => {
|
||||||
|
// console.log(key + ' -- ' + totalsObject[key])
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
@ -114,5 +123,16 @@ export default new Vuex.Store({
|
|||||||
getSocket: state => {
|
getSocket: state => {
|
||||||
return state.socket
|
return state.socket
|
||||||
},
|
},
|
||||||
|
totals: state => {
|
||||||
|
return state.userTotals
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchAndUpdateUserTotals ({ commit }) {
|
||||||
|
axios.post('/api/user/totals')
|
||||||
|
.then( ({data}) => {
|
||||||
|
commit('setUserTotals', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
@ -334,6 +334,10 @@ Note.get = (userId, noteId) => {
|
|||||||
WHERE note.user_id = ? AND note.id = ? LIMIT 1`, [userId,noteId])
|
WHERE note.user_id = ? AND note.id = ? LIMIT 1`, [userId,noteId])
|
||||||
.then((rows, fields) => {
|
.then((rows, fields) => {
|
||||||
|
|
||||||
|
const created = Math.round((+new Date)/1000)
|
||||||
|
|
||||||
|
db.promise().query(`UPDATE note SET opened = ? WHERE (id = ?)`, [created, noteId])
|
||||||
|
|
||||||
//Return note data
|
//Return note data
|
||||||
resolve(rows[0][0])
|
resolve(rows[0][0])
|
||||||
|
|
||||||
|
@ -113,3 +113,56 @@ User.create = (username, password) => {
|
|||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Counts notes, pinned notes, archived notes, shared notes, unread notes, total files and types
|
||||||
|
User.getCounts = (userId) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
let countTotals = {}
|
||||||
|
|
||||||
|
db.promise().query(
|
||||||
|
`SELECT
|
||||||
|
SUM(pinned = 1 && archived = 0 && share_user_id IS NULL) AS pinnedNotes,
|
||||||
|
SUM(pinned = 0 && archived = 1 && share_user_id IS NULL) AS archivedNotes,
|
||||||
|
SUM(share_user_id IS NULL) AS totalNotes,
|
||||||
|
SUM(share_user_id != ?) AS sharedToNotes,
|
||||||
|
SUM( (share_user_id != ? && opened IS null) || (share_user_id != ? && updated > opened) ) AS unreadNotes
|
||||||
|
FROM note
|
||||||
|
WHERE user_id = ?`, [userId, userId, userId, userId])
|
||||||
|
.then( (rows, fields) => {
|
||||||
|
|
||||||
|
Object.assign(countTotals, rows[0][0]) //combine results
|
||||||
|
|
||||||
|
return db.promise().query(
|
||||||
|
`SELECT count(id) AS sharedFromNotes
|
||||||
|
FROM note WHERE share_user_id = ?`, [userId]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.then( (rows, fields) => {
|
||||||
|
|
||||||
|
Object.assign(countTotals, rows[0][0]) //combine results
|
||||||
|
|
||||||
|
return db.promise().query(
|
||||||
|
`SELECT
|
||||||
|
SUM(attachment_type = 1) as linkFiles,
|
||||||
|
SUM(attachment_type != 1) as otherFiles,
|
||||||
|
COUNT(id) as totalFiles
|
||||||
|
FROM attachment WHERE visible = 1
|
||||||
|
AND user_id = ?
|
||||||
|
`, [userId]
|
||||||
|
)
|
||||||
|
}).then( (rows, fields) => {
|
||||||
|
|
||||||
|
Object.assign(countTotals, rows[0][0]) //combine results
|
||||||
|
|
||||||
|
//Convert everything to an int or 0
|
||||||
|
Object.keys(countTotals).forEach( key => {
|
||||||
|
const count = parseInt(countTotals[key])
|
||||||
|
countTotals[key] = count ? count : 0
|
||||||
|
})
|
||||||
|
|
||||||
|
resolve(countTotals)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
@ -46,4 +46,11 @@ router.post('/login', function (req, res) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// fetch counts of users notes
|
||||||
|
router.post('/totals', function (req, res) {
|
||||||
|
User.getCounts(req.headers.userId)
|
||||||
|
.then( countsObject => res.send( countsObject ))
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
Loading…
Reference in New Issue
Block a user