diff --git a/.babelrc b/.babelrc index 6674048..d301495 100644 --- a/.babelrc +++ b/.babelrc @@ -1,9 +1,4 @@ { - "presets": [ - "@babel/preset-env", - "@babel/preset-react" - ], - "plugins": [ - "@babel/plugin-transform-runtime" - ] + "presets": ["@babel/preset-env", "@babel/preset-react"], + "plugins": ["@babel/plugin-transform-runtime"] } diff --git a/assets/index.html b/assets/index.html new file mode 100644 index 0000000..3087f29 --- /dev/null +++ b/assets/index.html @@ -0,0 +1,99 @@ +Html Webpack Plugin: +
+  Error: Child compilation failed:
+  Entry module not found: Error: Can't resolve '/Users/liuguo/repos/hypha-desktop/src/static/index.html' in '/Users/li  uguo/repos/hypha-desktop/src':
+  Error: Can't resolve '/Users/liuguo/repos/hypha-desktop/src/static/index.html' in '/Users/liuguo/repos/hypha-desktop  /src'
+  
+  - compiler.js:79 
+    [hypha-desktop]/[html-webpack-plugin]/lib/compiler.js:79:16
+  
+  - Compiler.js:343 
+    [hypha-desktop]/[webpack]/lib/Compiler.js:343:11
+  
+  - Compiler.js:671 
+    [hypha-desktop]/[webpack]/lib/Compiler.js:671:15
+  
+  
+  - Hook.js:154 AsyncSeriesHook.lazyCompileHook
+    [hypha-desktop]/[tapable]/lib/Hook.js:154:20
+  
+  - Compiler.js:668 
+    [hypha-desktop]/[webpack]/lib/Compiler.js:668:31
+  
+  
+  - Hook.js:154 AsyncSeriesHook.lazyCompileHook
+    [hypha-desktop]/[tapable]/lib/Hook.js:154:20
+  
+  - Compilation.js:1385 
+    [hypha-desktop]/[webpack]/lib/Compilation.js:1385:35
+  
+  
+  - Hook.js:154 AsyncSeriesHook.lazyCompileHook
+    [hypha-desktop]/[tapable]/lib/Hook.js:154:20
+  
+  - Compilation.js:1376 
+    [hypha-desktop]/[webpack]/lib/Compilation.js:1376:32
+  
+  
+  - Hook.js:154 AsyncSeriesHook.lazyCompileHook
+    [hypha-desktop]/[tapable]/lib/Hook.js:154:20
+  
+  - Compilation.js:1371 
+    [hypha-desktop]/[webpack]/lib/Compilation.js:1371:36
+  
+  
+  - Hook.js:154 AsyncSeriesHook.lazyCompileHook
+    [hypha-desktop]/[tapable]/lib/Hook.js:154:20
+  
+  - Compilation.js:1367 
+    [hypha-desktop]/[webpack]/lib/Compilation.js:1367:32
+  
+  
+  - Hook.js:154 AsyncSeriesHook.lazyCompileHook
+    [hypha-desktop]/[tapable]/lib/Hook.js:154:20
+  
+  - Compilation.js:1304 Compilation.seal
+    [hypha-desktop]/[webpack]/lib/Compilation.js:1304:27
+  
+  - Compiler.js:665 
+    [hypha-desktop]/[webpack]/lib/Compiler.js:665:18
+  
+  - Compilation.js:1224 
+    [hypha-desktop]/[webpack]/lib/Compilation.js:1224:4
+  
+  
+  - Hook.js:154 AsyncSeriesHook.lazyCompileHook
+    [hypha-desktop]/[tapable]/lib/Hook.js:154:20
+  
+  - Compilation.js:1216 Compilation.finish
+    [hypha-desktop]/[webpack]/lib/Compilation.js:1216:28
+  
+  - Compiler.js:662 
+    [hypha-desktop]/[webpack]/lib/Compiler.js:662:17
+  
+  
+  - Compilation.js:1148 
+    [hypha-desktop]/[webpack]/lib/Compilation.js:1148:12
+  
+  - Compilation.js:1007 errorAndCallback
+    [hypha-desktop]/[webpack]/lib/Compilation.js:1007:6
+  
+  - Compilation.js:1038 
+    [hypha-desktop]/[webpack]/lib/Compilation.js:1038:14
+  
+  - NormalModuleFactory.js:401 
+    [hypha-desktop]/[webpack]/lib/NormalModuleFactory.js:401:22
+  
+  - NormalModuleFactory.js:130 
+    [hypha-desktop]/[webpack]/lib/NormalModuleFactory.js:130:21
+  
+  - NormalModuleFactory.js:224 
+    [hypha-desktop]/[webpack]/lib/NormalModuleFactory.js:224:22
+  
+  - async.js:2830 
+    [hypha-desktop]/[neo-async]/async.js:2830:7
+  
+  - async.js:6877 
+    [hypha-desktop]/[neo-async]/async.js:6877:13
+  
+
\ No newline at end of file diff --git a/assets/index.js b/assets/index.js new file mode 100644 index 0000000..8548ed5 --- /dev/null +++ b/assets/index.js @@ -0,0 +1,100 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/*!***********************!*\ + !*** multi index.tsx ***! + \***********************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +!(function webpackMissingModule() { var e = new Error("Cannot find module 'index.tsx'"); e.code = 'MODULE_NOT_FOUND'; throw e; }()); + + +/***/ }) +/******/ ]); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/assets/index.js.map b/assets/index.js.map new file mode 100644 index 0000000..0999610 --- /dev/null +++ b/assets/index.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap"],"names":[],"mappings":";QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA","file":"index.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n"],"sourceRoot":""} \ No newline at end of file diff --git a/package.json b/package.json index 770383b..a35e2c3 100644 --- a/package.json +++ b/package.json @@ -5,15 +5,16 @@ "description": "Hypha Desktop Application", "main": "out/index.js", "scripts": { - "build:pre": "npm run build:module; npm run build:copy; npm run build:ui", + "build:pre": "npm run build:module; npm run build:copy; npm run build:ui; npm run build:main", "build:copy": "babel src --out-dir out --copy-files; shx rm -rf out/ui", "build:module": "npm install .", + "build:main": "tsc -p tsconfig.json", "build:ui": "webpack --env production --colors --config webpack.ui.config.js", "build:electron": "electron-builder", "clean:ui": "shx rm -rf assets/ui/", - "dev:electron": "wait-on assets/ui/index.js; cross-env NODE_ENV=development electron -r @babel/register src/index.js", + "dev:electron": "wait-on assets/ui/index.js npm run build:main; concurrently \"tsc -p tsconfig.json -w\" \"cross-env NODE_ENV=development electron -r @babel/register out/index.js\"", "dev:ui": "webpack --env development --colors --config webpack.ui.config.js", - "format:ui": "prettier --write \"src/ui/**/*.{js,json}\"", + "format:ui": "prettier --write \"src/ui/**/*.{js,json,ts,tsx,css}\"", "start:dev": "NODE_ENV=development npm run clean:ui; concurrently \"npm:dev:ui\" \"npm:dev:electron\"" }, "husky": { @@ -46,6 +47,7 @@ "@svgr/webpack": "^4.3.2", "@types/react": "^16.8.23", "@types/react-dom": "^16.8.4", + "awesome-typescript-loader": "^5.2.1", "babel-loader": "^8.0.6", "concurrently": "^4.1.1", "copy-webpack-plugin": "^5.0.3", @@ -59,35 +61,30 @@ "npm-run-all": "^4.1.5", "prettier": "^1.18.2", "shx": "^0.3.2", + "source-map-loader": "^1.0.0", "style-loader": "^0.23.1", + "ts-loader": "^7.0.5", + "typescript": "^3.9.3", + "typings-for-css-modules-loader": "^1.7.0", "wait-on": "^3.3.0", "webpack": "^4.35.3", "webpack-build-notifier": "^1.0.3", "webpack-cli": "^3.3.6" }, "dependencies": { - "@apollo/react-hooks": "^3.1.0", "@babel/runtime": "^7.5.4", - "apollo-cache-inmemory": "^1.6.3", - "apollo-client": "^2.6.4", - "apollo-link-http": "^1.5.16", "cheerio": "^1.0.0-rc.3", "classnames": "^2.2.6", "electron-store": "^4.0.0", "file-type": "^12.0.1", "fix-path": "^2.1.0", "go-ipfs-dep": "0.4.21", - "graphql": "^14.5.4", - "graphql-tag": "^2.10.1", - "graphql-tools": "^4.0.5", - "graphql-transport-electron": "^1.0.1", "ipfs-http-client": "^33.1.0", "ipfsd-ctl": "^0.44.1", "is-ipfs": "^0.6.1", "isomorphic-fetch": "^2.2.1", "iterall": "^1.2.2", "lodash": "^4.17.15", - "node-fetch": "^2.6.0", "react": "^16.8.6", "react-dom": "^16.8.6", "react-feather": "^2.0.3" diff --git a/src/common/daemon.js b/src/common/daemon.ts similarity index 100% rename from src/common/daemon.js rename to src/common/daemon.ts diff --git a/src/common/enum.js b/src/common/enum.ts similarity index 100% rename from src/common/enum.js rename to src/common/enum.ts diff --git a/src/common/ipfs.js b/src/common/ipfs.ts similarity index 100% rename from src/common/ipfs.js rename to src/common/ipfs.ts diff --git a/src/common/store.js b/src/common/store.ts similarity index 100% rename from src/common/store.js rename to src/common/store.ts diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..d7e961e --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1 @@ +declare module "*.css" diff --git a/src/index.js b/src/index.ts similarity index 60% rename from src/index.js rename to src/index.ts index aabf769..4f1483e 100644 --- a/src/index.js +++ b/src/index.ts @@ -1,11 +1,11 @@ -import { app } from 'electron' -import fixPath from 'fix-path' +import { app } from "electron" +import fixPath from "fix-path" -import main from './main' +import main from "./main" fixPath() -app.setAppUserModelId('io.hypha.desktop') +app.setAppUserModelId("io.hypha.desktop") if (app.dock) { app.dock.hide() @@ -15,8 +15,8 @@ if (!app.requestSingleInstanceLock()) { process.exit(0) } -process.on('uncaughtException', console.log) -process.on('unhandledRejection', console.log) +process.on("uncaughtException", console.log) +process.on("unhandledRejection", console.log) const context = {} diff --git a/src/main/index.js b/src/main/index.js deleted file mode 100644 index c0d44c8..0000000 --- a/src/main/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import makeMenu from './makeMenu' -import makeTray from './makeTray' -import makeUI from './makeUi' -import runDaemon from './runDaemon' -import runServer from './server' - -export default async context => { - await runDaemon(context) - await runServer(context) - await makeMenu(context) - await makeTray(context) - await makeUI(context) -} diff --git a/src/main/index.ts b/src/main/index.ts new file mode 100644 index 0000000..95b3825 --- /dev/null +++ b/src/main/index.ts @@ -0,0 +1,11 @@ +import makeMenu from "./makeMenu" +import makeTray from "./makeTray" +import makeUI from "./makeUi" +import runDaemon from "./runDaemon" + +export default async (context) => { + await runDaemon(context) + await makeMenu(context) + await makeTray(context) + await makeUI(context) +} diff --git a/src/main/makeMenu.js b/src/main/makeMenu.js deleted file mode 100644 index ac7ab34..0000000 --- a/src/main/makeMenu.js +++ /dev/null @@ -1,47 +0,0 @@ -import { app, Menu } from 'electron' - -import { PLATFORM } from '../common/enum' - -export default () => { - const items = [ - { - label: 'Edit', - submenu: [ - { role: 'undo' }, - { role: 'redo' }, - { role: 'cut' }, - { role: 'copy' }, - { role: 'paste' }, - { role: 'selectall' } - ] - }, - { - label: 'View', - submenu: [ - { role: 'reload' }, - { role: 'forcereload' }, - { role: 'toggledevtools' } - ] - }, - { - role: 'window', - submenu: [] - }, - { - role: 'help', - submenu: [] - } - ] - - if (process.platform === PLATFORM.MAC) { - items.unshift({ - label: app.getName(), - submenu: [] - }) - } - - const menu = Menu.buildFromTemplate(items) - Menu.setApplicationMenu(menu) - - console.log('[MENU] started') -} diff --git a/src/main/makeMenu.ts b/src/main/makeMenu.ts new file mode 100644 index 0000000..0a84d95 --- /dev/null +++ b/src/main/makeMenu.ts @@ -0,0 +1,47 @@ +import { app, Menu, MenuItem } from "electron" + +import { PLATFORM } from "../common/enum" + +export default (context) => { + const items = [ + { + label: "Edit", + submenu: [ + { role: "undo" }, + { role: "redo" }, + { role: "cut" }, + { role: "copy" }, + { role: "paste" }, + { role: "selectall" }, + ], + }, + { + label: "View", + submenu: [ + { role: "reload" }, + { role: "forcereload" }, + { role: "toggledevtools" }, + ], + }, + { + role: "window", + submenu: [], + }, + { + role: "help", + submenu: [], + }, + ] + + if (process.platform === PLATFORM.MAC) { + items.unshift({ + label: app.getName(), + submenu: [], + }) + } + + const menu = Menu.buildFromTemplate((items as unknown) as MenuItem[]) + Menu.setApplicationMenu(menu) + + console.log("[MENU] started") +} diff --git a/src/main/makeTray.js b/src/main/makeTray.js deleted file mode 100644 index 63c3acb..0000000 --- a/src/main/makeTray.js +++ /dev/null @@ -1,38 +0,0 @@ -import { app, ipcMain, Menu, Tray } from 'electron' -import { join } from 'path' -import os from 'os' - -import { PLATFORM } from '../common/enum' - -import { version } from '../../package.json' - -const icon = join(__dirname, '../../assets/icons/tray/icon.png') - -export default (context) => { - const items = [ - { - label: `Hypha (${version})`, - click: () => { context.launchUI() } - }, - { type: 'separator' }, - { - label: 'Quit', - click: () => { app.quit() } - } - ] - const menu = Menu.buildFromTemplate(items) - - const tray = new Tray(icon) - tray.setContextMenu(menu) - - if (os.platform() !== PLATFORM.MAC) { - tray.on('click', event => { - event.preventDefault() - tray.popUpContextMenu() - }) - } - - context.tray = tray - - console.log('[TRAY] started') -} diff --git a/src/main/makeTray.ts b/src/main/makeTray.ts new file mode 100644 index 0000000..0e1ea29 --- /dev/null +++ b/src/main/makeTray.ts @@ -0,0 +1,42 @@ +import { app, ipcMain, Menu, Tray, MenuItem } from "electron" +import { join } from "path" +import os from "os" + +import { PLATFORM } from "../common/enum" + +import { version } from "../../package.json" + +const icon = join(__dirname, "../../assets/icons/tray/icon.png") + +export default (context) => { + const items = [ + { + label: `Hypha (${version})`, + click: () => { + context.launchUI() + }, + }, + { type: "separator" }, + { + label: "Quit", + click: () => { + app.quit() + }, + }, + ] + const menu = Menu.buildFromTemplate((items as unknown) as MenuItem[]) + + const tray = new Tray(icon) + tray.setContextMenu(menu) + + if (os.platform() !== PLATFORM.MAC) { + tray.on("click", (event) => { + event.preventDefault() + tray.popUpContextMenu() + }) + } + + context.tray = tray + + console.log("[TRAY] started") +} diff --git a/src/main/makeUi.js b/src/main/makeUi.ts similarity index 53% rename from src/main/makeUi.js rename to src/main/makeUi.ts index 13212be..c238467 100644 --- a/src/main/makeUi.js +++ b/src/main/makeUi.ts @@ -1,11 +1,17 @@ -import { app, BrowserWindow, ipcMain, session } from 'electron' -import { join } from 'path' +import { + app, + BrowserWindow, + ipcMain, + session, + BrowserWindowConstructorOptions, +} from "electron" +import { join } from "path" -import { SIZE } from '../common/enum' +import { SIZE } from "../common/enum" // Electron reload shoule be used only in development -if (process.env.NODE_ENV === 'development') { - require('electron-reload')(join(__dirname, '../../assets/ui')) +if (process.env.NODE_ENV === "development") { + require("electron-reload")(join(__dirname, "../../assets/ui")) } const { WIDTH, HEIGHT } = SIZE @@ -18,34 +24,34 @@ const config = { autoHideMenuBar: true, fullscreenWindowTitle: true, show: false, - title: 'Hypha', - titleBarStyle: 'hiddenInset', + title: "Hypha", + titleBarStyle: "hiddenInset", webPreferences: { allowRunningInsecureContent: false, - preload: join(__dirname, 'preloadUi.js'), + preload: join(__dirname, "preloadUi.js"), nodeIntegration: true, - webSecurity: false // TODO - } -} + webSecurity: false, // TODO + }, +} as BrowserWindowConstructorOptions const initWindow = () => { const window = new BrowserWindow(config) - window.on('close', event => { + window.on("close", (event) => { event.preventDefault() if (app.dock) { app.dock.hide() } window.hide() - console.log('[UI] hidden') + console.log("[UI] hidden") }) return window } -export default async context => { - const window = initWindow(context) - const uiPath = 'file://' + join(__dirname, '../../assets/ui', 'index.html') +export default (context) => { + const window = initWindow() + const uiPath = "file://" + join(__dirname, "../../assets/ui", "index.html") let apiAddress = null context.webui = window @@ -59,19 +65,19 @@ export default async context => { } } - app.on('before-quit', () => { - window.removeAllListeners('close') + app.on("before-quit", () => { + window.removeAllListeners("close") }) session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => { - delete details.requestHeaders['Origin'] + delete details.requestHeaders["Origin"] callback({ cancel: false, requestHeaders: details.requestHeaders }) }) - return new Promise(resolve => { + return new Promise((resolve) => { window.loadURL(uiPath) - window.once('ready-to-show', () => { - console.log('[UI] started') + window.once("ready-to-show", () => { + console.log("[UI] started") window.show() window.focus() diff --git a/src/main/preloadUi.js b/src/main/preloadUi.js deleted file mode 100644 index 381a78e..0000000 --- a/src/main/preloadUi.js +++ /dev/null @@ -1,10 +0,0 @@ -// Make window draggable -document.addEventListener('DOMContentLoaded', event => { - const windowTopBar = document.createElement('div') - windowTopBar.style.width = '100%' - windowTopBar.style.height = '32px' - windowTopBar.style.position = 'absolute' - windowTopBar.style.top = windowTopBar.style.left = 0 - windowTopBar.style.webkitAppRegion = 'drag' - document.body.appendChild(windowTopBar) -}) diff --git a/src/main/preloadUi.ts b/src/main/preloadUi.ts new file mode 100644 index 0000000..9680f06 --- /dev/null +++ b/src/main/preloadUi.ts @@ -0,0 +1,10 @@ +// Make window draggable +document.addEventListener("DOMContentLoaded", (event) => { + const windowTopBar = document.createElement("div") + windowTopBar.style.width = "100%" + windowTopBar.style.height = "32px" + windowTopBar.style.position = "absolute" + windowTopBar.style.top = windowTopBar.style.left = "0" + windowTopBar.style["-webkit-app-region"] = "drag" + document.body.appendChild(windowTopBar) +}) diff --git a/src/main/runDaemon.js b/src/main/runDaemon.ts similarity index 50% rename from src/main/runDaemon.js rename to src/main/runDaemon.ts index 7bd3ca2..df87ec0 100644 --- a/src/main/runDaemon.js +++ b/src/main/runDaemon.ts @@ -1,12 +1,11 @@ -import { app, ipcMain } from 'electron' -import fs from 'fs-extra' -import { join } from 'path' +import { app, ipcMain } from "electron" +import fs from "fs-extra" +import { join } from "path" -import createIpfsDaemon from '../common/daemon' -import store from '../common/store' +import createIpfsDaemon from "../common/daemon" +import store from "../common/store" export default async (context) => { - let daemon = null let online = null @@ -17,16 +16,18 @@ export default async (context) => { return undefined } - const config = store.get('ipfsConfig') + const config = store.get("ipfsConfig") as + | undefined + | { [key: string]: string } try { daemon = await createIpfsDaemon(config) if (!config.path) { config.path = daemon.repoPath - store.set('ipfsConfig', config) + store.set("ipfsConfig", config) } - console.log('[IPFS] started') + console.log("[IPFS] started") } catch (error) { - console.log('[IPFS]', error) + console.log("[IPFS]", error) } } @@ -35,20 +36,20 @@ export default async (context) => { return undefined } - if (!fs.existsSync(join(daemon.repoPath, 'config'))) { + if (!fs.existsSync(join(daemon.repoPath, "config"))) { daemon = null return undefined } - return new Promise(resolve => { + return new Promise((resolve) => { const ipfsDaemon = daemon daemon = null - ipfsDaemon.stop(180, error => { + ipfsDaemon.stop(180, (error) => { if (error) { - console.log('[IPFS] ', error) + console.log("[IPFS] ", error) return resolve(error) } - console.log('[IPFS] stopped') + console.log("[IPFS] stopped") resolve() }) }) @@ -59,15 +60,15 @@ export default async (context) => { await start() } - ipcMain.on('stopIpfs', stop) - ipcMain.on('startIpfs', start) - ipcMain.on('restartIpfs', restart) - ipcMain.on('ipfsConfigChanged', restart) - app.on('before-quit', stop) + ipcMain.on("stopIpfs", stop) + ipcMain.on("startIpfs", start) + ipcMain.on("restartIpfs", restart) + ipcMain.on("ipfsConfigChanged", restart) + app.on("before-quit", stop) await start() - ipcMain.on('online-status-changed', (_, isOnline) => { + ipcMain.on("online-status-changed", (_, isOnline) => { if (online === false && daemon && isOnline) { restart() } diff --git a/src/main/server/index.js b/src/main/server/index.js deleted file mode 100644 index 955bb23..0000000 --- a/src/main/server/index.js +++ /dev/null @@ -1,34 +0,0 @@ -import { ipcMain } from 'electron' -// NOTE: The library ships with a fork of apollo-link-schema that also supports subscriptions. -import { createSchemaLink, createIpcExecutor } from 'graphql-transport-electron' -import { - makeRemoteExecutableSchema, - introspectSchema, - mergeSchemas -} from 'graphql-tools' -import { HttpLink } from 'apollo-link-http' -import fetch from 'node-fetch' - -import localSchema from './schema' - -export default async context => { - const remoteUrl = 'https://server-test.matters.news/graphql' - const remoteLink = new HttpLink({ - uri: remoteUrl, - fetch - }) - // Introspect schema - const remoteSchema = await introspectSchema(remoteLink) - // Make remote executable schema - const remoteExecutableSchema = makeRemoteExecutableSchema({ - schema: remoteSchema, - remoteLink - }) - const schema = mergeSchemas({ - schemas: [remoteExecutableSchema, localSchema] - }) - - const link = createSchemaLink({ schema }) - createIpcExecutor({ link, ipc: ipcMain }) - console.log('[SERVER] started') -} diff --git a/src/main/server/queries/article.js b/src/main/server/queries/article.js deleted file mode 100644 index d5dbf51..0000000 --- a/src/main/server/queries/article.js +++ /dev/null @@ -1,30 +0,0 @@ -import * as cheerio from 'cheerio' -import { loadArticle } from '../../../common/ipfs' - -export default { - Query: { - article: async (root, { input: { hash } }) => { - const content = await loadArticle(hash) - return { content, hash } - } - }, - // get meta data from html for now - Article: { - title: ({ content }) => - cheerio - .load(content)('title') - .text() || '-', - summary: ({ content }) => - cheerio - .load(content)("meta[name='description']") - .attr('content') || '-', - author: ({ content }) => - cheerio - .load(content)("meta[property='article:author']") - .attr('content') || '-', - createdAt: ({ content }) => - cheerio - .load(content)("time[itemprop='datePublished']") - .attr('datetime') || '-' - } -} diff --git a/src/main/server/queries/index.js b/src/main/server/queries/index.js deleted file mode 100644 index db16eb2..0000000 --- a/src/main/server/queries/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import { merge } from 'lodash' -// local -import user from './user' -import article from './article' - -export default merge(article, user) diff --git a/src/main/server/queries/user.js b/src/main/server/queries/user.js deleted file mode 100644 index fa0efe4..0000000 --- a/src/main/server/queries/user.js +++ /dev/null @@ -1,45 +0,0 @@ -import { ipfs, loadArticle } from '../../../common/ipfs' - -export default { - Query: { - viewer: async (root, args, context) => { - const pinset = await ipfs.pin.ls() - - // resolve subcription for local user - // use pinset as subscriptions for now - const subscriptionEdges = (await Promise.all( - pinset - .filter(({ type }) => type !== 'indirect') - .map(async data => { - try { - const { hash } = data - - // Parse entry html - const content = await loadArticle(hash) - return { cursor: hash, node: { content, hash } } - } catch (error) { - console.error(error) - return null - } - }) - )).filter(edge => edge.node.content) - - const totalCount = subscriptionEdges.length - - return { - subscriptions: { - totalCount, - pageInfo: { - startCursor: totalCount ? subscriptionEdges[0].cursor : null, - endCursor: totalCount - ? subscriptionEdges[totalCount - 1].cursor - : null, - hasNextPage: false, - hasPreviousPage: false - }, - edges: subscriptionEdges - } - } - } - } -} diff --git a/src/main/server/schema.js b/src/main/server/schema.js deleted file mode 100644 index c619bf0..0000000 --- a/src/main/server/schema.js +++ /dev/null @@ -1,11 +0,0 @@ -import { makeExecutableSchema } from 'graphql-tools' - -import typeDefs from './types' -import queries from './queries' - -const schema = makeExecutableSchema({ - typeDefs, - resolvers: queries -}) - -export default schema diff --git a/src/main/server/types/article.js b/src/main/server/types/article.js deleted file mode 100644 index 68d530d..0000000 --- a/src/main/server/types/article.js +++ /dev/null @@ -1,40 +0,0 @@ -export default /* GraphQL */ ` - extend type Query { - article(input: ArticleInput!): Article - } - - input ArticleInput { - hash: String - } - - type Article { - "Author of this article." - author: String! - - "Article title." - title: String! - - "A short summary for this article." - summary: String! - - "IPFS hash of this article." - hash: String! - - "Content of this article." - content: String! - - "Publish timestamp." - createdAt: String! - } - - type ArticleConnection implements Connection { - totalCount: Int! - pageInfo: PageInfo! - edges: [ArticleEdge!] - } - - type ArticleEdge { - cursor: String! - node: Article! - } -` diff --git a/src/main/server/types/index.js b/src/main/server/types/index.js deleted file mode 100644 index 9f9f562..0000000 --- a/src/main/server/types/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import article from './article' -import user from './user' - -const Root = /* GraphQL */ ` - type Query - - type PageInfo { - startCursor: String - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - } - - interface Connection { - totalCount: Int! - pageInfo: PageInfo! - } - - input ConnectionArgs { - after: String - first: Int - } - - schema { - query: Query - } -` - -export default [Root, article, user] diff --git a/src/main/server/types/user.js b/src/main/server/types/user.js deleted file mode 100644 index ccdd0f4..0000000 --- a/src/main/server/types/user.js +++ /dev/null @@ -1,10 +0,0 @@ -export default /* GraphQL */ ` - extend type Query { - viewer: User - } - - type User { - "Artilces current user subscribed to." - subscriptions(input: ConnectionArgs): ArticleConnection! - } -` diff --git a/src/ui/common/apollo.js b/src/ui/common/apollo.js deleted file mode 100644 index b9c9e0a..0000000 --- a/src/ui/common/apollo.js +++ /dev/null @@ -1,10 +0,0 @@ -import { ApolloClient } from 'apollo-client' -import { InMemoryCache } from 'apollo-cache-inmemory' -import { ipcRenderer } from 'electron' -import { createIpcLink } from 'graphql-transport-electron' - -const link = createIpcLink({ ipc: ipcRenderer }) -export const client = new ApolloClient({ - cache: new InMemoryCache(), - link -}) diff --git a/src/ui/common/enum.js b/src/ui/common/enum.ts similarity index 100% rename from src/ui/common/enum.js rename to src/ui/common/enum.ts diff --git a/src/ui/components/App/index.js b/src/ui/components/App/index.js deleted file mode 100644 index 66c8089..0000000 --- a/src/ui/components/App/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react' -import { ApolloProvider } from '@apollo/react-hooks' - -import { client } from '../../common/apollo' -import { HashProvider } from '../Context/hash' -import { PageProvider } from '../Context/page' -import { Page } from '../Page' -import { SideMenu } from '../Menu/side' - -import css from './style.css' - -export const App = () => ( - - - -
- - -
-
-
-
-) diff --git a/src/ui/components/App/index.tsx b/src/ui/components/App/index.tsx new file mode 100644 index 0000000..d11b4e7 --- /dev/null +++ b/src/ui/components/App/index.tsx @@ -0,0 +1,19 @@ +import React from 'react' + +import { HashProvider } from '../Context/hash' +import { PageProvider } from '../Context/page' +import { Page } from '../Page' +import { SideMenu } from '../Menu/side' + +import * as css from './style.css' + +export const App = () => ( + + +
+ + +
+
+
+) diff --git a/src/ui/components/Bar/hash.js b/src/ui/components/Bar/hash.tsx similarity index 100% rename from src/ui/components/Bar/hash.js rename to src/ui/components/Bar/hash.tsx diff --git a/src/ui/components/Button/pin.js b/src/ui/components/Button/pin.tsx similarity index 100% rename from src/ui/components/Button/pin.js rename to src/ui/components/Button/pin.tsx diff --git a/src/ui/components/Context/hash.js b/src/ui/components/Context/hash.tsx similarity index 77% rename from src/ui/components/Context/hash.js rename to src/ui/components/Context/hash.tsx index 4fe908a..ab84c3b 100644 --- a/src/ui/components/Context/hash.js +++ b/src/ui/components/Context/hash.tsx @@ -2,12 +2,12 @@ import React, { createContext, useState } from 'react' export const HashContext = createContext({ hash: '', - setHash: hash => {} + setHash: (hash: string) => {} }) export const HashConsumer = HashContext.Consumer -export const HashProvider = ({ children, defaultHash }) => { +export const HashProvider = ({ children, defaultHash = '' }) => { const [hash, setHash] = useState(defaultHash) return ( diff --git a/src/ui/components/Context/page.js b/src/ui/components/Context/page.js deleted file mode 100644 index 805b898..0000000 --- a/src/ui/components/Context/page.js +++ /dev/null @@ -1,18 +0,0 @@ -import React, { createContext, useEffect, useState } from 'react' - -export const PageContext = createContext({ - page: 'explore', - setPage: hash => {} -}) - -export const PageConsumer = PageContext.Consumer - -export const PageProvider = ({ children, defaultPage }) => { - const [page, setPage] = useState(defaultPage || 'explore') - - return ( - - {children} - - ) -} diff --git a/src/ui/components/Context/page.tsx b/src/ui/components/Context/page.tsx new file mode 100644 index 0000000..5477253 --- /dev/null +++ b/src/ui/components/Context/page.tsx @@ -0,0 +1,20 @@ +import React, { createContext, useState } from 'react' + +// export type PageNameType = "explore" | "article" | "peers" | "bootstrap" + +export const PageContext = createContext({ + page: 'explore', + setPage: (hash: string) => {} +}) + +export const PageConsumer = PageContext.Consumer + +export const PageProvider = ({ children, defaultPage = 'explore' }) => { + const [page, setPage] = useState(defaultPage) + + return ( + + {children} + + ) +} diff --git a/src/ui/components/Hooks/useInterval.js b/src/ui/components/Hooks/useInterval.tsx similarity index 100% rename from src/ui/components/Hooks/useInterval.js rename to src/ui/components/Hooks/useInterval.tsx diff --git a/src/ui/components/Menu/side.css b/src/ui/components/Menu/side.css index 6e5e1ca..bb33bd5 100644 --- a/src/ui/components/Menu/side.css +++ b/src/ui/components/Menu/side.css @@ -6,7 +6,7 @@ flex-direction: column; flex-shrink: 0; - background-color: #FDF5ED; + background-color: #fdf5ed; box-shadow: inset -7px 0 9px -7px rgba(244, 207, 169, 0.5); width: 14rem; max-width: 14rem; @@ -41,11 +41,11 @@ } .menu li:not(.active):hover { - color: #EF8A73; + color: #ef8a73; } .menu li:not(.active):hover .icon { - color: #EF8A73; + color: #ef8a73; } .active { diff --git a/src/ui/components/Menu/side.js b/src/ui/components/Menu/side.js deleted file mode 100644 index 7cd86fc..0000000 --- a/src/ui/components/Menu/side.js +++ /dev/null @@ -1,54 +0,0 @@ -import React, { useContext } from 'react' -import { FileText, GitCommit, Radio, Search } from 'react-feather' - -import { PageContext } from '../Context/page' - -import css from './side.css' - -export const SideMenu = () => { - const { page, setPage } = useContext(PageContext) - - const click = target => { - if (target === page) { - return undefined - } - setPage(target) - } - - return ( -
-
Hypha
-
-
  • click('explore')} - > - - Explore -
  • -
  • click('articles')} - > - - Articles -
  • -
  • click('peers')} - > - - Peers -
  • -
  • click('bootstrap')} - > - - Bootstrap -
  • -
    -
    Version: 0.1.1
    -
    - ) -} diff --git a/src/ui/components/Menu/side.tsx b/src/ui/components/Menu/side.tsx new file mode 100644 index 0000000..bbc5406 --- /dev/null +++ b/src/ui/components/Menu/side.tsx @@ -0,0 +1,46 @@ +import React, { useContext } from 'react' +import { FileText, GitCommit, Radio, Search } from 'react-feather' + +import { PageContext } from '../Context/page' + +import css from './side.css' + +export const SideMenu = () => { + const pageItems = [ + { name: 'Explore', Icon: Search }, + { name: 'Articles', Icon: FileText }, + { name: 'Peers', Icon: Radio }, + { name: 'Bootstrap', Icon: GitCommit } + ] + + const { page, setPage } = useContext(PageContext) + + const click = target => { + if (target === page) { + return undefined + } + setPage(target) + } + + const Item = ({ name, Icon }) => { + const key = name.toLowerCase() + return ( +
  • click(key)}> + + {name} +
  • + ) + } + + return ( +
    +
    Hypha
    +
    + {pageItems.map(({ name, Icon }, i) => ( + + ))} +
    +
    Version: 0.1.1
    +
    + ) +} diff --git a/src/ui/components/Page/articles.js b/src/ui/components/Page/articles.js deleted file mode 100644 index 096015e..0000000 --- a/src/ui/components/Page/articles.js +++ /dev/null @@ -1,92 +0,0 @@ -import React, { useContext } from 'react' -import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' - -import { Spinner } from '../Spinner' -import css from './articles.css' -import { HashContext } from '../Context/hash' -import { PageContext } from '../Context/page' - -export const Articles = () => { - const SUBSCRIPTIONS = gql` - { - viewer { - subscriptions { - edges { - node { - title - author - createdAt - hash - } - } - } - } - } - ` - const { setPage } = useContext(PageContext) - const { setHash } = useContext(HashContext) - - const { loading, error, data } = useQuery(SUBSCRIPTIONS) - - if (loading) { - return ( - - ) - } - - if (error) { - return
    {JSON.stringify(error)}
    - } - - return ( -
    - {data.viewer.subscriptions.edges.map(({ node }, index) => { - const timestamp = new Date(node.createdAt) - const createdAt = isNaN(timestamp.getTime()) - ? '-' - : timestamp.toLocaleDateString('en-US', { - year: 'numeric', - month: 'short', - day: 'numeric' - }) - - const { author, title, hash } = node - return ( -
    - { - setPage('explore') - setHash(hash) - }} - style={{ - cursor: 'pointer' - }} - > - {title} - - - {author} - {createdAt} -
    - ) - })} -
    - ) -} diff --git a/src/ui/components/Page/articles.tsx b/src/ui/components/Page/articles.tsx new file mode 100644 index 0000000..04e48dd --- /dev/null +++ b/src/ui/components/Page/articles.tsx @@ -0,0 +1,73 @@ +import React, { useContext } from 'react' + +import { Spinner } from '../Spinner' +import css from './articles.css' +import { HashContext } from '../Context/hash' +import { PageContext } from '../Context/page' + +export const Articles = () => { + const { setPage } = useContext(PageContext) + const { setHash } = useContext(HashContext) + + // if (loading) { + // return ( + // + // ) + // } + + // if (error) { + // return
    {JSON.stringify(error)}
    + // } + + return ( +
    + {/* {data.viewer.subscriptions.edges.map(({ node }, index) => { + const timestamp = new Date(node.createdAt) + const createdAt = isNaN(timestamp.getTime()) + ? "-" + : timestamp.toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + }) + + const { author, title, hash } = node + return ( +
    + { + setPage("explore") + setHash(hash) + }} + style={{ + cursor: "pointer", + }} + > + {title} + + + {author} + {createdAt} +
    + ) + })} */} +
    + ) +} diff --git a/src/ui/components/Page/bootstrap.css b/src/ui/components/Page/bootstrap.css index 1cbaf3c..6449187 100644 --- a/src/ui/components/Page/bootstrap.css +++ b/src/ui/components/Page/bootstrap.css @@ -9,7 +9,7 @@ flex-direction: column; border-bottom: 1px solid rgba(239, 232, 227, 0.6); - color: #725B54; + color: #725b54; font-size: 0.9rem; width: 100%; padding: 2rem 0 1rem 2rem; @@ -33,10 +33,10 @@ } .node { - background-color: #FFFFFF; + background-color: #ffffff; border-radius: 0.5rem; border: 1.5px solid rgba(250, 232, 214, 0.95); - color: #725B54; + color: #725b54; font-size: 0.9rem; width: 100%; height: 2.4rem; @@ -51,12 +51,12 @@ } .nodesIcon { - color: #725B54; + color: #725b54; margin: 0 0.5rem 0 0; } .nodesText { - color: #D86143; + color: #d86143; margin: 0 0 0 0.3rem; } @@ -65,7 +65,7 @@ } .trash:hover { - color: #EF8A73; + color: #ef8a73; } .spinner { @@ -81,7 +81,7 @@ } .head { - color: #725B54; + color: #725b54; font-size: 0.9rem; font-weight: bold; width: 100%; @@ -118,5 +118,5 @@ } .body td:last-child { - text-align: center; + text-align: center; } diff --git a/src/ui/components/Page/bootstrap.js b/src/ui/components/Page/bootstrap.tsx similarity index 91% rename from src/ui/components/Page/bootstrap.js rename to src/ui/components/Page/bootstrap.tsx index 7c348ad..b1f2873 100644 --- a/src/ui/components/Page/bootstrap.js +++ b/src/ui/components/Page/bootstrap.tsx @@ -10,8 +10,8 @@ import css from './bootstrap.css' const NodesTable = ({ nodes, remove }) => { const rows = nodes.map((node, index) => ( - {node} - + {node} + remove(node)} /> @@ -22,8 +22,8 @@ const NodesTable = ({ nodes, remove }) => { - - + +
    Boostrap nodes addressBoostrap nodes address
    @@ -107,6 +107,7 @@ export const Bootstrap = () => { {loading && ( { {hash && loading && ( { const rows = peers.map((peer, index) => ( - {peer.peer._idB58String} - {peer.addr.toString('utf8')} + {peer.peer._idB58String} + {peer.addr.toString('utf8')} )) @@ -20,8 +20,8 @@ const PeersTable = ({ peers }) => { - - + +
    Peer IDAddressPeer IDAddress
    @@ -78,6 +78,7 @@ export const Peers = () => { {loading && ( { - if (!env || env === 'development') { +module.exports = (env) => { + if (!env || env === "development") { return { ...config, - mode: 'development', + + mode: "development", + + devtool: "source-map", + plugins: [ ...config.plugins, new WebpackBuildNotifierPlugin({ - title: 'Hypha Build' - }) + title: "Hypha Build", + }), ], watch: true, watchOptions: { - aggregateTimeout: 500 - } + aggregateTimeout: 500, + }, } } + return config }