0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-23 23:03:01 +00:00

Compare commits

...

130 Commits

Author SHA1 Message Date
Trevor Buckner
2ea853aeda Up version to 3.2.0 2022-08-27 13:28:31 -04:00
Trevor Buckner
215b64f5a6 Merge pull request #2287 from naturalcrit/dependabot/npm_and_yarn/mongoose-6.5.3
Bump mongoose from 6.5.2 to 6.5.3
2022-08-27 13:05:57 -04:00
Trevor Buckner
239526f4ee Merge pull request #2286 from naturalcrit/dependabot/npm_and_yarn/eslint-plugin-react-7.31.0
Bump eslint-plugin-react from 7.30.1 to 7.31.0
2022-08-27 13:05:49 -04:00
Trevor Buckner
697d4d98bf Merge pull request #2285 from naturalcrit/dependabot/npm_and_yarn/babel/core-7.18.13
Bump @babel/core from 7.18.10 to 7.18.13
2022-08-27 13:05:37 -04:00
Trevor Buckner
87bc27544b Merge pull request #2290 from naturalcrit/MakeV3Default
Make V3 default setting in /new, and homepage
2022-08-27 13:05:26 -04:00
Trevor Buckner
1bbddacbf3 Merge pull request #2233 from jeddai/document-tags
Document tags
2022-08-27 13:05:09 -04:00
Trevor Buckner
cf2e6aa900 Update notificationPopup.jsx 2022-08-27 13:01:53 -04:00
Charlie Humphreys
6aa9eda5fb update if in stringArrayEditor.jsx componentDidUpdate 2022-08-27 00:52:44 -05:00
Trevor Buckner
3271eae4df Make V3 default setting in /new, and homepage 2022-08-27 01:51:30 -04:00
Charlie Humphreys
5227bbc1d4 add tag length limit, fix tag editing issues 2022-08-26 10:42:34 -05:00
dependabot[bot]
f3cdb0dfc2 Bump mongoose from 6.5.2 to 6.5.3
Bumps [mongoose](https://github.com/Automattic/mongoose) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/6.5.2...6.5.3)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-25 03:01:34 +00:00
dependabot[bot]
9a6d409800 Bump eslint-plugin-react from 7.30.1 to 7.31.0
Bumps [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) from 7.30.1 to 7.31.0.
- [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases)
- [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.30.1...v7.31.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-react
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-25 03:01:07 +00:00
dependabot[bot]
7f343d4634 Bump @babel/core from 7.18.10 to 7.18.13
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.18.10 to 7.18.13.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.13/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-23 03:01:18 +00:00
Trevor Buckner
ed933bdf92 Merge pull request #2273 from naturalcrit/dependabot/npm_and_yarn/babel/plugin-transform-runtime-7.18.10
Bump @babel/plugin-transform-runtime from 7.18.6 to 7.18.10
2022-08-22 21:34:43 -04:00
Trevor Buckner
1c4dac6a52 Merge pull request #2255 from naturalcrit/dependabot/npm_and_yarn/jest-28.1.3
Bump jest from 28.1.2 to 28.1.3
2022-08-22 21:34:29 -04:00
Trevor Buckner
f734b29a00 Merge pull request #2284 from naturalcrit/dependabot/npm_and_yarn/marked-4.0.19
Bump marked from 4.0.17 to 4.0.19
2022-08-22 21:33:32 -04:00
dependabot[bot]
889fcb84b2 Bump @babel/plugin-transform-runtime from 7.18.6 to 7.18.10
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.18.6 to 7.18.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.10/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-23 01:33:29 +00:00
Trevor Buckner
fa4db2acfa Merge pull request #2279 from naturalcrit/dependabot/npm_and_yarn/eslint-8.22.0
Bump eslint from 8.19.0 to 8.22.0
2022-08-22 21:33:22 -04:00
Trevor Buckner
167e5c87ae Merge pull request #2283 from naturalcrit/dependabot/npm_and_yarn/googleapis-107.0.0
Bump googleapis from 105.0.0 to 107.0.0
2022-08-22 21:33:13 -04:00
Trevor Buckner
42db46fa33 Merge pull request #2276 from naturalcrit/dependabot/npm_and_yarn/mongoose-6.5.2
Bump mongoose from 6.4.4 to 6.5.2
2022-08-22 21:32:51 -04:00
Trevor Buckner
257920d9db Merge pull request #2272 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.18.10
Bump @babel/preset-env from 7.18.6 to 7.18.10
2022-08-22 21:32:08 -04:00
Trevor Buckner
88a09415e5 Merge pull request #2271 from naturalcrit/dependabot/npm_and_yarn/babel/core-7.18.10
Bump @babel/core from 7.18.6 to 7.18.10
2022-08-22 21:31:56 -04:00
dependabot[bot]
a86f5232cc Bump marked from 4.0.17 to 4.0.19
Bumps [marked](https://github.com/markedjs/marked) from 4.0.17 to 4.0.19.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Changelog](https://github.com/markedjs/marked/blob/master/.releaserc.json)
- [Commits](https://github.com/markedjs/marked/compare/v4.0.17...v4.0.19)

---
updated-dependencies:
- dependency-name: marked
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-22 03:01:12 +00:00
dependabot[bot]
4fa0d701c9 Bump googleapis from 105.0.0 to 107.0.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 105.0.0 to 107.0.0.
- [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases)
- [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v105.0.0...googleapis-v107.0.0)

---
updated-dependencies:
- dependency-name: googleapis
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-22 03:00:49 +00:00
dependabot[bot]
2544563fea Bump eslint from 8.19.0 to 8.22.0
Bumps [eslint](https://github.com/eslint/eslint) from 8.19.0 to 8.22.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.19.0...v8.22.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-15 03:01:04 +00:00
dependabot[bot]
65f943bc21 Bump mongoose from 6.4.4 to 6.5.2
Bumps [mongoose](https://github.com/Automattic/mongoose) from 6.4.4 to 6.5.2.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/6.4.4...6.5.2)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-10 03:01:14 +00:00
Charlie Humphreys
ae9eef4fba update location of tags type rewrite 2022-08-05 15:37:22 -05:00
dependabot[bot]
539fb8365f Bump @babel/preset-env from 7.18.6 to 7.18.10
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.18.6 to 7.18.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.10/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-02 03:01:49 +00:00
dependabot[bot]
0e65685b8e Bump @babel/core from 7.18.6 to 7.18.10
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.18.6 to 7.18.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.10/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-02 03:01:18 +00:00
dependabot[bot]
a3038868b9 Bump jest from 28.1.2 to 28.1.3
Bumps [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest) from 28.1.2 to 28.1.3.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v28.1.3/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-14 03:01:19 +00:00
Trevor Buckner
80334a1649 Merge pull request #2252 from naturalcrit/dependabot/npm_and_yarn/mongoose-6.4.4
Bump mongoose from 6.4.2 to 6.4.4
2022-07-08 14:19:16 -04:00
dependabot[bot]
3fe43a1f4d Bump mongoose from 6.4.2 to 6.4.4
Bumps [mongoose](https://github.com/Automattic/mongoose) from 6.4.2 to 6.4.4.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/6.4.2...6.4.4)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-08 17:45:19 +00:00
Trevor Buckner
b257b4c0fc Merge pull request #2251 from naturalcrit/dependabot/npm_and_yarn/moment-2.29.4
Bump moment from 2.29.3 to 2.29.4
2022-07-08 13:44:39 -04:00
dependabot[bot]
10cc040f16 Bump moment from 2.29.3 to 2.29.4
Bumps [moment](https://github.com/moment/moment) from 2.29.3 to 2.29.4.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/moment/moment/compare/2.29.3...2.29.4)

---
updated-dependencies:
- dependency-name: moment
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-07 03:01:21 +00:00
Trevor Buckner
985f4abb45 Merge pull request #2232 from naturalcrit/dependabot/npm_and_yarn/eslint-plugin-react-7.30.1
Bump eslint-plugin-react from 7.30.0 to 7.30.1
2022-07-05 20:53:07 -04:00
Trevor Buckner
e20fa09170 Merge pull request #2239 from naturalcrit/dependabot/npm_and_yarn/babel/plugin-transform-runtime-7.18.6
Bump @babel/plugin-transform-runtime from 7.18.5 to 7.18.6
2022-07-05 20:52:59 -04:00
Trevor Buckner
b2a4054561 Merge pull request #2249 from naturalcrit/dependabot/npm_and_yarn/googleapis-105.0.0
Bump googleapis from 104.0.0 to 105.0.0
2022-07-05 20:52:44 -04:00
dependabot[bot]
23e378dcdf Bump googleapis from 104.0.0 to 105.0.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 104.0.0 to 105.0.0.
- [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases)
- [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v104.0.0...googleapis-v105.0.0)

---
updated-dependencies:
- dependency-name: googleapis
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-05 03:01:08 +00:00
dependabot[bot]
6d6c9ab102 Bump @babel/plugin-transform-runtime from 7.18.5 to 7.18.6
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.18.5 to 7.18.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.6/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-04 05:47:05 +00:00
dependabot[bot]
3f191d5b47 Bump eslint-plugin-react from 7.30.0 to 7.30.1
Bumps [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) from 7.30.0 to 7.30.1.
- [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases)
- [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.30.0...v7.30.1)

---
updated-dependencies:
- dependency-name: eslint-plugin-react
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-04 05:46:21 +00:00
Trevor Buckner
5362cc90f4 Merge pull request #2237 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.18.6
Bump @babel/preset-env from 7.18.2 to 7.18.6
2022-07-04 01:46:00 -04:00
Trevor Buckner
66ae4dc67d Merge pull request #2246 from naturalcrit/dependabot/npm_and_yarn/eslint-8.19.0
Bump eslint from 8.18.0 to 8.19.0
2022-07-04 01:45:52 -04:00
Trevor Buckner
7d8890a61a Merge pull request #2247 from naturalcrit/dependabot/npm_and_yarn/mongoose-6.4.2
Bump mongoose from 6.4.1 to 6.4.2
2022-07-04 01:45:45 -04:00
Trevor Buckner
0697673382 Merge pull request #2248 from naturalcrit/dependabot/npm_and_yarn/supertest-6.2.4
Bump supertest from 6.2.3 to 6.2.4
2022-07-04 01:45:38 -04:00
dependabot[bot]
1646fa14e6 Bump supertest from 6.2.3 to 6.2.4
Bumps [supertest](https://github.com/visionmedia/supertest) from 6.2.3 to 6.2.4.
- [Release notes](https://github.com/visionmedia/supertest/releases)
- [Commits](https://github.com/visionmedia/supertest/compare/v6.2.3...v6.2.4)

---
updated-dependencies:
- dependency-name: supertest
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-04 03:02:11 +00:00
dependabot[bot]
2f104712ed Bump mongoose from 6.4.1 to 6.4.2
Bumps [mongoose](https://github.com/Automattic/mongoose) from 6.4.1 to 6.4.2.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/6.4.1...6.4.2)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-04 03:01:58 +00:00
dependabot[bot]
c23ba26171 Bump eslint from 8.18.0 to 8.19.0
Bumps [eslint](https://github.com/eslint/eslint) from 8.18.0 to 8.19.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.18.0...v8.19.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-04 03:01:30 +00:00
dependabot[bot]
fa59f8b156 Bump @babel/preset-env from 7.18.2 to 7.18.6
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.18.2 to 7.18.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.6/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-02 14:20:49 +00:00
Trevor Buckner
bc66ad0221 Merge pull request #2238 from naturalcrit/dependabot/npm_and_yarn/mongoose-6.4.1
Bump mongoose from 6.4.0 to 6.4.1
2022-07-02 10:19:31 -04:00
Trevor Buckner
08c52e4bac Merge pull request #2240 from naturalcrit/dependabot/npm_and_yarn/babel/core-7.18.6
Bump @babel/core from 7.18.5 to 7.18.6
2022-07-02 10:19:13 -04:00
Trevor Buckner
0741066554 Merge pull request #2241 from naturalcrit/dependabot/npm_and_yarn/babel/preset-react-7.18.6
Bump @babel/preset-react from 7.17.12 to 7.18.6
2022-07-02 10:19:05 -04:00
Trevor Buckner
24458ad2af Merge pull request #2242 from naturalcrit/dependabot/npm_and_yarn/jest-28.1.2
Bump jest from 28.1.1 to 28.1.2
2022-07-02 10:18:57 -04:00
dependabot[bot]
e38975177f Bump jest from 28.1.1 to 28.1.2
Bumps [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest) from 28.1.1 to 28.1.2.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v28.1.2/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-30 03:01:28 +00:00
dependabot[bot]
abebe1b3a0 Bump @babel/preset-react from 7.17.12 to 7.18.6
Bumps [@babel/preset-react](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-react) from 7.17.12 to 7.18.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.6/packages/babel-preset-react)

---
updated-dependencies:
- dependency-name: "@babel/preset-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-28 03:02:41 +00:00
dependabot[bot]
ae8942d989 Bump @babel/core from 7.18.5 to 7.18.6
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.18.5 to 7.18.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.6/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-28 03:02:03 +00:00
dependabot[bot]
3cded29729 Bump mongoose from 6.4.0 to 6.4.1
Bumps [mongoose](https://github.com/Automattic/mongoose) from 6.4.0 to 6.4.1.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/6.4.0...6.4.1)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-28 03:01:32 +00:00
Charlie Humphreys
c7cfa86205 remove migration in favor of as-edited approach 2022-06-25 00:43:47 -05:00
Charlie Humphreys
8ee832e633 add MIGRATE environment variable 2022-06-25 00:00:41 -05:00
Charlie Humphreys
c7d5d6800b add close button for string array edit input, update styles 2022-06-24 23:48:32 -05:00
Charlie Humphreys
e963672b65 update tag setup to be based on the latest version of the stringArrayEditor.jsx 2022-06-24 23:31:20 -05:00
Charlie Humphreys
1965218e74 Merge branch 'master' into document-tags
# Conflicts:
#	client/homebrew/editor/metadataEditor/metadataEditor.jsx
#	server/homebrew.api.js
2022-06-24 22:55:35 -05:00
Trevor Buckner
3649099d00 Merge pull request #2229 from naturalcrit/dependabot/npm_and_yarn/googleapis-104.0.0
Bump googleapis from 103.0.0 to 104.0.0
2022-06-22 00:24:28 -04:00
Trevor Buckner
5d7a46abfe Merge pull request #2228 from naturalcrit/dependabot/npm_and_yarn/codemirror-5.65.6
Bump codemirror from 5.65.5 to 5.65.6
2022-06-22 00:24:14 -04:00
dependabot[bot]
209f0ec32a Bump codemirror from 5.65.5 to 5.65.6
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.65.5 to 5.65.6.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/codemirror5/blob/5.65.6/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.65.5...5.65.6)

---
updated-dependencies:
- dependency-name: codemirror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-22 04:21:42 +00:00
dependabot[bot]
b336a6b6a3 Bump googleapis from 103.0.0 to 104.0.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 103.0.0 to 104.0.0.
- [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases)
- [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v103.0.0...googleapis-v104.0.0)

---
updated-dependencies:
- dependency-name: googleapis
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-22 04:21:26 +00:00
Trevor Buckner
32833459e4 Merge pull request #2230 from naturalcrit/dependabot/npm_and_yarn/shell-quote-1.7.3
Bump shell-quote from 1.7.2 to 1.7.3
2022-06-22 00:20:18 -04:00
dependabot[bot]
0e450f724b Bump shell-quote from 1.7.2 to 1.7.3
Bumps [shell-quote](https://github.com/substack/node-shell-quote) from 1.7.2 to 1.7.3.
- [Release notes](https://github.com/substack/node-shell-quote/releases)
- [Changelog](https://github.com/substack/node-shell-quote/blob/master/CHANGELOG.md)
- [Commits](https://github.com/substack/node-shell-quote/compare/v1.7.2...1.7.3)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-22 04:18:40 +00:00
Trevor Buckner
c4ee39461e Merge pull request #2223 from jlgraves/master
Implements issue #2215 - spell-casting ability
2022-06-21 15:40:54 -04:00
Trevor Buckner
586ff67fa0 Small tweaks 2022-06-21 15:38:20 -04:00
jlgraves
dbbc529a57 Merge branch 'master' into master 2022-06-20 15:34:00 -07:00
jlgraves
a4c80f2bbf added review changes requested
- correctly centred the legacy snippet
- fixed a couple small typos in both files
2022-06-20 15:33:51 -07:00
Trevor Buckner
a76715d2b3 Merge pull request #2224 from jeddai/update-react-router-dom
react-router-dom 5->6 upgrade
2022-06-20 17:27:19 -04:00
Trevor Buckner
9073435ff3 Update package-lock.json 2022-06-20 17:26:52 -04:00
Trevor Buckner
802e798492 Merge branch 'master' into update-react-router-dom 2022-06-20 17:24:59 -04:00
Charlie Humphreys
c0405fae08 add catch-all route 2022-06-20 13:19:43 -05:00
Charlie Humphreys
755b9dcc89 update based on feedback, remove query-string dependency, add/move favicon url 2022-06-20 12:53:26 -05:00
Trevor Buckner
038d0c5b68 Merge pull request #1699 from naturalcrit/v3ClassTableTweaks
Allow wide classTables to bleed into top margin
2022-06-20 11:36:38 -04:00
Trevor Buckner
5ac65245b7 Merge pull request #2226 from naturalcrit/dependabot/npm_and_yarn/mongoose-6.4.0
Bump mongoose from 6.3.8 to 6.4.0
2022-06-19 23:12:53 -04:00
Trevor Buckner
06cbcb047b Merge pull request #2225 from naturalcrit/dependabot/npm_and_yarn/eslint-8.18.0
Bump eslint from 8.17.0 to 8.18.0
2022-06-19 23:12:45 -04:00
dependabot[bot]
60811a3295 Bump mongoose from 6.3.8 to 6.4.0
Bumps [mongoose](https://github.com/Automattic/mongoose) from 6.3.8 to 6.4.0.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/6.3.8...6.4.0)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-20 03:01:19 +00:00
dependabot[bot]
5ca2899b54 Bump eslint from 8.17.0 to 8.18.0
Bumps [eslint](https://github.com/eslint/eslint) from 8.17.0 to 8.18.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.17.0...v8.18.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-20 03:00:52 +00:00
Charlie Humphreys
60a372de68 update react-router-dom to 6.3.0, add WithRoute function component wrapper 2022-06-18 16:20:51 -05:00
jlgraves
01c16b4a1c Updating legacy class feature
Separated into two commits - one for new, one for legacy
2022-06-18 12:26:07 -07:00
jlgraves
2ebfd19169 Adding in spell-casting ability
Re issue #2215 added spellcasting ability modifier
2022-06-18 11:30:35 -07:00
Trevor Buckner
f7d8c6434f Small tweaks 2022-06-16 22:41:04 -04:00
Trevor Buckner
936fdff871 Update CSS, Shrink vertical border margins slightly 2022-06-16 22:13:52 -04:00
Trevor Buckner
da3460f3bf Merge branch 'master' into v3ClassTableTweaks 2022-06-16 22:08:01 -04:00
Trevor Buckner
8ea58d22ba Merge pull request #2218 from naturalcrit/dependabot/npm_and_yarn/babel/core-7.18.5
Bump @babel/core from 7.18.2 to 7.18.5
2022-06-16 15:31:18 -04:00
dependabot[bot]
4f9ac9cad0 Bump @babel/core from 7.18.2 to 7.18.5
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.18.2 to 7.18.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.5/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-16 03:47:29 +00:00
Trevor Buckner
7dafd3ee77 Merge pull request #2217 from naturalcrit/dependabot/npm_and_yarn/marked-4.0.17
Bump marked from 4.0.16 to 4.0.17
2022-06-15 23:47:06 -04:00
Trevor Buckner
4e403dd0e9 Merge pull request #2220 from naturalcrit/dependabot/npm_and_yarn/babel/plugin-transform-runtime-7.18.5
Bump @babel/plugin-transform-runtime from 7.18.2 to 7.18.5
2022-06-15 23:46:46 -04:00
dependabot[bot]
530d1bee8d Bump marked from 4.0.16 to 4.0.17
Bumps [marked](https://github.com/markedjs/marked) from 4.0.16 to 4.0.17.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Changelog](https://github.com/markedjs/marked/blob/master/.releaserc.json)
- [Commits](https://github.com/markedjs/marked/compare/v4.0.16...v4.0.17)

---
updated-dependencies:
- dependency-name: marked
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-16 03:45:26 +00:00
Trevor Buckner
e8c7bcfee9 Merge pull request #2219 from naturalcrit/dependabot/npm_and_yarn/mongoose-6.3.8
Bump mongoose from 6.3.6 to 6.3.8
2022-06-15 23:44:14 -04:00
Trevor Buckner
6ed12f41f9 Merge pull request #2222 from naturalcrit/dependabot/npm_and_yarn/googleapis-103.0.0
Bump googleapis from 101.0.0 to 103.0.0
2022-06-15 23:44:07 -04:00
dependabot[bot]
a754959a7d Bump googleapis from 101.0.0 to 103.0.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 101.0.0 to 103.0.0.
- [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases)
- [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v101.0.0...googleapis-v103.0.0)

---
updated-dependencies:
- dependency-name: googleapis
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-16 03:01:32 +00:00
dependabot[bot]
d0334c69a2 Bump @babel/plugin-transform-runtime from 7.18.2 to 7.18.5
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.18.2 to 7.18.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.5/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-14 03:02:23 +00:00
dependabot[bot]
2063d12c42 Bump mongoose from 6.3.6 to 6.3.8
Bumps [mongoose](https://github.com/Automattic/mongoose) from 6.3.6 to 6.3.8.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/6.3.6...6.3.8)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-14 03:02:02 +00:00
Trevor Buckner
5601b932c8 Fix Clone to New broken with #2114 2022-06-09 01:09:18 -04:00
Trevor Buckner
204b379fcb Merge pull request #2114 from jeddai/google-document-stubs
Google Drive document stubs
2022-06-09 00:37:49 -04:00
Trevor Buckner
5f7abe2a8f Merge branch 'master' into pr/2114 2022-06-09 00:33:44 -04:00
Trevor Buckner
385a22f199 Merge pull request #2214 from naturalcrit/3.1.1
v3.1.1
2022-06-09 00:27:51 -04:00
Trevor Buckner
1045867d73 Up version to 3.1.1 2022-06-09 00:26:22 -04:00
Charlie Humphreys
890fbcc250 Merge branch 'master' into google-document-stubs 2022-06-06 15:04:24 +00:00
Charlie Humphreys
18ce232be9 Merge branch 'google-document-stubs' of https://github.com/jeddai/homebrewery into google-document-stubs 2022-06-06 15:01:10 +00:00
Charlie Humphreys
92b3f8252e fix data reuse and add field to projection 2022-06-06 14:59:40 +00:00
Trevor Buckner
45950cb8b4 Merge branch 'google-document-stubs' of https://github.com/jeddai/homebrewery into pr/2114 2022-05-28 23:10:53 -04:00
Trevor Buckner
f1f82abdb4 Merge branch 'master' into pr/2114 2022-05-28 23:10:14 -04:00
Charlie Humphreys
bade09c24c linting 2022-05-26 04:46:07 +00:00
Charlie Humphreys
b54b7e317e pre-filter brews list when finding google brews 2022-05-26 04:45:50 +00:00
Charlie Humphreys
627c52e845 Add comments and adjust google brew for loop 2022-05-26 04:40:33 +00:00
Charlie Humphreys
55c91217ab fix deletion, update urls 2022-05-25 03:03:43 +00:00
Charlie Humphreys
7691e4d24e Merge branch 'master' into google-document-stubs 2022-05-22 00:40:57 +00:00
Charlie Humphreys
452aa1feaf lint 2022-05-14 04:19:54 +00:00
Charlie Humphreys
74d22a05d0 swap transferToGoogle to saveToGoogle in a spot I missed 2022-05-14 04:19:36 +00:00
Charlie Humphreys
ac905ddf3f add comments, update query params, adjust code based on feedback 2022-05-14 04:17:39 +00:00
Charlie Humphreys
ff885dc6c2 Merge branch 'master' into google-document-stubs 2022-05-11 23:35:31 -05:00
Charlie Humphreys
01441e0610 adjust code based on feedback 2022-05-11 23:34:48 -05:00
Charlie Humphreys
2e145e7ff1 Merge branch 'master' into google-document-stubs 2022-04-15 00:23:51 -05:00
Charlie Humphreys
8a5e9aa1f6 update app to better handle google brew stubs 2022-04-15 00:22:51 -05:00
Charlie Humphreys
e7fe68d20c merge master into google-document-stubs 2022-04-14 23:31:07 -05:00
Charlie Humphreys
39d29abb19 update app to list stubbed google docs only once 2022-04-11 12:55:19 -05:00
Charlie Humphreys
4941dbb5bd add exclusions for stub props 2022-04-06 11:11:11 -05:00
Charlie Humphreys
e5021259bb swap usage of delete on possible Mongoose object for setting the value to undefined 2022-03-30 23:34:39 -05:00
Charlie Humphreys
6791df7f75 Merge branch 'master' into google-document-stubs
# Conflicts:
#	server/homebrew.api.js
2022-03-30 23:26:41 -05:00
Charlie Humphreys
fa8d47400f update API and frontend for google drive document stubs 2022-03-30 23:23:45 -05:00
Charlie Humphreys
e2fe77ade7 Update tag keydown handler to allow tags that match a certain pattern
#758
2021-11-18 23:05:39 -06:00
Charlie Humphreys
19c13342c4 Add autoFocus functionality when editing tags and allow for multi-tag entry by comma separation
#758
2021-11-17 23:56:57 -06:00
Charlie Humphreys
330bf20d61 Update UI to support tags
#758
2021-11-17 22:26:25 -06:00
Charlie Humphreys
22f9efd58a Update API to support homebrew tags
#758
2021-11-17 22:22:33 -06:00
Trevor Buckner
4bebdfda79 Allow wide classTables to bleed into top margin 2021-09-18 00:37:21 -04:00
28 changed files with 3476 additions and 2815 deletions

View File

@@ -39,6 +39,47 @@ pre {
## changelog
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
### Thursday 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

View File

@@ -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 <li key='psa'>
<em>V3.0.0 Released!</em> <br />
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:&nbsp;
<a target='_blank' href='https://homebrewery.naturalcrit.com/v3_preview'>V3 Welcome Page</a> and&nbsp;
<a target='_blank' href='https://homebrewery.naturalcrit.com/changelog'>the Changelog</a>.
<br /><br />
<em>BE WARNED:</em> 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.
<br /><br />
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).
</li>;
},
refreshGoogle : function (){
return <li key='refreshGoogle'>
<em>Refresh your Google Drive Credentials!</em> <br />
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&nbsp;
<a target='_blank' href='https://www.naturalcrit.com/login'>
logout page
</a>
, sign out, and then sign back in "with Google" to refresh your credentials. See&nbsp;
<a target='_blank' href='https://github.com/naturalcrit/homebrewery/discussions/1580'>
this discussion on Github
</a> for more details.
</li>;
},
faq : function(){
return <li key='faq'>
<em>Protect your work! </em> <br />
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!&nbsp;
<a target='_blank' href='https://www.reddit.com/r/homebrewery/comments/adh6lh/faqs_psas_announcements/'>
See the FAQ
</a> to learn how to avoid losing your work!
</li>;
},
return (
<>
<li key='psa'>
<em>V3.2.0 Released!</em> <br />
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.
</li>
<li key='stubs'>
<em>Change to Google Drive Storage!</em> <br />
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.
</li>
<li key='googleDriveFolder'>
<em>Don't delete your Homebrewery folder on Google Drive!</em> <br />
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, <em>do not delete it</em>!
We cannot help you recover files that you have deleted from your own
Google Drive.
</li>
<li key='faq'>
<em>Protect your work! </em> <br />
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!&nbsp;
<a target='_blank' href='https://www.reddit.com/r/homebrewery/comments/adh6lh/faqs_psas_announcements/'>
See the FAQ
</a> to learn how to avoid losing your work!
</li>
</>
);
}
},
checkNotifications : function(){
const hideDismiss = localStorage.getItem(DISMISS_KEY);

View File

@@ -1,9 +1,11 @@
/* 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 StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx');
const SYSTEMS = ['5e', '4e', '3.5e', 'Pathfinder'];
@@ -17,7 +19,7 @@ const MetadataEditor = createClass({
editId : null,
title : '',
description : '',
tags : '',
tags : [],
published : false,
authors : [],
systems : [],
@@ -45,9 +47,10 @@ const MetadataEditor = createClass({
},
handleFieldChange : function(name, e){
this.props.onChange(_.merge({}, this.props.metadata, {
this.props.onChange({
...this.props.metadata,
[name] : e.target.value
}));
});
},
handleSystem : function(system, e){
if(e.target.checked){
@@ -64,9 +67,10 @@ const MetadataEditor = createClass({
this.props.onChange(this.props.metadata);
},
handlePublish : function(val){
this.props.onChange(_.merge({}, this.props.metadata, {
this.props.onChange({
...this.props.metadata,
published : val
}));
});
},
handleDelete : function(){
@@ -161,8 +165,8 @@ const MetadataEditor = createClass({
V3
</label>
<a href='/v3_preview' target='_blank' rel='noopener noreferrer'>
Click here for a quick intro to V3!
<a href='/legacy' target='_blank' rel='noopener noreferrer'>
Click here to see the demo page for the old Legacy renderer!
</a>
</div>
</div>;
@@ -193,13 +197,11 @@ const MetadataEditor = createClass({
</button>
{this.renderThumbnail()}
</div>
{/*}
<div className='field tags'>
<label>tags</label>
<textarea value={this.props.metadata.tags}
onChange={(e)=>this.handleFieldChange('tags', e)} />
</div>
*/}
<StringArrayEditor label='tags' valuePatterns={[/^(?:(?:group|meta|system|type):)?[A-Za-z0-9][A-Za-z0-9 \/.\-]{0,40}$/]}
placeholder='add tag' unique={true}
values={this.props.metadata.tags}
onChange={(e)=>this.handleFieldChange('tags', e)}/>
{this.renderAuthors()}

View File

@@ -1,3 +1,4 @@
@import 'naturalcrit/styles/colors.less';
.metadataEditor{
position : absolute;
@@ -108,4 +109,86 @@
font-size: 0.8em;
line-height : 1.5em;
}
.field .list {
display: flex;
flex-wrap: wrap;
> * {
flex: 0 0 auto;
}
#groupedIcon {
#backgroundColors;
display: inline-block;
height: ~"calc(100% + 0.6em)";
position: relative;
top: -0.3em;
right: -0.3em;
cursor: pointer;
min-width: 20px;
text-align: center;
color: white;
i {
position: relative;
top: 50%;
transform: translateY(-50%);
}
&:not(:last-child) {
border-right: 1px solid black;
}
&:last-child {
border-radius: 0 0.5em 0.5em 0;
}
}
.badge {
background-color: #dddddd;
border-radius: .5em;
font-size: .9em;
margin: 2px;
padding: .3em;
.icon {
#groupedIcon
}
}
.input-group {
height: ~"calc(.9em + 4px + .6em)";
input {
border-radius: .5em 0 0 .5em;
}
input:last-child {
border-radius: .5em;
}
.value {
width: 7.5vw;
min-width: 75px;
height: 100%;
}
.invalid:focus {
background-color: pink;
}
.icon {
#groupedIcon;
height: 97%;
font-size: .8em;
right: 1px;
top: -.54em;
i {
font-size: 1.125em;
}
}
}
}
}

View File

@@ -9,6 +9,7 @@ module.exports = function(classname){
classname = classname.toLowerCase();
const hitDie = _.sample([4, 6, 8, 10, 12]);
const spellSkill = _.sample(['Wisdom', 'Charisma', 'Intelligence']);
const abilityList = ['Strength', 'Dexerity', 'Constitution', 'Wisdom', 'Charisma', 'Intelligence'];
const skillList = ['Acrobatics', 'Animal Handling', 'Arcana', 'Athletics', 'Deception', 'History', 'Insight', 'Intimidation', 'Investigation', 'Medicine', 'Nature', 'Perception', 'Performance', 'Persuasion', 'Religion', 'Sleight of Hand', 'Stealth', 'Survival'];
@@ -27,11 +28,19 @@ module.exports = function(classname){
**Armor:** :: ${_.sampleSize(['Light armor', 'Medium armor', 'Heavy armor', 'Shields'], _.random(0, 3)).join(', ') || 'None'}
**Weapons:** :: ${_.sampleSize(['Squeegee', 'Rubber Chicken', 'Simple weapons', 'Martial weapons'], _.random(0, 2)).join(', ') || 'None'}
**Tools:** :: ${_.sampleSize(['Artian\'s tools', 'one musical instrument', 'Thieve\'s tools'], _.random(0, 2)).join(', ') || 'None'}
**Tools:** :: ${_.sampleSize(['Artisan\'s tools', 'one musical instrument', 'Thieves\' tools'], _.random(0, 2)).join(', ') || 'None'}
**Saving Throws:** :: ${_.sampleSize(abilityList, 2).join(', ')}
**Skills:** :: Choose two from ${_.sampleSize(skillList, _.random(4, 6)).join(', ')}
#### Spellcasting Ability
{{text-align:center
**Spell save DC**:: = ${_.sample([6, 8, 10])} + your proficiency bonus + your ${spellSkill} modifier
**Spell attack modifier**:: = your proficiency bonus + your ${spellSkill} modifier
}}
#### Equipment
You start with the following equipment, in addition to the equipment granted by your background:
- *(a)* a martial weapon and a shield or *(b)* two martial weapons

View File

@@ -8,6 +8,7 @@ module.exports = function(classname){
classname = classname.toLowerCase();
const hitDie = _.sample([4, 6, 8, 10, 12]);
const spellSkill = _.sample(['Wisdom', 'Charisma', 'Intelligence']);
const abilityList = ['Strength', 'Dexerity', 'Constitution', 'Wisdom', 'Charisma', 'Intelligence'];
const skillList = ['Acrobatics ', 'Animal Handling', 'Arcana', 'Athletics', 'Deception', 'History', 'Insight', 'Intimidation', 'Investigation', 'Medicine', 'Nature', 'Perception', 'Performance', 'Persuasion', 'Religion', 'Sleight of Hand', 'Stealth', 'Survival'];
@@ -26,12 +27,21 @@ module.exports = function(classname){
'___',
`- **Armor:** ${_.sampleSize(['Light armor', 'Medium armor', 'Heavy armor', 'Shields'], _.random(0, 3)).join(', ') || 'None'}`,
`- **Weapons:** ${_.sampleSize(['Squeegee', 'Rubber Chicken', 'Simple weapons', 'Martial weapons'], _.random(0, 2)).join(', ') || 'None'}`,
`- **Tools:** ${_.sampleSize(['Artian\'s tools', 'one musical instrument', 'Thieve\'s tools'], _.random(0, 2)).join(', ') || 'None'}`,
`- **Tools:** ${_.sampleSize(['Artisan\'s tools', 'one musical instrument', 'Thieves\' tools'], _.random(0, 2)).join(', ') || 'None'}`,
'',
'___',
`- **Saving Throws:** ${_.sampleSize(abilityList, 2).join(', ')}`,
`- **Skills:** Choose two from ${_.sampleSize(skillList, _.random(4, 6)).join(', ')}`,
'',
'#### Spellcasting Ability',
'',
`<div style=text-align:center>`,
'___',
`- **Spell save DC** = ${_.sample([6, 8, 10])} + your proficiency bonus + your ${spellSkill} modifier`,
'',
`- **Spell attack modifier** = your proficiency bonus + your ${spellSkill} modifier`,
`</div>`,
'',
'#### Equipment',
'You start with the following equipment, in addition to the equipment granted by your background:',
'- *(a)* a martial weapon and a shield or *(b)* two martial weapons',

View File

@@ -0,0 +1,142 @@
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const StringArrayEditor = createClass({
displayName : 'StringArrayEditor',
getDefaultProps : function() {
return {
label : '',
values : [],
valuePatterns : null,
placeholder : '',
unique : false,
cannotEdit : [],
onChange : ()=>{}
};
},
getInitialState : function() {
return {
valueContext : !!this.props.values ? this.props.values.map((value)=>({
value,
editing : false
})) : [],
temporaryValue : '',
updateValue : ''
};
},
componentDidUpdate : function(prevProps) {
if(!_.eq(this.props.values, prevProps.values)) {
this.setState({
valueContext : this.props.values ? this.props.values.map((newValue)=>({
value : newValue,
editing : this.state.valueContext.find(({ value })=>value === newValue)?.editing || false
})) : []
});
}
},
handleChange : function(value) {
this.props.onChange({
target : {
value
}
});
},
addValue : function(value){
this.handleChange(_.uniq([...this.props.values, value]));
this.setState({
temporaryValue : ''
});
},
removeValue : function(index){
this.handleChange(this.props.values.filter((_, i)=>i !== index));
},
updateValue : function(value, index){
const valueContext = this.state.valueContext;
valueContext[index].value = value;
valueContext[index].editing = false;
this.handleChange(valueContext.map((context)=>context.value));
this.setState({ valueContext, updateValue: '' });
},
editValue : function(index){
if(!!this.props.cannotEdit && this.props.cannotEdit.includes(this.props.values[index])) {
return;
}
const valueContext = this.state.valueContext.map((context, i)=>{
context.editing = index === i;
return context;
});
this.setState({ valueContext, updateValue: this.props.values[index] });
},
valueIsValid : function(value, index) {
const values = _.clone(this.props.values);
if(index !== undefined) {
values.splice(index, 1);
}
const matchesPatterns = !this.props.valuePatterns || this.props.valuePatterns.some((pattern)=>!!(value || '').match(pattern));
const uniqueIfSet = !this.props.unique || !values.includes(value);
return matchesPatterns && uniqueIfSet;
},
handleValueInputKeyDown : function(event, index) {
if(event.key === 'Enter') {
if(this.valueIsValid(event.target.value, index)) {
if(index !== undefined) {
this.updateValue(event.target.value, index);
} else {
this.addValue(event.target.value);
}
}
} else if(event.key === 'Escape') {
this.closeEditInput(index);
}
},
closeEditInput : function(index) {
const valueContext = this.state.valueContext;
valueContext[index].editing = false;
this.setState({ valueContext, updateValue: '' });
},
render : function() {
const valueElements = Object.values(this.state.valueContext).map((context, i)=>context.editing
? <React.Fragment key={i}>
<div className='input-group'>
<input type='text' className={`value ${this.valueIsValid(this.state.updateValue, i) ? '' : 'invalid'}`} autoFocus placeholder={this.props.placeholder}
value={this.state.updateValue}
onKeyDown={(e)=>this.handleValueInputKeyDown(e, i)}
onChange={(e)=>this.setState({ updateValue: e.target.value })}/>
{<div className='icon steel' onClick={(e)=>{ e.stopPropagation(); this.closeEditInput(i); }}><i className='fa fa-undo fa-fw'/></div>}
{this.valueIsValid(this.state.updateValue, i) ? <div className='icon steel' onClick={(e)=>{ e.stopPropagation(); this.updateValue(this.state.updateValue, i); }}><i className='fa fa-check fa-fw'/></div> : null}
</div>
</React.Fragment>
: <div className='badge' key={i} onClick={()=>this.editValue(i)}>{context.value}
{!!this.props.cannotEdit && this.props.cannotEdit.includes(context.value) ? null : <div className='icon steel' onClick={(e)=>{ e.stopPropagation(); this.removeValue(i); }}><i className='fa fa-times fa-fw'/></div>}
</div>
);
return <div className='field values'>
<label>{this.props.label}</label>
<div className='list'>
{valueElements}
<div className='input-group'>
<input type='text' className={`value ${this.valueIsValid(this.state.temporaryValue) ? '' : 'invalid'}`} placeholder={this.props.placeholder}
value={this.state.temporaryValue}
onKeyDown={(e)=>this.handleValueInputKeyDown(e)}
onChange={(e)=>this.setState({ temporaryValue: e.target.value })}/>
{this.valueIsValid(this.state.temporaryValue) ? <div className='icon steel' onClick={(e)=>{ e.stopPropagation(); this.addValue(this.state.temporaryValue); }}><i className='fa fa-check fa-fw'/></div> : null}
</div>
</div>
</div>;
}
});
module.exports = StringArrayEditor;

View File

@@ -1,8 +1,8 @@
require('./homebrew.less');
const React = require('react');
const createClass = require('create-react-class');
const { StaticRouter:Router, Switch, Route } = require('react-router-dom');
const queryString = require('query-string');
const { StaticRouter:Router } = require('react-router-dom/server');
const { Route, Routes, useParams, useSearchParams } = require('react-router-dom');
const HomePage = require('./pages/homePage/homePage.jsx');
const EditPage = require('./pages/editPage/editPage.jsx');
@@ -12,6 +12,19 @@ const NewPage = require('./pages/newPage/newPage.jsx');
//const ErrorPage = require('./pages/errorPage/errorPage.jsx');
const PrintPage = require('./pages/printPage/printPage.jsx');
const WithRoute = (props)=>{
const params = useParams();
const searchParams = useSearchParams();
const Element = props.el;
const allProps = {
...props,
...params,
...searchParams,
el : undefined
};
return <Element {...allProps} />;
};
const Homebrew = createClass({
displayName : 'Homebrewery',
getDefaultProps : function() {
@@ -43,25 +56,24 @@ const Homebrew = createClass({
},
render : function (){
return (
<Router location={this.props.url}>
<div className='homebrew'>
<Switch>
<Route path='/edit/:id' component={(routeProps)=><EditPage id={routeProps.match.params.id} brew={this.props.brew} />}/>
<Route path='/share/:id' component={(routeProps)=><SharePage id={routeProps.match.params.id} brew={this.props.brew} />}/>
<Route path='/new/:id' component={(routeProps)=><NewPage id={routeProps.match.params.id} brew={this.props.brew} />}/>
<Route path='/new' exact component={(routeProps)=><NewPage />}/>
<Route path='/user/:username' component={(routeProps)=><UserPage username={routeProps.match.params.username} brews={this.props.brews} query={queryString.parse(routeProps.location.search)}/>}/>
<Route path='/print/:id' component={(routeProps)=><PrintPage brew={this.props.brew} query={queryString.parse(routeProps.location.search)} />}/>
<Route path='/print' exact component={(routeProps)=><PrintPage query={queryString.parse(routeProps.location.search)} />}/>
<Route path='/changelog' exact component={()=><SharePage brew={this.props.brew} />}/>
<Route path='/faq' exact component={()=><SharePage brew={this.props.brew} />}/>
<Route path='/v3_preview' exact component={()=><HomePage brew={this.props.brew} />}/>
<Route path='/' component={()=><HomePage brew={this.props.brew} />}/>
</Switch>
</div>
</Router>
);
return <Router location={this.props.url}>
<div className='homebrew'>
<Routes>
<Route path='/edit/:id' element={<WithRoute el={EditPage} brew={this.props.brew} />} />
<Route path='/share/:id' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/new/:id' element={<WithRoute el={NewPage} brew={this.props.brew} />} />
<Route path='/new' element={<WithRoute el={NewPage}/>} />
<Route path='/user/:username' element={<WithRoute el={UserPage} brews={this.props.brews} />} />
<Route path='/print/:id' element={<WithRoute el={PrintPage} brew={this.props.brew} />} />
<Route path='/print' element={<WithRoute el={PrintPage} />} />
<Route path='/changelog' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/faq' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/legacy' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
<Route path='/' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
<Route path='/*' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
</Routes>
</div>
</Router>;
}
});

View File

@@ -91,7 +91,7 @@
&:nth-of-type(2){ background-color: darken(@purple, 30%); }
}
.item{
#backgroundColors;
#backgroundColorsHover;
.animate(background-color);
position : relative;
display : block;

View File

@@ -16,8 +16,8 @@ const BrewItem = createClass({
brew : {
title : '',
description : '',
authors : []
authors : [],
stubbed : true
}
};
},
@@ -50,7 +50,7 @@ const BrewItem = createClass({
if(!this.props.brew.editId) return;
let editLink = this.props.brew.editId;
if(this.props.brew.googleId) {
if(this.props.brew.googleId && !this.props.brew.stubbed) {
editLink = this.props.brew.googleId + editLink;
}
@@ -63,7 +63,7 @@ const BrewItem = createClass({
if(!this.props.brew.shareId) return;
let shareLink = this.props.brew.shareId;
if(this.props.brew.googleId) {
if(this.props.brew.googleId && !this.props.brew.stubbed) {
shareLink = this.props.brew.googleId + shareLink;
}
@@ -76,7 +76,7 @@ const BrewItem = createClass({
if(!this.props.brew.shareId) return;
let shareLink = this.props.brew.shareId;
if(this.props.brew.googleId) {
if(this.props.brew.googleId && !this.props.brew.stubbed) {
shareLink = this.props.brew.googleId + shareLink;
}
@@ -86,7 +86,7 @@ const BrewItem = createClass({
},
renderGoogleDriveIcon : function(){
if(!this.props.brew.gDrive) return;
if(!this.props.brew.googleId) return;
return <span>
<img className='googleDriveIcon' src={googleDriveIcon} alt='googleDriveIcon' />
@@ -104,8 +104,8 @@ const BrewItem = createClass({
</div>
<hr />
<div className='info'>
<span title={`Authors:\n${brew.authors.join('\n')}`}>
<i className='fas fa-user'/> {brew.authors.join(', ')}
<span title={`Authors:\n${brew.authors?.join('\n')}`}>
<i className='fas fa-user'/> {brew.authors?.join(', ')}
</span>
<br />
<span title={`Last viewed: ${moment(brew.lastViewed).local().format(dateFormatString)}`}>

View File

@@ -114,7 +114,7 @@ const EditPage = createClass({
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, { text: text }),
brew : { ...prevState.brew, text: text },
isPending : true,
htmlErrors : htmlErrors
}), ()=>this.trySave());
@@ -122,14 +122,17 @@ const EditPage = createClass({
handleStyleChange : function(style){
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, { style: style }),
brew : { ...prevState.brew, style: style },
isPending : true
}), ()=>this.trySave());
},
handleMetaChange : function(metadata){
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, metadata),
brew : {
...prevState.brew,
...metadata
},
isPending : true,
}), ()=>this.trySave());
@@ -200,7 +203,7 @@ const EditPage = createClass({
const brew = this.state.brew;
brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1;
const params = `${transfer ? `?transfer${this.state.saveGoogle ? 'To' : 'From'}Google=true` : ''}`;
const params = `${transfer ? `?${this.state.saveGoogle ? 'saveToGoogle' : 'removeFromGoogle'}=true` : ''}`;
const res = await request
.put(`/api/update/${brew.editId}${params}`)
.send(brew)
@@ -210,16 +213,14 @@ const EditPage = createClass({
});
this.savedBrew = res.body;
if(transfer) {
history.replaceState(null, null, `/edit/${this.savedBrew.googleId ?? ''}${this.savedBrew.editId}`);
}
history.replaceState(null, null, `/edit/${this.savedBrew.editId}`);
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, {
brew : { ...prevState.brew,
googleId : this.savedBrew.googleId ? this.savedBrew.googleId : null,
editId : this.savedBrew.editId,
shareId : this.savedBrew.shareId
}),
},
isPending : false,
isSaving : false,
}));
@@ -340,7 +341,7 @@ const EditPage = createClass({
},
processShareId : function() {
return this.state.brew.googleId ?
return this.state.brew.googleId && !this.state.brew.stubbed ?
this.state.brew.googleId + this.state.brew.shareId :
this.state.brew.shareId;
},

View File

@@ -52,7 +52,7 @@ const HomePage = createClass({
},
handleTextChange : function(text){
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, { text: text })
brew : { ...prevState.brew, text: text }
}));
},
renderNavbar : function(){

View File

@@ -1,50 +1,68 @@
# The Homebrewery
```css
.page #example + table td {
border:1px dashed #00000030;
}
.page {
padding-bottom : 1.1cm;
}
```
# The Homebrewery *V3*
Welcome traveler from an antique land. Please sit and tell us of what you have seen. The unheard of monsters, who slither and bite. Tell us of the wondrous items and and artifacts you have found, their mysteries yet to be unlocked. Of the vexing vocations and surprising skills you have seen.
### Homebrew D&D made easy
The Homebrewery makes the creation and sharing of authentic looking Fifth-Edition homebrews easy. It uses [Markdown](https://help.github.com/articles/markdown-basics/) with a little CSS magic to make your brews come to life.
**Try it!** Simply edit the text on the left and watch it *update live* on the right.
**Try it!** Simply edit the text on the left and watch it *update live* on the right. Note that not every button is visible on this demo page. Click New {{fas,fa-plus-square}} in the navbar above to start brewing with all the features!
### Editing and Sharing
When you create your own homebrew you will be given a *edit url* and a *share url*. Any changes you make will be automatically saved to the database within a few seconds. Anyone with the edit url will be able to make edits to your homebrew. So be careful about who you share it with.
When you create your own homebrew, you will be given a *edit url* and a *share url*.
Any changes you make while on the *edit url* will be automatically saved to the database within a few seconds. Anyone with the edit url will be able to make edits to your homebrew, so be careful about who you share it with.
Anyone with the *share url* will be able to access a read-only version of your homebrew.
## Helping out
{{note
##### PDF Creation
PDF Printing works best in Google Chrome. If you are having quality/consistency issues, try using Chrome to print instead.
After clicking the "Print" item in the navbar a new page will open and a print dialog will pop-up.
* Set the **Destination** to "Save as PDF"
* Set **Paper Size** to "Letter"
* If you are printing on A4 paper, make sure to have the **PRINT → {{far,fa-file}} A4 Pagesize** snippet in your brew
* In **Options** make sure "Background Images" is selected.
* Hit print and enjoy! You're done!
If you want to save ink or have a monochrome printer, add the **PRINT → {{fas,fa-tint}} Ink Friendly** snippet to your brew!
}}
![homebrew mug](http://i.imgur.com/hMna6G0.png) {position:absolute,bottom:20px,left:130px,width:220px}
{{artist,bottom:160px,left:100px
##### Homebrew Mug
[naturalcrit](https://homebrew.naturalcrit.com)
}}
{{pageNumber 1}}
{{footnote PART 1 | FANCINESS}}
\column
## New in V3.0.0
We've implemented an extended Markdown-like syntax for block and span elements, plus a few other changes, eliminating the need for HTML tags like `div` and `span` in most cases. No raw HTML tags should be needed in a brew (*but can still be used if you insist*).
Much of the syntax and styling has changed in V3, so converting a Legacy brew to V3 (or vice-versa) will require tweaking your document. *However*, all brews made prior to the release of v3.0.0 will still render normally, and you may switch between the "Legacy" brew renderer and the newer "V3" renderer via the {{fa,fa-info-circle}} **Properties** button on your brew at any time.
Scroll down to the next page for a brief summary of the changes and new features available in V3!
#### New Things All The Time!
Check out the latest updates in the full changelog [here](/changelog).
### Helping out
Like this tool? Want to buy me a beer? [Head here](https://www.patreon.com/Naturalcrit) to help me keep the servers running.
This tool will **always** be free, never have ads, and I will never offer any "premium" features or whatever.
>##### PDF Exporting
> PDF Printing works best in Chrome. If you are having quality/consistency issues, try using Chrome to print instead.
>
> After clicking the "Print" item in the navbar a new page will open and a print dialog will pop-up.
> * Set the **Destination** to "Save as PDF"
> * Set **Paper Size** to "Letter"
> * If you are printing on A4 paper, make sure to have the "A4 page size snippet" in your brew
> * In **Options** make sure "Background Images" is selected.
> * Hit print and enjoy! You're done!
>
> If you want to save ink or have a monochrome printer, add the **Ink Friendly** snippet to your brew before you print
```
```
## V3.0.0 Released!
With the latest major update to *The Homebrewery* we've implemented an extended Markdown-like syntax for block and span elements, plus a few other changes, eliminating the need for HTML tags like **div** and **span** in most cases. No raw HTML tags should be needed in a brew, and going forward, raw HTML will no longer receive debugging support (*but can still be used if you insist*).
**You can enable V3 via the <span class="fa fa-info-circle" style="text-indent:0"></span> Properties button!**
## New Things All The Time!
What's new in the latest update? Check out the full changelog [here](/changelog)
### Bugs, Issues, Suggestions?
Take a quick look at our [Frequently Asked Questions page](/faq) to see if your question has a handy answer.
@@ -53,56 +71,105 @@ Need help getting started or just the right look for your brew? Head to [r/Homeb
Have an idea to make The Homebrewery better? Or did you find something that wasn't quite right? Check out the [GitHub Repo](https://github.com/naturalcrit/homebrewery/) to report technical issues.
### Legal Junk
The Homebrewery is licensed using the [MIT License](https://github.com/naturalcrit/homebrewery/blob/master/license). This means you are free to use The Homebrewery codebase any way that you want, except for claiming that you made it yourself.
The Homebrewery is licensed using the [MIT License](https://github.com/naturalcrit/homebrewery/blob/master/license). Which means you are free to use The Homebrewery codebase any way that you want, except for claiming that you made it yourself.
If you wish to sell or in some way gain profit for what you make on this site, it's your responsibility to ensure you have the proper licenses/rights for any images or resources used.
If you wish to sell or in some way gain profit for what's created on this site, it's your responsibility to ensure you have the proper licenses/rights for any images or resources used.
### More Resources
#### Crediting Me
If you'd like to credit me in your brew, I'd be flattered! Just reference that you made it with The Homebrewery.
### More Homebrew Resources
<a href='https://discord.gg/by3deKx' target='_blank'><img src='/assets/discordOfManyThings.svg' alt='Discord of Many Things Logo' title='Discord of Many Things Logo' style='width:50px; float: right; padding-left: 10px;'/></a>
If you are looking for more 5e Homebrew resources check out [r/UnearthedArcana](https://www.reddit.com/r/UnearthedArcana/) and their list of useful resources [here](https://www.reddit.com/r/UnearthedArcana/wiki/resources). The <a href='https://discord.gg/by3deKx' target='_blank' title='Discord of Many Things'>Discord of Many Things</a> is another great resource to connect with fellow homebrewers for help and feedback.
<img src='https://i.imgur.com/hMna6G0.png' style='position:absolute;bottom:40px;right:30px;width:280px' />
<div class='pageNumber'>1</div>
<div class='footnote'>PART 1 | FANCINESS</div>
<div style='position: absolute; top: 20px; right: 20px;'>
<a href='https://discord.gg/by3deKx' target='_blank' title='Discord of Many Things'><img src='/assets/discord.png' style='height:30px'/></a>
{{position:absolute;top:20px;right:20px;width:auto
<a href='https://discord.gg/by3deKx' target='_blank' title='Discord of Many Things' style='color: black;'><img src='/assets/discord.png' style='height:30px'/></a>
<a href='https://github.com/naturalcrit/homebrewery' target='_blank' title='Github' style='color: black; padding-left: 5px;'><img src='/assets/github.png' style='height:30px'/></a>
<a href='https://patreon.com/NaturalCrit' target='_blank' title='Patreon' style='color: black; padding-left: 5px;'><img src='/assets/patreon.png' style='height:30px'/></a>
<a href='https://www.reddit.com/r/homebrewery/' target='_blank' title='Reddit' style='color: black; padding-left: 5px;'><img src='/assets/reddit.png' style='height:30px'/></a>
</div>
}}
\page
# Appendix
## Markdown+
The Homebrewery aims to make homebrewing as simple as possible, providing a live editor with Markdown syntax that is more human-readable and faster to write with than raw HTML.
### Not quite Markdown
Although the Homebrewery uses Markdown, to get all the styling features from the PHB, we had to get a little creative. Some base HTML elements are not used as expected and I've had to include a few new keywords.
In version 3.0.0, with a goal of adding maximum flexibility without users resorting to complex HTML to accomplish simple tasks, Homebrewery provides an extended verision of Markdown with additional syntax.
**You can enable V3 via the {{fa,fa-info-circle}} Properties button!**
___
* **Horizontal Rules** are generally used to *modify* existing elements into a different style. For example, a horizontal rule before a blockquote will give it the style of a Monster Stat Block instead of a note.
* **New Pages** are controlled by the author. It's impossible for the site to detect when the end of a page is reached, so indicate you'd like to start a new page, use the new page snippet to get the syntax.
* **Code Blocks** are used only to indicate column breaks. Since they don't allow for styling within them, they weren't that useful to use.
* **HTML** can be used to get *just* the right look for your homebrew. I've included some examples in the snippet icons above the editor.
### Curly Brackets
The biggest change in V3 is the replacement of `<span></span>` and `<div></div>` with `{{ }}` for a cleaner custom formatting. Inline spans and block elements can be created and given ID's and Classes, as well as css properties, each of which are comma separated with no spaces. Use double quotes if a value requires spaces. Spans and Blocks start the same:
#### Span
My favorite author is {{pen,#author,color:orange,font-family:"trebuchet ms" Brandon Sanderson}}. The orange text has a class of `pen`, an id of `author`, is colored orange, and given a new font. The first space outside of quotes marks the beginning of the content.
#### Block
{{purple,#book,text-align:center,background:#aa88aa55
My favorite book is Wheel of Time. This block has a class of `purple`, an id of `book`, and centered text with a colored background. The opening and closing brackets are on lines separate from the block contents.
}}
```
```
#### Injection
For any element not inside a span or block, you can *inject* attributes using the same syntax but with single brackets in a single line immediately after the element.
Inline elements like *italics* {color:#D35400} or images require the injection on the same line.
### Images
Images must be hosted online somewhere, like imgur. You use the address to that image to reference it in your brew. Images can be included 'inline' with the text using Markdown-style images. However for background images more control is needed.
Block elements like headers require the injection to start on the line immediately following.
Background images should be included as HTML-style img tags. Using inline CSS you can precisely position your image where you'd like it to be. I have added both a inflow image snippet and a background image snippet to give you exmaples of how to do it.
##### A Purple Header
{color:purple,text-align:center}
\* *this does not currently work for tables yet*
### Vertical Spacing
A blank line can be achieved with a run of one or more `:` alone on a line. More `:`'s will create more space.
### Crediting Me
If you'd like to credit The Homebrewery in your brew, I'd be flattered! Just reference that you made it with The Homebrewery.
::
Much nicer than `<br><br><br><br><br>`
### Definition Lists
**Example** :: V3 uses HTML *definition lists* to create "lists" with hanging indents.
<div class='pageNumber'>2</div>
<div class='footnote'>PART 2 | BORING STUFF</div>
### Column Breaks
Column and page breaks with `\column` and `\page`.
\column
### Tables
Tables now allow column & row spanning between cells. This is included in some updated snippets, but a simplified example is given below.
A cell can be spanned across columns by grouping multiple pipe `|` characters at the end of a cell.
Row spanning is achieved by adding a `^` at the end of a cell just before the `|`.
These can be combined to span a cell across both columns and rows. Cells must have the same colspan if they are to be rowspan'd.
##### Example
| Head A | Spanned Header ||
| Head B | Head C | Head D |
|:-------|:------:|:------:|
| 1A | 1B | 1C |
| 2A ^| 2B | 2C |
| 3A ^| 3B 3C ||
| 4A | 4B 4C^||
| 5A ^| 5B | 5C |
| 6A | 6B ^| 6C |
## Images
Images must be hosted online somewhere, like [Imgur](https://www.imgur.com). You use the address to that image to reference it in your brew\*.
Using *Curly Injection* you can assign an id, classes, or inline CSS properties to the Markdown image syntax.
![alt-text](https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg) {width:100px,border:"2px solid",border-radius:10px}
\* *When using Imgur-hosted images, use the "direct link", which can be found when you click into your image in the Imgur interace.*
## Snippets
Homebrewery comes with a series of *code snippets* found at the top of the editor pane that make it easy to create brews as quickly as possible. Just set your cursor where you want the code to appear in the editor pane, choose a snippet, and make the adjustments you need.
## Style Editor Panel
{{fa,fa-paint-brush}} Technically released prior to v3 but still new to many users, check out the new **Style Editor** located on the right side of the Snippet bar. This editor accepts CSS for styling without requiring `<style>` tags-- anything that would have gone inside style tags before can now be placed here, and snippets that insert CSS styles are now located on that tab.
{{pageNumber 2}}
{{footnote PART 2 | BORING STUFF}}

View File

@@ -0,0 +1,108 @@
# The Homebrewery
Welcome traveler from an antique land. Please sit and tell us of what you have seen. The unheard of monsters, who slither and bite. Tell us of the wondrous items and and artifacts you have found, their mysteries yet to be unlocked. Of the vexing vocations and surprising skills you have seen.
### Homebrew D&D made easy
The Homebrewery makes the creation and sharing of authentic looking Fifth-Edition homebrews easy. It uses [Markdown](https://help.github.com/articles/markdown-basics/) with a little CSS magic to make your brews come to life.
**Try it!** Simply edit the text on the left and watch it *update live* on the right.
### Editing and Sharing
When you create your own homebrew you will be given a *edit url* and a *share url*. Any changes you make will be automatically saved to the database within a few seconds. Anyone with the edit url will be able to make edits to your homebrew. So be careful about who you share it with.
Anyone with the *share url* will be able to access a read-only version of your homebrew.
## Helping out
Like this tool? Want to buy me a beer? [Head here](https://www.patreon.com/Naturalcrit) to help me keep the servers running.
This tool will **always** be free, never have ads, and I will never offer any "premium" features or whatever.
>##### PDF Exporting
> PDF Printing works best in Chrome. If you are having quality/consistency issues, try using Chrome to print instead.
>
> After clicking the "Print" item in the navbar a new page will open and a print dialog will pop-up.
> * Set the **Destination** to "Save as PDF"
> * Set **Paper Size** to "Letter"
> * If you are printing on A4 paper, make sure to have the "A4 page size snippet" in your brew
> * In **Options** make sure "Background Images" is selected.
> * Hit print and enjoy! You're done!
>
> If you want to save ink or have a monochrome printer, add the **Ink Friendly** snippet to your brew before you print
```
```
## V3.0.0 Released!
With the latest major update to *The Homebrewery* we've implemented an extended Markdown-like syntax for block and span elements, plus a few other changes, eliminating the need for HTML tags like **div** and **span** in most cases. No raw HTML tags should be needed in a brew, and going forward, raw HTML will no longer receive debugging support (*but can still be used if you insist*).
**You can enable V3 via the <span class="fa fa-info-circle" style="text-indent:0"></span> Properties button!**
## New Things All The Time!
What's new in the latest update? Check out the full changelog [here](/changelog)
### Bugs, Issues, Suggestions?
Take a quick look at our [Frequently Asked Questions page](/faq) to see if your question has a handy answer.
Need help getting started or just the right look for your brew? Head to [r/Homebrewery](https://www.reddit.com/r/homebrewery/submit?selftext=true&title=%5BIssue%5D%20Describe%20Your%20Issue%20Here) and let us know!
Have an idea to make The Homebrewery better? Or did you find something that wasn't quite right? Check out the [GitHub Repo](https://github.com/naturalcrit/homebrewery/) to report technical issues.
### Legal Junk
The Homebrewery is licensed using the [MIT License](https://github.com/naturalcrit/homebrewery/blob/master/license). This means you are free to use The Homebrewery codebase any way that you want, except for claiming that you made it yourself.
If you wish to sell or in some way gain profit for what you make on this site, it's your responsibility to ensure you have the proper licenses/rights for any images or resources used.
### More Resources
<a href='https://discord.gg/by3deKx' target='_blank'><img src='/assets/discordOfManyThings.svg' alt='Discord of Many Things Logo' title='Discord of Many Things Logo' style='width:50px; float: right; padding-left: 10px;'/></a>
If you are looking for more 5e Homebrew resources check out [r/UnearthedArcana](https://www.reddit.com/r/UnearthedArcana/) and their list of useful resources [here](https://www.reddit.com/r/UnearthedArcana/wiki/resources). The <a href='https://discord.gg/by3deKx' target='_blank' title='Discord of Many Things'>Discord of Many Things</a> is another great resource to connect with fellow homebrewers for help and feedback.
<img src='https://i.imgur.com/hMna6G0.png' style='position:absolute;bottom:40px;right:30px;width:280px' />
<div class='pageNumber'>1</div>
<div class='footnote'>PART 1 | FANCINESS</div>
<div style='position: absolute; top: 20px; right: 20px;'>
<a href='https://discord.gg/by3deKx' target='_blank' title='Discord of Many Things'><img src='/assets/discord.png' style='height:30px'/></a>
<a href='https://github.com/naturalcrit/homebrewery' target='_blank' title='Github' style='color: black; padding-left: 5px;'><img src='/assets/github.png' style='height:30px'/></a>
<a href='https://patreon.com/NaturalCrit' target='_blank' title='Patreon' style='color: black; padding-left: 5px;'><img src='/assets/patreon.png' style='height:30px'/></a>
<a href='https://www.reddit.com/r/homebrewery/' target='_blank' title='Reddit' style='color: black; padding-left: 5px;'><img src='/assets/reddit.png' style='height:30px'/></a>
</div>
\page
# Appendix
### Not quite Markdown
Although the Homebrewery uses Markdown, to get all the styling features from the PHB, we had to get a little creative. Some base HTML elements are not used as expected and I've had to include a few new keywords.
___
* **Horizontal Rules** are generally used to *modify* existing elements into a different style. For example, a horizontal rule before a blockquote will give it the style of a Monster Stat Block instead of a note.
* **New Pages** are controlled by the author. It's impossible for the site to detect when the end of a page is reached, so indicate you'd like to start a new page, use the new page snippet to get the syntax.
* **Code Blocks** are used only to indicate column breaks. Since they don't allow for styling within them, they weren't that useful to use.
* **HTML** can be used to get *just* the right look for your homebrew. I've included some examples in the snippet icons above the editor.
```
```
### Images
Images must be hosted online somewhere, like imgur. You use the address to that image to reference it in your brew. Images can be included 'inline' with the text using Markdown-style images. However for background images more control is needed.
Background images should be included as HTML-style img tags. Using inline CSS you can precisely position your image where you'd like it to be. I have added both a inflow image snippet and a background image snippet to give you exmaples of how to do it.
### Crediting Me
If you'd like to credit The Homebrewery in your brew, I'd be flattered! Just reference that you made it with The Homebrewery.
<div class='pageNumber'>2</div>
<div class='footnote'>PART 2 | BORING STUFF</div>

View File

@@ -1,175 +0,0 @@
```css
.page #example + table td {
border:1px dashed #00000030;
}
.page {
padding-bottom : 1.1cm;
}
```
# The Homebrewery *V3*
Welcome traveler from an antique land. Please sit and tell us of what you have seen. The unheard of monsters, who slither and bite. Tell us of the wondrous items and and artifacts you have found, their mysteries yet to be unlocked. Of the vexing vocations and surprising skills you have seen.
### Homebrew D&D made easy
The Homebrewery makes the creation and sharing of authentic looking Fifth-Edition homebrews easy. It uses [Markdown](https://help.github.com/articles/markdown-basics/) with a little CSS magic to make your brews come to life.
**Try it!** Simply edit the text on the left and watch it *update live* on the right. Note that not every button is visible on this demo page. Click New {{fas,fa-plus-square}} in the navbar above to start brewing with all the features!
### Editing and Sharing
When you create your own homebrew, you will be given a *edit url* and a *share url*.
Any changes you make while on the *edit url* will be automatically saved to the database within a few seconds. Anyone with the edit url will be able to make edits to your homebrew, so be careful about who you share it with.
Anyone with the *share url* will be able to access a read-only version of your homebrew.
{{note
##### PDF Creation
PDF Printing works best in Google Chrome. If you are having quality/consistency issues, try using Chrome to print instead.
After clicking the "Print" item in the navbar a new page will open and a print dialog will pop-up.
* Set the **Destination** to "Save as PDF"
* Set **Paper Size** to "Letter"
* If you are printing on A4 paper, make sure to have the **PRINT → {{far,fa-file}} A4 Pagesize** snippet in your brew
* In **Options** make sure "Background Images" is selected.
* Hit print and enjoy! You're done!
If you want to save ink or have a monochrome printer, add the **PRINT → {{fas,fa-tint}} Ink Friendly** snippet to your brew!
}}
![homebrew mug](http://i.imgur.com/hMna6G0.png) {position:absolute,bottom:20px,left:130px,width:220px}
{{artist,bottom:160px,left:100px
##### Homebrew Mug
[naturalcrit](https://homebrew.naturalcrit.com)
}}
{{pageNumber 1}}
{{footnote PART 1 | FANCINESS}}
\column
## New in V3.0.0
We've implemented an extended Markdown-like syntax for block and span elements, plus a few other changes, eliminating the need for HTML tags like `div` and `span` in most cases. No raw HTML tags should be needed in a brew (*but can still be used if you insist*).
Much of the syntax and styling has changed in V3, so converting a Legacy brew to V3 (or vice-versa) will require tweaking your document. *However*, all brews made prior to the release of v3.0.0 will still render normally, and you may switch between the "Legacy" brew renderer and the newer "V3" renderer via the {{fa,fa-info-circle}} **Properties** button on your brew at any time.
Scroll down to the next page for a brief summary of the changes and new features available in V3!
#### New Things All The Time!
Check out the latest updates in the full changelog [here](/changelog).
### Helping out
Like this tool? Want to buy me a beer? [Head here](https://www.patreon.com/Naturalcrit) to help me keep the servers running.
This tool will **always** be free, never have ads, and I will never offer any "premium" features or whatever.
### Bugs, Issues, Suggestions?
Take a quick look at our [Frequently Asked Questions page](/faq) to see if your question has a handy answer.
Need help getting started or just the right look for your brew? Head to [r/Homebrewery](https://www.reddit.com/r/homebrewery/submit?selftext=true&title=%5BIssue%5D%20Describe%20Your%20Issue%20Here) and let us know!
Have an idea to make The Homebrewery better? Or did you find something that wasn't quite right? Check out the [GitHub Repo](https://github.com/naturalcrit/homebrewery/) to report technical issues.
### Legal Junk
The Homebrewery is licensed using the [MIT License](https://github.com/naturalcrit/homebrewery/blob/master/license). Which means you are free to use The Homebrewery codebase any way that you want, except for claiming that you made it yourself.
If you wish to sell or in some way gain profit for what's created on this site, it's your responsibility to ensure you have the proper licenses/rights for any images or resources used.
#### Crediting Me
If you'd like to credit me in your brew, I'd be flattered! Just reference that you made it with The Homebrewery.
### More Homebrew Resources
<a href='https://discord.gg/by3deKx' target='_blank'><img src='/assets/discordOfManyThings.svg' alt='Discord of Many Things Logo' title='Discord of Many Things Logo' style='width:50px; float: right; padding-left: 10px;'/></a>
If you are looking for more 5e Homebrew resources check out [r/UnearthedArcana](https://www.reddit.com/r/UnearthedArcana/) and their list of useful resources [here](https://www.reddit.com/r/UnearthedArcana/wiki/resources). The <a href='https://discord.gg/by3deKx' target='_blank' title='Discord of Many Things'>Discord of Many Things</a> is another great resource to connect with fellow homebrewers for help and feedback.
{{position:absolute;top:20px;right:20px;width:auto
<a href='https://discord.gg/by3deKx' target='_blank' title='Discord of Many Things' style='color: black;'><img src='/assets/discord.png' style='height:30px'/></a>
<a href='https://github.com/naturalcrit/homebrewery' target='_blank' title='Github' style='color: black; padding-left: 5px;'><img src='/assets/github.png' style='height:30px'/></a>
<a href='https://patreon.com/NaturalCrit' target='_blank' title='Patreon' style='color: black; padding-left: 5px;'><img src='/assets/patreon.png' style='height:30px'/></a>
<a href='https://www.reddit.com/r/homebrewery/' target='_blank' title='Reddit' style='color: black; padding-left: 5px;'><img src='/assets/reddit.png' style='height:30px'/></a>
}}
\page
## Markdown+
The Homebrewery aims to make homebrewing as simple as possible, providing a live editor with Markdown syntax that is more human-readable and faster to write with than raw HTML.
In version 3.0.0, with a goal of adding maximum flexibility without users resorting to complex HTML to accomplish simple tasks, Homebrewery provides an extended verision of Markdown with additional syntax.
**You can enable V3 via the {{fa,fa-info-circle}} Properties button!**
### Curly Brackets
The biggest change in V3 is the replacement of `<span></span>` and `<div></div>` with `{{ }}` for a cleaner custom formatting. Inline spans and block elements can be created and given ID's and Classes, as well as css properties, each of which are comma separated with no spaces. Use double quotes if a value requires spaces. Spans and Blocks start the same:
#### Span
My favorite author is {{pen,#author,color:orange,font-family:"trebuchet ms" Brandon Sanderson}}. The orange text has a class of `pen`, an id of `author`, is colored orange, and given a new font. The first space outside of quotes marks the beginning of the content.
#### Block
{{purple,#book,text-align:center,background:#aa88aa55
My favorite book is Wheel of Time. This block has a class of `purple`, an id of `book`, and centered text with a colored background. The opening and closing brackets are on lines separate from the block contents.
}}
#### Injection
For any element not inside a span or block, you can *inject* attributes using the same syntax but with single brackets in a single line immediately after the element.
Inline elements like *italics* {color:#D35400} or images require the injection on the same line.
Block elements like headers require the injection to start on the line immediately following.
##### A Purple Header
{color:purple,text-align:center}
\* *this does not currently work for tables yet*
### Vertical Spacing
A blank line can be achieved with a run of one or more `:` alone on a line. More `:`'s will create more space.
::
Much nicer than `<br><br><br><br><br>`
### Definition Lists
**Example** :: V3 uses HTML *definition lists* to create "lists" with hanging indents.
### Column Breaks
Column and page breaks with `\column` and `\page`.
\column
### Tables
Tables now allow column & row spanning between cells. This is included in some updated snippets, but a simplified example is given below.
A cell can be spanned across columns by grouping multiple pipe `|` characters at the end of a cell.
Row spanning is achieved by adding a `^` at the end of a cell just before the `|`.
These can be combined to span a cell across both columns and rows. Cells must have the same colspan if they are to be rowspan'd.
##### Example
| Head A | Spanned Header ||
| Head B | Head C | Head D |
|:-------|:------:|:------:|
| 1A | 1B | 1C |
| 2A ^| 2B | 2C |
| 3A ^| 3B 3C ||
| 4A | 4B 4C^||
| 5A ^| 5B | 5C |
| 6A | 6B ^| 6C |
## Images
Images must be hosted online somewhere, like [Imgur](https://www.imgur.com). You use the address to that image to reference it in your brew\*.
Using *Curly Injection* you can assign an id, classes, or inline CSS properties to the Markdown image syntax.
![alt-text](https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg) {width:100px,border:"2px solid",border-radius:10px}
\* *When using Imgur-hosted images, use the "direct link", which can be found when you click into your image in the Imgur interace.*
## Snippets
Homebrewery comes with a series of *code snippets* found at the top of the editor pane that make it easy to create brews as quickly as possible. Just set your cursor where you want the code to appear in the editor pane, choose a snippet, and make the adjustments you need.
## Style Editor Panel
{{fa,fa-paint-brush}} Technically released prior to v3 but still new to many users, check out the new **Style Editor** located on the right side of the Snippet bar. This editor accepts CSS for styling without requiring `<style>` tags-- anything that would have gone inside style tags before can now be placed here, and snippets that insert CSS styles are now located on that tab.
{{pageNumber 2}}
{{footnote PART 2 | BORING STUFF}}

View File

@@ -73,7 +73,7 @@ const NewPage = createClass({
published : false,
authors : [],
systems : brew.systems || [],
renderer : brew.renderer || 'legacy'
renderer : brew.renderer || 'V3'
},
isSaving : false,
@@ -112,7 +112,7 @@ const NewPage = createClass({
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, { text: text }),
brew : { ...prevState.brew, text: text },
htmlErrors : htmlErrors
}));
localStorage.setItem(BREWKEY, text);
@@ -120,14 +120,14 @@ const NewPage = createClass({
handleStyleChange : function(style){
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, { style: style }),
brew : { ...prevState.brew, style: style },
}));
localStorage.setItem(STYLEKEY, style);
},
handleMetaChange : function(metadata){
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, metadata),
brew : { ...prevState.brew, ...metadata },
}));
localStorage.setItem(METAKEY, JSON.stringify({
// 'title' : this.state.brew.title,
@@ -162,7 +162,7 @@ const NewPage = createClass({
brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1;
const res = await request
.post(`/api${this.state.saveGoogle ? '?transferToGoogle=true' : ''}`)
.post(`/api${this.state.saveGoogle ? '?saveToGoogle=true' : ''}`)
.send(brew)
.catch((err)=>{
console.log(err);
@@ -174,7 +174,7 @@ const NewPage = createClass({
localStorage.removeItem(BREWKEY);
localStorage.removeItem(STYLEKEY);
localStorage.removeItem(METAKEY);
window.location = `/edit/${brew.googleId ?? ''}${brew.editId}`;
window.location = `/edit/${brew.editId}`;
},
renderSaveButton : function(){

View File

@@ -49,7 +49,7 @@ const SharePage = createClass({
},
processShareId : function() {
return this.props.brew.googleId ?
return this.props.brew.googleId && !this.props.brew.stubbed ?
this.props.brew.googleId + this.props.brew.shareId :
this.props.brew.shareId;
},

View File

@@ -8,7 +8,7 @@ module.exports = async(name, title = '', props = {})=>{
<link href="//use.fontawesome.com/releases/v5.15.1/css/all.css" rel="stylesheet" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
<link href=${`/${name}/bundle.css`} rel='stylesheet' />
<link rel="icon" href="/assets/homebrew/favicon.ico" type="image/x-icon" />
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />
<meta property="og:title" content="${props.brew?.title || 'Homebrewery - Untitled Brew'}">
<meta property="og:url" content="${HOMEBREWERY_PUBLIC_URL}/${props.brew?.shareId ? `share/${props.brew.shareId}` : ''}">
<meta property="og:image" content="${props.brew?.thumbnail || `${HOMEBREWERY_PUBLIC_URL}/thumbnail.png`}">

4620
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown",
"version": "3.1.1",
"version": "3.2.0",
"engines": {
"node": "16.11.x"
},
@@ -51,13 +51,13 @@
]
},
"dependencies": {
"@babel/core": "^7.18.2",
"@babel/plugin-transform-runtime": "^7.18.2",
"@babel/preset-env": "^7.18.2",
"@babel/preset-react": "^7.17.12",
"@babel/core": "^7.18.13",
"@babel/plugin-transform-runtime": "^7.18.10",
"@babel/preset-env": "^7.18.10",
"@babel/preset-react": "^7.18.6",
"body-parser": "^1.20.0",
"classnames": "^2.3.1",
"codemirror": "^5.65.5",
"codemirror": "^5.65.6",
"cookie-parser": "^1.4.6",
"create-react-class": "^15.7.0",
"dedent-tabs": "^0.10.1",
@@ -65,31 +65,30 @@
"express-async-handler": "^1.2.0",
"express-static-gzip": "2.1.7",
"fs-extra": "10.1.0",
"googleapis": "101.0.0",
"googleapis": "107.0.0",
"js-yaml": "^4.1.0",
"jwt-simple": "^0.5.6",
"less": "^3.13.1",
"lodash": "^4.17.21",
"marked": "4.0.16",
"marked": "4.0.19",
"marked-extended-tables": "^1.0.3",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.29.3",
"mongoose": "^6.3.6",
"moment": "^2.29.4",
"mongoose": "^6.5.3",
"nanoid": "3.3.4",
"nconf": "^0.12.0",
"query-string": "7.1.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-frame-component": "4.1.3",
"react-router-dom": "5.3.0",
"react-router-dom": "6.3.0",
"sanitize-filename": "1.6.3",
"superagent": "^6.1.0",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
},
"devDependencies": {
"eslint": "^8.17.0",
"eslint-plugin-react": "^7.30.0",
"jest": "^28.1.1",
"supertest": "^6.2.3"
"eslint": "^8.22.0",
"eslint-plugin-react": "^7.31.0",
"jest": "^28.1.3",
"supertest": "^6.2.4"
}
}

View File

@@ -26,6 +26,7 @@ const build = async ({ bundle, render, ssr })=>{
await fs.outputFile('./build/homebrew/ssr.js', ssr);
await fs.copy('./themes/fonts', './build/fonts');
await fs.copy('./themes/assets', './build/assets');
await fs.copy('./client/homebrew/favicon.ico', './build/assets/favicon.ico');
let src = './themes/5ePhbLegacy.style.less';
//Parse brew theme files
less.render(fs.readFileSync(src).toString(), {

View File

@@ -9,47 +9,12 @@ const yaml = require('js-yaml');
const app = express();
const config = require('./config.js');
const homebrewApi = require('./homebrew.api.js');
const { homebrewApi, getBrew } = require('./homebrew.api.js');
const GoogleActions = require('./googleActions.js');
const serveCompressedStaticAssets = require('./static-assets.mv.js');
const sanitizeFilename = require('sanitize-filename');
const asyncHandler = require('express-async-handler');
const brewAccessTypes = ['edit', 'share', 'raw'];
//Get the brew object from the HB database or Google Drive
const getBrewFromId = asyncHandler(async (id, accessType)=>{
if(!brewAccessTypes.includes(accessType))
throw ('Invalid Access Type when getting brew');
let brew;
if(id.length > 12) {
const googleId = id.slice(0, -12);
id = id.slice(-12);
brew = await GoogleActions.getGoogleBrew(googleId, id, accessType);
} else {
brew = await HomebrewModel.get(accessType == 'edit' ? { editId: id } : { shareId: id });
brew = brew.toObject(); // Convert MongoDB object to standard Javascript Object
}
brew = sanitizeBrew(brew, accessType === 'edit' ? false : true);
//Split brew.text into text and style
//unless the Access Type is RAW, in which case return immediately
if(accessType == 'raw') {
return brew;
}
splitTextStyleAndMetadata(brew);
return brew;
});
const sanitizeBrew = (brew, full=false)=>{
delete brew._id;
delete brew.__v;
if(full){
delete brew.editId;
}
return brew;
};
const splitTextStyleAndMetadata = (brew)=>{
brew.text = brew.text.replaceAll('\r\n', '\n');
if(brew.text.startsWith('```metadata')) {
@@ -66,6 +31,15 @@ const splitTextStyleAndMetadata = (brew)=>{
}
};
const sanitizeBrew = (brew, accessType)=>{
brew._id = undefined;
brew.__v = undefined;
if(accessType !== 'edit'){
brew.editId = undefined;
}
return brew;
};
app.use('/', serveCompressedStaticAssets(`build`));
//app.use(express.static(`${__dirname}/build`));
@@ -93,12 +67,12 @@ app.use((req, res, next)=>{
app.use(homebrewApi);
app.use(require('./admin.api.js'));
const HomebrewModel = require('./homebrew.model.js').model;
const welcomeText = require('fs').readFileSync('client/homebrew/pages/homePage/welcome_msg.md', 'utf8');
const welcomeTextV3 = require('fs').readFileSync('client/homebrew/pages/homePage/welcome_msg_v3.md', 'utf8');
const migrateText = require('fs').readFileSync('client/homebrew/pages/homePage/migrate.md', 'utf8');
const changelogText = require('fs').readFileSync('changelog.md', 'utf8');
const faqText = require('fs').readFileSync('faq.md', 'utf8');
const HomebrewModel = require('./homebrew.model.js').model;
const welcomeText = require('fs').readFileSync('client/homebrew/pages/homePage/welcome_msg.md', 'utf8');
const welcomeTextLegacy = require('fs').readFileSync('client/homebrew/pages/homePage/welcome_msg_legacy.md', 'utf8');
const migrateText = require('fs').readFileSync('client/homebrew/pages/homePage/migrate.md', 'utf8');
const changelogText = require('fs').readFileSync('changelog.md', 'utf8');
const faqText = require('fs').readFileSync('faq.md', 'utf8');
String.prototype.replaceAll = function(s, r){return this.split(s).join(r);};
@@ -108,63 +82,60 @@ app.get('/robots.txt', (req, res)=>{
});
//Home page
app.get('/', async (req, res, next)=>{
const brew = {
text : welcomeText
app.get('/', (req, res, next)=>{
req.brew = {
text : welcomeText,
renderer : 'V3'
};
req.brew = brew;
splitTextStyleAndMetadata(req.brew);
return next();
});
//Home page v3
app.get('/v3_preview', async (req, res, next)=>{
const brew = {
text : welcomeTextV3,
renderer : 'V3'
app.get('/legacy', (req, res, next)=>{
req.brew = {
text : welcomeTextLegacy,
renderer : 'legacy'
};
splitTextStyleAndMetadata(brew);
req.brew = brew;
splitTextStyleAndMetadata(req.brew);
return next();
});
//Legacy/Other Document -> v3 Migration Guide
app.get('/migrate', async (req, res, next)=>{
const brew = {
app.get('/migrate', (req, res, next)=>{
req.brew = {
text : migrateText,
renderer : 'V3'
};
splitTextStyleAndMetadata(brew);
req.brew = brew;
splitTextStyleAndMetadata(req.brew);
return next();
});
//Changelog page
app.get('/changelog', async (req, res, next)=>{
const brew = {
req.brew = {
title : 'Changelog',
text : changelogText,
renderer : 'V3'
};
splitTextStyleAndMetadata(brew);
req.brew = brew;
splitTextStyleAndMetadata(req.brew);
return next();
});
//FAQ page
app.get('/faq', async (req, res, next)=>{
const brew = {
req.brew = {
title : 'FAQ',
text : faqText,
renderer : 'V3'
};
splitTextStyleAndMetadata(brew);
req.brew = brew;
splitTextStyleAndMetadata(req.brew);
return next();
});
//Source page
app.get('/source/:id', asyncHandler(async (req, res)=>{
const brew = await getBrewFromId(req.params.id, 'raw');
app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{
const { brew } = req;
const replaceStrings = { '&': '&amp;', '<': '&lt;', '>': '&gt;' };
let text = brew.text;
@@ -173,11 +144,12 @@ app.get('/source/:id', asyncHandler(async (req, res)=>{
}
text = `<code><pre style="white-space: pre-wrap;">${text}</pre></code>`;
res.status(200).send(text);
}));
});
//Download brew source page
app.get('/download/:id', asyncHandler(async (req, res)=>{
const brew = await getBrewFromId(req.params.id, 'raw');
app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
const { brew } = req;
sanitizeBrew(brew, 'share');
const prefix = 'HB - ';
let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', '');
@@ -188,13 +160,14 @@ app.get('/download/:id', asyncHandler(async (req, res)=>{
'Content-Disposition' : `attachment; filename="${fileName}.txt"`
});
res.status(200).send(brew.text);
}));
});
//User Page
app.get('/user/:username', async (req, res, next)=>{
const ownAccount = req.account && (req.account.username == req.params.username);
const fields = [
'googleId',
'title',
'pageCount',
'description',
@@ -205,7 +178,8 @@ app.get('/user/:username', async (req, res, next)=>{
'editId',
'createdAt',
'updatedAt',
'lastViewed'
'lastViewed',
'tags'
];
let brews = await HomebrewModel.getByUser(req.params.username, ownAccount, fields)
@@ -220,58 +194,71 @@ app.get('/user/:username', async (req, res, next)=>{
console.error(err);
});
if(googleBrews) {
if(googleBrews && googleBrews.length > 0) {
for (const brew of brews.filter((brew)=>brew.googleId)) {
const match = googleBrews.findIndex((b)=>b.editId === brew.editId);
if(match !== -1) {
brew.googleId = googleBrews[match].googleId;
brew.stubbed = true;
brew.pageCount = googleBrews[match].pageCount;
brew.renderer = googleBrews[match].renderer;
brew.version = googleBrews[match].version;
googleBrews.splice(match, 1);
}
}
googleBrews = googleBrews.map((brew)=>({ ...brew, authors: [req.account.username] }));
brews = _.concat(brews, googleBrews);
}
}
req.brews = _.map(brews, (brew)=>{
return sanitizeBrew(brew, !ownAccount);
return sanitizeBrew(brew, ownAccount ? 'edit' : 'share');
});
return next();
});
//Edit Page
app.get('/edit/:id', asyncHandler(async (req, res, next)=>{
app.get('/edit/:id', asyncHandler(getBrew('edit')), (req, res, next)=>{
req.brew = req.brew.toObject ? req.brew.toObject() : req.brew;
sanitizeBrew(req.brew, 'edit');
splitTextStyleAndMetadata(req.brew);
res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save.
const brew = await getBrewFromId(req.params.id, 'edit');
req.brew = brew;
return next();
}));
});
//New Page
app.get('/new/:id', asyncHandler(async (req, res, next)=>{
const brew = await getBrewFromId(req.params.id, 'share');
brew.title = `CLONE - ${brew.title}`;
req.brew = brew;
app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
sanitizeBrew(req.brew, 'share');
splitTextStyleAndMetadata(req.brew);
req.brew.title = `CLONE - ${req.brew.title}`;
return next();
}));
});
//Share Page
app.get('/share/:id', asyncHandler(async (req, res, next)=>{
const brew = await getBrewFromId(req.params.id, 'share');
app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{
const { brew } = req;
if(req.params.id.length > 12) {
if(req.params.id.length > 12 && !brew._id) {
const googleId = req.params.id.slice(0, -12);
const shareId = req.params.id.slice(-12);
await GoogleActions.increaseView(googleId, shareId, 'share', brew)
.catch((err)=>{next(err);});
.catch((err)=>{next(err);});
} else {
await HomebrewModel.increaseView({ shareId: brew.shareId });
}
req.brew = brew;
sanitizeBrew(req.brew, 'share');
splitTextStyleAndMetadata(req.brew);
return next();
}));
//Print Page
app.get('/print/:id', asyncHandler(async (req, res, next)=>{
const brew = await getBrewFromId(req.params.id, 'share');
req.brew = brew;
return next();
}));
app.get('/print/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
sanitizeBrew(req.brew, 'share');
splitTextStyleAndMetadata(req.brew);
next();
});
const nodeEnv = config.get('node_env');
const isLocalEnvironment = config.get('local_environments').includes(nodeEnv);
@@ -291,7 +278,7 @@ if(isLocalEnvironment){
//Render the page
const templateFn = require('./../client/template.js');
app.use((req, res)=>{
app.use(asyncHandler(async (req, res, next)=>{
// Create configuration object
const configuration = {
local : isLocalEnvironment,
@@ -309,13 +296,14 @@ app.use((req, res)=>{
config : configuration
};
const title = req.brew ? req.brew.title : '';
templateFn('homebrew', title, props)
.then((page)=>{ res.send(page); })
.catch((err)=>{
console.log(err);
return res.sendStatus(500);
});
});
const page = await templateFn('homebrew', title, props)
.catch((err)=>{
console.log(err);
return res.sendStatus(500);
});
if(!page) return;
res.send(page);
}));
//v=====----- Error-Handling Middleware -----=====v//
//Format Errors so all fields will be sent
@@ -339,6 +327,13 @@ app.use((err, req, res, next)=>{
console.error(err);
res.status(status).send(getPureError(err));
});
app.use((req, res)=>{
if(!res.headersSent) {
console.error('Headers have not been sent, responding with a server error.', req.url);
res.status(500).send('An error occurred and the server did not send a response. The error has been logged, please note the time this occurred and report this issue.');
}
});
//^=====--------------------------------------=====^//
module.exports = {

View File

@@ -124,7 +124,6 @@ const GoogleActions = {
title : file.properties.title,
description : file.description,
views : parseInt(file.properties.views),
tags : '',
published : file.properties.published ? file.properties.published == 'true' : false,
systems : [],
thumbnail : file.properties.thumbnail
@@ -143,12 +142,11 @@ const GoogleActions = {
description : `${brew.description}`,
properties : {
title : brew.title,
published : brew.published,
version : brew.version,
renderer : brew.renderer,
tags : brew.tags,
shareId : brew.shareId || nanoid(12),
editId : brew.editId || nanoid(12),
pageCount : brew.pageCount,
systems : brew.systems.join(),
renderer : brew.renderer || 'legacy',
isStubbed : true,
thumbnail : brew.thumbnail
}
},
@@ -161,10 +159,9 @@ const GoogleActions = {
console.log('Error saving to google');
console.error(err);
throw (err);
//return res.status(500).send('Error while saving');
});
return (brew);
return true;
},
newGoogleBrew : async (auth, brew)=>{
@@ -178,17 +175,18 @@ const GoogleActions = {
const folderId = await GoogleActions.getGoogleFolder(auth);
const fileMetadata = {
'name' : `${brew.title}.txt`,
'description' : `${brew.description}`,
'parents' : [folderId],
'properties' : { //AppProperties is not accessible
'shareId' : brew.shareId || nanoid(12),
'editId' : brew.editId || nanoid(12),
'title' : brew.title,
'views' : '0',
'pageCount' : brew.pageCount,
'renderer' : brew.renderer || 'legacy',
'thumbnail' : brew.thumbnail || ''
name : `${brew.title}.txt`,
description : `${brew.description}`,
parents : [folderId],
properties : { //AppProperties is not accessible
shareId : brew.shareId || nanoid(12),
editId : brew.editId || nanoid(12),
title : brew.title,
pageCount : brew.pageCount,
renderer : brew.renderer || 'legacy',
isStubbed : true,
version : 1,
thumbnail : brew.thumbnail || ''
}
};
@@ -215,26 +213,7 @@ const GoogleActions = {
console.error(err);
});
const newHomebrew = {
text : brew.text,
shareId : fileMetadata.properties.shareId,
editId : fileMetadata.properties.editId,
createdAt : new Date(),
updatedAt : new Date(),
gDrive : true,
googleId : obj.data.id,
pageCount : fileMetadata.properties.pageCount,
title : brew.title,
description : brew.description,
tags : '',
published : brew.published,
renderer : brew.renderer,
authors : [],
systems : []
};
return newHomebrew;
return obj.data.id;
},
getGoogleBrew : async (id, accessId, accessType)=>{
@@ -247,7 +226,6 @@ const GoogleActions = {
.catch((err)=>{
console.log('Error loading from Google');
throw (err);
return;
});
if(obj) {
@@ -257,9 +235,7 @@ const GoogleActions = {
throw ('Share ID does not match');
}
const serviceDrive = google.drive({ version: 'v3' });
const file = await serviceDrive.files.get({
const file = await drive.files.get({
fileId : id,
fields : 'description, properties',
alt : 'media'
@@ -276,7 +252,7 @@ const GoogleActions = {
text : file.data,
description : obj.data.description,
tags : obj.data.properties.tags ? obj.data.properties.tags : '',
tags : obj.data.properties.tags ? obj.data.properties.tags : '',
systems : obj.data.properties.systems ? obj.data.properties.systems.split(',') : [],
authors : [],
published : obj.data.properties.published ? obj.data.properties.published == 'true' : false,
@@ -291,7 +267,6 @@ const GoogleActions = {
renderer : obj.data.properties.renderer ? obj.data.properties.renderer : 'legacy',
thumbnail : obj.data.properties.thumbnail || '',
gDrive : true,
googleId : id
};
@@ -299,14 +274,11 @@ const GoogleActions = {
}
},
deleteGoogleBrew : async (auth, id)=>{
deleteGoogleBrew : async (auth, id, accessId)=>{
const drive = google.drive({ version: 'v3', auth });
const googleId = id.slice(0, -12);
const accessId = id.slice(-12);
const obj = await drive.files.get({
fileId : googleId,
fileId : id,
fields : 'properties'
})
.catch((err)=>{
@@ -315,11 +287,11 @@ const GoogleActions = {
});
if(obj && obj.data.properties.editId != accessId) {
throw ('Not authorized to delete this Google brew');
throw { status: 403, message: 'Not authorized to delete this Google brew' };
}
await drive.files.update({
fileId : googleId,
fileId : id,
resource : { trashed: true }
})
.catch((err)=>{

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-lines */
const _ = require('lodash');
const HomebrewModel = require('./homebrew.model.js').model;
const router = require('express').Router();
@@ -6,6 +7,7 @@ const GoogleActions = require('./googleActions.js');
const Markdown = require('../shared/naturalcrit/markdown.js');
const yaml = require('js-yaml');
const asyncHandler = require('express-async-handler');
const { nanoid } = require('nanoid');
// const getTopBrews = (cb) => {
// HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) {
@@ -13,6 +15,56 @@ const asyncHandler = require('express-async-handler');
// });
// };
const getBrew = (accessType)=>{
// Create middleware with the accessType passed in as part of the scope
return async (req, res, next)=>{
// Set the id and initial potential google id, where the google id is present on the existing brew.
let id = req.params.id, googleId = req.body?.googleId;
// If the id is longer than 12, then it's a google id + the edit id. This splits the longer id up.
if(id.length > 12) {
googleId = id.slice(0, -12);
id = id.slice(-12);
}
// Try to find the document in the Homebrewery database -- if it doesn't exist, that's fine.
let stub = await HomebrewModel.get(accessType === 'edit' ? { editId: id } : { shareId: id })
.catch((err)=>{
if(googleId) {
console.warn(`Unable to find document stub for ${accessType}Id ${id}`);
} else {
console.warn(err);
}
});
stub = stub?.toObject();
// If there is a google id, try to find the google brew
if(googleId || stub?.googleId) {
let googleError;
const googleBrew = await GoogleActions.getGoogleBrew(googleId || stub?.googleId, id, accessType)
.catch((err)=>{
console.warn(err);
googleError = err;
});
// If we can't find the google brew and there is a google id for the brew, throw an error.
if(!googleBrew) throw googleError;
// Combine the Homebrewery stub with the google brew, or if the stub doesn't exist just use the google brew
stub = stub ? _.assign({ ...excludeStubProps(stub), stubbed: true }, excludeGoogleProps(googleBrew)) : googleBrew;
}
// If after all of that we still don't have a brew, throw an exception
if(!stub) {
throw 'Brew not found in Homebrewery database or Google Drive';
}
if(typeof stub.tags === 'string') {
stub.tags = [];
}
req.brew = stub;
next();
};
};
const mergeBrewText = (brew)=>{
let text = brew.text;
if(brew.style !== undefined) {
@@ -33,15 +85,33 @@ const MAX_TITLE_LENGTH = 100;
const getGoodBrewTitle = (text)=>{
const tokens = Markdown.marked.lexer(text);
return (tokens.find((token)=>token.type == 'heading' || token.type == 'paragraph')?.text || 'No Title')
return (tokens.find((token)=>token.type === 'heading' || token.type === 'paragraph')?.text || 'No Title')
.slice(0, MAX_TITLE_LENGTH);
};
const excludePropsFromUpdate = (brew)=>{
// Remove undesired properties
const propsToExclude = ['views', 'lastViewed'];
const modified = _.clone(brew);
const propsToExclude = ['_id', 'views', 'lastViewed', 'editId', 'shareId', 'googleId'];
for (const prop of propsToExclude) {
delete brew[prop];
delete modified[prop];
}
return modified;
};
const excludeGoogleProps = (brew)=>{
const modified = _.clone(brew);
const propsToExclude = ['tags', 'systems', 'published', 'authors', 'owner', 'views'];
for (const prop of propsToExclude) {
delete modified[prop];
}
return modified;
};
const excludeStubProps = (brew)=>{
const propsToExclude = ['text', 'textBin', 'renderer', 'pageCount', 'version'];
for (const prop of propsToExclude) {
brew[prop] = undefined;
}
return brew;
};
@@ -55,33 +125,17 @@ const beforeNewSave = (account, brew)=>{
brew.text = mergeBrewText(brew);
};
const newLocalBrew = async (brew)=>{
const newHomebrew = new HomebrewModel(brew);
// Compress brew text to binary before saving
newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text);
// Delete the non-binary text field since it's not needed anymore
newHomebrew.text = undefined;
let saved = await newHomebrew.save()
.catch((err)=>{
console.error(err, err.toString(), err.stack);
throw `Error while creating new brew, ${err.toString()}`;
});
saved = saved.toObject();
saved.gDrive = false;
return saved;
};
const newGoogleBrew = async (account, brew, res)=>{
const oAuth2Client = GoogleActions.authCheck(account, res);
return await GoogleActions.newGoogleBrew(oAuth2Client, brew);
const newBrew = excludeGoogleProps(brew);
return await GoogleActions.newGoogleBrew(oAuth2Client, newBrew);
};
const newBrew = async (req, res)=>{
const brew = req.body;
const { transferToGoogle } = req.query;
const { saveToGoogle } = req.query;
delete brew.editId;
delete brew.shareId;
@@ -89,148 +143,179 @@ const newBrew = async (req, res)=>{
beforeNewSave(req.account, brew);
let saved;
if(transferToGoogle) {
saved = await newGoogleBrew(req.account, brew, res)
const newHomebrew = new HomebrewModel(brew);
newHomebrew.editId = nanoid(12);
newHomebrew.shareId = nanoid(12);
let googleId, saved;
if(saveToGoogle) {
googleId = await newGoogleBrew(req.account, newHomebrew, res)
.catch((err)=>{
res.status(err.status || err.response.status).send(err.message || err);
console.error(err);
res.status(err?.status || err?.response?.status || 500).send(err?.message || err);
});
if(!googleId) return;
excludeStubProps(newHomebrew);
newHomebrew.googleId = googleId;
} else {
saved = await newLocalBrew(brew)
.catch((err)=>{
res.status(500).send(err);
});
// Compress brew text to binary before saving
newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text);
// Delete the non-binary text field since it's not needed anymore
newHomebrew.text = undefined;
}
saved = await newHomebrew.save()
.catch((err)=>{
console.error(err, err.toString(), err.stack);
throw `Error while creating new brew, ${err.toString()}`;
});
if(!saved) return;
return res.status(200).send(saved);
saved = saved.toObject();
res.status(200).send(saved);
};
const updateBrew = async (req, res)=>{
let brew = excludePropsFromUpdate(req.body);
const { transferToGoogle, transferFromGoogle } = req.query;
// Initialize brew from request and body, destructure query params, set a constant for the google id, and set the initial value for the after-save method
let brew = _.assign(req.brew, excludePropsFromUpdate(req.body));
const { saveToGoogle, removeFromGoogle } = req.query;
const googleId = brew.googleId;
let afterSave = async ()=>true;
let saved;
if(brew.googleId && transferFromGoogle) {
beforeNewSave(req.account, brew);
brew.text = mergeBrewText(brew);
saved = await newLocalBrew(brew)
.catch((err)=>{
console.error(err);
res.status(500).send(err);
});
if(!saved) return;
if(brew.googleId && removeFromGoogle) {
// If the google id exists and we're removing it from google, set afterSave to delete the google brew and mark the brew's google id as undefined
afterSave = async ()=>{
return await deleteGoogleBrew(req.account, googleId, brew.editId, res)
.catch((err)=>{
console.error(err);
res.status(err?.status || err?.response?.status || 500).send(err.message || err);
});
};
await deleteGoogleBrew(req.account, `${brew.googleId}${brew.editId}`, res)
brew.googleId = undefined;
} else if(!brew.googleId && saveToGoogle) {
// If we don't have a google id and the user wants to save to google, create the google brew and set the google id on the brew
brew.googleId = await newGoogleBrew(req.account, excludeGoogleProps(brew), res)
.catch((err)=>{
console.error(err);
res.status(err.status || err.response.status).send(err.message || err);
});
} else if(!brew.googleId && transferToGoogle) {
saved = await newGoogleBrew(req.account, brew, res)
.catch((err)=>{
console.error(err);
res.status(err.status || err.response.status).send(err.message || err);
});
if(!saved) return;
await deleteLocalBrew(req.account, brew.editId)
.catch((err)=>{
console.error(err);
res.status(err.status).send(err.message);
});
if(!brew.googleId) return;
} else if(brew.googleId) {
brew.text = mergeBrewText(brew);
saved = await GoogleActions.updateGoogleBrew(brew)
// If the google id exists and no other actions are being performed, update the google brew
const updated = await GoogleActions.updateGoogleBrew(excludeGoogleProps(brew))
.catch((err)=>{
console.error(err);
res.status(err.response?.status || 500).send(err);
res.status(err?.response?.status || 500).send(err);
});
if(!updated) return;
}
if(brew.googleId) {
// If the google id exists after all those actions, exclude the props that are stored in google and aren't needed for rendering the brew items
excludeStubProps(brew);
} else {
const dbBrew = await HomebrewModel.get({ editId: req.params.id })
.catch((err)=>{
console.error(err);
return res.status(500).send('Error while saving');
});
brew = _.merge(dbBrew, brew);
brew.text = mergeBrewText(brew);
// Compress brew text to binary before saving
brew.textBin = zlib.deflateRawSync(brew.text);
// Delete the non-binary text field since it's not needed anymore
brew.text = undefined;
brew.updatedAt = new Date();
if(req.account) {
brew.authors = _.uniq(_.concat(brew.authors, req.account.username));
}
brew.markModified('authors');
brew.markModified('systems');
saved = await brew.save();
}
brew.updatedAt = new Date();
if(req.account) {
brew.authors = _.uniq(_.concat(brew.authors, req.account.username));
}
// Fetch the brew from the database again (if it existed there to begin with), and assign the existing brew to it
brew = _.assign(await HomebrewModel.findOne({ _id: brew._id }), brew);
if(!brew.markModified) {
// If it wasn't in the database, create a new db brew
brew = new HomebrewModel(brew);
}
brew.markModified('authors');
brew.markModified('systems');
// Save the database brew
const saved = await brew.save()
.catch((err)=>{
console.error(err);
res.status(err.status || 500).send(err.message || 'Unable to save brew to Homebrewery database');
});
if(!saved) return;
// Call and wait for afterSave to complete
const after = await afterSave();
if(!after) return;
if(!res.headersSent) return res.status(200).send(saved);
res.status(200).send(saved);
};
const deleteBrew = async (req, res)=>{
if(req.params.id.length > 12) {
const deleted = await deleteGoogleBrew(req.account, req.params.id, res)
.catch((err)=>{
res.status(500).send(err);
});
if(deleted) return res.status(200).send();
} else {
const deleted = await deleteLocalBrew(req.account, req.params.id)
.catch((err)=>{
res.status(err.status).send(err.message);
});
if(deleted) return res.status(200).send(deleted);
return res.status(200).send();
}
};
const deleteLocalBrew = async (account, id)=>{
const brew = await HomebrewModel.findOne({ editId: id });
if(!brew) {
throw { status: 404, message: 'Can not find homebrew with that id' };
}
if(account) {
// Remove current user as author
brew.authors = _.pull(brew.authors, account.username);
brew.markModified('authors');
}
if(brew.authors.length === 0) {
// Delete brew if there are no authors left
await brew.remove()
.catch((err)=>{
console.error(err);
throw { status: 500, message: 'Error while removing' };
});
} else {
// Otherwise, save the brew with updated author list
return await brew.save()
.catch((err)=>{
throw { status: 500, message: err };
});
}
};
const deleteGoogleBrew = async (account, id, res)=>{
const deleteGoogleBrew = async (account, id, editId, res)=>{
const auth = await GoogleActions.authCheck(account, res);
await GoogleActions.deleteGoogleBrew(auth, id);
await GoogleActions.deleteGoogleBrew(auth, id, editId);
return true;
};
router.post('/api', asyncHandler(newBrew));
router.put('/api/:id', asyncHandler(updateBrew));
router.put('/api/update/:id', asyncHandler(updateBrew));
router.delete('/api/:id', asyncHandler(deleteBrew));
router.get('/api/remove/:id', asyncHandler(deleteBrew));
const deleteBrew = async (req, res)=>{
let brew = req.brew;
const { googleId, editId } = brew;
const account = req.account;
const isOwner = account && (brew.authors.length === 0 || brew.authors[0] === account.username);
// If the user is the owner and the file is saved to google, mark the google brew for deletion
const shouldDeleteGoogleBrew = googleId && isOwner;
module.exports = router;
if(brew._id) {
brew = _.assign(await HomebrewModel.findOne({ _id: brew._id }), brew);
if(account) {
// Remove current user as author
brew.authors = _.pull(brew.authors, account.username);
brew.markModified('authors');
}
if(brew.authors.length === 0) {
// Delete brew if there are no authors left
await brew.remove()
.catch((err)=>{
console.error(err);
throw { status: 500, message: 'Error while removing' };
});
} else {
if(shouldDeleteGoogleBrew) {
// When there are still authors remaining, we delete the google brew but store the full brew in the Homebrewery database
brew.googleId = undefined;
brew.textBin = zlib.deflateRawSync(brew.text);
brew.text = undefined;
}
// Otherwise, save the brew with updated author list
await brew.save()
.catch((err)=>{
throw { status: 500, message: err };
});
}
}
if(shouldDeleteGoogleBrew) {
const deleted = await deleteGoogleBrew(account, googleId, editId, res)
.catch((err)=>{
console.error(err);
res.status(500).send(err);
});
if(!deleted) return;
}
res.status(204).send();
};
router.post('/api', asyncHandler(newBrew));
router.put('/api/:id', asyncHandler(getBrew('edit')), asyncHandler(updateBrew));
router.put('/api/update/:id', asyncHandler(getBrew('edit')), asyncHandler(updateBrew));
router.delete('/api/:id', asyncHandler(getBrew('edit')), asyncHandler(deleteBrew));
router.get('/api/remove/:id', asyncHandler(getBrew('edit')), asyncHandler(deleteBrew));
module.exports = {
homebrewApi : router,
getBrew
};

View File

@@ -6,13 +6,14 @@ const zlib = require('zlib');
const HomebrewSchema = mongoose.Schema({
shareId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
editId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
googleId : { type: String },
title : { type: String, default: '' },
text : { type: String, default: '' },
textBin : { type: Buffer },
pageCount : { type: Number, default: 1 },
description : { type: String, default: '' },
tags : { type: String, default: '' },
tags : [String],
systems : [String],
renderer : { type: String, default: '' },
authors : [String],

View File

@@ -50,7 +50,7 @@ nav{
}
}
.navItem{
#backgroundColors;
#backgroundColorsHover;
.animate(background-color);
padding : 8px 12px;
cursor : pointer;

View File

@@ -23,6 +23,29 @@
@grey : #7F8C8D;
#backgroundColors {
&.tealLight{ background-color : @tealLight };
&.teal{ background-color : @teal };
&.greenLight{ background-color : @greenLight };
&.green{ background-color : @green };
&.blueLight{ background-color : @blueLight };
&.blue{ background-color : @blue };
&.purpleLight{ background-color : @purpleLight };
&.purple{ background-color : @purple };
&.steelLight{ background-color : @steelLight };
&.steel{ background-color : @steel };
&.yellowLight{ background-color : @yellowLight };
&.yellow{ background-color : @yellow };
&.orangeLight{ background-color : @orangeLight };
&.orange{ background-color : @orange };
&.redLight{ background-color : @redLight };
&.red{ background-color : @red };
&.silverLight{ background-color : @silverLight };
&.silver{ background-color : @silver };
&.greyLight{ background-color : @greyLight };
&.grey{ background-color : @grey };
}
#backgroundColorsHover {
&.tealLight:hover{ background-color : @tealLight };
&.teal:hover{ background-color : @teal };
&.greenLight:hover{ background-color : @greenLight };

View File

@@ -603,8 +603,8 @@ body {
white-space : nowrap;
}
&.frame {
margin-top : 0.66cm;
margin-bottom : 1.05cm;
margin-top : 0.7cm;
margin-bottom : 0.9cm;
margin-left : -0.1cm;
margin-right : -0.1cm;
width : calc(100% + 0.2cm);
@@ -612,11 +612,14 @@ body {
background-color : white;
border : initial;
border-style : solid;
border-image-outset : 0.55cm 0.3cm;
border-image-outset : 0.4cm 0.3cm;
border-image-repeat : stretch;
border-image-slice : 200;
border-image-source : @frameBorderImage;
border-image-width : 47px;
&.wide:first-child {
margin-top: 0.12cm;
}
}
&.decoration {
position:relative;