Created a uniform menu for notes that works on mobile
Added list sorting Added shared notes Fixed some little bugs here and there
This commit is contained in:
@@ -43,7 +43,7 @@ Attachment.textSearch = (userId, searchTerm) => {
|
||||
})
|
||||
}
|
||||
|
||||
Attachment.search = (userId, noteId) => {
|
||||
Attachment.search = (userId, noteId, attachmentType) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let params = [userId]
|
||||
@@ -54,6 +54,11 @@ Attachment.search = (userId, noteId) => {
|
||||
params.push(noteId)
|
||||
}
|
||||
|
||||
if(Number.isInteger(attachmentType)){
|
||||
query += 'AND attachment_type = ? '
|
||||
params.push(attachmentType)
|
||||
}
|
||||
|
||||
query += 'ORDER BY last_indexed DESC '
|
||||
|
||||
db.promise()
|
||||
|
@@ -14,6 +14,35 @@ let Note = module.exports = {}
|
||||
|
||||
const gm = require('gm')
|
||||
|
||||
// --------------
|
||||
|
||||
Note.migrateNoteTextToNewTable = () => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
db.promise()
|
||||
.query('SELECT id, text FROM note WHERE note_raw_text_id IS NULL')
|
||||
.then((rows, fields) => {
|
||||
rows[0].forEach( ({id, text}) => {
|
||||
|
||||
db.promise()
|
||||
.query('INSERT INTO note_raw_text (text) VALUES (?)', [text])
|
||||
.then((rows, fields) => {
|
||||
|
||||
db.promise()
|
||||
.query(`UPDATE note SET note_raw_text_id = ? WHERE (id = ?)`, [rows[0].insertId, id])
|
||||
.then((rows, fields) => {
|
||||
|
||||
return 'Nice'
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
resolve('Its probably running... :-D')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Note.fixAttachmentThumbnails = () => {
|
||||
const filePath = '../staticFiles/'
|
||||
db.promise()
|
||||
@@ -66,6 +95,8 @@ Note.stressTest = () => {
|
||||
})
|
||||
}
|
||||
|
||||
// --------------
|
||||
|
||||
Note.create = (userId, noteText, quickNote = 0) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -74,34 +105,18 @@ Note.create = (userId, noteText, quickNote = 0) => {
|
||||
const created = Math.round((+new Date)/1000)
|
||||
|
||||
db.promise()
|
||||
.query('INSERT INTO note (user_id, text, updated, created, quick_note) VALUES (?,?,?,?,?)',
|
||||
[userId, noteText, created, created, quickNote])
|
||||
.then((rows, fields) => {
|
||||
// New notes are empty, don't add to solr index
|
||||
// Note.reindex(userId, rows[0].insertId)
|
||||
resolve(rows[0].insertId) //Only return the new note ID when creating a new note
|
||||
.query(`INSERT INTO note_raw_text (text) VALUE ('')`)
|
||||
.then( (rows, fields) => {
|
||||
|
||||
const rawTextId = rows[0].insertId
|
||||
|
||||
return db.promise()
|
||||
.query('INSERT INTO note (user_id, note_raw_text_id, updated, created, quick_note) VALUES (?,?,?,?,?)',
|
||||
[userId, rawTextId, created, created, quickNote])
|
||||
})
|
||||
.catch(console.log)
|
||||
})
|
||||
}
|
||||
|
||||
Note.reindexAll = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
db.promise()
|
||||
.query(`
|
||||
|
||||
SELECT id, user_id FROM note;
|
||||
|
||||
`)
|
||||
.then((rows, fields) => {
|
||||
console.log()
|
||||
|
||||
rows[0].forEach(item => {
|
||||
Note.reindex(item.user_id, item.id)
|
||||
})
|
||||
|
||||
resolve(true)
|
||||
// Indexing is done on save
|
||||
resolve(rows[0].insertId) //Only return the new note ID when creating a new note
|
||||
})
|
||||
.catch(console.log)
|
||||
})
|
||||
@@ -154,8 +169,23 @@ Note.update = (userId, noteId, noteText, color, pinned, archived) => {
|
||||
const now = Math.round((+new Date)/1000)
|
||||
|
||||
db.promise()
|
||||
.query('UPDATE note SET text = ?, pinned = ?, archived = ?, updated = ?, color = ? WHERE id = ? AND user_id = ? LIMIT 1',
|
||||
[noteText, pinned, archived, now, color, noteId, userId])
|
||||
.query('SELECT note_raw_text_id FROM note WHERE id = ? AND user_id = ?', [noteId, userId])
|
||||
.then((rows, fields) => {
|
||||
|
||||
const textId = rows[0][0]['note_raw_text_id']
|
||||
|
||||
//Update Note text
|
||||
return db.promise()
|
||||
.query('UPDATE note_raw_text SET text = ? WHERE id = ?', [noteText, textId])
|
||||
})
|
||||
.then( (rows, fields) => {
|
||||
|
||||
//Update other note attributes
|
||||
return db.promise()
|
||||
.query('UPDATE note SET pinned = ?, archived = ?, updated = ?, color = ? WHERE id = ? AND user_id = ? LIMIT 1',
|
||||
[pinned, archived, now, color, noteId, userId])
|
||||
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
|
||||
//Async solr note reindex
|
||||
@@ -171,7 +201,6 @@ Note.update = (userId, noteId, noteText, color, pinned, archived) => {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Delete a note and all its remaining parts
|
||||
//
|
||||
@@ -179,16 +208,54 @@ Note.delete = (userId, noteId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
//
|
||||
// Delete, note, text index, tags
|
||||
// Delete, note, text, search index and associated tags
|
||||
// Leave the attachments, they can be deleted on their own
|
||||
//
|
||||
// Leave Tags, their text is shared
|
||||
|
||||
db.promise().query('DELETE FROM note WHERE note.id = ? AND note.user_id = ?', [noteId,userId])
|
||||
let rawTextId = null
|
||||
let noteTextCount = 0
|
||||
|
||||
// Lookup the note text ID, we need this to count usages
|
||||
db.promise()
|
||||
.query('SELECT note_raw_text_id FROM note WHERE id = ? AND user_id = ?', [noteId, userId])
|
||||
.then((rows, fields) => {
|
||||
return db.promise().query('DELETE FROM note_text_index WHERE note_text_index.note_id = ? AND note_text_index.user_id = ?', [noteId,userId])
|
||||
|
||||
//Save the raw text ID
|
||||
rawTextId = rows[0][0]['note_raw_text_id']
|
||||
|
||||
return db.promise()
|
||||
.query('SELECT count(id) as count FROM note WHERE note_raw_text_id = ?', [rawTextId])
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
return db.promise().query('DELETE FROM note_tag WHERE note_tag.note_id = ? AND note_tag.user_id = ?', [noteId,userId])
|
||||
|
||||
//Save the number of times the note is used
|
||||
noteTextCount = rows[0][0]['count']
|
||||
|
||||
//Don't delete text if its shared
|
||||
if(noteTextCount == 1){
|
||||
//If text is only used on one note, delete it (its not shared)
|
||||
return db.promise()
|
||||
.query('SELECT count(id) as count FROM note WHERE note_raw_text_id = ?', [rawTextId])
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
})
|
||||
.then( results => {
|
||||
// Delete Note entry for this user.
|
||||
return db.promise()
|
||||
.query('DELETE FROM note WHERE note.id = ? AND note.user_id = ?', [noteId, userId])
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
// Delete search index
|
||||
return db.promise()
|
||||
.query('DELETE FROM note_text_index WHERE note_text_index.note_id = ? AND note_text_index.user_id = ?', [noteId,userId])
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
// delete tags
|
||||
return db.promise()
|
||||
.query('DELETE FROM note_tag WHERE note_tag.note_id = ? AND note_tag.user_id = ?', [noteId,userId])
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
resolve(true)
|
||||
@@ -251,9 +318,19 @@ Note.get = (userId, noteId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.promise()
|
||||
.query(`
|
||||
SELECT note.text, note.updated, note.pinned, note.archived, note.color, count(distinct attachment.id) as attachment_count
|
||||
SELECT
|
||||
note_raw_text.text,
|
||||
note.updated,
|
||||
note.pinned,
|
||||
note.archived,
|
||||
note.color,
|
||||
count(distinct attachment.id) as attachment_count,
|
||||
note.note_raw_text_id as rawTextId,
|
||||
user.username as shareUsername
|
||||
FROM note
|
||||
JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id)
|
||||
LEFT JOIN attachment ON (note.id = attachment.note_id)
|
||||
LEFT JOIN user ON (note.share_user_id = user.id)
|
||||
WHERE note.user_id = ? AND note.id = ? LIMIT 1`, [userId,noteId])
|
||||
.then((rows, fields) => {
|
||||
|
||||
@@ -265,6 +342,7 @@ Note.get = (userId, noteId) => {
|
||||
})
|
||||
}
|
||||
|
||||
//Public note share action -> may not be used
|
||||
Note.getShared = (noteId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.promise()
|
||||
@@ -356,20 +434,33 @@ Note.search = (userId, searchQuery, searchTags, fastFilters) => {
|
||||
|
||||
// Base of the query, modified with fastFilters
|
||||
// Add to query for character counts -> CHAR_LENGTH(note.text) as chars
|
||||
let searchParams = [userId]
|
||||
let noteSearchQuery = `
|
||||
SELECT note.id,
|
||||
SUBSTRING(note.text, 1, 1500) as text,
|
||||
SUBSTRING(note_raw_text.text, 1, 1500) as text,
|
||||
updated,
|
||||
color,
|
||||
count(distinct note_tag.id) as tag_count,
|
||||
count(distinct attachment.id) as attachment_count,
|
||||
note.pinned,
|
||||
note.archived
|
||||
note.archived,
|
||||
GROUP_CONCAT(DISTINCT tag.text) as tags,
|
||||
GROUP_CONCAT(DISTINCT attachment.file_location) as thumbs,
|
||||
shareUser.username as username
|
||||
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)
|
||||
LEFT JOIN tag ON (tag.id = note_tag.tag_id)
|
||||
LEFT JOIN attachment ON (note.id = attachment.note_id)
|
||||
LEFT JOIN user as shareUser ON (note.share_user_id = shareUser.id)
|
||||
WHERE note.user_id = ?`
|
||||
let searchParams = [userId]
|
||||
|
||||
//Show shared notes
|
||||
if(fastFilters.onlyShowSharedNotes == 1){
|
||||
noteSearchQuery += ' AND note.share_user_id IS NOT NULL' //Show Archived
|
||||
} else {
|
||||
noteSearchQuery += ' AND note.share_user_id IS NULL'
|
||||
}
|
||||
|
||||
//If text search returned results, limit search to those ids
|
||||
if(textSearchIds.length > 0){
|
||||
@@ -418,15 +509,15 @@ Note.search = (userId, searchQuery, searchTags, fastFilters) => {
|
||||
// Always prioritize pinned notes in searches.
|
||||
|
||||
//Default Sort, order by last updated
|
||||
let defaultOrderBy = ' ORDER BY note.pinned DESC, updated DESC, created DESC, opened DESC, id DESC'
|
||||
let defaultOrderBy = ' ORDER BY note.pinned DESC, note.updated DESC, note.created DESC, note.opened DESC, id DESC'
|
||||
|
||||
//Order by Last Created Date
|
||||
if(fastFilters.lastCreated == 1){
|
||||
defaultOrderBy = ' ORDER BY note.pinned DESC, created DESC, updated DESC, opened DESC, id DESC'
|
||||
defaultOrderBy = ' ORDER BY note.pinned DESC, note.created DESC, note.updated DESC, note.opened DESC, id DESC'
|
||||
}
|
||||
//Order by last Opened Date
|
||||
if(fastFilters.lastOpened == 1){
|
||||
defaultOrderBy = ' ORDER BY note.pinned DESC, opened DESC, updated DESC, created DESC, id DESC'
|
||||
defaultOrderBy = ' ORDER BY note.pinned DESC, opened DESC, note.updated DESC, note.created DESC, id DESC'
|
||||
}
|
||||
|
||||
//Append Order by to query
|
||||
|
127
server/models/ShareNote.js
Normal file
127
server/models/ShareNote.js
Normal file
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// All actions in noteController.js
|
||||
//
|
||||
|
||||
|
||||
const db = require('@config/database')
|
||||
|
||||
const Note = require('@models/Note')
|
||||
|
||||
let ShareNote = module.exports = {}
|
||||
|
||||
// Share a note with a user, given the correct username
|
||||
ShareNote.addUser = (userId, noteId, rawTextId, username) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let shareUserId = null
|
||||
let newNoteShare = null
|
||||
|
||||
//Check that user actually exists
|
||||
db.promise().query(`SELECT id FROM user WHERE username = ?`, [username])
|
||||
.then((rows, fields) => {
|
||||
|
||||
if(rows[0].length == 0){
|
||||
throw new Error('User Does Not Exist')
|
||||
}
|
||||
|
||||
shareUserId = rows[0][0]['id']
|
||||
|
||||
//Check if note has already been added for user
|
||||
return db.promise()
|
||||
.query('SELECT id FROM note WHERE user_id = ? AND note_raw_text_id = ?', [shareUserId, rawTextId])
|
||||
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
|
||||
if(rows[0].length != 0){
|
||||
throw new Error('User Already Has Note')
|
||||
}
|
||||
|
||||
//Lookup note to share with user, clone this data to create users new note
|
||||
return db.promise()
|
||||
.query(`SELECT * FROM note WHERE id = ? LIMIT 1`, [noteId])
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
|
||||
newNoteShare = rows[0][0]
|
||||
|
||||
//Modify note with the share attributes we want
|
||||
delete newNoteShare['id']
|
||||
delete newNoteShare['opened']
|
||||
newNoteShare['share_user_id'] = userId //User who shared the note
|
||||
newNoteShare['user_id'] = shareUserId //User who gets note
|
||||
|
||||
//Setup db colums, db values and number of '?' to put into prepared statement
|
||||
let dbColumns = []
|
||||
let dbValues = []
|
||||
let escapeChars = []
|
||||
|
||||
//Pull out all the data we need from object to create prepared statemnt
|
||||
Object.keys(newNoteShare).forEach( key => {
|
||||
escapeChars.push('?')
|
||||
dbColumns.push(key)
|
||||
dbValues.push(newNoteShare[key])
|
||||
})
|
||||
|
||||
//Stick all the note value back into query, insert updated note
|
||||
return db.promise()
|
||||
.query(`INSERT INTO note (${dbColumns.join()}) VALUES (${escapeChars.join()})`, dbValues)
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
|
||||
//Success!
|
||||
return resolve(true)
|
||||
})
|
||||
.catch(error => {
|
||||
// console.log(error)
|
||||
resolve(false)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Get users who see a shared note
|
||||
ShareNote.getUsers = (userId, rawTextId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.promise()
|
||||
.query(`
|
||||
SELECT username, note.id as noteId
|
||||
FROM note
|
||||
JOIN user ON (user.id = note.user_id)
|
||||
WHERE note_raw_text_id = ?
|
||||
AND share_user_id = ?
|
||||
AND user_id != ?
|
||||
`, [rawTextId, userId, userId])
|
||||
.then((rows, fields) => {
|
||||
|
||||
//Return a list of user names
|
||||
return resolve (rows[0])
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Remove a user from a shared note
|
||||
ShareNote.removeUser = (userId, noteId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
//note.id = noteId, share_user_id = userId
|
||||
db.promise()
|
||||
.query('SELECT user_id FROM note WHERE id = ? AND share_user_id = ?', [noteId, userId])
|
||||
.then( (rows, fields) => {
|
||||
|
||||
//User has shared this note, with this user
|
||||
if(rows[0].length == 1 && Number.isInteger(rows[0][0]['user_id'])){
|
||||
|
||||
Note.delete(rows[0][0]['user_id'], noteId)
|
||||
.then( result => {
|
||||
resolve(result)
|
||||
})
|
||||
|
||||
} else {
|
||||
return resolve(false)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
}
|
@@ -10,7 +10,7 @@ Tag.userTags = (userId) => {
|
||||
JOIN note_tag ON tag.id = note_tag.tag_id
|
||||
WHERE note_tag.user_id = ?
|
||||
GROUP BY tag.id
|
||||
ORDER BY usages DESC
|
||||
ORDER BY id DESC
|
||||
`, [userId])
|
||||
.then( (rows, fields) => {
|
||||
resolve(rows[0])
|
||||
@@ -98,39 +98,50 @@ Tag.add = (tagText) => {
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
// Get all tags AND tags associated to note
|
||||
//
|
||||
Tag.get = (userId, noteId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
//Update last opened date of note
|
||||
const now = Math.round((+new Date)/1000)
|
||||
db.promise()
|
||||
.query('UPDATE note SET opened = ? WHERE id = ? AND user_id = ? LIMIT 1', [now, noteId, userId])
|
||||
.then((rows, fields) => {})
|
||||
Tag.userTags(userId).then(userTags => {
|
||||
db.promise()
|
||||
.query(`SELECT tag_id as tagId, id as entryId
|
||||
FROM note_tag
|
||||
WHERE user_id = ? AND note_id = ?;`, [userId, noteId])
|
||||
.then((rows, fields) => {
|
||||
|
||||
db.promise()
|
||||
.query(`SELECT note_tag.id, tag.text FROM note_tag
|
||||
JOIN tag ON (tag.id = note_tag.tag_id)
|
||||
WHERE user_id = ? AND note_id = ?;`, [userId, noteId])
|
||||
.then((rows, fields) => {
|
||||
resolve(rows[0]) //Return all tags found by query
|
||||
//pull IDs out of returned results
|
||||
// let ids = rows[0].map( item => {})
|
||||
|
||||
resolve({'noteTagIds':rows[0], 'allTags':userTags }) //Return all tags found by query
|
||||
})
|
||||
.catch(console.log)
|
||||
})
|
||||
.catch(console.log)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
// Get all tags for a note and concatinate into a string 'all, tags, like, this'
|
||||
//
|
||||
Tag.string = (userId, noteId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
Tag.get(userId, noteId).then(tagArray => {
|
||||
db.promise()
|
||||
.query(`SELECT GROUP_CONCAT(DISTINCT tag.text SEPARATOR ', ') as text
|
||||
FROM note_tag
|
||||
JOIN tag ON note_tag.tag_id = tag.id
|
||||
WHERE user_id = ? AND note_id = ?;`, [userId, noteId])
|
||||
.then((rows, fields) => {
|
||||
|
||||
let tagString = ''
|
||||
tagArray.forEach( (tag, i) => {
|
||||
if(i > 0){ tagString += ',' }
|
||||
tagString += tag.text
|
||||
})
|
||||
//Output comma delimited list of tag strings
|
||||
resolve(tagString)
|
||||
let finalText = rows[0][0]['text']
|
||||
if(finalText == null){
|
||||
finalText = ''
|
||||
}
|
||||
|
||||
return resolve(finalText) //Return all tags found by query
|
||||
})
|
||||
.catch(console.log)
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,7 @@ router.use(function setUserId (req, res, next) {
|
||||
})
|
||||
|
||||
router.post('/search', function (req, res) {
|
||||
Attachment.search(userId, req.body.noteId)
|
||||
Attachment.search(userId, req.body.noteId, req.body.attachmentType)
|
||||
.then( data => res.send(data) )
|
||||
})
|
||||
|
||||
|
@@ -2,6 +2,7 @@ var express = require('express')
|
||||
var router = express.Router()
|
||||
|
||||
let Notes = require('@models/Note');
|
||||
let ShareNote = require('@models/ShareNote');
|
||||
|
||||
let userId = null
|
||||
let socket = null
|
||||
@@ -18,6 +19,9 @@ router.use(function setUserId (req, res, next) {
|
||||
next()
|
||||
})
|
||||
|
||||
//
|
||||
// Note actions
|
||||
//
|
||||
router.post('/get', function (req, res) {
|
||||
req.io.emit('welcome_homie', 'Welcome, dont poop from excitement')
|
||||
Notes.get(userId, req.body.noteId)
|
||||
@@ -58,15 +62,35 @@ router.post('/difftext', function (req, res) {
|
||||
})
|
||||
})
|
||||
|
||||
//
|
||||
// Share Note Actions
|
||||
//
|
||||
router.post('/getshareusers', function (req, res) {
|
||||
ShareNote.getUsers(userId, req.body.rawTextId)
|
||||
.then(results => res.send(results))
|
||||
})
|
||||
|
||||
router.post('/shareadduser', function (req, res) {
|
||||
ShareNote.addUser(userId, req.body.noteId, req.body.rawTextId, req.body.username)
|
||||
.then(results => res.send(results))
|
||||
})
|
||||
|
||||
router.post('/shareremoveuser', function (req, res) {
|
||||
ShareNote.removeUser(userId, req.body.noteId)
|
||||
.then(results => res.send(results))
|
||||
})
|
||||
|
||||
|
||||
//
|
||||
// Testing Action
|
||||
//
|
||||
//Reindex all notes. Not a very good function, not public
|
||||
router.get('/reindex5yu43prchuj903mrc', function (req, res) {
|
||||
|
||||
Notes.fixAttachmentThumbnails()
|
||||
res.send('A whole mess is going on in the background')
|
||||
Notes.migrateNoteTextToNewTable().then(status => {
|
||||
return res.send(status)
|
||||
})
|
||||
|
||||
// Notes.stressTest().then( i => {
|
||||
// // Notes.reindexAll().then( result => res.send('Welcome to reindex...oh god'))
|
||||
// })
|
||||
})
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user