mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 17:56:25 +00:00
Merge pull request #1636 from mirsch/remove-volatile-hash-from-note-links
remove volatile hash from note links - #1623
This commit is contained in:
@@ -517,9 +517,20 @@ export default class MarkdownPreview extends React.Component {
|
||||
|
||||
handlelinkClick (e) {
|
||||
const noteHash = e.target.href.split('/').pop()
|
||||
const regexIsNoteLink = /^(.{20})-(.{20})$/
|
||||
// this will match the new uuid v4 hash and the old hash
|
||||
// e.g.
|
||||
// :note:1c211eb7dcb463de6490 and
|
||||
// :note:7dd23275-f2b4-49cb-9e93-3454daf1af9c
|
||||
const regexIsNoteLink = /^:note:([a-zA-Z0-9-]{20,36})$/
|
||||
if (regexIsNoteLink.test(noteHash)) {
|
||||
eventEmitter.emit('list:jump', noteHash)
|
||||
eventEmitter.emit('list:jump', noteHash.replace(':note:', ''))
|
||||
}
|
||||
// this will match the old link format storage.key-note.key
|
||||
// e.g.
|
||||
// 877f99c3268608328037-1c211eb7dcb463de6490
|
||||
const regexIsLegacyNoteLink = /^(.{20})-(.{20})$/
|
||||
if (regexIsLegacyNoteLink.test(noteHash)) {
|
||||
eventEmitter.emit('list:jump', noteHash.split('-')[1])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,9 +62,9 @@ const NoteItem = ({
|
||||
? 'item--active'
|
||||
: 'item'
|
||||
}
|
||||
key={`${note.storage}-${note.key}`}
|
||||
onClick={e => handleNoteClick(e, `${note.storage}-${note.key}`)}
|
||||
onContextMenu={e => handleNoteContextMenu(e, `${note.storage}-${note.key}`)}
|
||||
key={note.key}
|
||||
onClick={e => handleNoteClick(e, note.key)}
|
||||
onContextMenu={e => handleNoteContextMenu(e, note.key)}
|
||||
onDragStart={e => handleDragStart(e, note)}
|
||||
draggable='true'
|
||||
>
|
||||
|
||||
@@ -28,9 +28,9 @@ const NoteItemSimple = ({
|
||||
? 'item-simple--active'
|
||||
: 'item-simple'
|
||||
}
|
||||
key={`${note.storage}-${note.key}`}
|
||||
onClick={e => handleNoteClick(e, `${note.storage}-${note.key}`)}
|
||||
onContextMenu={e => handleNoteContextMenu(e, `${note.storage}-${note.key}`)}
|
||||
key={note.key}
|
||||
onClick={e => handleNoteClick(e, note.key)}
|
||||
onContextMenu={e => handleNoteContextMenu(e, note.key)}
|
||||
onDragStart={e => handleDragStart(e, note)}
|
||||
draggable='true'
|
||||
>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
const crypto = require('crypto')
|
||||
const _ = require('lodash')
|
||||
const uuidv4 = require('uuid/v4')
|
||||
|
||||
module.exports = function (length) {
|
||||
if (!_.isFinite(length)) length = 10
|
||||
module.exports = function (uuid) {
|
||||
if (typeof uuid === typeof true && uuid) {
|
||||
return uuidv4()
|
||||
}
|
||||
const length = 10
|
||||
return crypto.randomBytes(length).toString('hex')
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
hashHistory.replace({
|
||||
pathname: location.pathname,
|
||||
query: {
|
||||
key: newNote.storage + '-' + newNote.key
|
||||
key: newNote.key
|
||||
}
|
||||
})
|
||||
this.setState({
|
||||
@@ -393,7 +393,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
<InfoPanel
|
||||
storageName={currentOption.storage.name}
|
||||
folderName={currentOption.folder.name}
|
||||
noteLink={`[${note.title}](${location.query.key})`}
|
||||
noteLink={`[${note.title}](:note:${location.query.key})`}
|
||||
updatedAt={formatDate(note.updatedAt)}
|
||||
createdAt={formatDate(note.createdAt)}
|
||||
exportAsMd={this.exportAsMd}
|
||||
|
||||
@@ -147,7 +147,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
hashHistory.replace({
|
||||
pathname: location.pathname,
|
||||
query: {
|
||||
key: newNote.storage + '-' + newNote.key
|
||||
key: newNote.key
|
||||
}
|
||||
})
|
||||
this.setState({
|
||||
@@ -650,7 +650,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
<InfoPanel
|
||||
storageName={currentOption.storage.name}
|
||||
folderName={currentOption.folder.name}
|
||||
noteLink={`[${note.title}](${location.query.key})`}
|
||||
noteLink={`[${note.title}](:note:${location.query.key})`}
|
||||
updatedAt={formatDate(note.updatedAt)}
|
||||
createdAt={formatDate(note.createdAt)}
|
||||
exportAsMd={this.showWarning}
|
||||
|
||||
@@ -56,11 +56,8 @@ class Detail extends React.Component {
|
||||
const { location, data, config } = this.props
|
||||
let note = null
|
||||
if (location.query.key != null) {
|
||||
const splitted = location.query.key.split('-')
|
||||
const storageKey = splitted.shift()
|
||||
const noteKey = splitted.shift()
|
||||
|
||||
note = data.noteMap.get(storageKey + '-' + noteKey)
|
||||
const noteKey = location.query.key
|
||||
note = data.noteMap.get(noteKey)
|
||||
}
|
||||
|
||||
if (note == null) {
|
||||
|
||||
@@ -34,7 +34,7 @@ function sortByUpdatedAt (a, b) {
|
||||
}
|
||||
|
||||
function findNoteByKey (notes, noteKey) {
|
||||
return notes.find((note) => `${note.storage}-${note.key}` === noteKey)
|
||||
return notes.find((note) => note.key === noteKey)
|
||||
}
|
||||
|
||||
function findNotesByKeys (notes, noteKeys) {
|
||||
@@ -42,7 +42,7 @@ function findNotesByKeys (notes, noteKeys) {
|
||||
}
|
||||
|
||||
function getNoteKey (note) {
|
||||
return `${note.storage}-${note.key}`
|
||||
return note.key
|
||||
}
|
||||
|
||||
class NoteList extends React.Component {
|
||||
@@ -118,10 +118,10 @@ class NoteList extends React.Component {
|
||||
componentDidUpdate (prevProps) {
|
||||
const { location } = this.props
|
||||
const { selectedNoteKeys } = this.state
|
||||
const visibleNoteKeys = this.notes.map(note => `${note.storage}-${note.key}`)
|
||||
const visibleNoteKeys = this.notes.map(note => note.key)
|
||||
const note = this.notes[0]
|
||||
const prevKey = prevProps.location.query.key
|
||||
const noteKey = visibleNoteKeys.includes(prevKey) ? prevKey : note && `${note.storage}-${note.key}`
|
||||
const noteKey = visibleNoteKeys.includes(prevKey) ? prevKey : note && note.key
|
||||
|
||||
if (note && location.query.key == null) {
|
||||
const { router } = this.context
|
||||
@@ -589,11 +589,9 @@ class NoteList extends React.Component {
|
||||
})
|
||||
if (dialogueButtonIndex === 1) return
|
||||
Promise.all(
|
||||
selectedNoteKeys.map((uniqueKey) => {
|
||||
const storageKey = uniqueKey.split('-')[0]
|
||||
const noteKey = uniqueKey.split('-')[1]
|
||||
selectedNotes.map((note) => {
|
||||
return dataApi
|
||||
.deleteNote(storageKey, noteKey)
|
||||
.deleteNote(note.storage, note.key)
|
||||
})
|
||||
)
|
||||
.then((data) => {
|
||||
@@ -654,19 +652,18 @@ class NoteList extends React.Component {
|
||||
content: firstNote.content
|
||||
})
|
||||
.then((note) => {
|
||||
const uniqueKey = note.storage + '-' + note.key
|
||||
dispatch({
|
||||
type: 'UPDATE_NOTE',
|
||||
note: note
|
||||
})
|
||||
|
||||
this.setState({
|
||||
selectedNoteKeys: [uniqueKey]
|
||||
selectedNoteKeys: [note.key]
|
||||
})
|
||||
|
||||
hashHistory.push({
|
||||
pathname: location.pathname,
|
||||
query: {key: uniqueKey}
|
||||
query: {key: note.key}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -160,10 +160,8 @@ class SideNav extends React.Component {
|
||||
|
||||
emptyTrash (entries) {
|
||||
const { dispatch } = this.props
|
||||
const deletionPromises = entries.map((storageAndNoteKey) => {
|
||||
const storageKey = storageAndNoteKey.split('-')[0]
|
||||
const noteKey = storageAndNoteKey.split('-')[1]
|
||||
return dataApi.deleteNote(storageKey, noteKey)
|
||||
const deletionPromises = entries.map((note) => {
|
||||
return dataApi.deleteNote(note.storage, note.key)
|
||||
})
|
||||
Promise.all(deletionPromises)
|
||||
.then((arrayOfStorageAndNoteKeys) => {
|
||||
@@ -179,9 +177,9 @@ class SideNav extends React.Component {
|
||||
|
||||
handleFilterButtonContextMenu (event) {
|
||||
const { data } = this.props
|
||||
const entries = data.trashedSet.toJS()
|
||||
const trashedNotes = data.trashedSet.toJS().map((uniqueKey) => data.noteMap.get(uniqueKey))
|
||||
const menu = Menu.buildFromTemplate([
|
||||
{ label: 'Empty Trash', click: () => this.emptyTrash(entries) }
|
||||
{ label: 'Empty Trash', click: () => this.emptyTrash(trashedNotes) }
|
||||
])
|
||||
menu.popup()
|
||||
}
|
||||
|
||||
@@ -52,12 +52,12 @@ function createNote (storageKey, input) {
|
||||
return storage
|
||||
})
|
||||
.then(function saveNote (storage) {
|
||||
let key = keygen()
|
||||
let key = keygen(true)
|
||||
let isUnique = false
|
||||
while (!isUnique) {
|
||||
try {
|
||||
sander.statSync(path.join(storage.path, 'notes', key + '.cson'))
|
||||
key = keygen()
|
||||
key = keygen(true)
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
isUnique = true
|
||||
|
||||
@@ -39,12 +39,12 @@ function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
|
||||
return resolveStorageData(newStorage)
|
||||
.then(function findNewNoteKey (_newStorage) {
|
||||
newStorage = _newStorage
|
||||
newNoteKey = keygen()
|
||||
newNoteKey = keygen(true)
|
||||
let isUnique = false
|
||||
while (!isUnique) {
|
||||
try {
|
||||
sander.statSync(path.join(newStorage.path, 'notes', newNoteKey + '.cson'))
|
||||
newNoteKey = keygen()
|
||||
newNoteKey = keygen(true)
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
isUnique = true
|
||||
|
||||
@@ -35,7 +35,7 @@ class NewNoteModal extends React.Component {
|
||||
content: ''
|
||||
})
|
||||
.then((note) => {
|
||||
const noteHash = `${note.storage}-${note.key}`
|
||||
const noteHash = note.key
|
||||
dispatch({
|
||||
type: 'UPDATE_NOTE',
|
||||
note: note
|
||||
@@ -75,7 +75,7 @@ class NewNoteModal extends React.Component {
|
||||
}]
|
||||
})
|
||||
.then((note) => {
|
||||
const noteHash = `${note.storage}-${note.key}`
|
||||
const noteHash = note.key
|
||||
dispatch({
|
||||
type: 'UPDATE_NOTE',
|
||||
note: note
|
||||
|
||||
@@ -27,7 +27,7 @@ function data (state = defaultDataMap(), action) {
|
||||
|
||||
action.notes.some((note) => {
|
||||
if (note === undefined) return true
|
||||
const uniqueKey = note.storage + '-' + note.key
|
||||
const uniqueKey = note.key
|
||||
const folderKey = note.storage + '-' + note.folder
|
||||
state.noteMap.set(uniqueKey, note)
|
||||
|
||||
@@ -66,7 +66,7 @@ function data (state = defaultDataMap(), action) {
|
||||
case 'UPDATE_NOTE':
|
||||
{
|
||||
const note = action.note
|
||||
const uniqueKey = note.storage + '-' + note.key
|
||||
const uniqueKey = note.key
|
||||
const folderKey = note.storage + '-' + note.folder
|
||||
const oldNote = state.noteMap.get(uniqueKey)
|
||||
|
||||
@@ -162,9 +162,9 @@ function data (state = defaultDataMap(), action) {
|
||||
case 'MOVE_NOTE':
|
||||
{
|
||||
const originNote = action.originNote
|
||||
const originKey = originNote.storage + '-' + originNote.key
|
||||
const originKey = originNote.key
|
||||
const note = action.note
|
||||
const uniqueKey = note.storage + '-' + note.key
|
||||
const uniqueKey = note.key
|
||||
const folderKey = note.storage + '-' + note.folder
|
||||
const oldNote = state.noteMap.get(uniqueKey)
|
||||
|
||||
@@ -297,7 +297,7 @@ function data (state = defaultDataMap(), action) {
|
||||
}
|
||||
case 'DELETE_NOTE':
|
||||
{
|
||||
const uniqueKey = action.storageKey + '-' + action.noteKey
|
||||
const uniqueKey = action.noteKey
|
||||
const targetNote = state.noteMap.get(uniqueKey)
|
||||
|
||||
state = Object.assign({}, state)
|
||||
@@ -423,7 +423,7 @@ function data (state = defaultDataMap(), action) {
|
||||
state.folderNoteMap = new Map(state.folderNoteMap)
|
||||
state.tagNoteMap = new Map(state.tagNoteMap)
|
||||
action.notes.forEach((note) => {
|
||||
const uniqueKey = note.storage + '-' + note.key
|
||||
const uniqueKey = note.key
|
||||
const folderKey = note.storage + '-' + note.folder
|
||||
state.noteMap.set(uniqueKey, note)
|
||||
|
||||
@@ -483,7 +483,7 @@ function data (state = defaultDataMap(), action) {
|
||||
state.tagNoteMap = new Map(state.tagNoteMap)
|
||||
state.starredSet = new Set(state.starredSet)
|
||||
notes.forEach((note) => {
|
||||
const noteKey = storage.key + '-' + note.key
|
||||
const noteKey = note.key
|
||||
state.noteMap.delete(noteKey)
|
||||
state.starredSet.delete(noteKey)
|
||||
note.tags.forEach((tag) => {
|
||||
|
||||
@@ -88,7 +88,8 @@
|
||||
"sanitize-html": "^1.18.2",
|
||||
"striptags": "^2.2.1",
|
||||
"superagent": "^1.2.0",
|
||||
"superagent-promise": "^1.0.3"
|
||||
"superagent-promise": "^1.0.3",
|
||||
"uuid": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^0.16.0",
|
||||
|
||||
8
tests/fixtures/TestDummy.js
vendored
8
tests/fixtures/TestDummy.js
vendored
@@ -107,9 +107,9 @@ function dummyStorage (storagePath, override = {}) {
|
||||
var notesData = []
|
||||
var noteCount = Math.floor((Math.random() * 15)) + 2
|
||||
for (var i = 0; i < noteCount; i++) {
|
||||
var key = keygen()
|
||||
var key = keygen(true)
|
||||
while (notesData.some((note) => note.key === key)) {
|
||||
key = keygen()
|
||||
key = keygen(true)
|
||||
}
|
||||
|
||||
var noteData = dummyNote({
|
||||
@@ -149,9 +149,9 @@ function dummyLegacyStorage (storagePath, override = {}) {
|
||||
var folderNotes = []
|
||||
var noteCount = Math.floor((Math.random() * 5)) + 1
|
||||
for (var i = 0; i < noteCount; i++) {
|
||||
var key = keygen(6)
|
||||
var key = keygen(true)
|
||||
while (folderNotes.some((note) => note.key === key)) {
|
||||
key = keygen(6)
|
||||
key = keygen(true)
|
||||
}
|
||||
|
||||
var noteData = dummyNote({
|
||||
|
||||
@@ -6795,6 +6795,10 @@ uuid@^2.0.1, uuid@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
|
||||
|
||||
uuid@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
|
||||
|
||||
validate-npm-package-license@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
|
||||
|
||||
Reference in New Issue
Block a user