mirror of
https://github.com/marktext/marktext.git
synced 2025-05-02 19:41:39 +08:00
Merge e5db7de9ea
into 11c8cc1e19
This commit is contained in:
commit
c2b5e5c9fe
@ -86,7 +86,8 @@
|
||||
"vue-electron": "^1.0.6",
|
||||
"vue-router": "^3.5.3",
|
||||
"vuex": "^3.6.2",
|
||||
"webfontloader": "^1.6.28"
|
||||
"webfontloader": "^1.6.28",
|
||||
"web3.storage": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.9",
|
||||
|
@ -3,6 +3,7 @@ import EventEmitter from 'events'
|
||||
import log from 'electron-log'
|
||||
import Watcher, { WATCHER_STABILITY_THRESHOLD, WATCHER_STABILITY_POLL_INTERVAL } from '../filesystem/watcher'
|
||||
import { WindowType } from '../windows/base'
|
||||
import { Web3Storage, getFilesFromPath } from 'web3.storage'
|
||||
|
||||
class WindowActivityList {
|
||||
constructor () {
|
||||
@ -424,6 +425,16 @@ class WindowManager extends EventEmitter {
|
||||
this._watcher.ignoreChangedEvent(windowId, pathname, duration)
|
||||
})
|
||||
|
||||
ipcMain.on('add-file-to-ipfs', async (pathname) => {
|
||||
// A changed event is emitted earliest after the stability threshold.
|
||||
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6ZXRocjoweDU4ZDc1ZjYzN2Y5NDc2YzVkQmU1OGIxNzEyN0Q1MGU0NDgxMzUzQjQiLCJpc3MiOiJ3ZWIzLXN0b3JhZ2UiLCJpYXQiOjE2NjE0MDU2Mzc2MDQsIm5hbWUiOiJ4aW5taW5zdSJ9.sb1ATMTwOtsquSn6kTWQylCRUZjVDWrGUq5o6sLHlis'
|
||||
const storage = new Web3Storage({ token })
|
||||
|
||||
const files = await getFilesFromPath(pathname)
|
||||
const cid = await storage.put(files)
|
||||
console.log('Content added with CID:', cid)
|
||||
})
|
||||
|
||||
ipcMain.on('window-close-by-id', id => {
|
||||
this.forceCloseById(id)
|
||||
})
|
||||
|
@ -30,3 +30,12 @@ export const writeFile = (pathname, content, extension, options = 'utf-8') => {
|
||||
|
||||
return fs.outputFile(pathname, content, options)
|
||||
}
|
||||
|
||||
export const writeFileToIpfs = async (pathname, content, extension, options = 'utf-8') => {
|
||||
if (!pathname) {
|
||||
return Promise.reject(new Error('[ERROR] Cannot save file without path.'))
|
||||
}
|
||||
pathname = !extension || pathname.endsWith(extension) ? pathname : `${pathname}${extension}`
|
||||
|
||||
return fs.outputFile(pathname, content, options)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import iconv from 'iconv-lite'
|
||||
import { LINE_ENDING_REG, LF_LINE_ENDING_REG, CRLF_LINE_ENDING_REG } from '../config'
|
||||
import { isDirectory2 } from 'common/filesystem'
|
||||
import { isMarkdownFile } from 'common/filesystem/paths'
|
||||
import { normalizeAndResolvePath, writeFile } from '../filesystem'
|
||||
import { normalizeAndResolvePath, writeFile, writeFileToIpfs } from '../filesystem'
|
||||
import { guessEncoding } from './encoding'
|
||||
|
||||
const getLineEnding = lineEnding => {
|
||||
@ -67,6 +67,28 @@ export const writeMarkdownFile = (pathname, content, options) => {
|
||||
return writeFile(pathname, buffer, extension, undefined)
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the content file to ipfs.
|
||||
*
|
||||
* @param {string} pathname The path to the file.
|
||||
* @param {string} content The buffer to save.
|
||||
* @param {IMarkdownDocumentOptions} options The markdown document options
|
||||
*/
|
||||
export const writeMarkdownFileToIpfs = (pathname, content, options) => {
|
||||
const { adjustLineEndingOnSave, lineEnding } = options
|
||||
const { encoding, isBom } = options.encoding
|
||||
const extension = path.extname(pathname) || '.md'
|
||||
|
||||
if (adjustLineEndingOnSave) {
|
||||
content = convertLineEndings(content, lineEnding)
|
||||
}
|
||||
|
||||
const buffer = iconv.encode(content, encoding, { addBOM: isBom })
|
||||
|
||||
// TODO(@fxha): "safeSaveDocuments" using temporary file and rename syscall.
|
||||
return writeFileToIpfs(pathname, buffer, extension, undefined)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of a markdown file.
|
||||
*
|
||||
|
@ -9,7 +9,7 @@ import { showTabBar } from './view'
|
||||
import { COMMANDS } from '../../commands'
|
||||
import { EXTENSION_HASN, PANDOC_EXTENSIONS, URL_REG } from '../../config'
|
||||
import { normalizeAndResolvePath, writeFile } from '../../filesystem'
|
||||
import { writeMarkdownFile } from '../../filesystem/markdown'
|
||||
import { writeMarkdownFile, writeMarkdownFileToIpfs } from '../../filesystem/markdown'
|
||||
import { getPath, getRecommendTitleFromMarkdownString } from '../../utils'
|
||||
import pandoc from '../../utils/pandoc'
|
||||
|
||||
@ -158,6 +158,59 @@ const handleResponseForSave = async (e, { id, filename, markdown, pathname, opti
|
||||
})
|
||||
}
|
||||
|
||||
const handleResponseForSaveToIpfs = async (e, { id, filename, markdown, pathname, options, defaultPath }) => {
|
||||
const win = BrowserWindow.fromWebContents(e.sender)
|
||||
let recommendFilename = getRecommendTitleFromMarkdownString(markdown)
|
||||
if (!recommendFilename) {
|
||||
recommendFilename = filename || 'Untitled'
|
||||
}
|
||||
|
||||
// If the file doesn't exist on disk add it to the recently used documents later
|
||||
// and execute file from filesystem watcher for a short time. The file may exists
|
||||
// on disk nevertheless but is already tracked by MarkText.
|
||||
const alreadyExistOnDisk = !!pathname
|
||||
|
||||
let filePath = pathname
|
||||
|
||||
if (!filePath) {
|
||||
const { filePath: dialogPath, canceled } = await dialog.showSaveDialog(win, {
|
||||
defaultPath: path.join(defaultPath || getPath('documents'), `${recommendFilename}.md`)
|
||||
})
|
||||
|
||||
if (dialogPath && !canceled) {
|
||||
filePath = dialogPath
|
||||
}
|
||||
}
|
||||
|
||||
// Save dialog canceled by user - no error.
|
||||
if (!filePath) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
filePath = path.resolve(filePath)
|
||||
const extension = path.extname(filePath) || '.md'
|
||||
filePath = !filePath.endsWith(extension) ? filePath += extension : filePath
|
||||
return writeMarkdownFileToIpfs(filePath, markdown, options, win)
|
||||
.then(() => {
|
||||
if (!alreadyExistOnDisk) {
|
||||
ipcMain.emit('window-add-file-path', win.id, filePath)
|
||||
ipcMain.emit('menu-add-recently-used', filePath)
|
||||
|
||||
const filename = path.basename(filePath)
|
||||
win.webContents.send('mt::set-pathname', { id, pathname: filePath, filename })
|
||||
} else {
|
||||
ipcMain.emit('window-file-saved', win.id, filePath)
|
||||
win.webContents.send('mt::tab-saved', id)
|
||||
}
|
||||
ipcMain.emit('add-file-to-ipfs', filePath)
|
||||
return id
|
||||
})
|
||||
.catch(err => {
|
||||
log.error('Error while saving:', err)
|
||||
win.webContents.send('mt::tab-save-failure', id, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
const showUnsavedFilesMessage = async (win, files) => {
|
||||
const { response } = await dialog.showMessageBox(win, {
|
||||
type: 'warning',
|
||||
@ -317,6 +370,8 @@ ipcMain.on('mt::close-window-confirm', async (e, unsavedFiles) => {
|
||||
|
||||
ipcMain.on('mt::response-file-save', handleResponseForSave)
|
||||
|
||||
ipcMain.on('mt::response-file-save-to-ipfs', handleResponseForSaveToIpfs)
|
||||
|
||||
ipcMain.on('mt::response-export', handleResponseForExport)
|
||||
|
||||
ipcMain.on('mt::response-print', handleResponseForPrint)
|
||||
@ -574,6 +629,12 @@ export const save = win => {
|
||||
}
|
||||
}
|
||||
|
||||
export const saveToIpfs = win => {
|
||||
if (win && win.webContents) {
|
||||
win.webContents.send('mt::editor-ask-file-save-to-ipfs')
|
||||
}
|
||||
}
|
||||
|
||||
export const saveAs = win => {
|
||||
if (win && win.webContents) {
|
||||
win.webContents.send('mt::editor-ask-file-save-as')
|
||||
|
@ -33,6 +33,12 @@ export default function (keybindings, userPreference, recentlyUsedFiles) {
|
||||
click (menuItem, browserWindow) {
|
||||
actions.openFolder(browserWindow)
|
||||
}
|
||||
}, {
|
||||
label: 'Open File From Ipfs',
|
||||
accelerator: keybindings.getAccelerator('file.open-file'),
|
||||
click (menuItem, browserWindow) {
|
||||
actions.openFile(browserWindow)
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
@ -87,6 +93,12 @@ export default function (keybindings, userPreference, recentlyUsedFiles) {
|
||||
click (menuItem, browserWindow) {
|
||||
actions.saveAs(browserWindow)
|
||||
}
|
||||
}, {
|
||||
label: 'Save to Ipfs',
|
||||
accelerator: keybindings.getAccelerator('file.save'),
|
||||
click (menuItem, browserWindow) {
|
||||
actions.saveToIpfs(browserWindow)
|
||||
}
|
||||
}, {
|
||||
label: 'Auto Save',
|
||||
type: 'checkbox',
|
||||
|
@ -11,6 +11,7 @@ const commandDescriptions = Object.freeze({
|
||||
'file.open-folder': 'File: Open Folder',
|
||||
'file.save': 'File: Save',
|
||||
'file.save-as': 'File: Save As...',
|
||||
'file.save-to-ipfs': 'File: Save to Ipfs...',
|
||||
'file.move-file': 'File: Move...',
|
||||
'file.rename-file': 'File: Rename...',
|
||||
'file.quick-open': 'File: Show quick open dialog',
|
||||
|
@ -63,6 +63,11 @@ const commands = [
|
||||
execute: async () => {
|
||||
ipcRenderer.emit('mt::editor-ask-file-save', null)
|
||||
}
|
||||
}, {
|
||||
id: 'file.save-to-ipfs',
|
||||
execute: async () => {
|
||||
ipcRenderer.emit('mt::editor-ask-file-save-to-ipfs', null)
|
||||
}
|
||||
}, {
|
||||
id: 'file.save-as',
|
||||
execute: async () => {
|
||||
|
@ -144,6 +144,7 @@ export default {
|
||||
dispatch('LISTEN_FOR_SAVE_AS')
|
||||
dispatch('LISTEN_FOR_MOVE_TO')
|
||||
dispatch('LISTEN_FOR_SAVE')
|
||||
dispatch('LISTEN_FOR_SAVE_TO_IPFS')
|
||||
dispatch('LISTEN_FOR_SET_PATHNAME')
|
||||
dispatch('LISTEN_FOR_BOOTSTRAP_WINDOW')
|
||||
dispatch('LISTEN_FOR_SAVE_CLOSE')
|
||||
|
@ -430,6 +430,24 @@ const actions = {
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_SAVE_TO_IPFS ({ state, rootState }) {
|
||||
ipcRenderer.on('mt::editor-ask-file-save-to-ipfs', () => {
|
||||
const { id, filename, pathname, markdown } = state.currentFile
|
||||
const options = getOptionsFromState(state.currentFile)
|
||||
const defaultPath = getRootFolderFromState(rootState)
|
||||
if (id) {
|
||||
ipcRenderer.send('mt::response-file-save-to-ipfs', {
|
||||
id,
|
||||
filename,
|
||||
pathname,
|
||||
markdown,
|
||||
options,
|
||||
defaultPath
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// need pass some data to main process when `save as` menu item clicked
|
||||
LISTEN_FOR_SAVE_AS ({ state, rootState }) {
|
||||
ipcRenderer.on('mt::editor-ask-file-save-as', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user