From 10ec12fe7f5b0b4be76664f978ebed353e9dd90e Mon Sep 17 00:00:00 2001 From: cha0s Date: Sun, 28 Jun 2020 08:15:23 -0500 Subject: [PATCH] flow: real resources! --- package.json | 2 + src/client/app.jsx | 12 +- src/client/hooks/useSocket.js | 12 + src/client/index.jsx | 1 + src/client/index.scss | 1 - ...react-sortable-tree-theme-file-explorer.js | 796 ++++++++++++++++++ src/client/resources.jsx | 28 +- src/client/sidebar.jsx | 88 +- src/client/sidebar.raw.scss | 7 +- src/client/state.js | 49 ++ src/client/store.js | 2 + src/common/packets/resources.packet.js | 12 + src/server/sockets/index.js | 33 +- yarn.lock | 178 +++- 14 files changed, 1192 insertions(+), 29 deletions(-) create mode 100644 src/client/hooks/useSocket.js create mode 100644 src/client/react-sortable-tree-theme-file-explorer.js create mode 100644 src/client/state.js create mode 100644 src/common/packets/resources.packet.js diff --git a/package.json b/package.json index 5aa169e..2c58ab0 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "express": "^4.17.1", "express-session": "^1.17.1", "express-socket.io-session": "^1.3.5", + "glob": "^7.1.6", "html-entities": "1.3.1", "immer": "^7.0.1", "lodash.debounce": "^4.0.8", @@ -47,6 +48,7 @@ "react-hot-loader": "^4.12.21", "react-markdown": "^4.3.1", "react-redux": "^7.2.0", + "react-sortable-tree": "^2.7.1", "react-tabs": "^3.1.1", "redux": "^4.0.5", "scwp": "1.x", diff --git a/src/client/app.jsx b/src/client/app.jsx index 13483db..3b8182a 100644 --- a/src/client/app.jsx +++ b/src/client/app.jsx @@ -4,11 +4,16 @@ import {hot} from 'react-hot-loader'; import classnames from 'classnames'; import React, {useState} from 'react'; +import {useSelector} from 'react-redux'; import TypeRenderersContext from './context/typeRenderers'; import Resources from './resources'; import Sidebar from './sidebar'; import SidebarIcons from './sidebar-icons'; +import { + activeResourceUriSelector, + resourceUrisSelector, +} from './state'; import {all as allTypeRenderers} from './type-renderers.scwp'; const typeRenderMap = () => Object.values(allTypeRenderers()).reduce((r, M) => { @@ -18,6 +23,8 @@ const typeRenderMap = () => Object.values(allTypeRenderers()).reduce((r, M) => { const App = () => { const [typeRenderers] = useState(typeRenderMap()); + const activeResourceUri = useSelector(activeResourceUriSelector); + const resourceUris = useSelector(resourceUrisSelector); const [activeSidebar, setActiveSidebar] = useState(0); const [isSidebarExpanded, setIsSidebarExpanded] = useState(true); return ( @@ -48,7 +55,10 @@ const App = () => { {'sidebar-expanded': isSidebarExpanded}, )} > - + diff --git a/src/client/hooks/useSocket.js b/src/client/hooks/useSocket.js new file mode 100644 index 0000000..a3d2d10 --- /dev/null +++ b/src/client/hooks/useSocket.js @@ -0,0 +1,12 @@ +import {useEffect} from 'react'; + +import {SocketClient} from '@avocado/net/client/socket'; + +const frontendOrigin = window.location.href; +const isSecure = 'https' === frontendOrigin.substr(0, 5); +export const socket = new SocketClient(frontendOrigin, {secure: isSecure}); + +export default function useSocket(fn) { + // eslint-disable-next-line react-hooks/exhaustive-deps + useEffect(() => fn(socket), []); +} diff --git a/src/client/index.jsx b/src/client/index.jsx index 71f9e9a..973660f 100644 --- a/src/client/index.jsx +++ b/src/client/index.jsx @@ -1,4 +1,5 @@ import './index.scss'; + import 'react-hot-loader'; import {enableMapSet} from 'immer'; diff --git a/src/client/index.scss b/src/client/index.scss index 4e9e477..a9e2d54 100644 --- a/src/client/index.scss +++ b/src/client/index.scss @@ -140,7 +140,6 @@ select { } *:focus { - position: relative; box-shadow: 0 0 2px 0 var(--active-color); outline: none; z-index: 1; diff --git a/src/client/react-sortable-tree-theme-file-explorer.js b/src/client/react-sortable-tree-theme-file-explorer.js new file mode 100644 index 0000000..cad9ecc --- /dev/null +++ b/src/client/react-sortable-tree-theme-file-explorer.js @@ -0,0 +1,796 @@ +!function(root, factory) { + "object" == typeof exports && "object" == typeof module ? module.exports = factory() : "function" == typeof define && define.amd ? define([], factory) : "object" == typeof exports ? exports.ReactSortableTreeThemeFileExplorer = factory() : root.ReactSortableTreeThemeFileExplorer = factory(); +}("undefined" != typeof self ? self : this, function() { + /******/ + return function(modules) { + /******/ + /******/ + // 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: !1, + /******/ + exports: {} + }; + /******/ + /******/ + // Return the exports of the module + /******/ + /******/ + /******/ + // Execute the module function + /******/ + /******/ + /******/ + // Flag the module as loaded + /******/ + return modules[moduleId].call(module.exports, module, module.exports, __webpack_require__), + module.l = !0, module.exports; + } + // webpackBootstrap + /******/ + // The module cache + /******/ + var installedModules = {}; + /******/ + /******/ + // Load entry module and return exports + /******/ + /******/ + /******/ + /******/ + // expose the modules object (__webpack_modules__) + /******/ + /******/ + /******/ + // expose the module cache + /******/ + /******/ + /******/ + // define getter function for harmony exports + /******/ + /******/ + /******/ + // getDefaultExport function for compatibility with non-harmony modules + /******/ + /******/ + /******/ + // Object.prototype.hasOwnProperty.call + /******/ + /******/ + /******/ + // __webpack_public_path__ + /******/ + return __webpack_require__.m = modules, __webpack_require__.c = installedModules, + __webpack_require__.d = function(exports, name, getter) { + /******/ + __webpack_require__.o(exports, name) || /******/ + Object.defineProperty(exports, name, { + /******/ + configurable: !1, + /******/ + enumerable: !0, + /******/ + get: getter + }); + }, __webpack_require__.n = function(module) { + /******/ + var getter = module && module.__esModule ? /******/ + function() { + return module.default; + } : /******/ + function() { + return module; + }; + /******/ + /******/ + return __webpack_require__.d(getter, "a", getter), getter; + }, __webpack_require__.o = function(object, property) { + return Object.prototype.hasOwnProperty.call(object, property); + }, __webpack_require__.p = "", __webpack_require__(__webpack_require__.s = 4); + }([ /* 0 */ + /***/ + function(module, exports) { + module.exports = require("react"); + }, /* 1 */ + /***/ + function(module, exports) { + module.exports = require("prop-types"); + }, /* 2 */ + /***/ + function(module, exports) { + function cssWithMappingToString(item, useSourceMap) { + var content = item[1] || "", cssMapping = item[3]; + if (!cssMapping) return content; + if (useSourceMap && "function" == typeof btoa) { + var sourceMapping = toComment(cssMapping); + return [ content ].concat(cssMapping.sources.map(function(source) { + return "/*# sourceURL=" + cssMapping.sourceRoot + source + " */"; + })).concat([ sourceMapping ]).join("\n"); + } + return [ content ].join("\n"); + } + // Adapted from convert-source-map (MIT) + function toComment(sourceMap) { + return "/*# sourceMappingURL=data:application/json;charset=utf-8;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */"; + } + /* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + // css base code, injected by the css-loader + module.exports = function(useSourceMap) { + var list = []; + // return the list of modules as css string + // import a list of modules into the list + return list.toString = function() { + return this.map(function(item) { + var content = cssWithMappingToString(item, useSourceMap); + return item[2] ? "@media " + item[2] + "{" + content + "}" : content; + }).join(""); + }, list.i = function(modules, mediaQuery) { + "string" == typeof modules && (modules = [ [ null, modules, "" ] ]); + for (var alreadyImportedModules = {}, i = 0; i < this.length; i++) { + var id = this[i][0]; + "number" == typeof id && (alreadyImportedModules[id] = !0); + } + for (i = 0; i < modules.length; i++) { + var item = modules[i]; + // skip already imported module + // this implementation is not 100% perfect for weird media query combinations + // when a module is imported multiple times with different media queries. + // I hope this will never occur (Hey this way we have smaller bundles) + "number" == typeof item[0] && alreadyImportedModules[item[0]] || (mediaQuery && !item[2] ? item[2] = mediaQuery : mediaQuery && (item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"), + list.push(item)); + } + }, list; + }; + }, /* 3 */ + /***/ + function(module, exports, __webpack_require__) { + function addStylesToDom(styles, options) { + for (var i = 0; i < styles.length; i++) { + var item = styles[i], domStyle = stylesInDom[item.id]; + if (domStyle) { + domStyle.refs++; + for (var j = 0; j < domStyle.parts.length; j++) domStyle.parts[j](item.parts[j]); + for (;j < item.parts.length; j++) domStyle.parts.push(addStyle(item.parts[j], options)); + } else { + for (var parts = [], j = 0; j < item.parts.length; j++) parts.push(addStyle(item.parts[j], options)); + stylesInDom[item.id] = { + id: item.id, + refs: 1, + parts: parts + }; + } + } + } + function listToStyles(list, options) { + for (var styles = [], newStyles = {}, i = 0; i < list.length; i++) { + var item = list[i], id = options.base ? item[0] + options.base : item[0], css = item[1], media = item[2], sourceMap = item[3], part = { + css: css, + media: media, + sourceMap: sourceMap + }; + newStyles[id] ? newStyles[id].parts.push(part) : styles.push(newStyles[id] = { + id: id, + parts: [ part ] + }); + } + return styles; + } + function insertStyleElement(options, style) { + var target = getElement(options.insertInto); + if (!target) throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid."); + var lastStyleElementInsertedAtTop = stylesInsertedAtTop[stylesInsertedAtTop.length - 1]; + if ("top" === options.insertAt) lastStyleElementInsertedAtTop ? lastStyleElementInsertedAtTop.nextSibling ? target.insertBefore(style, lastStyleElementInsertedAtTop.nextSibling) : target.appendChild(style) : target.insertBefore(style, target.firstChild), + stylesInsertedAtTop.push(style); else if ("bottom" === options.insertAt) target.appendChild(style); else { + if ("object" != typeof options.insertAt || !options.insertAt.before) throw new Error("[Style Loader]\n\n Invalid value for parameter 'insertAt' ('options.insertAt') found.\n Must be 'top', 'bottom', or Object.\n (https://github.com/webpack-contrib/style-loader#insertat)\n"); + var nextSibling = getElement(options.insertInto + " " + options.insertAt.before); + target.insertBefore(style, nextSibling); + } + } + function removeStyleElement(style) { + if (null === style.parentNode) return !1; + style.parentNode.removeChild(style); + var idx = stylesInsertedAtTop.indexOf(style); + idx >= 0 && stylesInsertedAtTop.splice(idx, 1); + } + function createStyleElement(options) { + var style = document.createElement("style"); + return options.attrs.type = "text/css", addAttrs(style, options.attrs), insertStyleElement(options, style), + style; + } + function createLinkElement(options) { + var link = document.createElement("link"); + return options.attrs.type = "text/css", options.attrs.rel = "stylesheet", addAttrs(link, options.attrs), + insertStyleElement(options, link), link; + } + function addAttrs(el, attrs) { + Object.keys(attrs).forEach(function(key) { + el.setAttribute(key, attrs[key]); + }); + } + function addStyle(obj, options) { + var style, update, remove, result; + // If a transform function was defined, run it on the css + if (options.transform && obj.css) { + if (!(result = options.transform(obj.css))) // If the transform function returns a falsy value, don't add this css. + // This allows conditional loading of css + return function() {}; + // If transform returns a value, use that instead of the original css. + // This allows running runtime transformations on the css. + obj.css = result; + } + if (options.singleton) { + var styleIndex = singletonCounter++; + style = singleton || (singleton = createStyleElement(options)), update = applyToSingletonTag.bind(null, style, styleIndex, !1), + remove = applyToSingletonTag.bind(null, style, styleIndex, !0); + } else obj.sourceMap && "function" == typeof URL && "function" == typeof URL.createObjectURL && "function" == typeof URL.revokeObjectURL && "function" == typeof Blob && "function" == typeof btoa ? (style = createLinkElement(options), + update = updateLink.bind(null, style, options), remove = function() { + removeStyleElement(style), style.href && URL.revokeObjectURL(style.href); + }) : (style = createStyleElement(options), update = applyToTag.bind(null, style), + remove = function() { + removeStyleElement(style); + }); + return update(obj), function(newObj) { + if (newObj) { + if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap) return; + update(obj = newObj); + } else remove(); + }; + } + function applyToSingletonTag(style, index, remove, obj) { + var css = remove ? "" : obj.css; + if (style.styleSheet) style.styleSheet.cssText = replaceText(index, css); else { + var cssNode = document.createTextNode(css), childNodes = style.childNodes; + childNodes[index] && style.removeChild(childNodes[index]), childNodes.length ? style.insertBefore(cssNode, childNodes[index]) : style.appendChild(cssNode); + } + } + function applyToTag(style, obj) { + var css = obj.css, media = obj.media; + if (media && style.setAttribute("media", media), style.styleSheet) style.styleSheet.cssText = css; else { + for (;style.firstChild; ) style.removeChild(style.firstChild); + style.appendChild(document.createTextNode(css)); + } + } + function updateLink(link, options, obj) { + var css = obj.css, sourceMap = obj.sourceMap, autoFixUrls = void 0 === options.convertToAbsoluteUrls && sourceMap; + (options.convertToAbsoluteUrls || autoFixUrls) && (css = fixUrls(css)), sourceMap && (// http://stackoverflow.com/a/26603875 + css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */"); + var blob = new Blob([ css ], { + type: "text/css" + }), oldSrc = link.href; + link.href = URL.createObjectURL(blob), oldSrc && URL.revokeObjectURL(oldSrc); + } + /* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + var stylesInDom = {}, isOldIE = function(fn) { + var memo; + return function() { + return void 0 === memo && (memo = fn.apply(this, arguments)), memo; + }; + }(function() { + // Test for IE <= 9 as proposed by Browserhacks + // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805 + // Tests for existence of standard globals is to allow style-loader + // to operate correctly into non-standard environments + // @see https://github.com/webpack-contrib/style-loader/issues/177 + return window && document && document.all && !window.atob; + }), getElement = function(fn) { + var memo = {}; + return function(selector) { + if (void 0 === memo[selector]) { + var styleTarget = fn.call(this, selector); + // Special case to return head of iframe instead of iframe itself + if (styleTarget instanceof window.HTMLIFrameElement) try { + // This will throw an exception if access to iframe is blocked + // due to cross-origin restrictions + styleTarget = styleTarget.contentDocument.head; + } catch (e) { + styleTarget = null; + } + memo[selector] = styleTarget; + } + return memo[selector]; + }; + }(function(target) { + return document.querySelector(target); + }), singleton = null, singletonCounter = 0, stylesInsertedAtTop = [], fixUrls = __webpack_require__(8); + module.exports = function(list, options) { + if ("undefined" != typeof DEBUG && DEBUG && "object" != typeof document) throw new Error("The style-loader cannot be used in a non-browser environment"); + options = options || {}, options.attrs = "object" == typeof options.attrs ? options.attrs : {}, + // Force single-tag solution on IE6-9, which has a hard limit on the # of