Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ipfs #3446

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
11 changes: 11 additions & 0 deletions src/main/app/windowManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand Down Expand Up @@ -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)
})
Expand Down
9 changes: 9 additions & 0 deletions src/main/filesystem/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
24 changes: 23 additions & 1 deletion src/main/filesystem/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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.
*
Expand Down
63 changes: 62 additions & 1 deletion src/main/menu/actions/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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')
Expand Down
12 changes: 12 additions & 0 deletions src/main/menu/templates/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}]
}

Expand Down Expand Up @@ -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',
Expand Down
1 change: 1 addition & 0 deletions src/renderer/commands/descriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
5 changes: 5 additions & 0 deletions src/renderer/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
1 change: 1 addition & 0 deletions src/renderer/pages/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
18 changes: 18 additions & 0 deletions src/renderer/store/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down