mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2025-12-31 15:12:39 +00:00
Merge branch 'master' into new-page-in-new-tab
This commit is contained in:
@@ -45,12 +45,13 @@ let rawPages = [];
|
||||
|
||||
const BrewRenderer = (props)=>{
|
||||
props = {
|
||||
text : '',
|
||||
style : '',
|
||||
renderer : 'legacy',
|
||||
theme : '5ePHB',
|
||||
lang : '',
|
||||
errors : [],
|
||||
text : '',
|
||||
style : '',
|
||||
renderer : 'legacy',
|
||||
theme : '5ePHB',
|
||||
lang : '',
|
||||
errors : [],
|
||||
currentEditorPage : 0,
|
||||
...props
|
||||
};
|
||||
|
||||
@@ -94,6 +95,9 @@ const BrewRenderer = (props)=>{
|
||||
if(Math.abs(index - state.viewablePageNumber) <= 3)
|
||||
return true;
|
||||
|
||||
if(index + 1 == props.currentEditorPage)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -143,6 +147,9 @@ const BrewRenderer = (props)=>{
|
||||
if(props.errors && props.errors.length)
|
||||
return renderedPages;
|
||||
|
||||
if(rawPages.length != renderedPages.length) // Re-render all pages when page count changes
|
||||
renderedPages.length = 0;
|
||||
|
||||
_.forEach(rawPages, (page, index)=>{
|
||||
if((shouldRender(index) || !renderedPages[index]) && typeof window !== 'undefined'){
|
||||
renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
.metadataEditor{
|
||||
position : absolute;
|
||||
z-index : 10000;
|
||||
z-index : 5;
|
||||
box-sizing : border-box;
|
||||
width : 100%;
|
||||
padding : 25px;
|
||||
|
||||
@@ -74,6 +74,7 @@ const Snippetbar = createClass({
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
mergeCustomizer : function(valueA, valueB, key) {
|
||||
if(key == 'snippets') {
|
||||
const result = _.reverse(_.unionBy(_.reverse(valueB), _.reverse(valueA), 'name')); // Join snippets together, with preference for the current theme over the base theme
|
||||
@@ -102,10 +103,12 @@ const Snippetbar = createClass({
|
||||
this.props.onInject(injectedText);
|
||||
},
|
||||
|
||||
toggleThemeSelector : function(){
|
||||
this.setState({
|
||||
themeSelector : !this.state.themeSelector
|
||||
});
|
||||
toggleThemeSelector : function(e){
|
||||
if(e.target.tagName != 'SELECT'){
|
||||
this.setState({
|
||||
themeSelector : !this.state.themeSelector
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
changeTheme : function(e){
|
||||
@@ -119,7 +122,7 @@ const Snippetbar = createClass({
|
||||
|
||||
renderThemeSelector : function(){
|
||||
return <div className='themeSelector'>
|
||||
<select value={this.props.currentEditorTheme} onChange={this.changeTheme} onMouseDown={(this.changeTheme)}>
|
||||
<select value={this.props.currentEditorTheme} onChange={this.changeTheme} >
|
||||
{EditorThemes.map((theme, key)=>{
|
||||
return <option key={key} value={theme}>{theme}</option>;
|
||||
})}
|
||||
@@ -176,8 +179,9 @@ const Snippetbar = createClass({
|
||||
<div className={`editorTool editorTheme ${this.state.themeSelector ? 'active' : ''}`}
|
||||
onClick={this.toggleThemeSelector} >
|
||||
<i className='fas fa-palette' />
|
||||
{this.state.themeSelector && this.renderThemeSelector()}
|
||||
</div>
|
||||
{this.state.themeSelector && this.renderThemeSelector()}
|
||||
|
||||
<div className='divider'></div>
|
||||
<div className={cx('text', { selected: this.props.view === 'text' })}
|
||||
onClick={()=>this.props.onViewChange('text')}>
|
||||
@@ -228,7 +232,7 @@ const SnippetGroup = createClass({
|
||||
return _.map(snippets, (snippet)=>{
|
||||
return <div className='snippet' key={snippet.name} onClick={(e)=>this.handleSnippetClick(e, snippet)}>
|
||||
<i className={snippet.icon} />
|
||||
<span className='name'>{snippet.name}</span>
|
||||
<span className='name'title={snippet.name}>{snippet.name}</span>
|
||||
{snippet.experimental && <span className='beta'>beta</span>}
|
||||
{snippet.subsnippets && <>
|
||||
<i className='fas fa-caret-right'></i>
|
||||
|
||||
@@ -1,177 +1,190 @@
|
||||
@import (less) './client/icons/customIcons.less';
|
||||
.snippetBar{
|
||||
@import (less) '././././themes/fonts/5e/fonts.less';
|
||||
|
||||
.snippetBar {
|
||||
@menuHeight : 25px;
|
||||
position : relative;
|
||||
height : @menuHeight;
|
||||
background-color : #ddd;
|
||||
.editors{
|
||||
color : black;
|
||||
background-color : #DDDDDD;
|
||||
|
||||
.editors {
|
||||
position : absolute;
|
||||
display : flex;
|
||||
top : 0px;
|
||||
right : 0px;
|
||||
height : @menuHeight;
|
||||
display : flex;
|
||||
justify-content : space-between;
|
||||
&>div{
|
||||
height : @menuHeight;
|
||||
height : @menuHeight;
|
||||
& > div {
|
||||
width : @menuHeight;
|
||||
cursor : pointer;
|
||||
height : @menuHeight;
|
||||
line-height : @menuHeight;
|
||||
text-align : center;
|
||||
&:hover,&.selected{
|
||||
background-color : #999;
|
||||
}
|
||||
&.text{
|
||||
cursor : pointer;
|
||||
&:hover,&.selected { background-color : #999999; }
|
||||
&.text {
|
||||
.tooltipLeft('Brew Editor');
|
||||
}
|
||||
&.style{
|
||||
&.style {
|
||||
.tooltipLeft('Style Editor');
|
||||
}
|
||||
&.meta{
|
||||
&.meta {
|
||||
.tooltipLeft('Properties');
|
||||
}
|
||||
&.undo{
|
||||
&.undo {
|
||||
.tooltipLeft('Undo');
|
||||
font-size : 0.75em;
|
||||
color : grey;
|
||||
&.active{
|
||||
color : black;
|
||||
}
|
||||
&.active { color : inherit; }
|
||||
}
|
||||
&.redo{
|
||||
&.redo {
|
||||
.tooltipLeft('Redo');
|
||||
font-size : 0.75em;
|
||||
color : grey;
|
||||
&.active{
|
||||
color : black;
|
||||
}
|
||||
&.active { color : inherit; }
|
||||
}
|
||||
&.foldAll{
|
||||
&.foldAll {
|
||||
.tooltipLeft('Fold All');
|
||||
font-size : 0.75em;
|
||||
color : grey;
|
||||
&.active{
|
||||
color : black;
|
||||
}
|
||||
color : inherit;
|
||||
}
|
||||
&.unfoldAll{
|
||||
&.unfoldAll {
|
||||
.tooltipLeft('Unfold All');
|
||||
font-size : 0.75em;
|
||||
color : grey;
|
||||
&.active{
|
||||
color : black;
|
||||
}
|
||||
color : inherit;
|
||||
}
|
||||
&.editorTheme{
|
||||
&.editorTheme {
|
||||
.tooltipLeft('Editor Themes');
|
||||
font-size : 0.75em;
|
||||
color : black;
|
||||
&.active{
|
||||
color : white;
|
||||
background-color: black;
|
||||
&.active {
|
||||
position : relative;
|
||||
background-color : #999999;
|
||||
}
|
||||
}
|
||||
&.divider {
|
||||
background: linear-gradient(#000, #000) no-repeat center/1px 100%;
|
||||
width: 5px;
|
||||
&:hover{
|
||||
background-color: inherit;
|
||||
}
|
||||
width : 5px;
|
||||
background : linear-gradient(currentColor, currentColor) no-repeat center/1px 100%;
|
||||
&:hover { background-color : inherit; }
|
||||
}
|
||||
}
|
||||
.themeSelector{
|
||||
position: absolute;
|
||||
left: -65px;
|
||||
top: 30px;
|
||||
z-index: 999;
|
||||
width: 170px;
|
||||
background-color: black;
|
||||
border-radius: 5px;
|
||||
.themeSelector {
|
||||
position : absolute;
|
||||
top : 25px;
|
||||
right : 0;
|
||||
z-index : 10;
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content : center;
|
||||
width : 170px;
|
||||
height : inherit;
|
||||
background-color : inherit;
|
||||
}
|
||||
}
|
||||
.snippetBarButton{
|
||||
height : @menuHeight;
|
||||
line-height : @menuHeight;
|
||||
.snippetBarButton {
|
||||
display : inline-block;
|
||||
height : @menuHeight;
|
||||
padding : 0px 5px;
|
||||
font-weight : 800;
|
||||
font-size : 0.625em;
|
||||
font-weight : 800;
|
||||
line-height : @menuHeight;
|
||||
text-transform : uppercase;
|
||||
cursor : pointer;
|
||||
&:hover, &.selected{
|
||||
background-color : #999;
|
||||
}
|
||||
i{
|
||||
vertical-align : middle;
|
||||
&:hover, &.selected { background-color : #999999; }
|
||||
i {
|
||||
margin-right : 3px;
|
||||
font-size : 1.4em;
|
||||
vertical-align : middle;
|
||||
}
|
||||
}
|
||||
.toggleMeta{
|
||||
position : absolute;
|
||||
top : 0px;
|
||||
right : 0px;
|
||||
border-left : 1px solid black;
|
||||
.tooltipLeft("Edit Brew Properties");
|
||||
.toggleMeta {
|
||||
position : absolute;
|
||||
top : 0px;
|
||||
right : 0px;
|
||||
border-left : 1px solid black;
|
||||
.tooltipLeft('Edit Brew Properties');
|
||||
}
|
||||
.snippetGroup{
|
||||
border-right : 1px solid black;
|
||||
&:hover{
|
||||
&>.dropdown{
|
||||
visibility : visible;
|
||||
}
|
||||
.snippetGroup {
|
||||
border-right : 1px solid currentColor;
|
||||
&:hover {
|
||||
& > .dropdown { visibility : visible; }
|
||||
}
|
||||
.dropdown{
|
||||
.dropdown {
|
||||
position : absolute;
|
||||
top : 100%;
|
||||
visibility : hidden;
|
||||
z-index : 1000;
|
||||
margin-left : -5px;
|
||||
padding : 0px;
|
||||
background-color : #ddd;
|
||||
.snippet{
|
||||
position: relative;
|
||||
.animate(background-color);
|
||||
margin-left : -5px;
|
||||
visibility : hidden;
|
||||
background-color : #DDDDDD;
|
||||
.snippet {
|
||||
position : relative;
|
||||
display : flex;
|
||||
align-items : center;
|
||||
min-width : max-content;
|
||||
padding : 5px;
|
||||
cursor : pointer;
|
||||
font-size : 10px;
|
||||
i{
|
||||
cursor : pointer;
|
||||
.animate(background-color);
|
||||
i {
|
||||
height : 1.2em;
|
||||
margin-right : 8px;
|
||||
font-size : 1.2em;
|
||||
height : 1.2em;
|
||||
&~i{
|
||||
margin-right: 0;
|
||||
margin-left: 5px;
|
||||
& ~ i {
|
||||
margin-right : 0;
|
||||
margin-left : 5px;
|
||||
}
|
||||
/* Fonts */
|
||||
&.font {
|
||||
height : auto;
|
||||
&::before {
|
||||
font-size : 1.4em;
|
||||
content : 'ABC';
|
||||
}
|
||||
|
||||
&.OpenSans {font-family : 'OpenSans';}
|
||||
&.CodeBold {font-family : 'CodeBold';}
|
||||
&.CodeLight {font-family : 'CodeLight';}
|
||||
&.ScalySansRemake {font-family : 'ScalySansRemake';}
|
||||
&.BookInsanityRemake {font-family : 'BookInsanityRemake';}
|
||||
&.MrEavesRemake {font-family : 'MrEavesRemake';}
|
||||
&.SolberaImitationRemake {font-family : 'SolberaImitationRemake';}
|
||||
&.ScalySansSmallCapsRemake {font-family : 'ScalySansSmallCapsRemake';}
|
||||
&.WalterTurncoat {font-family : 'WalterTurncoat';}
|
||||
&.Lato {font-family : 'Lato';}
|
||||
&.Courier {font-family : 'Courier';}
|
||||
&.NodestoCapsCondensed {font-family : 'NodestoCapsCondensed';}
|
||||
&.Overpass {font-family : 'Overpass';}
|
||||
&.Davek {font-family : 'Davek';}
|
||||
&.Iokharic {font-family : 'Iokharic';}
|
||||
&.Rellanic {font-family : 'Rellanic';}
|
||||
&.TimesNewRoman {font-family : 'Times New Roman';}
|
||||
}
|
||||
}
|
||||
.name {
|
||||
margin-right : auto;
|
||||
}
|
||||
.name { margin-right : auto; }
|
||||
.beta {
|
||||
color : white;
|
||||
padding : 4px 6px;
|
||||
line-height : 1em;
|
||||
margin-left : 5px;
|
||||
align-self : center;
|
||||
padding : 4px 6px;
|
||||
margin-left : 5px;
|
||||
font-family : monospace;
|
||||
line-height : 1em;
|
||||
color : white;
|
||||
background : grey;
|
||||
border-radius : 12px;
|
||||
font-family : monospace;
|
||||
}
|
||||
&:hover{
|
||||
background-color : #999;
|
||||
&>.dropdown{
|
||||
&:hover {
|
||||
background-color : #999999;
|
||||
& > .dropdown {
|
||||
visibility : visible;
|
||||
&.side {
|
||||
left: 100%;
|
||||
top: 0%;
|
||||
margin-left:0;
|
||||
box-shadow: -1px 1px 2px 0px #999;
|
||||
top : 0%;
|
||||
left : 100%;
|
||||
margin-left : 0;
|
||||
box-shadow : -1px 1px 2px 0px #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,11 @@ const ErrorNavItem = createClass({
|
||||
this.props.parent.setState(state);
|
||||
};
|
||||
|
||||
const error = this.props.error;
|
||||
const response = error.response;
|
||||
const status = response.status;
|
||||
const message = response.body?.message;
|
||||
const error = this.props.error;
|
||||
const response = error.response;
|
||||
const status = response.status;
|
||||
const HBErrorCode = response.body?.HBErrorCode;
|
||||
const message = response.body?.message;
|
||||
let errMsg = '';
|
||||
try {
|
||||
errMsg += `${error.toString()}\n\n`;
|
||||
@@ -40,7 +41,9 @@ const ErrorNavItem = createClass({
|
||||
{message ?? 'Conflict: please refresh to get latest changes'}
|
||||
</div>
|
||||
</Nav.item>;
|
||||
} else if(status === 412) {
|
||||
}
|
||||
|
||||
if(status === 412) {
|
||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||
Oops!
|
||||
<div className='errorContainer' onClick={clearError}>
|
||||
@@ -48,6 +51,36 @@ const ErrorNavItem = createClass({
|
||||
</div>
|
||||
</Nav.item>;
|
||||
}
|
||||
|
||||
if(HBErrorCode === '04') {
|
||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||
Oops!
|
||||
<div className='errorContainer' onClick={clearError}>
|
||||
You are no longer signed in as an author of
|
||||
this brew! Were you signed out from a different
|
||||
window? Visit our log in page, then try again!
|
||||
<br></br>
|
||||
<a target='_blank' rel='noopener noreferrer'
|
||||
href={`https://www.naturalcrit.com/login?redirect=${window.location.href}`}>
|
||||
<div className='confirm'>
|
||||
Sign In
|
||||
</div>
|
||||
</a>
|
||||
<div className='deny'>
|
||||
Not Now
|
||||
</div>
|
||||
</div>
|
||||
</Nav.item>;
|
||||
}
|
||||
|
||||
if(response.body?.errors?.[0].reason == 'storageQuotaExceeded') {
|
||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||
Oops!
|
||||
<div className='errorContainer' onClick={clearError}>
|
||||
Can't save because your Google Drive seems to be full!
|
||||
</div>
|
||||
</Nav.item>;
|
||||
}
|
||||
|
||||
if(response.req.url.match(/^\/api.*Google.*$/m)){
|
||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||
@@ -57,6 +90,7 @@ const ErrorNavItem = createClass({
|
||||
expired! Visit our log in page to sign out
|
||||
and sign back in with Google,
|
||||
then try saving again!
|
||||
<br></br>
|
||||
<a target='_blank' rel='noopener noreferrer'
|
||||
href={`https://www.naturalcrit.com/login?redirect=${window.location.href}`}>
|
||||
<div className='confirm'>
|
||||
|
||||
@@ -50,7 +50,8 @@ const EditPage = createClass({
|
||||
url : '',
|
||||
autoSave : true,
|
||||
autoSaveWarning : false,
|
||||
unsavedTime : new Date()
|
||||
unsavedTime : new Date(),
|
||||
currentEditorPage : 0
|
||||
};
|
||||
},
|
||||
savedBrew : null,
|
||||
@@ -109,9 +110,10 @@ const EditPage = createClass({
|
||||
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
|
||||
|
||||
this.setState((prevState)=>({
|
||||
brew : { ...prevState.brew, text: text },
|
||||
isPending : true,
|
||||
htmlErrors : htmlErrors
|
||||
brew : { ...prevState.brew, text: text },
|
||||
isPending : true,
|
||||
htmlErrors : htmlErrors,
|
||||
currentEditorPage : this.refs.editor.getCurrentPage()
|
||||
}), ()=>{if(this.state.autoSave) this.trySave();});
|
||||
},
|
||||
|
||||
@@ -405,6 +407,7 @@ const EditPage = createClass({
|
||||
theme={this.state.brew.theme}
|
||||
errors={this.state.htmlErrors}
|
||||
lang={this.state.brew.lang}
|
||||
currentEditorPage={this.state.currentEditorPage}
|
||||
/>
|
||||
</SplitPane>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user