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:
Max G
2020-02-10 17:44:43 +00:00
parent 133a86e09e
commit 86f7f61933
23 changed files with 1395 additions and 330 deletions

View File

@@ -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()

View File

@@ -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
View 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)
}
})
})
}

View File

@@ -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)
})
}

View File

@@ -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) )
})

View File

@@ -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'))
// })
})