feat: user preference and fix issues #45 #16

This commit is contained in:
Jocs 2018-03-21 17:11:18 +08:00
parent 3e28b7e328
commit 39f5400544
12 changed files with 186 additions and 31 deletions

13
.github/CHANGELOG.md vendored
View File

@ -1,6 +1,15 @@
### 0.7.18
### 0.8.4
**Bug fix**
**:cactus:Feature**
- Add user preferences in `Mark Text menu`, the shoutcut is `CmdorCtrl + ,`, you can set the default `theme` and `autoSave`.
- Add `autoSave` to `file menu`, the default value is in `preferences.md` which you can open in `Mark Text menu`. #45
**:butterfly:Optimization**
- Theme can be saved in user preferences now #16
**:beetle:Bug fix**
- fix: prevent open image or file directly when drag and drop over Mark Text #42
- fix: set theme to all the open window not just the active one.

View File

@ -1,6 +1,6 @@
{
"name": "marktext",
"version": "0.7.17",
"version": "0.8.4",
"author": "Jocs <luoran1988@126.com>",
"description": "Next generation markdown editor",
"license": "MIT",

View File

@ -6,6 +6,7 @@ import path from 'path'
import { app, dialog, ipcMain, BrowserWindow } from 'electron'
import createWindow, { windows } from '../createWindow'
import { EXTENSIONS, EXTENSION_HASN } from '../config'
import { getUserPreference, setUserPreference } from '../utils'
const watchAndReload = (pathname, win) => { // when i build, and failed.
// const watcher = chokidar.watch(pathname, {
@ -114,6 +115,13 @@ ipcMain.on('AGANI::response-close-confirm', (e, { filename, pathname, markdown }
})
ipcMain.on('AGANI::response-file-save', handleResponseForSave)
ipcMain.on('AGANI::ask-for-auto-save', e => {
const win = BrowserWindow.fromWebContents(e.sender)
const { autoSave } = getUserPreference()
win.webContents.send('AGANI::auto-save', autoSave)
})
ipcMain.on('AGANI::response-export', handleResponseForExport)
ipcMain.on('AGANI::close-window', e => {
@ -155,6 +163,15 @@ export const saveAs = win => {
win.webContents.send('AGANI::ask-file-save-as')
}
export const autoSave = (menuItem, win) => {
// TODO
export const autoSave = (menuItem, browserWindow) => {
const { checked } = menuItem
setUserPreference('autoSave', checked)
.then(() => {
for (const win of windows.values()) {
win.webContents.send('AGANI::auto-save', checked)
}
})
.catch(err => {
console.log(err)
})
}

View File

@ -1,7 +1,7 @@
import fs from 'fs'
import path from 'path'
import { ipcMain } from 'electron'
import { getMenuItem } from '../utils'
import { getMenuItem, setUserPreference } from '../utils'
import { windows } from '../createWindow'
/**
* Set `__static` path to static files in production
@ -17,9 +17,15 @@ const THEME_PATH = path.join(__static, '/themes')
const themeCSS = {}
export const selectTheme = (theme, themeCSS) => {
for (const win of windows.values()) {
win.webContents.send('AGANI::theme', { theme, themeCSS })
}
setUserPreference('theme', theme)
.then(() => {
for (const win of windows.values()) {
win.webContents.send('AGANI::theme', { theme, themeCSS })
}
})
.catch(err => {
console.log(err)
})
}
const getSelectTheme = () => {

View File

@ -26,7 +26,7 @@ export const EXTENSION_HASN = {
pdf: '.pdf'
}
export const DEFAULT_THEME = 'dark'
// export const DEFAULT_THEME = 'dark'
export const VIEW_MENU_ITEM = {
'Source Code Mode': false,

View File

@ -1,4 +1,7 @@
import * as actions from '../actions/file'
import { getUserPreference } from '../utils'
const { autoSave } = getUserPreference()
export default {
label: 'File',
@ -37,9 +40,10 @@ export default {
}
}, {
label: 'Auto Save',
type: 'radio',
type: 'checkbox',
checked: autoSave,
click (menuItem, browserWindow) {
actions.autoSave(browserWindow)
actions.autoSave(menuItem, browserWindow)
}
}, {
type: 'separator'

View File

@ -1,18 +1,21 @@
import * as actions from '../actions/theme'
import { getUserPreference } from '../utils'
const { theme } = getUserPreference()
export default {
label: 'Theme',
submenu: [{
label: 'Dark',
type: 'radio',
checked: false,
checked: theme === 'dark',
click (menuItem, browserWindow) {
actions.selectTheme('dark')
}
}, {
label: 'Light',
type: 'radio',
checked: true,
checked: theme === 'light',
click (menuItem, browserWindow) {
actions.selectTheme('light')
}

View File

@ -1,6 +1,47 @@
import fs from 'fs'
import path from 'path'
import { Menu } from 'electron'
const JSON_REG = /```json(.+)```/g
const preferencePath = path.join(__static, 'preference.md')
let PREFERENCE_CACHE = null
export const getMenuItem = menuName => {
const menus = Menu.getApplicationMenu()
return menus.items.find(menu => menu.label === menuName)
}
export const getUserPreference = () => {
if (PREFERENCE_CACHE) {
return PREFERENCE_CACHE
} else {
const content = fs.readFileSync(preferencePath, 'utf-8')
try {
const userSetting = JSON_REG.exec(content.replace(/\n/g, ''))[1]
PREFERENCE_CACHE = JSON.parse(userSetting)
return PREFERENCE_CACHE
} catch (err) {
// todo notice the user
console.log(err)
}
}
}
export const setUserPreference = (key, value) => {
const preUserSetting = getUserPreference()
const newUserSetting = PREFERENCE_CACHE = Object.assign({}, preUserSetting, { [key]: value })
return new Promise((resolve, reject) => {
const content = fs.readFileSync(preferencePath, 'utf-8')
const tokens = content.split('```')
const newContent = tokens[0] +
'```json\n' +
JSON.stringify(newUserSetting, null, 2) +
'\n```' +
tokens[2]
fs.writeFile(preferencePath, newContent, 'utf-8', err => {
if (err) reject(err)
else resolve(newUserSetting)
})
})
}

View File

@ -7,6 +7,7 @@
:word-count="wordCount"
:theme="theme"
:platform="platform"
:is-saved="isSaved"
></title-bar>
<editor
:typewriter="typewriter"
@ -57,7 +58,7 @@
},
computed: {
...mapState([
'pathname', 'filename', 'windowActive', 'wordCount',
'pathname', 'filename', 'isSaved', 'windowActive', 'wordCount',
'typewriter', 'focus', 'sourceCode', 'markdown',
'cursor', 'theme', 'themeCSS', 'platform'
])
@ -66,6 +67,7 @@
const { dispatch } = this.$store
dispatch('ASK_FOR_THEME')
dispatch('ASK_FOR_AUTO_SAVE')
dispatch('ASK_FOR_MODE')
dispatch('LISTEN_FOR_CLOSE')
dispatch('LISTEN_FOR_SAVE_AS')

View File

@ -10,6 +10,7 @@
</svg>
</span>
<span :class="[{ 'title-no-drag': platform === 'win32' }]">{{ filename }}</span>
<span class="save-dot" :class="{'show': !isSaved}"></span>
</div>
<div :class="platform === 'win32' ? 'left-toolbar' : 'right-toolbar'">
<div v-if="platform === 'win32'" class="windows-titlebar-menu title-no-drag" @click.stop="handleMenuClick">&#9776;</div>
@ -48,7 +49,8 @@
active: Boolean,
wordCount: Object,
theme: String,
platform: String
platform: String,
isSaved: Boolean
},
computed: {
paths () {
@ -81,7 +83,10 @@
},
handleMenuClick () {
const win = remote.getCurrentWindow()
remote.Menu.getApplicationMenu().popup({window: win, x: 23, y: 20})
remote
.Menu
.getApplicationMenu()
.popup({ window: win, x: 23, y: 20 })
}
}
}
@ -121,9 +126,25 @@
text-align: center;
transition: all .25s ease-in-out;
}
.active .save-dot {
margin-left: 3px;
width: 7px;
height: 7px;
display: inline-block;
border-radius: 50%;
background: rgba(242, 134, 94, .7);
visibility: hidden;
}
.active .save-dot.show {
visibility: visible;
}
.title:hover {
color: #303133;
}
.title:hover .save-dot {
background: rgb(242, 134, 94);
}
.right-toolbar {
padding: 0 10px;
height: 100%;

View File

@ -16,6 +16,7 @@ const state = {
sourceCode: false, // source code mode
pathname: '',
isSaved: true,
autoSave: false,
markdown: '',
cursor: null,
windowActive: true,
@ -51,7 +52,7 @@ const mutations = {
window.__dirname = path.dirname(pathname)
state.pathname = pathname
},
SET_STATUS (state, status) {
SET_SAVE_STATUS (state, status) {
state.isSaved = status
},
SET_MARKDOWN (state, markdown) {
@ -62,6 +63,9 @@ const mutations = {
},
SET_CURSOR (state, cursor) {
state.cursor = cursor
},
SET_AUTO_SAVE (state, autoSave) {
state.autoSave = autoSave
}
}
@ -72,9 +76,24 @@ const actions = {
commit('SET_THEME', themes)
})
},
ASK_FOR_AUTO_SAVE ({ commit, state }) {
ipcRenderer.send('AGANI::ask-for-auto-save')
ipcRenderer.on('AGANI::auto-save', (e, autoSave) => {
const { pathname, markdown } = state
commit('SET_AUTO_SAVE', autoSave)
if (autoSave && pathname) {
commit('SET_SAVE_STATUS', true)
ipcRenderer.send('AGANI::response-file-save', { pathname, markdown })
}
})
},
SEARCH ({ commit }, value) {
commit('SET_SEARCH', value)
},
ASK_FOR_MODE ({ commit }) {
ipcRenderer.send('AGANI::ask-for-mode')
ipcRenderer.on('AGANI::res-for-mode', (e, modes) => {
@ -86,70 +105,86 @@ const actions = {
})
})
},
LINTEN_WIN_STATUS ({ commit }) {
ipcRenderer.on('AGANI::window-active-status', (e, { status }) => {
commit('SET_WIN_STATUS', status)
})
},
LISTEN_FOR_SAVE ({ commit, state }) {
ipcRenderer.on('AGANI::ask-file-save', () => {
const { pathname, markdown } = state
ipcRenderer.send('AGANI::response-file-save', { pathname, markdown })
commit('SET_SAVE_STATUS', true)
})
},
LISTEN_FOR_SAVE_AS ({ commit, state }) {
ipcRenderer.on('AGANI::ask-file-save-as', () => {
const { pathname, markdown } = state
ipcRenderer.send('AGANI::response-file-save-as', { pathname, markdown })
})
},
GET_FILENAME ({ commit, state }) {
ipcRenderer.on('AGANI::set-pathname', (e, { pathname, filename }) => {
commit('SET_FILENAME', filename)
commit('SET_PATHNAME', pathname)
commit('SET_STATUS', true)
commit('SET_SAVE_STATUS', true)
})
},
LISTEN_FOR_FILE_LOAD ({ commit, state }) {
ipcRenderer.on('AGANI::file-loaded', (e, { file, filename, pathname }) => {
commit('SET_FILENAME', filename)
commit('SET_PATHNAME', pathname)
commit('SET_MARKDOWN', file)
commit('SET_STATUS', true)
commit('SET_SAVE_STATUS', true)
bus.$emit('file-loaded', file)
})
},
LISTEN_FOR_FILE_CHANGE ({ commit, state }) {
ipcRenderer.on('AGANI::file-change', (e, { file, filename, pathname }) => {
const { windowActive } = state
commit('SET_FILENAME', filename)
commit('SET_PATHNAME', pathname)
commit('SET_MARKDOWN', file)
commit('SET_STATUS', true)
commit('SET_SAVE_STATUS', true)
if (!windowActive) {
bus.$emit('file-loaded', file)
}
})
},
EDITE_FILE ({ commit }) {
commit('SET_STATUS', false)
commit('SET_SAVE_STATUS', false)
},
EXPORT ({ commit, state }, { type, content }) {
const { filename, pathname } = state
ipcRenderer.send('AGANI::response-export', { type, content, filename, pathname })
},
SAVE_FILE ({ commit, state }, { markdown, wordCount, cursor }) {
const { pathname, autoSave, markdown: oldMarkdown } = state
commit('SET_MARKDOWN', markdown)
// set word count
if (wordCount) commit('SET_WORD_COUNT', wordCount)
// set cursor
if (cursor) commit('SET_CURSOR', cursor)
const { pathname } = state
if (pathname) {
commit('SET_STATUS', true)
ipcRenderer.send('AGANI::response-file-save', { pathname, markdown })
} else {
commit('SET_STATUS', false)
// save to file only when the markdown changed!
if (markdown !== oldMarkdown) {
if (pathname && autoSave) {
commit('SET_SAVE_STATUS', true)
ipcRenderer.send('AGANI::response-file-save', { pathname, markdown })
} else {
commit('SET_SAVE_STATUS', false)
}
}
},
SELECTION_CHANGE ({ commit }, changes) {
const { start, end } = changes
if (start.key === end.key && start.block.text) {
@ -163,15 +198,18 @@ const actions = {
ipcRenderer.send('AGANI::selection-change', changes)
},
SELECTION_FORMATS ({ commit }, formats) {
ipcRenderer.send('AGANI::selection-formats', formats)
},
// listen for export from main process
LISTEN_FOR_EXPORT ({ commit, state }) {
ipcRenderer.on('AGANI::export', (e, { type }) => {
bus.$emit('export', type)
})
},
LISTEN_FOR_INSERT_IMAGE ({ commit, state }) {
ipcRenderer.on('AGANI::INSERT_IMAGE', (e, { filename: imagePath, type }) => {
if (type === 'absolute' || type === 'relative') {
@ -186,16 +224,19 @@ const actions = {
}
})
},
LISTEN_FOR_EDIT ({ commit }) {
ipcRenderer.on('AGANI::edit', (e, { type }) => {
bus.$emit(type)
})
},
LISTEN_FOR_VIEW ({ commit }) {
ipcRenderer.on('AGANI::view', (e, data) => {
commit('SET_MODE', data)
})
},
LISTEN_FOR_PARAGRAPH_INLINE_STYLE ({ commit }) {
ipcRenderer.on('AGANI::paragraph', (e, { type }) => {
bus.$emit('paragraph', type)
@ -204,6 +245,7 @@ const actions = {
bus.$emit('format', type)
})
},
LISTEN_FOR_CLOSE ({ commit, state }) {
ipcRenderer.on('AGANI::ask-for-close', e => {
const { isSaved, markdown, pathname, filename } = state

View File

@ -1,6 +1,10 @@
#### User Preferences
### :bust_in_silhouette:User Preferences
Edit and save to update preferences:
Edit and save to update preferences, You can only change the json bellow!
- **theme**: *String* `dark` or `light`
- **autoSave**: *Boolean* `true` or `false`
```json
{
@ -9,4 +13,10 @@ Edit and save to update preferences:
}
```
More user preferences comming.
More user preferences comming soon.
**Please use `CmdOrCtrl + S` to save your preferences and reload Mark Text to use your setting!**
> Your friends at Mark Text