This commit is contained in:
xinminsu 2024-06-13 10:04:35 +00:00 committed by GitHub
commit c2b5e5c9fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 144 additions and 3 deletions

View File

@ -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",

View File

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

View File

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

View File

@ -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.
*

View 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')

View File

@ -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',

View File

@ -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',

View File

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

View File

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

View File

@ -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', () => {