1
0
mirror of https://github.com/BoostIo/Boostnote synced 2026-01-09 06:59:20 +00:00

Merge branch 'master' into feature-editor-line-lines

This commit is contained in:
David Pavlík
2017-12-23 23:06:26 +01:00
committed by GitHub
222 changed files with 6346 additions and 2127 deletions

View File

@@ -2,19 +2,47 @@ const AWS = require('aws-sdk')
const AMA = require('aws-sdk-mobile-analytics')
const ConfigManager = require('browser/main/lib/ConfigManager')
const remote = require('electron').remote
const os = require('os')
let mobileAnalyticsClient
AWS.config.region = 'us-east-1'
if (process.env.NODE_ENV === 'production' && ConfigManager.default.get().amaEnabled) {
if (!getSendEventCond()) {
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxxxxxxxxxxxxxxxxxxxxxx'
})
const mobileAnalyticsClient = new AMA.Manager({
const validPlatformName = convertPlatformName(os.platform())
mobileAnalyticsClient = new AMA.Manager({
appId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
appTitle: 'xxxxxxxxxx'
appTitle: 'xxxxxxxxxx',
appVersionName: remote.app.getVersion().toString(),
platform: validPlatformName
})
}
function convertPlatformName (platformName) {
if (platformName === 'darwin') {
return 'MacOS'
} else if (platformName === 'win32') {
return 'Windows'
} else if (platformName === 'linux') {
return 'Linux'
} else {
return ''
}
}
function getSendEventCond () {
const isDev = process.env.NODE_ENV !== 'production'
const isDisable = !ConfigManager.default.get().amaEnabled
const isOffline = !window.navigator.onLine
return isDev || isDisable || isOffline
}
function initAwsMobileAnalytics () {
if (process.env.NODE_ENV !== 'production' || !ConfigManager.default.get().amaEnabled) return
if (getSendEventCond()) return
AWS.config.credentials.get((err) => {
if (!err) {
console.log('Cognito Identity ID: ' + AWS.config.credentials.identityId)
@@ -24,16 +52,28 @@ function initAwsMobileAnalytics () {
})
}
function recordDynamicCustomEvent (type) {
if (process.env.NODE_ENV !== 'production' || !ConfigManager.default.get().amaEnabled) return
mobileAnalyticsClient.recordEvent(type)
function recordDynamicCustomEvent (type, options = {}) {
if (getSendEventCond()) return
try {
mobileAnalyticsClient.recordEvent(type, options)
} catch (analyticsError) {
if (analyticsError instanceof ReferenceError) {
console.log(analyticsError.name + ': ' + analyticsError.message)
}
}
}
function recordStaticCustomEvent () {
if (process.env.NODE_ENV !== 'production' || !ConfigManager.default.get().amaEnabled) return
mobileAnalyticsClient.recordEvent('UI_COLOR_THEME', {
uiColorTheme: ConfigManager.default.get().ui.theme
})
if (getSendEventCond()) return
try {
mobileAnalyticsClient.recordEvent('UI_COLOR_THEME', {
uiColorTheme: ConfigManager.default.get().ui.theme
})
} catch (analyticsError) {
if (analyticsError instanceof ReferenceError) {
console.log(analyticsError.name + ': ' + analyticsError.message)
}
}
}
module.exports = {

View File

@@ -13,10 +13,10 @@ function release (el) {
function fire (command) {
console.info('COMMAND >>', command)
let splitted = command.split(':')
let target = splitted[0]
let targetCommand = splitted[1]
let targetCallees = callees
const splitted = command.split(':')
const target = splitted[0]
const targetCommand = splitted[1]
const targetCallees = callees
.filter((callee) => callee.name === target)
targetCallees.forEach((callee) => {

View File

@@ -6,8 +6,6 @@ const win = global.process.platform === 'win32'
const electron = require('electron')
const { ipcRenderer } = electron
const consts = require('browser/lib/consts')
const path = require('path')
const fs = require('fs')
let isInitialized = false
@@ -25,6 +23,7 @@ export const DEFAULT_CONFIG = {
},
ui: {
theme: 'default',
showCopyNotification: true,
disableDirectWrite: false,
defaultNote: 'ALWAYS_ASK' // 'ALWAYS_ASK', 'SNIPPET_NOTE', 'MARKDOWN_NOTE'
},
@@ -36,13 +35,19 @@ export const DEFAULT_CONFIG = {
indentType: 'space',
indentSize: '2',
displayLineNumbers: true,
switchPreview: 'BLUR' // Available value: RIGHTCLICK, BLUR
switchPreview: 'BLUR', // Available value: RIGHTCLICK, BLUR
scrollPastEnd: false,
type: 'SPLIT'
},
preview: {
fontSize: '14',
fontFamily: win ? 'Segoe UI' : 'Lato',
codeBlockTheme: 'dracula',
lineNumber: true
lineNumber: true,
latexInlineOpen: '$',
latexInlineClose: '$',
latexBlockOpen: '$$',
latexBlockClose: '$$'
}
}
@@ -91,7 +96,11 @@ function get () {
: 'default'
if (config.editor.theme !== 'default') {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + config.editor.theme + '.css')
if (config.editor.theme.startsWith('solarized')) {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/solarized.css')
} else {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + config.editor.theme + '.css')
}
}
}
@@ -99,13 +108,17 @@ function get () {
}
function set (updates) {
let currentConfig = get()
let newConfig = Object.assign({}, DEFAULT_CONFIG, currentConfig, updates)
const currentConfig = get()
const newConfig = Object.assign({}, DEFAULT_CONFIG, currentConfig, updates)
if (!validate(newConfig)) throw new Error('INVALID CONFIG')
_save(newConfig)
if (newConfig.ui.theme === 'dark') {
document.body.setAttribute('data-theme', 'dark')
} else if (newConfig.ui.theme === 'white') {
document.body.setAttribute('data-theme', 'white')
} else if (newConfig.ui.theme === 'solarized-dark') {
document.body.setAttribute('data-theme', 'solarized-dark')
} else {
document.body.setAttribute('data-theme', 'default')
}
@@ -117,12 +130,16 @@ function set (updates) {
editorTheme.setAttribute('rel', 'stylesheet')
document.head.appendChild(editorTheme)
}
let newTheme = consts.THEMES.some((theme) => theme === newConfig.editor.theme)
const newTheme = consts.THEMES.some((theme) => theme === newConfig.editor.theme)
? newConfig.editor.theme
: 'default'
if (newTheme !== 'default') {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + newTheme + '.css')
if (newTheme.startsWith('solarized')) {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/solarized.css')
} else {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + newTheme + '.css')
}
}
ipcRenderer.send('config-renew', {
@@ -131,7 +148,7 @@ function set (updates) {
}
function assignConfigValues (originalConfig, rcConfig) {
let config = Object.assign({}, DEFAULT_CONFIG, originalConfig, rcConfig)
const config = Object.assign({}, DEFAULT_CONFIG, originalConfig, rcConfig)
config.hotkey = Object.assign({}, DEFAULT_CONFIG.hotkey, originalConfig.hotkey, rcConfig.hotkey)
config.ui = Object.assign({}, DEFAULT_CONFIG.ui, originalConfig.ui, rcConfig.ui)
config.editor = Object.assign({}, DEFAULT_CONFIG.editor, originalConfig.editor, rcConfig.editor)

View File

@@ -19,7 +19,7 @@ function setZoom (zoomFactor, noSave = false) {
}
function getZoom () {
let config = ConfigManager.get()
const config = ConfigManager.get()
return config.zoom
}

View File

@@ -1,7 +1,5 @@
const fs = require('fs')
const path = require('path')
const _ = require('lodash')
const sander = require('sander')
const { findStorage } = require('browser/lib/findStorage')
/**

View File

@@ -23,7 +23,6 @@ const { findStorage } = require('browser/lib/findStorage')
* ```
*/
function createFolder (storageKey, input) {
let rawStorages
let targetStorage
try {
if (input == null) throw new Error('No input found.')
@@ -41,7 +40,7 @@ function createFolder (storageKey, input) {
while (storage.folders.some((folder) => folder.key === key)) {
key = keygen()
}
let newFolder = {
const newFolder = {
key,
color: input.color,
name: input.name

View File

@@ -66,12 +66,16 @@ function createNote (storageKey, input) {
}
}
}
let noteData = Object.assign({}, input, {
key,
createdAt: new Date(),
updatedAt: new Date(),
storage: storageKey
})
const noteData = Object.assign({},
{
createdAt: new Date(),
updatedAt: new Date()
},
input, // input may contain more accurate dates
{
key,
storage: storageKey
})
CSON.writeFileSync(path.join(storage.path, 'notes', key + '.cson'), _.omit(noteData, ['key', 'storage']))

View File

@@ -19,7 +19,6 @@ const { findStorage } = require('browser/lib/findStorage')
* ```
*/
function deleteFolder (storageKey, folderKey) {
let rawStorages
let targetStorage
try {
targetStorage = findStorage(storageKey)
@@ -38,17 +37,17 @@ function deleteFolder (storageKey, folderKey) {
})
})
.then(function deleteFolderAndNotes (data) {
let { storage, notes } = data
const { storage, notes } = data
storage.folders = storage.folders
.filter(function excludeTargetFolder (folder) {
return folder.key !== folderKey
})
let targetNotes = notes.filter(function filterTargetNotes (note) {
const targetNotes = notes.filter(function filterTargetNotes (note) {
return note.folder === folderKey
})
let deleteAllNotes = targetNotes
const deleteAllNotes = targetNotes
.map(function deleteNote (note) {
const notePath = path.join(storage.path, 'notes', note.key + '.cson')
return sander.unlink(notePath)

View File

@@ -1,5 +1,4 @@
const resolveStorageData = require('./resolveStorageData')
const _ = require('lodash')
const path = require('path')
const sander = require('sander')
const { findStorage } = require('browser/lib/findStorage')
@@ -14,7 +13,7 @@ function deleteNote (storageKey, noteKey) {
return resolveStorageData(targetStorage)
.then(function deleteNoteFile (storage) {
let notePath = path.join(storage.path, 'notes', noteKey + '.cson')
const notePath = path.join(storage.path, 'notes', noteKey + '.cson')
try {
sander.unlinkSync(notePath)

View File

@@ -6,6 +6,7 @@ const dataApi = {
createFolder: require('./createFolder'),
updateFolder: require('./updateFolder'),
deleteFolder: require('./deleteFolder'),
reorderFolder: require('./reorderFolder'),
createNote: require('./createNote'),
updateNote: require('./updateNote'),
deleteNote: require('./deleteNote'),

View File

@@ -20,7 +20,7 @@ const CSON = require('@rokt33r/season')
* 3. empty directory
*/
function init () {
let fetchStorages = function () {
const fetchStorages = function () {
let rawStorages
try {
rawStorages = JSON.parse(window.localStorage.getItem('storages'))
@@ -34,8 +34,8 @@ function init () {
.map(resolveStorageData))
}
let fetchNotes = function (storages) {
let findNotesFromEachStorage = storages
const fetchNotes = function (storages) {
const findNotesFromEachStorage = storages
.map((storage) => {
return resolveStorageNotes(storage)
.then((notes) => {

View File

@@ -9,7 +9,7 @@ const sander = require('sander')
function migrateFromV5Storage (storageKey, data) {
let targetStorage
try {
let cachedStorageList = JSON.parse(localStorage.getItem('storages'))
const cachedStorageList = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
targetStorage = _.find(cachedStorageList, {key: storageKey})
@@ -24,15 +24,15 @@ function migrateFromV5Storage (storageKey, data) {
}
function importAll (storage, data) {
let oldArticles = data.articles
let notes = []
const oldArticles = data.articles
const notes = []
data.folders
.forEach(function (oldFolder) {
let folderKey = keygen()
while (storage.folders.some((folder) => folder.key === folderKey)) {
folderKey = keygen()
}
let newFolder = {
const newFolder = {
key: folderKey,
name: oldFolder.name,
color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7]
@@ -40,7 +40,7 @@ function importAll (storage, data) {
storage.folders.push(newFolder)
let articles = oldArticles.filter((article) => article.FolderKey === oldFolder.key)
const articles = oldArticles.filter((article) => article.FolderKey === oldFolder.key)
articles.forEach((article) => {
let noteKey = keygen()
let isUnique = false
@@ -59,7 +59,7 @@ function importAll (storage, data) {
}
if (article.mode === 'markdown') {
let newNote = {
const newNote = {
tags: article.tags,
createdAt: article.createdAt,
updatedAt: article.updatedAt,
@@ -73,7 +73,7 @@ function importAll (storage, data) {
}
notes.push(newNote)
} else {
let newNote = {
const newNote = {
tags: article.tags,
createdAt: article.createdAt,
updatedAt: article.updatedAt,

View File

@@ -20,7 +20,7 @@ function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
.then(function saveNote (_oldStorage) {
oldStorage = _oldStorage
let noteData
let notePath = path.join(oldStorage.path, 'notes', noteKey + '.cson')
const notePath = path.join(oldStorage.path, 'notes', noteKey + '.cson')
try {
noteData = CSON.readFileSync(notePath)
} catch (err) {

View File

@@ -1,6 +1,5 @@
const _ = require('lodash')
const resolveStorageData = require('./resolveStorageData')
const { findStorage } = require('browser/lib/findStorage')
/**
* @param {String} key
@@ -19,7 +18,7 @@ function renameStorage (key, name) {
console.error(err)
return Promise.reject(err)
}
let targetStorage = _.find(cachedStorageList, {key: key})
const targetStorage = _.find(cachedStorageList, {key: key})
if (targetStorage == null) return Promise.reject('Storage')
targetStorage.name = name

View File

@@ -0,0 +1,42 @@
const _ = require('lodash')
_.move = require('lodash-move').default
const path = require('path')
const resolveStorageData = require('./resolveStorageData')
const CSON = require('@rokt33r/season')
const { findStorage } = require('browser/lib/findStorage')
/**
* @param {String} storageKey
* @param {number} oldIndex
* @param {number} newIndex
*
* @return {Object}
* ```
* {
* storage: Object
* }
* ```
*/
function reorderFolder (storageKey, oldIndex, newIndex) {
let targetStorage
try {
if (!_.isNumber(oldIndex)) throw new Error('oldIndex must be a number.')
if (!_.isNumber(newIndex)) throw new Error('newIndex must be a number.')
targetStorage = findStorage(storageKey)
} catch (e) {
return Promise.reject(e)
}
return resolveStorageData(targetStorage)
.then(function reorderFolder (storage) {
storage.folders = _.move(storage.folders, oldIndex, newIndex)
CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version']))
return {
storage
}
})
}
module.exports = reorderFolder

View File

@@ -4,7 +4,7 @@ const CSON = require('@rokt33r/season')
const migrateFromV6Storage = require('./migrateFromV6Storage')
function resolveStorageData (storageCache) {
let storage = {
const storage = {
key: storageCache.key,
name: storageCache.name,
type: storageCache.type,
@@ -13,7 +13,7 @@ function resolveStorageData (storageCache) {
const boostnoteJSONPath = path.join(storageCache.path, 'boostnote.json')
try {
let jsonData = CSON.readFileSync(boostnoteJSONPath)
const jsonData = CSON.readFileSync(boostnoteJSONPath)
if (!_.isArray(jsonData.folders)) throw new Error('folders should be an array.')
storage.folders = jsonData.folders
storage.version = jsonData.version
@@ -28,7 +28,7 @@ function resolveStorageData (storageCache) {
storage.version = '1.0'
}
let version = parseInt(storage.version, 10)
const version = parseInt(storage.version, 10)
if (version >= 1) {
if (version > 1) {
console.log('The repository version is newer than one of current app.')

View File

@@ -16,13 +16,13 @@ function resolveStorageNotes (storage) {
}
notePathList = []
}
let notes = notePathList
const notes = notePathList
.filter(function filterOnlyCSONFile (notePath) {
return /\.cson$/.test(notePath)
})
.map(function parseCSONFile (notePath) {
try {
let data = CSON.readFileSync(path.join(notesDirPath, notePath))
const data = CSON.readFileSync(path.join(notesDirPath, notePath))
data.key = path.basename(notePath, '.cson')
data.storage = storage.key
return data

View File

@@ -2,6 +2,7 @@ const _ = require('lodash')
const path = require('path')
const resolveStorageData = require('./resolveStorageData')
const CSON = require('@rokt33r/season')
const { findStorage } = require('browser/lib/findStorage')
/**
* @param {String} storageKey
@@ -22,25 +23,20 @@ const CSON = require('@rokt33r/season')
* ```
*/
function updateFolder (storageKey, folderKey, input) {
let rawStorages
let targetStorage
try {
if (input == null) throw new Error('No input found.')
if (!_.isString(input.name)) throw new Error('Name must be a string.')
if (!_.isString(input.color)) throw new Error('Color must be a string.')
rawStorages = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(rawStorages)) throw new Error('Target storage doesn\'t exist.')
targetStorage = _.find(rawStorages, {key: storageKey})
if (targetStorage == null) throw new Error('Target storage doesn\'t exist.')
targetStorage = findStorage(storageKey)
} catch (e) {
return Promise.reject(e)
}
return resolveStorageData(targetStorage)
.then(function updateFolder (storage) {
let targetFolder = _.find(storage.folders, {key: folderKey})
const targetFolder = _.find(storage.folders, {key: folderKey})
if (targetFolder == null) throw new Error('Target folder doesn\'t exist.')
targetFolder.name = input.name
targetFolder.color = input.color

View File

@@ -5,7 +5,7 @@ const CSON = require('@rokt33r/season')
const { findStorage } = require('browser/lib/findStorage')
function validateInput (input) {
let validatedInput = {}
const validatedInput = {}
if (input.tags != null) {
if (!_.isArray(input.tags)) validatedInput.tags = []
@@ -26,6 +26,10 @@ function validateInput (input) {
validatedInput.isTrashed = !!input.isTrashed
}
if (input.isPinned !== undefined) {
validatedInput.isPinned = !!input.isPinned
}
validatedInput.type = input.type
switch (input.type) {
case 'MARKDOWN_NOTE':
@@ -77,7 +81,7 @@ function updateNote (storageKey, noteKey, input) {
return resolveStorageData(targetStorage)
.then(function saveNote (storage) {
let noteData
let notePath = path.join(storage.path, 'notes', noteKey + '.cson')
const notePath = path.join(storage.path, 'notes', noteKey + '.cson')
try {
noteData = CSON.readFileSync(notePath)
} catch (err) {
@@ -104,6 +108,7 @@ function updateNote (storageKey, noteKey, input) {
noteData.isStarred = false
noteData.isTrashed = false
noteData.tags = []
noteData.isPinned = false
}
if (noteData.type === 'SNIPPET_NOTE') {

View File

@@ -1,121 +0,0 @@
import store from 'browser/main/store'
const _ = require('lodash')
const keygen = require('browser/lib/keygen')
const Mixpanel = require('mixpanel')
const mixpanel = Mixpanel.init('7a0aca437d72dfd07cbcbf58d3b61f27', {key: 'fde4fd23f4d550f1b646bcd7d4374b1f'})
const moment = require('moment')
const electron = require('electron')
function _getClientKey () {
let clientKey = localStorage.getItem('clientKey')
if (!_.isString(clientKey) || clientKey.length !== 40) {
clientKey = keygen(20)
_setClientKey(clientKey)
}
return clientKey
}
function _setClientKey (newKey) {
localStorage.setItem('clientKey', newKey)
}
function _fetch () {
let events
try {
events = JSON.parse(localStorage.getItem('events'))
if (!_.isArray(events)) throw new Error('events is not an array.')
} catch (err) {
console.warn(err)
events = []
localStorage.setItem('events', JSON.stringify(events))
console.info('Events cache initialzed')
}
return events
}
function _keep (name, properties) {
let events = _fetch()
properties.time = new Date()
events.push({
name,
properties
})
localStorage.setItem('events', JSON.stringify(events))
}
function _keepUnique (name, properties) {
let events = _fetch()
properties.time = new Date()
events = events.filter((event) => event.name !== name)
events.push({
name,
properties
})
localStorage.setItem('events', JSON.stringify(events))
}
function _flush () {
let events = _fetch()
let spliced = events.splice(0, 50)
localStorage.setItem('events', JSON.stringify(events))
if (spliced.length > 0) {
let parsedEvents = spliced
.filter((event) => {
if (!_.isObject(event)) return false
if (!_.isString(event.name)) return false
if (!_.isObject(event.properties)) return false
if (!moment(event.properties.time).isValid()) return false
if (new Date() - moment(event.properties.time).toDate() > 1000 * 3600 * 24 * 3) return false
return true
})
.map((event) => {
return {
event: event.name,
properties: event.properties
}
})
mixpanel.import_batch(parsedEvents, {}, (errs) => {
if (errs.length > 0) {
let events = _fetch()
events = events.concat(spliced)
localStorage.setItem('events', JSON.stringify(events))
} else {
_flush()
}
})
let state = store.getState()
mixpanel.people.set(_getClientKey(), {
storage_count: state.data.storageMap.size,
note_count: state.data.noteMap.size,
version: electron.remote.app.getVersion()
})
}
}
setInterval(_flush, 1000 * 60 * 60)
function track (name, properties) {
switch (name) {
case 'MAIN_FOCUSED':
properties = Object.assign({}, properties, {
distinct_id: _getClientKey()
})
_keepUnique(name, properties)
break
default:
properties = Object.assign({}, properties, {
distinct_id: _getClientKey()
})
_keep(name, properties)
}
}
module.exports = {
_mp: mixpanel,
track
}

View File

@@ -16,7 +16,7 @@ class ModalBase extends React.Component {
close () {
if (modalBase != null) modalBase.setState({component: null, componentProps: null, isHidden: true})
// Toggle overflow style on NoteList
let list = document.querySelector('.NoteList__list___browser-main-NoteList-')
const list = document.querySelector('.NoteList__list___browser-main-NoteList-')
list.style.overflow = 'auto'
}
@@ -34,14 +34,14 @@ class ModalBase extends React.Component {
}
}
let el = document.createElement('div')
const el = document.createElement('div')
document.body.appendChild(el)
let modalBase = ReactDOM.render(<ModalBase />, el)
const modalBase = ReactDOM.render(<ModalBase />, el)
export function openModal (component, props) {
if (modalBase == null) { return }
// Hide scrollbar by removing overflow when modal opens
let list = document.querySelector('.NoteList__list___browser-main-NoteList-')
const list = document.querySelector('.NoteList__list___browser-main-NoteList-')
list.style.overflow = 'hidden'
document.body.setAttribute('data-modal', 'open')
modalBase.setState({component: component, componentProps: props, isHidden: false})