diff --git a/.circleci/config.yml b/.circleci/config.yml index 13d339892..3c48e7d34 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,7 +27,7 @@ jobs: # fallback to using the latest cache if no exact match is found - v1-dependencies- - - node/install-npm + - run: sudo npm install -g npm@8.10.0 - node/install-packages: app-dir: ~/homebrewery cache-path: node_modules @@ -55,13 +55,19 @@ jobs: at: . # run tests! - - run: + - run: + name: Test - API Unit Tests + command: npm run test:api-unit + - run: name: Test - Basic command: npm run test:basic - - run: + - run: + name: Test - Coverage + command: npm run test:coverage + - run: name: Test - Mustache Spans command: npm run test:mustache-span - - run: + - run: name: Test - Routes command: npm run test:route @@ -71,4 +77,4 @@ workflows: - build - test: requires: - - build \ No newline at end of file + - build diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..28da2ef34 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +contact_links: + - name: /r/Homebrewery Subreddit + url: https://www.reddit.com/r/homebrewery + about: The Homebrewery community on Reddit! + - name: Discord of Many Things + url: https://discord.gg/domt + about: "Join the conversation in the #formatting channel on DoMT!" \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..b87b267e6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,17 @@ +name: Feature Request +description: Have an idea to improve the Homebrewery? Let us know! +labels: ["feature request"] +body: + - type: markdown + attributes: + value: "We'd love to hear your idea! Please be sure to [search the current Issues](https://github.com/naturalcrit/homebrewery/issues) for any duplicate requests." + - type: textarea + id: user-request + attributes: + label: "Your idea:" + description: The best feature requests provide an explanation of the current issue and then an explanation of how it could be improved. Screenshots/images can be pasted right in as well! + validations: + required: true + - type: markdown + attributes: + value: "Please be sure to search for any close matches to your request in the GitHub Issues tracker before opening a new request, thanks!" diff --git a/.github/ISSUE_TEMPLATE/general_issue.yml b/.github/ISSUE_TEMPLATE/general_issue.yml new file mode 100644 index 000000000..18c19254e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/general_issue.yml @@ -0,0 +1,55 @@ +name: General Issue +description: Report an issue unrelated to Saving +body: + - type: markdown + attributes: + value: Please include as much information as possible. + - type: dropdown + id: renderer + attributes: + label: Renderer + description: Which renderer does this issue occur on? If you are unsure, you can check the renderer in the Properties Editor (click the "i" in the Snippet Menu bar above the editor). + options: + - v3 + - Legacy + - Both + validations: + required: true + - type: dropdown + id: browser + attributes: + label: Browser + description: Which browser were you using when the issue occurred? + options: + - Chrome + - Firefox + - Edge + - Safari + - other + validations: + required: true + - type: dropdown + id: operating-system + attributes: + label: Operating System + description: Which OS were you using when the issue occurred? + options: + - Windows + - MacOS + - Linux + - other + validations: + required: true + - type: textarea + id: user-description + attributes: + label: "What happened?" + description: Please include any steps you took leading up to the issue and if you can reproduce it. Let us know what you expected to happen, and what did happen. + validations: + required: true + - type: textarea + id: code + attributes: + label: Code + description: Paste in any relevant code snippet below. + render: gfm \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/save_issue.yml b/.github/ISSUE_TEMPLATE/save_issue.yml new file mode 100644 index 000000000..c08f485ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/save_issue.yml @@ -0,0 +1,26 @@ +name: Saving Issue +description: Report an issue Saving +labels: ["Saving"] +body: + - type: markdown + attributes: + value: | + Woops, sorry there was an issue Saving. Please add any detail you can to this report and check back soon! + - type: textarea + id: error-code + attributes: + label: Error Code + render: shell + - type: textarea + id: user-description + attributes: + label: "Your description of what happened:" + validations: + required: true + - type: markdown + attributes: + value: | + Thanks for the report. Here are some steps that may help in the meantime: + 1. Refreshing your Google credentials in Homebrewery by signing out, and back in (they expire after one year). + 2. Waiting a few minutes and trying again - sometimes there is just a momentary blip in the server. + 3. Check the Issues in Github or the /r/homebrewery subreddit to see if others are experiencing the same issue. diff --git a/.gitignore b/.gitignore index 2f081e2dc..150d81008 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ config/docker.* todo.md startDB.bat startMViewer.bat +.vscode + +coverage diff --git a/README.md b/README.md index 35f0150d1..df7f41503 100644 --- a/README.md +++ b/README.md @@ -21,24 +21,29 @@ below. First, install three programs that The Homebrewery requires to run and retrieve updates: -1. install [node](https://nodejs.org/en/) +1. install [node](https://nodejs.org/en/), version v16 or higher. 1. install [mongodb](https://www.mongodb.com/try/download/community) (Community version) For the easiest installation, follow these steps: 1. In the installer, uncheck the option to run as a service. 1. You can install MongoDB Compass if you want a GUI to view your database documents. + 1. If you install any version over 6.0, you will have to install [MongoDB Shell](https://www.mongodb.com/try/download/shell). 1. Go to the C:\ drive and create a folder called "data". 1. Inside the "data" folder, create a new folder called "db". - 1. Open a command prompt or other terminal and navigate to your MongoDB install folder (C:\Program Files\Mongo\Server\4.4\bin). + 1. Open a command prompt or other terminal and navigate to your MongoDB install folder (C:\Program Files\Mongo\Server\6.0\bin). 1. In the command prompt, run "mongod", which will start up your local database server. 1. While MongoD is running, open a second command prompt and navigate to the MongoDB install folder. - 1. In the second command prompt, run "mongo", which allows you to edit the database. - 1. Type `use homebrewery` to create The Homebrewery database. You should see `switched to db homebrewery`. - 1. Type `db.brews.insert({"title":"test"})` to create a blank document. You should see `WriteResult({ "nInserted" : 1 })`. 1. Search in Windows for "Advanced system settings" and open it. 1. Click "Environment variables", find the "path" variable, and double-click to open it. 1. Click "New" and paste in the path to the MongoDB "bin" folder. 1. Click "OK" three times to close all the windows. + 1. In the second command prompt, run "mongo", which allows you to edit the database. + 1. Type `use homebrewery` to create The Homebrewery database. You should see `switched to db homebrewery`. + 1. Type `db.brews.insertOne({"title":"test"})` to create a blank document. You should see `{ +acknowledged: true, +insertedId: ObjectId("63c2fce9e5ac5a94fe2410cf") +}` + 1. install [git](https://git-scm.com/downloads) (select the option that allows Git to run from the command prompt). Checkout the repo ([documentation][github-clone-repo-docs-url]): @@ -51,11 +56,19 @@ git clone https://github.com/naturalcrit/homebrewery.git Second, you will need to add the environment variable `NODE_ENV=local` to allow the project to run locally. -You can set this temporarily in your shell of choice: +You can set this **temporarily** (until you close the terminal) in your shell of choice with admin privileges: * Windows Powershell: `$env:NODE_ENV="local"` * Windows CMD: `set NODE_ENV=local` * Linux / macOS: `export NODE_ENV=local` +If you want to add this variable **permanently** the steps are as follows: + 1. Search in Windows for "Advanced system settings" and open it. + 1. Click "Environment variables". + 1. In System Variables, click "New" + 1. Click "New" and write `NODE_ENV` as a name and `local` as the value. + 1. Click "OK" three times to close all the windows. + This can be undone at any time if needed. + Third, you will need to install the Node dependencies, compile the app, and run it using the two commands: @@ -65,6 +78,13 @@ it using the two commands: You should now be able to go to [http://localhost:8000](http://localhost:8000) in your browser and use The Homebrewery offline. +If you had any issue at all, here are some links that may be useful: +- [Course](https://learn.mongodb.com/courses/m103-basic-cluster-administration) on cluster administration, useful for beginners +- [Mongo community forums](https://www.mongodb.com/community/forums/) +- Useful Stack Overflow links for your most probable errors: [1](https://stackoverflow.com/questions/44962540/mongod-and-mongo-commands-not-working-on-windows-10), [2](https://stackoverflow.com/questions/15053893/mongo-command-not-recognized-when-trying-to-connect-to-a-mongodb-server/41507803#41507803), [3](https://stackoverflow.com/questions/51224959/mongo-is-not-recognized-as-an-internal-or-external-command-operable-program-o) + +If you still have problems, post in [Our Subreddit](https://www.reddit.com/r/homebrewery/) and we will help you. + ### Running the application via Docker Please see the docs here: [README.DOCKER.md](./README.DOCKER.md) diff --git a/changelog.md b/changelog.md index 9fed601f8..0260a1f44 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,23 @@ ```css +.beta { + color : white; + padding : 4px 6px; + line-height : 1em; + background : grey; + border-radius : 12px; + font-family : monospace; + font-size : 10px; + font-weight : 800; + margin-top : -5px; + margin-bottom : -5px; +} + +.fac { + height: 1em; + line-height: 2em; + margin-bottom: -0.05cm +} + h5 { font-size: .35cm !important; } @@ -7,6 +26,11 @@ h5 { margin-left: 0px; } +.page .taskList { + display:block; + break-inside:auto; +} + .taskList li input { list-style-type : none; margin-left : -0.52cm; @@ -14,6 +38,11 @@ h5 { filter: brightness(1.1) drop-shadow(1px 2px 1px #222); } +.taskList ul { + margin-bottom: 0px; + margin-top: 0px; +} + .taskList li input[checked] { filter: sepia(100%) hue-rotate(60deg) saturate(3.5) contrast(4) brightness(1.1) drop-shadow(1px 2px 1px #222); } @@ -30,15 +59,367 @@ pre { margin-top : 0.1cm; } +.page ul + h5 { + margin-top: 0.25cm; +} + +.page p + h5 { + margin-top: 0.25cm; +} + .page .openSans { font-family: 'Open Sans'; font-size: 0.9em; } + +.page { + padding-bottom: 1.5cm; +} ``` ## changelog For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). + +### Tuesday 28/02/2023 - v3.7.0 +{{taskList + +{{note +**NOTE:** Some new snippets will now show a {{beta BETA}} tag. Feel free to use them, but be aware we may change how they work depending on your feedback. +}} + +##### Calculuschild + +* [x] New {{openSans **IMAGES → WATERCOLOR EDGE** {{fac,mask-edge}} }} and {{openSans **WATERCOLOR CORNER** {{fac,mask-corner}} }} snippets for V3, which adds a stylish watercolor texture to the edge of your images! (Thanks to /u/flamableconcrete on Reddit for providing these image masks!) + +* [x] Fix site not displaying on iOS devices + +##### 5e-Cleric + +* [x] New {{openSans **PHB → COVER PAGE** {{fac,book-front-cover}} }} snippet for V3, which adds a stylish coverpage to your brew! (Thanks to /u/Kaiburr_Kath-Hound on Reddit for providing some of these resources!) + +##### MichielDeMey (new contribuor!) + +* [x] Fix typo in testing scripts +* [x] Fix "mug" image not using HTTPS + +Fixes issues [#2687](https://github.com/naturalcrit/homebrewery/issues/2687) +}} + +### Saturday 18/02/2023 - v3.6.1 +{{taskList +##### G-Ambatte + +* [x] Fix users not being removed from Authors list correctly + +Fixes issues [#2674](https://github.com/naturalcrit/homebrewery/issues/2674) +}} + + +### Monday 23/01/2023 - v3.6.0 +{{taskList +##### calculuschild + +* [x] Fix Google Drive brews sometimes duplicating + +Fixes issues [#2603](https://github.com/naturalcrit/homebrewery/issues/2603) + +##### Jeddai + +* [x] Add unit tests with full coverage for the Homebrewery API + +* [x] Add message to refresh the browser if the user is missing an update to the Homebrewery + +Fixes issues [#2583](https://github.com/naturalcrit/homebrewery/issues/2583) + +##### G-Ambatte + +* [x] Auto-compile Themes CSS on development server + +##### 5e-Cleric + +* [x] Fix cloned brews inheriting the parent view count +}} + +\column + +### Friday 23/12/2022 - v3.5.0 +{{taskList + +##### Jeddai + +* [x] Only brew owners or invited authors can edit a brew + + - Visiting an `/edit` page of a brew that does not list you as an author will result in an error page. Authors can be added to any brew by opening its {{fa,fa-info-circle}} **Properties** menu and typing the author's username (case-sensitive) into the **Invited Authors** bubble. + - Warn user if a newer brew version has been saved on another device + +Fixes issues [#1987](https://github.com/naturalcrit/homebrewery/issues/1987) +}} + +\page + +### Saturday 10/12/2022 - v3.4.2 +{{taskList + +##### Jeddai + +* [x] Fix broken tags editor + +* [x] Reduce server load to fix some saving issues + +Fixes issues [#2322](https://github.com/naturalcrit/homebrewery/issues/2322) + +##### G-Ambatte + +* [x] Account page help link for Google Drive errors + +Fixes issues [#2520](https://github.com/naturalcrit/homebrewery/issues/2520) +}} + +### Monday 05/12/2022 - v3.4.1 +{{taskList + +##### G-Ambatte + +* [x] Fix Account page incorrect last login time + +Fixes issues [#2521](https://github.com/naturalcrit/homebrewery/issues/2521) + +##### Gazook + +* [x] Fix crashing on iOS and Safari browsers + +Fixes issues [#2531](https://github.com/naturalcrit/homebrewery/issues/2531) +}} + +### Monday 28/11/2022 - v3.4.0 +{{taskList + +##### G-Ambatte + +* [x] Fix for Chrome v108 handling of page size + +Fixes issues [#2445](https://github.com/naturalcrit/homebrewery/issues/2445), [#2516](https://github.com/naturalcrit/homebrewery/issues/2516) + +* [x] New account page with some user info, at {{openSans **USERNAME {{fa,fa-user}} → ACCOUNT {{fa,fa-user}}**}} + +Fixes issues [#2049](https://github.com/naturalcrit/homebrewery/issues/2049), [#2043](https://github.com/naturalcrit/homebrewery/issues/2043) + +* [x] Fix "Published/Private Brews" buttons on userpage + +Fixes issues [#2449](https://github.com/naturalcrit/homebrewery/issues/2449) + +##### Gazook + +* [x] Make autosave default on for new users + +* [x] Added link to our FAQ at {{openSans **NEED HELP? {{fa,fa-question-circle}} → FAQ {{fa,fa-question-circle}}**}} + +* [x] Fix curly blocks freezing with long property lists + +Fixes issues [#2393](https://github.com/naturalcrit/homebrewery/issues/2393) + +* [x] Items can now be removed from {{openSans **RECENT BREWS** {{fas,fa-history}} }} + +Fixes issues [#1918](https://github.com/naturalcrit/homebrewery/issues/1918) + +* [x] Curly injector syntax `{blue}` highlighting in editor + +Fixes issues [#1670](https://github.com/naturalcrit/homebrewery/issues/1670) + +}} + +### Thursday 28/10/2022 - v3.3.1 +{{taskList + +##### Calculuschild + +* [x] Fixes to several broken CSS styles from v3.3.0 + +Fixes issues [#2468](https://github.com/naturalcrit/homebrewery/issues/2468) + +##### Jeddai + +* [x] Reduce size of thumbnails on social media links + +}} + +### Friday 19/10/2022 - v3.3.0 +{{taskList + +##### Calculuschild + +* [x] Fix for tables broken by Chrome v106 + + +##### G-Ambatte: + +* [x] Fix Table of Contents broken by Chrome v106 + +Fixes issues [#2437](https://github.com/naturalcrit/homebrewery/issues/2437) + +* [x] Show brew thumbnails on user page + +Fixes issues [#2331](https://github.com/naturalcrit/homebrewery/issues/2331) + +* [x] Allow longer URLs for brew thumbnails + +Fixes issues [#2351](https://github.com/naturalcrit/homebrewery/issues/2351) + +* [x] Code no longer unfolds when inserting a snippet + +Fixes issues [#2135](https://github.com/naturalcrit/homebrewery/issues/2135) + +* [x] Fix brew settings being lost on first save + +Fixes issues [#2427](https://github.com/naturalcrit/homebrewery/issues/2427) + +##### Gazook: + +* [x] Several updates to bug reporting and error popups + +Fixes issues [#2376](https://github.com/naturalcrit/homebrewery/issues/2376) + +* [x] Fixes to userpage search bar + +Fixes issues [#1675](https://github.com/naturalcrit/homebrewery/issues/1675), [#2353](https://github.com/naturalcrit/homebrewery/issues/2353) + +* [x] Renderer *(legacy / V3)* now shown next to page # + +Fixes issues [#1928](https://github.com/naturalcrit/homebrewery/issues/1928) + +* [x] Prevent text selection when moving divider bar + +Fixes issues [#1632](https://github.com/naturalcrit/homebrewery/issues/1632) + +* [x] Tweak Monster Stat Block coloring + +Fixes issues [#2123](https://github.com/naturalcrit/homebrewery/issues/2123) + +* [x] Added dropdown button to toggle autosave + +Fixes issues [#1546](https://github.com/naturalcrit/homebrewery/issues/1546) + +}} + + +### Friday 08/09/2022 - v3.2.2 +{{taskList + +##### Jeddai: + +* [x] Fix brews not deleting from User page when removed from Google Drive externally. + + Fixes issues: [#2325](https://github.com/naturalcrit/homebrewery/issues/2325) + +##### G-Ambatte: + +* [x] Brew Tags are now searchable on the User page + +Fixes issues [#2317](https://github.com/naturalcrit/homebrewery/issues/2317), [#2319](https://github.com/naturalcrit/homebrewery/issues/2319), [#2334](https://github.com/naturalcrit/homebrewery/issues/2334) + +* [x] Several tweaks to the User page + + Fixes issues: [#1797](https://github.com/naturalcrit/homebrewery/issues/1797), [#2315](https://github.com/naturalcrit/homebrewery/issues/2315), [#2326](https://github.com/naturalcrit/homebrewery/issues/2326), [#2328](https://github.com/naturalcrit/homebrewery/issues/2328) +}} + + + +\page + +### Wednesday 31/08/2022 - v3.2.1 +{{taskList + +##### Calculuschild + +* [x] Reference Links should now work inside tables + + Fixes issues: [#2307](https://github.com/naturalcrit/homebrewery/issues/2307) + +##### Jeddai: + +* [x] Fix printing from `/new` not working + + Fixes issues: [#1789](https://github.com/naturalcrit/homebrewery/issues/1789), [#1806](https://github.com/naturalcrit/homebrewery/issues/1806) + +* [x] Fix broken snippet buttons on `/new` + + Fixes issues: [#2311](https://github.com/naturalcrit/homebrewery/issues/2311) + +##### G-Ambatte: + +* [x] Several small tweaks to the User page + + Fixes issues: [#2301](https://github.com/naturalcrit/homebrewery/issues/2301), [#2303](https://github.com/naturalcrit/homebrewery/issues/2303), [#2121](https://github.com/naturalcrit/homebrewery/issues/2121) +}} + +### Saturday 27/08/2022 - v3.2.0 +{{taskList + +##### Calculuschild + +* [x] The V3 renderer is now the default for new brews. + +* [x] Small tweaks to the spacing around the `classTable` style + +##### Jeddai: + +* [x] Brew transfers between Homebrewery and Google Drive now keep the same share and edit links! Metadata is now also kept across transfers. + + Fixes issues: [#1838](https://github.com/naturalcrit/homebrewery/issues/1838) + +* [x] Brews can now be labeled with tags; these will be searchable on the My Brews page in a future update. + + Fixes issues: [#758](https://github.com/naturalcrit/homebrewery/issues/758) + +##### Jlgraves: + +* [x] Small tweaks to the `ClassFeature` snippet + + Fixes issues: [#2215](https://github.com/naturalcrit/homebrewery/issues/2215) +}} + + +### Thursday 09/06/2022 - v3.1.1 +{{taskList + +##### Calculuschild: + +* [x] Fixed class table decorations appearing on top of the table in PDF output. + + Fixes issues: [#1784](https://github.com/naturalcrit/homebrewery/issues/1784) + +* [x] Fix bottom decoration on half class tables disappearing when the table is too short. + + Fixes issues: [#2202](https://github.com/naturalcrit/homebrewery/issues/2202) +}} + +### Monday 06/06/2022 - v3.1.0 +{{taskList + +##### G-Ambatte: + +* [x] "Jump to Preview/Editor" buttons added to the divider bar. Easily sync between the editor and preview panels! + + Fixes issues: [#1756](https://github.com/naturalcrit/homebrewery/issues/1756) + +* [x] Speedups to the user page for users with large and/or many brews. + + Fixes issues: [#2147](https://github.com/naturalcrit/homebrewery/issues/2147) + +* [x] Search text on the user page is saved to the URL for easy bookmarking in your browser + + Fixes issues: [#1858](https://github.com/naturalcrit/homebrewery/issues/1858) + +* [x] Added easy login system for offline installs. + + Fixes issues: [#269](https://github.com/naturalcrit/homebrewery/issues/269) + +* [x] New **THUMBNAIL** option in the {{fa,fa-info-circle}} **Properties** menu. This image will show up in social media links. + + Fixes issues: [#820](https://github.com/naturalcrit/homebrewery/issues/820) +}} + ### Wednesday 27/03/2022 - v3.0.8 {{taskList * [x] Style updates to user page. diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 2da7123cc..a41e01228 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -1,3 +1,4 @@ +/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/ require('./brewRenderer.less'); const React = require('react'); const createClass = require('create-react-class'); @@ -13,6 +14,8 @@ const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx'); const NotificationPopup = require('./notificationPopup/notificationPopup.jsx'); const Frame = require('react-frame-component').default; +const Themes = require('themes/themes.json'); + const PAGE_HEIGHT = 1056; const PPR_THRESHOLD = 50; @@ -23,6 +26,7 @@ const BrewRenderer = createClass({ text : '', style : '', renderer : 'legacy', + theme : '5ePHB', errors : [] }; }, @@ -105,7 +109,12 @@ const BrewRenderer = createClass({ renderPageInfo : function(){ return
- {this.state.viewablePageNumber + 1} / {this.state.pages.length} +
+ {this.props.renderer} +
+
+ {this.state.viewablePageNumber + 1} / {this.state.pages.length} +
; }, @@ -113,7 +122,7 @@ const BrewRenderer = createClass({ if(!this.state.usePPR) return; return
- Partial Page Renderer enabled, because your brew is so large. May effect rendering. + Partial Page Renderer is enabled, because your brew is so large. May affect rendering.
; }, @@ -125,7 +134,8 @@ const BrewRenderer = createClass({ renderStyle : function() { if(!this.props.style) return; - return
${this.props.style} ` }} />; + //return
@layer styleTab {\n${this.props.style}\n} ` }} />; + return
\n${this.props.style}\n` }} />; }, renderPage : function(pageText, index){ @@ -177,6 +187,9 @@ const BrewRenderer = createClass({ render : function(){ //render in iFrame so broken code doesn't crash the site. //Also render dummy page while iframe is mounting. + const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy'; + const themePath = this.props.theme ?? '5ePHB'; + const baseThemePath = Themes[rendererPath][themePath].baseTheme; return ( @@ -188,7 +201,7 @@ const BrewRenderer = createClass({
: null} -
- + + {baseThemePath && + + } + {/* Apply CSS from Style tab and render pages from Markdown tab */} {this.state.isMounted && diff --git a/client/homebrew/brewRenderer/brewRenderer.less b/client/homebrew/brewRenderer/brewRenderer.less index abb80fc5f..bde91c92e 100644 --- a/client/homebrew/brewRenderer/brewRenderer.less +++ b/client/homebrew/brewRenderer/brewRenderer.less @@ -21,11 +21,17 @@ right : 17px; bottom : 0; z-index : 1000; - padding : 8px 10px; background-color : #333; font-size : 10px; font-weight : 800; color : white; + div { + display: inline-block; + padding : 8px 10px; + &:not(:last-child){ + border-right: 1px solid #666; + } + } } .ppr_msg{ position : absolute; diff --git a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx index 92193d22c..f0a0f5f1f 100644 --- a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx +++ b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx @@ -4,7 +4,7 @@ const createClass = require('create-react-class'); const _ = require('lodash'); const cx = require('classnames'); //Unused variable -const DISMISS_KEY = 'dismiss_notification09-9-21'; +const DISMISS_KEY = 'dismiss_notification08-27-22'; const NotificationPopup = createClass({ displayName : 'NotificationPopup', @@ -22,45 +22,45 @@ const NotificationPopup = createClass({ }, notifications : { psa : function(){ - return
  • - V3.0.0 Released!
    - After a long and bumpy road, we decided it was high time we finally release version 3 of the homebrewery into the wild. You can check out a - brief overview and see how to opt-in to the new features here:  - V3 Welcome Page and  - the Changelog. -

    - BE WARNED: As we continue to develop V3, expect small tweaks in the styling, fonts, and snippets; your brews may look slightly - different from day-to-day. All of your old documents will continue to work as normal; we are not touching them. If you don't want to deal - with the possibility of slight formatting changes, you may choose to stick with the Legacy renderer on any of your brews for as long as you like. -

    - With this in mind, if you still wish to try out V3, you can opt-in any of your brews to the the V3 renderer. - This will likely break much of your formatting as a lot of the Markdown code has been updated, and starting from scratch may be cleaner. - (Don't worry, you can always change the renderer back to Legacy for any brew at any time). -
  • ; - }, - refreshGoogle : function (){ - return
  • - Refresh your Google Drive Credentials!
    - Currently a lot of people are striking issues with their Google credentials expiring, which happens one year after the last sign in via - Google. This can cause errors when trying to save your brews. If this happens, simply visit the  - - logout page - - , sign out, and then sign back in "with Google" to refresh your credentials. See  - - this discussion on Github - for more details. -
  • ; - }, - faq : function(){ - return
  • - Protect your work!
    - If you opt not to use your Google Drive, keep in mind that we do not save a history of your projects. Please make frequent backups of your brews!  - - See the FAQ - to learn how to avoid losing your work! -
  • ; - }, + return ( + <> +
  • + V3.2.0 Released!
    + We are happy to announce that after nearly a year of use by our many users, + we are making the V3 render mode the default setting for all new brews. + This mode has become quite popular, and has proven to be stable and powerful. + Of course, we will always keep the option to use the Legacy renderer for any + brew, which can still be accessed from the Properties menu. +
  • + +
  • + Change to Google Drive Storage!
    + We have made a change to the process of tranferring brews between Google + Drive and the Homebrewery storage. Starting now, any time a brew is + transferred, it will keep the same links instead of generating new ones! + We hope this change will help reduce issues where people "lost" their work + by trying to visit old links. +
  • + +
  • + Don't delete your Homebrewery folder on Google Drive!
    + We have had several reports of users losing their brews, not realizing + that they had deleted the files on their Google Drive. If you have a Homebrewery folder + on your Google Drive with *.txt files inside, do not delete it! + We cannot help you recover files that you have deleted from your own + Google Drive. +
  • + +
  • + Protect your work!
    + If you opt not to use your Google Drive, keep in mind that we do not save a history of your projects. Please make frequent backups of your brews!  + + See the FAQ + to learn how to avoid losing your work! +
  • + + ); + } }, checkNotifications : function(){ const hideDismiss = localStorage.getItem(DISMISS_KEY); diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index fce116fcc..58e84e4fc 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -19,11 +19,6 @@ const DEFAULT_STYLE_TEXT = dedent` color: black; }`; -const splice = function(str, index, inject){ - return str.slice(0, index) + inject + str.slice(index); -}; - - const Editor = createClass({ displayName : 'Editor', @@ -37,6 +32,7 @@ const Editor = createClass({ onTextChange : ()=>{}, onStyleChange : ()=>{}, onMetaChange : ()=>{}, + reportError : ()=>{}, renderer : 'legacy' }; @@ -61,8 +57,14 @@ const Editor = createClass({ window.removeEventListener('resize', this.updateEditorSize); }, - componentDidUpdate : function() { + componentDidUpdate : function(prevProps, prevState, snapshot) { this.highlightCustomMarkdown(); + if(prevProps.moveBrew !== this.props.moveBrew) { + this.brewJump(); + }; + if(prevProps.moveSource !== this.props.moveSource) { + this.sourceJump(); + }; }, updateEditorSize : function() { @@ -74,31 +76,24 @@ const Editor = createClass({ }, handleInject : function(injectText){ - let text; - if(this.isText()) text = this.props.brew.text; - if(this.isStyle()) text = this.props.brew.style ?? DEFAULT_STYLE_TEXT; - - const lines = text.split('\n'); - const cursorPos = this.refs.codeEditor.getCursorPosition(); - lines[cursorPos.line] = splice(lines[cursorPos.line], cursorPos.ch, injectText); - - const injectLines = injectText.split('\n'); - this.refs.codeEditor.setCursorPosition(cursorPos.line + injectLines.length, cursorPos.ch + injectLines[injectLines.length - 1].length); - - if(this.isText()) this.props.onTextChange(lines.join('\n')); - if(this.isStyle()) this.props.onStyleChange(lines.join('\n')); + this.refs.codeEditor?.injectText(injectText, false); }, handleViewChange : function(newView){ + this.props.setMoveArrows(newView === 'text'); this.setState({ view : newView }, this.updateEditorSize); //TODO: not sure if updateeditorsize needed }, getCurrentPage : function(){ - const lines = this.props.brew.text.split('\n').slice(0, this.cursorPosition.line + 1); + const lines = this.props.brew.text.split('\n').slice(0, this.refs.codeEditor.getCursorPosition().line + 1); return _.reduce(lines, (r, line)=>{ - if(line.indexOf('\\page') !== -1) r++; + if( + (this.props.renderer == 'legacy' && line.indexOf('\\page') !== -1) + || + (this.props.renderer == 'V3' && line.match(/^\\page$/)) + ) r++; return r; }, 1); }, @@ -120,6 +115,7 @@ const Editor = createClass({ //reset custom line styles codeMirror.removeLineClass(lineNumber, 'background', 'pageLine'); codeMirror.removeLineClass(lineNumber, 'text'); + codeMirror.removeLineClass(lineNumber, 'wrap', 'sourceMoveFlash'); // Styling for \page breaks if((this.props.renderer == 'legacy' && line.includes('\\page')) || @@ -142,9 +138,17 @@ const Editor = createClass({ codeMirror.addLineClass(lineNumber, 'text', 'columnSplit'); } + // Highlight injectors {style} + if(line.includes('{') && line.includes('}')){ + const regex = /(?:^|[^{\n])({(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\2})/gm; + let match; + while ((match = regex.exec(line)) != null) { + codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length }, { className: 'injection' }); + } + } // Highlight inline spans {{content}} if(line.includes('{{') && line.includes('}}')){ - const regex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g; + const regex = /{{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1 *|}}/g; let match; let blockCount = 0; while ((match = regex.exec(line)) != null) { @@ -163,7 +167,7 @@ const Editor = createClass({ // Highlight block divs {{\n Content \n}} let endCh = line.length+1; - const match = line.match(/^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/); + const match = line.match(/^ *{{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1 *$|^ *}}$/); if(match) endCh = match.index+match[0].length; codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' }); @@ -174,9 +178,76 @@ const Editor = createClass({ } }, - brewJump : function(){ - const currentPage = this.getCurrentPage(); - window.location.hash = `p${currentPage}`; + brewJump : function(targetPage=this.getCurrentPage()){ + if(!window) return; + // console.log(`Scroll to: p${targetPage}`); + const brewRenderer = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0]; + const currentPos = brewRenderer.scrollTop; + const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top; + const interimPos = targetPos >= 0 ? -30 : 30; + + const bounceDelay = 100; + const scrollDelay = 500; + + if(!this.throttleBrewMove) { + this.throttleBrewMove = _.throttle((currentPos, interimPos, targetPos)=>{ + brewRenderer.scrollTo({ top: currentPos + interimPos, behavior: 'smooth' }); + setTimeout(()=>{ + brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' }); + }, bounceDelay); + }, scrollDelay, { leading: true, trailing: false }); + }; + this.throttleBrewMove(currentPos, interimPos, targetPos); + + // const hashPage = (page != 1) ? `p${page}` : ''; + // window.location.hash = hashPage; + }, + + sourceJump : function(targetLine=null){ + if(this.isText()) { + if(targetLine == null) { + targetLine = 0; + + const pageCollection = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('page'); + const brewRendererHeight = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer').item(0).getBoundingClientRect().height; + + let currentPage = 1; + for (const page of pageCollection) { + if(page.getBoundingClientRect().bottom > (brewRendererHeight / 2)) { + currentPage = parseInt(page.id.slice(1)) || 1; + break; + } + } + + const textSplit = this.props.renderer == 'V3' ? /^\\page$/gm : /\\page/; + const textString = this.props.brew.text.split(textSplit).slice(0, currentPage-1).join(textSplit); + const textPosition = textString.length; + const lineCount = textString.match('\n') ? textString.slice(0, textPosition).split('\n').length : 0; + + targetLine = lineCount - 1; //Scroll to `\page`, which is one line back. + + let currentY = this.refs.codeEditor.codeMirror.getScrollInfo().top; + let targetY = this.refs.codeEditor.codeMirror.heightAtLine(targetLine, 'local', true); + + //Scroll 1/10 of the way every 10ms until 1px off. + const incrementalScroll = setInterval(()=>{ + currentY += (targetY - currentY) / 10; + this.refs.codeEditor.codeMirror.scrollTo(null, currentY); + + // Update target: target height is not accurate until within +-10 lines of the visible window + if(Math.abs(targetY - currentY > 100)) + targetY = this.refs.codeEditor.codeMirror.heightAtLine(targetLine, 'local', true); + + // End when close enough + if(Math.abs(targetY - currentY) < 1) { + this.refs.codeEditor.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference + this.refs.codeEditor.setCursorPosition({ line: targetLine + 1, ch: 0 }); + this.refs.codeEditor.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash'); + clearInterval(incrementalScroll); + } + }, 10); + } + } }, //Called when there are changes to the editor's dimensions @@ -221,7 +292,8 @@ const Editor = createClass({ rerenderParent={this.rerenderParent} /> + onChange={this.props.onMetaChange} + reportError={this.props.reportError}/> ; } }, @@ -248,6 +320,7 @@ const Editor = createClass({ onInject={this.handleInject} showEditButtons={this.props.showEditButtons} renderer={this.props.renderer} + theme={this.props.brew.theme} undo={this.undo} redo={this.redo} historySize={this.historySize()} /> diff --git a/client/homebrew/editor/editor.less b/client/homebrew/editor/editor.less index 810ee1710..b8f978ba3 100644 --- a/client/homebrew/editor/editor.less +++ b/client/homebrew/editor/editor.less @@ -29,6 +29,10 @@ font-weight : bold; //font-style: italic; } + .injection{ + color : green; + font-weight : bold; + } } .brewJump{ diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index 65ab78de2..3d77f557f 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -1,12 +1,26 @@ +/* eslint-disable max-lines */ require('./metadataEditor.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); const cx = require('classnames'); -const request = require('superagent'); +const request = require('../../utils/request-middleware.js'); +const Nav = require('naturalcrit/nav/nav.jsx'); +const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx'); + +const Themes = require('themes/themes.json'); +const validations = require('./validations.js'); const SYSTEMS = ['5e', '4e', '3.5e', 'Pathfinder']; +const homebreweryThumbnail = require('../../thumbnail.png'); + +const callIfExists = (val, fn, ...args)=>{ + if(val[fn]) { + val[fn](...args); + } +}; + const MetadataEditor = createClass({ displayName : 'MetadataEditor', getDefaultProps : function() { @@ -15,21 +29,58 @@ const MetadataEditor = createClass({ editId : null, title : '', description : '', - tags : '', + thumbnail : '', + tags : [], published : false, authors : [], systems : [], - renderer : 'legacy' + renderer : 'legacy', + theme : '5ePHB' }, - onChange : ()=>{} + onChange : ()=>{}, + reportError : ()=>{} }; }, - handleFieldChange : function(name, e){ - this.props.onChange(_.merge({}, this.props.metadata, { - [name] : e.target.value - })); + getInitialState : function(){ + return { + showThumbnail : true + }; }, + + toggleThumbnailDisplay : function(){ + this.setState({ + showThumbnail : !this.state.showThumbnail + }); + }, + + renderThumbnail : function(){ + if(!this.state.showThumbnail) return; + return ; + }, + + handleFieldChange : function(name, e){ + // load validation rules, and check input value against them + const inputRules = validations[name] ?? []; + const validationErr = inputRules.map((rule)=>rule(e.target.value)).filter(Boolean); + + // if no validation rules, save to props + if(validationErr.length === 0){ + callIfExists(e.target, 'setCustomValidity', ''); + this.props.onChange({ + ...this.props.metadata, + [name] : e.target.value + }); + } else { + // if validation issues, display built-in browser error popup with each error. + const errMessage = validationErr.map((err)=>{ + return `- ${err}`; + }).join('\n'); + callIfExists(e.target, 'setCustomValidity', errMessage); + callIfExists(e.target, 'reportValidity'); + } + }, + handleSystem : function(system, e){ if(e.target.checked){ this.props.metadata.systems.push(system); @@ -38,16 +89,26 @@ const MetadataEditor = createClass({ } this.props.onChange(this.props.metadata); }, + handleRenderer : function(renderer, e){ if(e.target.checked){ this.props.metadata.renderer = renderer; + if(renderer == 'legacy') + this.props.metadata.theme = '5ePHB'; } this.props.onChange(this.props.metadata); }, handlePublish : function(val){ - this.props.onChange(_.merge({}, this.props.metadata, { + this.props.onChange({ + ...this.props.metadata, published : val - })); + }); + }, + + handleTheme : function(theme){ + this.props.metadata.renderer = theme.renderer; + this.props.metadata.theme = theme.path; + this.props.onChange(this.props.metadata); }, handleDelete : function(){ @@ -61,8 +122,12 @@ const MetadataEditor = createClass({ request.delete(`/api/${this.props.metadata.googleId ?? ''}${this.props.metadata.editId}`) .send() - .end(function(err, res){ - window.location.href = '/'; + .end((err, res)=>{ + if(err) { + this.props.reportError(err); + } else { + window.location.href = '/'; + } }); }, @@ -116,6 +181,49 @@ const MetadataEditor = createClass({
    ; }, + renderThemeDropdown : function(){ + if(!global.enable_themes) return; + + const listThemes = (renderer)=>{ + return _.map(_.values(Themes[renderer]), (theme)=>{ + return
    this.handleTheme(theme)} title={''}> + {`${theme.renderer} : ${theme.name}`} + +
    +
    {`${theme.name}`} preview
    + +
    +
    ; + }); + }; + + const currentTheme = Themes[`${_.upperFirst(this.props.metadata.renderer)}`][this.props.metadata.theme]; + let dropdown; + + if(this.props.metadata.renderer == 'legacy') { + dropdown = + +
    + {`Themes are not supported in the Legacy Renderer`} +
    +
    ; + } else { + dropdown = + +
    + {`${_.upperFirst(currentTheme.renderer)} : ${currentTheme.name}`} +
    + {/*listThemes('Legacy')*/} + {listThemes('V3')} +
    ; + } + + return
    + + {dropdown} +
    ; + }, + renderRenderOptions : function(){ if(!global.enable_v3) return; @@ -142,8 +250,8 @@ const MetadataEditor = createClass({ V3 - - Click here for a quick intro to V3! + + Click here to see the demo page for the old Legacy renderer!
    ; @@ -151,26 +259,40 @@ const MetadataEditor = createClass({ render : function(){ return
    +

    Brew

    +
    this.handleFieldChange('title', e)} />
    -
    - -