mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2025-12-24 22:52:40 +00:00
Merge branch 'master' into Issue_2563
This commit is contained in:
29
client/components/dialog.jsx
Normal file
29
client/components/dialog.jsx
Normal file
@@ -0,0 +1,29 @@
|
||||
// Dialog box, for popups and modal blocking messages
|
||||
const React = require('react');
|
||||
const { useRef, useEffect } = React;
|
||||
|
||||
function Dialog({ dismissKey, closeText = 'Close', blocking = false, ...rest }) {
|
||||
const dialogRef = useRef(null);
|
||||
|
||||
useEffect(()=>{
|
||||
if(!dismissKey || !localStorage.getItem(dismissKey)) {
|
||||
blocking ? dialogRef.current?.showModal() : dialogRef.current?.show();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const dismiss = ()=>{
|
||||
dismissKey && localStorage.setItem(dismissKey, true);
|
||||
dialogRef.current?.close();
|
||||
};
|
||||
|
||||
return (
|
||||
<dialog ref={dialogRef} onCancel={dismiss} {...rest}>
|
||||
{rest.children}
|
||||
<button className='dismiss' onClick={dismiss}>
|
||||
{closeText}
|
||||
</button>
|
||||
</dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dialog;
|
||||
@@ -203,6 +203,12 @@ const BrewRenderer = (props)=>{
|
||||
</div>
|
||||
: null}
|
||||
|
||||
<ErrorBar errors={props.errors} />
|
||||
<div className='popups'>
|
||||
<RenderWarnings />
|
||||
<NotificationPopup />
|
||||
</div>
|
||||
|
||||
{/*render in iFrame so broken code doesn't crash the site.*/}
|
||||
<Frame id='BrewRenderer' initialContent={INITIAL_CONTENT}
|
||||
style={{ width: '100%', height: '100%', visibility: state.visibility }}
|
||||
@@ -215,11 +221,6 @@ const BrewRenderer = (props)=>{
|
||||
tabIndex={-1}
|
||||
style={{ height: state.height }}>
|
||||
|
||||
<ErrorBar errors={props.errors} />
|
||||
<div className='popups'>
|
||||
<RenderWarnings />
|
||||
<NotificationPopup />
|
||||
</div>
|
||||
<link href={`/themes/${rendererPath}/Blank/style.css`} type='text/css' rel='stylesheet'/>
|
||||
{baseThemePath &&
|
||||
<link href={`/themes/${rendererPath}/${baseThemePath}/style.css`} type='text/css' rel='stylesheet'/>
|
||||
|
||||
@@ -1,80 +1,45 @@
|
||||
require('./notificationPopup.less');
|
||||
const React = require('react');
|
||||
const createClass = require('create-react-class');
|
||||
const _ = require('lodash');
|
||||
|
||||
import Dialog from '../../../components/dialog.jsx';
|
||||
|
||||
const DISMISS_KEY = 'dismiss_notification12-04-23';
|
||||
const DISMISS_BUTTON = <i className='fas fa-times dismiss' />;
|
||||
|
||||
const NotificationPopup = createClass({
|
||||
displayName : 'NotificationPopup',
|
||||
getInitialState : function() {
|
||||
return {
|
||||
notifications : {}
|
||||
};
|
||||
},
|
||||
componentDidMount : function() {
|
||||
this.checkNotifications();
|
||||
window.addEventListener('resize', this.checkNotifications);
|
||||
},
|
||||
componentWillUnmount : function() {
|
||||
window.removeEventListener('resize', this.checkNotifications);
|
||||
},
|
||||
notifications : {
|
||||
psa : function(){
|
||||
return (
|
||||
<>
|
||||
<li key='psa'>
|
||||
<em>Don't store IMAGES in Google Drive</em><br />
|
||||
Google Drive is not an image service, and will block images from being used
|
||||
in brews if they get more views than expected. Google has confirmed they won't fix
|
||||
this, so we recommend you look for another image hosting service such as imgur, ImgBB or Google Photos.
|
||||
</li>
|
||||
const NotificationPopup = ()=>{
|
||||
return <Dialog className='notificationPopup' dismissKey={DISMISS_KEY} closeText={DISMISS_BUTTON} >
|
||||
<div className='header'>
|
||||
<i className='fas fa-info-circle info'></i>
|
||||
<h3>Notice</h3>
|
||||
<small>This website is always improving and we are still adding new features and squashing bugs. Keep the following in mind:</small>
|
||||
</div>
|
||||
<ul>
|
||||
<li key='psa'>
|
||||
<em>Don't store IMAGES in Google Drive</em><br />
|
||||
Google Drive is not an image service, and will block images from being used
|
||||
in brews if they get more views than expected. Google has confirmed they won't fix
|
||||
this, so we recommend you look for another image hosting service such as imgur, ImgBB or Google Photos.
|
||||
</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='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!
|
||||
<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);
|
||||
if(hideDismiss) return this.setState({ notifications: {} });
|
||||
|
||||
this.setState({
|
||||
notifications : _.mapValues(this.notifications, (fn)=>{ return fn(); }) //Convert notification functions into their return text value
|
||||
});
|
||||
},
|
||||
dismiss : function(){
|
||||
localStorage.setItem(DISMISS_KEY, true);
|
||||
this.checkNotifications();
|
||||
},
|
||||
render : function(){
|
||||
if(_.isEmpty(this.state.notifications)) return null;
|
||||
|
||||
return <div className='notificationPopup'>
|
||||
<i className='fas fa-times dismiss' onClick={this.dismiss}/>
|
||||
<i className='fas fa-info-circle info' />
|
||||
<div className='header'>
|
||||
<h3>Notice</h3>
|
||||
<small>This website is always improving and we are still adding new features and squashing bugs. Keep the following in mind:</small>
|
||||
</div>
|
||||
<ul>{_.values(this.state.notifications)}</ul>
|
||||
</div>;
|
||||
}
|
||||
});
|
||||
<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!
|
||||
<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>
|
||||
</ul>
|
||||
</Dialog>;
|
||||
};
|
||||
|
||||
module.exports = NotificationPopup;
|
||||
|
||||
@@ -1,64 +1,60 @@
|
||||
.popups{
|
||||
.popups {
|
||||
position : fixed;
|
||||
top : @navbarHeight;
|
||||
right : 15px;
|
||||
right : 24px;
|
||||
z-index : 10001;
|
||||
width : 450px;
|
||||
}
|
||||
|
||||
.notificationPopup{
|
||||
.notificationPopup {
|
||||
position : relative;
|
||||
display : inline-block;
|
||||
width : 100%;
|
||||
padding : 15px;
|
||||
padding-bottom : 10px;
|
||||
padding-left : 25px;
|
||||
background-color : @blue;
|
||||
color : white;
|
||||
a{
|
||||
color : #e0e5c1;
|
||||
background-color : @blue;
|
||||
border : none;
|
||||
&[open] { display : inline-block; }
|
||||
a {
|
||||
font-weight : 800;
|
||||
color : #E0E5C1;
|
||||
}
|
||||
i.info{
|
||||
i.info {
|
||||
position : absolute;
|
||||
top : 12px;
|
||||
left : 12px;
|
||||
opacity : 0.8;
|
||||
font-size : 2.5em;
|
||||
opacity : 0.8;
|
||||
}
|
||||
i.dismiss{
|
||||
position : absolute;
|
||||
top : 10px;
|
||||
right : 10px;
|
||||
cursor : pointer;
|
||||
opacity : 0.6;
|
||||
&:hover{
|
||||
opacity : 1;
|
||||
}
|
||||
button.dismiss {
|
||||
position : absolute;
|
||||
top : 10px;
|
||||
right : 10px;
|
||||
cursor : pointer;
|
||||
background-color : transparent;
|
||||
opacity : 0.6;
|
||||
&:hover { opacity : 1; }
|
||||
}
|
||||
.header {
|
||||
padding-left : 50px;
|
||||
}
|
||||
small{
|
||||
opacity : 0.7;
|
||||
.header { padding-left : 50px; }
|
||||
small {
|
||||
font-size : 0.6em;
|
||||
opacity : 0.7;
|
||||
}
|
||||
h3{
|
||||
h3 {
|
||||
font-size : 1.1em;
|
||||
font-weight : 800;
|
||||
}
|
||||
ul{
|
||||
ul {
|
||||
margin-top : 15px;
|
||||
font-size : 0.8em;
|
||||
list-style-position : outside;
|
||||
list-style-type : disc;
|
||||
li{
|
||||
li {
|
||||
margin-top : 1.4em;
|
||||
font-size : 0.8em;
|
||||
line-height : 1.4em;
|
||||
margin-top : 1.4em;
|
||||
em{
|
||||
font-weight : 800;
|
||||
}
|
||||
em { font-weight : 800; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
|
||||
const Editor = require('../../editor/editor.jsx');
|
||||
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
||||
|
||||
const LockNotification = require('./lockNotification/lockNotification.jsx');
|
||||
|
||||
const Markdown = require('naturalcrit/markdown.js');
|
||||
|
||||
const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js');
|
||||
@@ -52,7 +54,8 @@ const EditPage = createClass({
|
||||
autoSave : true,
|
||||
autoSaveWarning : false,
|
||||
unsavedTime : new Date(),
|
||||
currentEditorPage : 0
|
||||
currentEditorPage : 0,
|
||||
displayLockMessage : this.props.brew.lock || false
|
||||
};
|
||||
},
|
||||
|
||||
@@ -393,6 +396,7 @@ const EditPage = createClass({
|
||||
{this.renderNavbar()}
|
||||
|
||||
<div className='content'>
|
||||
{this.props.brew.lock && <LockNotification shareId={this.props.brew.shareId} message={this.props.brew.lock.editMessage} />}
|
||||
<SplitPane onDragFinish={this.handleSplitMove}>
|
||||
<Editor
|
||||
ref={this.editor}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
require('./lockNotification.less');
|
||||
const React = require('react');
|
||||
import Dialog from '../../../../components/dialog.jsx';
|
||||
|
||||
function LockNotification(props) {
|
||||
props = {
|
||||
shareId : 0,
|
||||
disableLock : ()=>{},
|
||||
message : '',
|
||||
...props
|
||||
};
|
||||
|
||||
const removeLock = ()=>{
|
||||
alert(`Not yet implemented - ID ${props.shareId}`);
|
||||
};
|
||||
|
||||
return <Dialog className='lockNotification' blocking closeText='CONTINUE TO EDITOR' >
|
||||
<h1>BREW LOCKED</h1>
|
||||
<p>This brew been locked by the Administrators. It will not be accessible by any method other than the Editor until the lock is removed.</p>
|
||||
<hr />
|
||||
<h3>LOCK REASON</h3>
|
||||
<p>{props.message || 'Unable to retrieve Lock Message'}</p>
|
||||
<hr />
|
||||
<p>Once you have resolved this issue, click REQUEST LOCK REMOVAL to notify the Administrators for review.</p>
|
||||
<p>Click CONTINUE TO EDITOR to temporarily hide this notification; it will reappear the next time the page is reloaded.</p>
|
||||
<button onClick={removeLock}>REQUEST LOCK REMOVAL</button>
|
||||
</Dialog>;
|
||||
};
|
||||
|
||||
module.exports = LockNotification;
|
||||
@@ -0,0 +1,27 @@
|
||||
.lockNotification {
|
||||
z-index : 1;
|
||||
width : 80%;
|
||||
padding : 10px;
|
||||
margin : 5% 10%;
|
||||
line-height : 1.5em;
|
||||
color : black;
|
||||
text-align : center;
|
||||
background-color : #CCCCCC;
|
||||
|
||||
&::backdrop { background-color : #000000AA; }
|
||||
|
||||
button {
|
||||
margin : 10px;
|
||||
color : white;
|
||||
background-color : #333333;
|
||||
|
||||
&:hover { background-color : #777777; }
|
||||
}
|
||||
|
||||
h1, h3 {
|
||||
font-family : 'Open Sans', sans-serif;
|
||||
font-weight : 800;
|
||||
}
|
||||
h1 { font-size : 24px; }
|
||||
h3 { font-size : 18px; }
|
||||
}
|
||||
@@ -140,7 +140,7 @@ const errorIndex = (props)=>{
|
||||
'100' : dedent`
|
||||
## This brew has been locked.
|
||||
|
||||
Please contact the Administrators to unlock this document.
|
||||
Only an author may request that this lock is removed.
|
||||
|
||||
:
|
||||
|
||||
|
||||
1505
package-lock.json
generated
1505
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@@ -82,18 +82,18 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.24.5",
|
||||
"@babel/plugin-transform-runtime": "^7.24.3",
|
||||
"@babel/preset-env": "^7.24.5",
|
||||
"@babel/preset-react": "^7.24.1",
|
||||
"@googleapis/drive": "^8.8.0",
|
||||
"@babel/core": "^7.24.7",
|
||||
"@babel/plugin-transform-runtime": "^7.24.7",
|
||||
"@babel/preset-env": "^7.24.7",
|
||||
"@babel/preset-react": "^7.24.7",
|
||||
"@googleapis/drive": "^8.10.0",
|
||||
"body-parser": "^1.20.2",
|
||||
"classnames": "^2.5.1",
|
||||
"codemirror": "^5.65.6",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"create-react-class": "^15.7.0",
|
||||
"dedent-tabs": "^0.10.3",
|
||||
"dompurify": "^3.1.4",
|
||||
"dompurify": "^3.1.5",
|
||||
"expr-eval": "^2.0.2",
|
||||
"express": "^4.19.2",
|
||||
"express-async-handler": "^1.2.0",
|
||||
@@ -110,7 +110,7 @@
|
||||
"marked-smartypants-lite": "^1.0.2",
|
||||
"markedLegacy": "npm:marked@^0.3.19",
|
||||
"moment": "^2.30.1",
|
||||
"mongoose": "^8.4.0",
|
||||
"mongoose": "^8.4.1",
|
||||
"nanoid": "3.3.4",
|
||||
"nconf": "^0.12.1",
|
||||
"react": "^18.3.1",
|
||||
@@ -123,8 +123,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-jest": "^28.5.0",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-jest": "^28.6.0",
|
||||
"eslint-plugin-react": "^7.34.2",
|
||||
"jest": "^29.7.0",
|
||||
"jest-expect-message": "^1.1.3",
|
||||
"postcss-less": "^6.0.0",
|
||||
|
||||
@@ -55,7 +55,7 @@ const api = {
|
||||
stub = stub?.toObject();
|
||||
|
||||
if(stub?.lock?.locked && accessType != 'edit') {
|
||||
throw { HBErrorCode: '100', code: stub.lock.code, message: stub.lock.message, brewId: stub.shareId, brewTitle: stub.title };
|
||||
throw { HBErrorCode: '100', code: stub.lock.code, message: stub.lock.shareMessage, brewId: stub.shareId, brewTitle: stub.title };
|
||||
}
|
||||
|
||||
// If there is a google id, try to find the google brew
|
||||
|
||||
@@ -300,7 +300,7 @@ describe('Tests for api', ()=>{
|
||||
});
|
||||
|
||||
it('access is denied to a locked brew', async()=>{
|
||||
const lockBrew = { title: 'test brew', shareId: '1', lock: { locked: true, code: 404, message: 'brew locked' } };
|
||||
const lockBrew = { title: 'test brew', shareId: '1', lock: { locked: true, code: 404, shareMessage: 'brew locked' } };
|
||||
model.get = jest.fn(()=>toBrewPromise(lockBrew));
|
||||
api.getId = jest.fn(()=>({ id: '1', googleId: undefined }));
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ const React = require('react');
|
||||
const createClass = require('create-react-class');
|
||||
const _ = require('lodash');
|
||||
|
||||
const DISMISS_KEY = 'dismiss_render_warning';
|
||||
import Dialog from '../../../client/components/dialog.jsx';
|
||||
|
||||
const RenderWarnings = createClass({
|
||||
displayName : 'RenderWarnings',
|
||||
@@ -34,9 +34,6 @@ const RenderWarnings = createClass({
|
||||
},
|
||||
},
|
||||
checkWarnings : function(){
|
||||
const hideDismiss = localStorage.getItem(DISMISS_KEY);
|
||||
if(hideDismiss) return this.setState({ warnings: {} });
|
||||
|
||||
this.setState({
|
||||
warnings : _.reduce(this.warnings, (r, fn, type)=>{
|
||||
const element = fn();
|
||||
@@ -45,20 +42,18 @@ const RenderWarnings = createClass({
|
||||
}, {})
|
||||
});
|
||||
},
|
||||
dismiss : function(){
|
||||
localStorage.setItem(DISMISS_KEY, true);
|
||||
this.checkWarnings();
|
||||
},
|
||||
render : function(){
|
||||
if(_.isEmpty(this.state.warnings)) return null;
|
||||
|
||||
return <div className='renderWarnings'>
|
||||
<i className='fas fa-times dismiss' onClick={this.dismiss}/>
|
||||
const DISMISS_KEY = 'dismiss_render_warning';
|
||||
const DISMISS_TEXT = <i className='fas fa-times dismiss' />;
|
||||
|
||||
return <Dialog className='renderWarnings' dismissKey={DISMISS_KEY} closeText={DISMISS_TEXT}>
|
||||
<i className='fas fa-exclamation-triangle ohno' />
|
||||
<h3>Render Warnings</h3>
|
||||
<small>If this homebrew is rendering badly if might be because of the following:</small>
|
||||
<ul>{_.values(this.state.warnings)}</ul>
|
||||
</div>;
|
||||
</Dialog>;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,53 +1,48 @@
|
||||
.renderWarnings{
|
||||
position : relative;
|
||||
float : right;
|
||||
display : inline-block;
|
||||
.renderWarnings {
|
||||
position : relative;
|
||||
float : right;
|
||||
width : 350px;
|
||||
padding : 20px;
|
||||
padding-bottom : 10px;
|
||||
padding-left : 85px;
|
||||
margin-bottom : 10px;
|
||||
background-color : @yellow;
|
||||
color : white;
|
||||
a{
|
||||
font-weight : 800;
|
||||
}
|
||||
i.ohno{
|
||||
background-color : @yellow;
|
||||
border : none;
|
||||
a { font-weight : 800; }
|
||||
i.ohno {
|
||||
position : absolute;
|
||||
top : 24px;
|
||||
left : 24px;
|
||||
opacity : 0.8;
|
||||
font-size : 2.5em;
|
||||
opacity : 0.8;
|
||||
}
|
||||
i.dismiss{
|
||||
position : absolute;
|
||||
top : 10px;
|
||||
right : 10px;
|
||||
cursor : pointer;
|
||||
opacity : 0.6;
|
||||
&:hover{
|
||||
opacity : 1;
|
||||
}
|
||||
button.dismiss {
|
||||
position : absolute;
|
||||
top : 10px;
|
||||
right : 10px;
|
||||
cursor : pointer;
|
||||
background-color : transparent;
|
||||
opacity : 0.6;
|
||||
&:hover { opacity : 1; }
|
||||
}
|
||||
small{
|
||||
opacity : 0.7;
|
||||
small {
|
||||
font-size : 0.6em;
|
||||
opacity : 0.7;
|
||||
}
|
||||
h3{
|
||||
h3 {
|
||||
font-size : 1.1em;
|
||||
font-weight : 800;
|
||||
}
|
||||
ul{
|
||||
ul {
|
||||
margin-top : 15px;
|
||||
font-size : 0.8em;
|
||||
list-style-position : outside;
|
||||
list-style-type : disc;
|
||||
li{
|
||||
li {
|
||||
font-size : 0.8em;
|
||||
line-height : 1.6em;
|
||||
em{
|
||||
font-weight : 800;
|
||||
}
|
||||
em { font-weight : 800; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@
|
||||
background-color: rgba(35,153,153,0.5);
|
||||
}
|
||||
.pageLine {
|
||||
background-color: rgba(255,255,255,0.75);
|
||||
background-color: rgba(255,255,255,0.5);
|
||||
& ~ pre.CodeMirror-line {
|
||||
color: black;
|
||||
}
|
||||
@@ -85,4 +85,4 @@
|
||||
// Future styling for themes with light backgrounds
|
||||
--dummyVar: 'currently unused';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user