add unsupported messages

This commit is contained in:
Sean Yesmunt 2019-03-18 01:06:41 -04:00
parent 2686c0d530
commit 4564ab9d0b
9 changed files with 335 additions and 300 deletions

View file

@ -18,7 +18,8 @@
"i18n": true, "i18n": true,
"__": true, "__": true,
"__n": true, "__n": true,
"app": true "app": true,
"IS_WEB": true
}, },
"rules": { "rules": {
"no-multi-spaces": 0, "no-multi-spaces": 0,

View file

@ -35,7 +35,6 @@ class FileSelector extends React.PureComponent<Props> {
constructor() { constructor() {
super(); super();
this.input = null;
// @if TARGET='web' // @if TARGET='web'
this.fileInput = React.createRef(); this.fileInput = React.createRef();
// @endif // @endif
@ -67,19 +66,20 @@ class FileSelector extends React.PureComponent<Props> {
); );
} }
handleFileInputSelection() { // TODO: Add this back for web publishing
const { files } = this.fileInput.current; // handleFileInputSelection() {
if (!files) { // const { files } = this.fileInput.current;
return; // if (!files) {
} // return;
// }
const filePath = files[0]; // const filePath = files[0];
const fileName = filePath.name; // const fileName = filePath.name;
if (this.props.onFileChosen) { // if (this.props.onFileChosen) {
this.props.onFileChosen(filePath, fileName); // this.props.onFileChosen(filePath, fileName);
} // }
} // }
input: ?HTMLInputElement; input: ?HTMLInputElement;
@ -91,27 +91,18 @@ class FileSelector extends React.PureComponent<Props> {
return ( return (
<React.Fragment> <React.Fragment>
{/* @if TARGET='app' */}
<FormField <FormField
webkitdirectory='true' webkitdirectory="true"
className='form-field--copyable' className="form-field--copyable"
type='text' type="text"
ref={input => { ref={this.fileInput}
if (this.input) this.input = input;
}}
onFocus={() => { onFocus={() => {
if (this.input) this.input.select(); if (this.fileInput) this.fileInput.current.select();
}} }}
readOnly='readonly' readOnly="readonly"
value={currentPath || __('No File Chosen')} value={currentPath || __('No File Chosen')}
inputButton={ inputButton={<Button button="primary" label={label} onClick={this.handleButtonClick} />}
<Button button='primary' onClick={() => this.handleButtonClick()} label={label} />
}
/> />
{/* @endif */}
{/* @if TARGET='web' */}
<input type='file' ref={this.fileInput} onChange={() => this.handleFileInputSelection()} />
{/* @endif */}
</React.Fragment> </React.Fragment>
); );
} }

View file

@ -0,0 +1,12 @@
import React from 'react';
import Button from 'component/button';
export default function UnsupportedOnWeb() {
return (
<div className="card__content help help--warning">
This page is not currently supported on the web.{' '}
<Button button="link" label={__('Download the desktop app')} href="https://lbry.com/get" />{' '}
for full feature support.
</div>
);
}

View file

@ -144,7 +144,6 @@ class MediaPlayer extends React.PureComponent<Props, State> {
else { else {
// Temp hack to help in some metadata loading cases // Temp hack to help in some metadata loading cases
setTimeout(() => { setTimeout(() => {
const currentMediaContainer = this.mediaContainer.current; const currentMediaContainer = this.mediaContainer.current;
// Clean any potential rogue instances // Clean any potential rogue instances
@ -210,7 +209,7 @@ class MediaPlayer extends React.PureComponent<Props, State> {
} }
const playerElement = this.mediaContainer.current; const playerElement = this.mediaContainer.current;
if (playerElement) { if (playerElement) {
if(playerElement.children && playerElement.children[0]) { if (playerElement.children && playerElement.children[0]) {
playerElement.children[0].play(); playerElement.children[0].play();
} }
} }
@ -291,6 +290,15 @@ class MediaPlayer extends React.PureComponent<Props, State> {
const { mediaType, contentType } = this.props; const { mediaType, contentType } = this.props;
const { unplayable, fileSource, hasMetadata } = this.state; const { unplayable, fileSource, hasMetadata } = this.state;
if (['audio', 'video'].indexOf(mediaType) === -1) {
return {
isLoading: false,
loadingStatus: __(
'This file type is not currently supported on lbry.tv. Try viewing it in the desktop app.'
),
};
}
const loader: { isLoading: boolean, loadingStatus: ?string } = { const loader: { isLoading: boolean, loadingStatus: ?string } = {
isLoading: false, isLoading: false,
loadingStatus: null, loadingStatus: null,

View file

@ -12,6 +12,7 @@ import ChannelSection from 'component/selectChannel';
import classnames from 'classnames'; import classnames from 'classnames';
import FileSelector from 'component/common/file-selector'; import FileSelector from 'component/common/file-selector';
import SelectThumbnail from 'component/selectThumbnail'; import SelectThumbnail from 'component/selectThumbnail';
import UnsupportedOnWeb from 'component/common/unsupported-on-web';
import BidHelpText from './internal/bid-help-text'; import BidHelpText from './internal/bid-help-text';
import NameHelpText from './internal/name-help-text'; import NameHelpText from './internal/name-help-text';
import LicenseType from './internal/license-type'; import LicenseType from './internal/license-type';
@ -346,297 +347,308 @@ class PublishForm extends React.PureComponent<Props> {
const shortUri = buildURI({ contentName: name }); const shortUri = buildURI({ contentName: name });
return ( return (
<Form onSubmit={this.handlePublish}> <React.Fragment>
<section className={classnames('card card--section', { 'card--disabled': publishing })}> {IS_WEB && <UnsupportedOnWeb />}
<header className="card__header"> <Form onSubmit={this.handlePublish}>
<h2 className="card__title card__title--flex-between"> <section
{__('Content')} className={classnames('card card--section', {
{(filePath || !!editingURI) && ( 'card--disabled': IS_WEB || publishing,
<Button })}
button="inverse" >
icon={ICONS.CLOSE}
label={__('Clear')}
onClick={clearPublish}
/>
)}
</h2>
<p className="card__subtitle">
{isStillEditing
? __('You are currently editing a claim.')
: __('What are you publishing?')}{' '}
{__('Read our')}{' '}
<Button button="link" label={__('FAQ')} href="https://lbry.io/faq/how-to-publish" />{' '}
{__('to learn more.')}
</p>
</header>
<div className="card__content">
<FileSelector currentPath={filePath} onFileChosen={this.handleFileChange} />
{!!isStillEditing && name && (
<p className="help">
{__("If you don't choose a file, the file from your existing claim")}
{` "${name}" `}
{__('will be used.')}
</p>
)}
</div>
</section>
<div className={classnames({ 'card--disabled': formDisabled })}>
<section className="card card--section">
<div className="card__content">
<FormField
type="text"
name="content_title"
label={__('Title')}
placeholder={__('Titular Title')}
disabled={formDisabled}
value={title}
onChange={e => updatePublishForm({ title: e.target.value })}
/>
<FormField
type="markdown"
name="content_description"
label={__('Description')}
placeholder={__('Description of your content')}
value={description}
disabled={formDisabled}
onChange={text => updatePublishForm({ description: text })}
/>
</div>
</section>
<section className="card card--section">
<header className="card__header"> <header className="card__header">
<h2 className="card__title">{__('Thumbnail')}</h2> <h2 className="card__title card__title--flex-between">
<p className="card__subtitle"> {__('Content')}
{uploadThumbnailStatus === THUMBNAIL_STATUSES.API_DOWN ? ( {(filePath || !!editingURI) && (
__('Enter a URL for your thumbnail.') <Button
) : ( button="inverse"
<React.Fragment> icon={ICONS.CLOSE}
{__('Upload your thumbnail (.png/.jpg/.jpeg/.gif) to')}{' '} label={__('Clear')}
<Button button="link" label={__('spee.ch')} href="https://spee.ch/about" />.{' '} onClick={clearPublish}
{__('Recommended size: 800x450 (16:9)')} />
</React.Fragment>
)} )}
</h2>
<p className="card__subtitle">
{isStillEditing
? __('You are currently editing a claim.')
: __('What are you publishing?')}{' '}
{__('Read our')}{' '}
<Button button="link" label={__('FAQ')} href="https://lbry.io/faq/how-to-publish" />{' '}
{__('to learn more.')}
</p> </p>
</header> </header>
<SelectThumbnail
thumbnailPath={thumbnailPath}
thumbnail={thumbnail}
uploadThumbnailStatus={uploadThumbnailStatus}
updatePublishForm={updatePublishForm}
formDisabled={formDisabled}
resetThumbnailStatus={resetThumbnailStatus}
/>
</section>
<section className="card card--section">
<header className="card__header">
<h2 className="card__title">{__('Price')}</h2>
<p className="card__subtitle">{__('How much will this content cost?')}</p>
</header>
<div className="card__content"> <div className="card__content">
<FormField <FileSelector currentPath={filePath} onFileChosen={this.handleFileChange} />
type="radio" {!!isStillEditing && name && (
name="content_free" <p className="help">
label={__('Free')} {__("If you don't choose a file, the file from your existing claim")}
checked={contentIsFree} {` "${name}" `}
disabled={formDisabled} {__('will be used.')}
onChange={() => updatePublishForm({ contentIsFree: true })}
/>
<FormField
type="radio"
name="content_cost"
label={__('Choose price')}
checked={!contentIsFree}
disabled={formDisabled}
onChange={() => updatePublishForm({ contentIsFree: false })}
/>
{!contentIsFree && (
<FormFieldPrice
name="content_cost_amount"
min="0"
price={price}
onChange={newPrice => updatePublishForm({ price: newPrice })}
/>
)}
{price.currency !== 'LBC' && (
<p className="form-field__help">
{__(
'All content fees are charged in LBC. For non-LBC payment methods, the number of credits charged will be adjusted based on the value of LBRY credits at the time of purchase.'
)}
</p> </p>
)} )}
</div> </div>
</section> </section>
<div className={classnames({ 'card--disabled': formDisabled })}>
<section className="card card--section"> <section className="card card--section">
<header className="card__header"> <div className="card__content">
<h2 className="card__title">{__('Anonymous or under a channel?')}</h2>
<p className="card__subtitle">
{__('This is a username or handle that your content can be found under.')}{' '}
{__('Ex. @Marvel, @TheBeatles, @BooksByJoe')}
</p>
</header>
<div className="card__content">
<ChannelSection channel={channel} onChannelChange={this.handleChannelChange} />
</div>
</section>
<section className="card card--section">
<header className="card__header">
<h2 className="card__title">{__('Where can people find this content?')}</h2>
<p className="card__subtitle">
{__(
'The LBRY URL is the exact address where people find your content (ex. lbry://myvideo).'
)}{' '}
<Button button="link" label={__('Learn more')} href="https://lbry.io/faq/naming" />
</p>
</header>
<div className="card__content">
<fieldset-group class="fieldset-group--smushed fieldset-group--disabled-prefix">
<fieldset-section>
<label>{__('Name')}</label>
<span className="form-field__prefix">{`lbry://${
!channel || channel === CHANNEL_ANONYMOUS || channel === CHANNEL_NEW
? ''
: `${channel}/`
}`}</span>
</fieldset-section>
<FormField <FormField
type="text" type="text"
name="content_name" name="content_title"
value={name} label={__('Title')}
onChange={event => this.handleNameChange(event.target.value)} placeholder={__('Titular Title')}
error={nameError} disabled={formDisabled}
value={title}
onChange={e => updatePublishForm({ title: e.target.value })}
/> />
</fieldset-group>
<div className="form-field__help"> <FormField
<NameHelpText type="markdown"
isStillEditing={isStillEditing} name="content_description"
uri={uri} label={__('Description')}
myClaimForUri={myClaimForUri} placeholder={__('Description of your content')}
onEditMyClaim={this.editExistingClaim} value={description}
disabled={formDisabled}
onChange={text => updatePublishForm({ description: text })}
/> />
</div> </div>
</div> </section>
<div className={classnames('card__content', { 'card--disabled': !name })}> <section className="card card--section">
<FormField <header className="card__header">
className="form-field--price-amount" <h2 className="card__title">{__('Thumbnail')}</h2>
type="number" <p className="card__subtitle">
name="content_bid" {uploadThumbnailStatus === THUMBNAIL_STATUSES.API_DOWN ? (
step="any" __('Enter a URL for your thumbnail.')
label={__('Deposit (LBC)')} ) : (
postfix="LBC" <React.Fragment>
value={bid} {__('Upload your thumbnail (.png/.jpg/.jpeg/.gif) to')}{' '}
error={bidError} <Button button="link" label={__('spee.ch')} href="https://spee.ch/about" />.{' '}
min="0" {__('Recommended size: 800x450 (16:9)')}
disabled={!name} </React.Fragment>
onChange={event => this.handleBidChange(parseFloat(event.target.value))} )}
placeholder={winningBidForClaimUri ? winningBidForClaimUri + 0.1 : 0.1} </p>
helper={ </header>
<BidHelpText
uri={shortUri} <SelectThumbnail
isResolvingUri={isResolvingUri} thumbnailPath={thumbnailPath}
amountNeededForTakeover={amountNeededForTakeover} thumbnail={thumbnail}
uploadThumbnailStatus={uploadThumbnailStatus}
updatePublishForm={updatePublishForm}
formDisabled={formDisabled}
resetThumbnailStatus={resetThumbnailStatus}
/>
</section>
<section className="card card--section">
<header className="card__header">
<h2 className="card__title">{__('Price')}</h2>
<p className="card__subtitle">{__('How much will this content cost?')}</p>
</header>
<div className="card__content">
<FormField
type="radio"
name="content_free"
label={__('Free')}
checked={contentIsFree}
disabled={formDisabled}
onChange={() => updatePublishForm({ contentIsFree: true })}
/>
<FormField
type="radio"
name="content_cost"
label={__('Choose price')}
checked={!contentIsFree}
disabled={formDisabled}
onChange={() => updatePublishForm({ contentIsFree: false })}
/>
{!contentIsFree && (
<FormFieldPrice
name="content_cost_amount"
min="0"
price={price}
onChange={newPrice => updatePublishForm({ price: newPrice })}
/> />
} )}
/> {price.currency !== 'LBC' && (
</div> <p className="form-field__help">
</section> {__(
'All content fees are charged in LBC. For non-LBC payment methods, the number of credits charged will be adjusted based on the value of LBRY credits at the time of purchase.'
)}
</p>
)}
</div>
</section>
<section className="card card--section"> <section className="card card--section">
<div className="card__content"> <header className="card__header">
<FormField <h2 className="card__title">{__('Anonymous or under a channel?')}</h2>
type="checkbox" <p className="card__subtitle">
name="content_is_mature" {__('This is a username or handle that your content can be found under.')}{' '}
label={__('Mature audiences only')} {__('Ex. @Marvel, @TheBeatles, @BooksByJoe')}
checked={nsfw} </p>
onChange={() => updatePublishForm({ nsfw: !nsfw })} </header>
/>
<FormField <div className="card__content">
label={__('Language')} <ChannelSection channel={channel} onChannelChange={this.handleChannelChange} />
type="select" </div>
name="content_language" </section>
value={language}
onChange={event => updatePublishForm({ language: event.target.value })}
>
<option value="en">{__('English')}</option>
<option value="zh">{__('Chinese')}</option>
<option value="fr">{__('French')}</option>
<option value="de">{__('German')}</option>
<option value="jp">{__('Japanese')}</option>
<option value="ru">{__('Russian')}</option>
<option value="es">{__('Spanish')}</option>
<option value="id">{__('Indonesian')}</option>
<option value="it">{__('Italian')}</option>
<option value="nl">{__('Dutch')}</option>
<option value="tr">{__('Turkish')}</option>
<option value="pl">{__('Polish')}</option>
<option value="ms">{__('Malay')}</option>
</FormField>
<LicenseType <section className="card card--section">
licenseType={licenseType} <header className="card__header">
otherLicenseDescription={otherLicenseDescription} <h2 className="card__title">{__('Where can people find this content?')}</h2>
licenseUrl={licenseUrl} <p className="card__subtitle">
handleLicenseChange={(newLicenseType, newLicenseUrl) => {__(
updatePublishForm({ 'The LBRY URL is the exact address where people find your content (ex. lbry://myvideo).'
licenseType: newLicenseType, )}{' '}
licenseUrl: newLicenseUrl, <Button
}) button="link"
} label={__('Learn more')}
handleLicenseDescriptionChange={event => href="https://lbry.io/faq/naming"
updatePublishForm({ />
otherLicenseDescription: event.target.value, </p>
}) </header>
}
handleLicenseUrlChange={event =>
updatePublishForm({ licenseUrl: event.target.value })
}
/>
</div>
</section>
<section className="card card--section"> <div className="card__content">
<div className="card__content"> <fieldset-group class="fieldset-group--smushed fieldset-group--disabled-prefix">
{__('By continuing, you accept the')}{' '} <fieldset-section>
<Button <label>{__('Name')}</label>
button="link" <span className="form-field__prefix">{`lbry://${
href="https://www.lbry.io/termsofservice" !channel || channel === CHANNEL_ANONYMOUS || channel === CHANNEL_NEW
label={__('LBRY Terms of Service')} ? ''
/> : `${channel}/`
. }`}</span>
</div> </fieldset-section>
</section> <FormField
type="text"
name="content_name"
value={name}
onChange={event => this.handleNameChange(event.target.value)}
error={nameError}
/>
</fieldset-group>
<div className="form-field__help">
<NameHelpText
isStillEditing={isStillEditing}
uri={uri}
myClaimForUri={myClaimForUri}
onEditMyClaim={this.editExistingClaim}
/>
</div>
</div>
<section className="card card--section"> <div className={classnames('card__content', { 'card--disabled': !name })}>
<div className="card__content"> <FormField
<div className="card__actions"> className="form-field--price-amount"
<Submit type="number"
label={submitLabel} name="content_bid"
disabled={ step="any"
formDisabled || label={__('Deposit (LBC)')}
!formValid || postfix="LBC"
uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS value={bid}
error={bidError}
min="0"
disabled={!name}
onChange={event => this.handleBidChange(parseFloat(event.target.value))}
placeholder={winningBidForClaimUri ? winningBidForClaimUri + 0.1 : 0.1}
helper={
<BidHelpText
uri={shortUri}
isResolvingUri={isResolvingUri}
amountNeededForTakeover={amountNeededForTakeover}
/>
} }
/> />
<Button button="link" onClick={this.handleCancelPublish} label={__('Cancel')} />
</div> </div>
</div> </section>
</section>
</div>
{!formDisabled && !formValid && this.renderFormErrors()} <section className="card card--section">
</Form> <div className="card__content">
<FormField
type="checkbox"
name="content_is_mature"
label={__('Mature audiences only')}
checked={nsfw}
onChange={() => updatePublishForm({ nsfw: !nsfw })}
/>
<FormField
label={__('Language')}
type="select"
name="content_language"
value={language}
onChange={event => updatePublishForm({ language: event.target.value })}
>
<option value="en">{__('English')}</option>
<option value="zh">{__('Chinese')}</option>
<option value="fr">{__('French')}</option>
<option value="de">{__('German')}</option>
<option value="jp">{__('Japanese')}</option>
<option value="ru">{__('Russian')}</option>
<option value="es">{__('Spanish')}</option>
<option value="id">{__('Indonesian')}</option>
<option value="it">{__('Italian')}</option>
<option value="nl">{__('Dutch')}</option>
<option value="tr">{__('Turkish')}</option>
<option value="pl">{__('Polish')}</option>
<option value="ms">{__('Malay')}</option>
</FormField>
<LicenseType
licenseType={licenseType}
otherLicenseDescription={otherLicenseDescription}
licenseUrl={licenseUrl}
handleLicenseChange={(newLicenseType, newLicenseUrl) =>
updatePublishForm({
licenseType: newLicenseType,
licenseUrl: newLicenseUrl,
})
}
handleLicenseDescriptionChange={event =>
updatePublishForm({
otherLicenseDescription: event.target.value,
})
}
handleLicenseUrlChange={event =>
updatePublishForm({ licenseUrl: event.target.value })
}
/>
</div>
</section>
<section className="card card--section">
<div className="card__content">
{__('By continuing, you accept the')}{' '}
<Button
button="link"
href="https://www.lbry.io/termsofservice"
label={__('LBRY Terms of Service')}
/>
.
</div>
</section>
<section className="card card--section">
<div className="card__content">
<div className="card__actions">
<Submit
label={submitLabel}
disabled={
formDisabled ||
!formValid ||
uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS
}
/>
<Button button="link" onClick={this.handleCancelPublish} label={__('Cancel')} />
</div>
</div>
</section>
</div>
{!formDisabled && !formValid && this.renderFormErrors()}
</Form>
</React.Fragment>
); );
} }
} }

View file

@ -95,6 +95,8 @@ class ModalRouter extends React.PureComponent<Props, State> {
} }
checkShowCreditIntro(props: Props) { checkShowCreditIntro(props: Props) {
// @if TARGET='app'
// This doesn't make sense to show until the web has wallet support
const { balance, page, isCreditIntroAcknowledged } = props; const { balance, page, isCreditIntroAcknowledged } = props;
if ( if (
@ -104,6 +106,7 @@ class ModalRouter extends React.PureComponent<Props, State> {
) { ) {
return MODALS.INSUFFICIENT_CREDITS; return MODALS.INSUFFICIENT_CREDITS;
} }
// @endif
return undefined; return undefined;
} }

View file

@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import Button from 'component/button'; import Button from 'component/button';
import { FormField } from 'component/common/form'; import { FormField } from 'component/common/form';
import { Lbry, doToast } from 'lbry-redux'; import { doToast } from 'lbry-redux';
import { Lbryio } from 'lbryinc';
import Page from 'component/page'; import Page from 'component/page';
class ReportPage extends React.Component { class ReportPage extends React.Component {
@ -26,7 +27,7 @@ class ReportPage extends React.Component {
this.setState({ this.setState({
submitting: true, submitting: true,
}); });
Lbry.report_bug({ message }).then(() => { Lbryio.call('event', 'desktop_error', { error_message }).then(() => {
this.setState({ this.setState({
submitting: false, submitting: false,
}); });

View file

@ -2,10 +2,12 @@
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import * as SETTINGS from 'constants/settings'; import * as SETTINGS from 'constants/settings';
import * as React from 'react'; import * as React from 'react';
import classnames from 'classnames';
import { FormField, FormFieldPrice, Form } from 'component/common/form'; import { FormField, FormFieldPrice, Form } from 'component/common/form';
import Button from 'component/button'; import Button from 'component/button';
import Page from 'component/page'; import Page from 'component/page';
import FileSelector from 'component/common/file-selector'; import FileSelector from 'component/common/file-selector';
import UnsupportedOnWeb from 'component/common/unsupported-on-web';
export type Price = { export type Price = {
currency: string, currency: string,
@ -147,12 +149,13 @@ class SettingsPage extends React.PureComponent<Props, State> {
return ( return (
<Page> <Page>
{IS_WEB && <UnsupportedOnWeb />}
{noDaemonSettings ? ( {noDaemonSettings ? (
<section className="card card--section"> <section className="card card--section">
<div className="card__title">{__('Failed to load settings.')}</div> <div className="card__title">{__('Failed to load settings.')}</div>
</section> </section>
) : ( ) : (
<React.Fragment> <div className={classnames({ 'card--disabled': IS_WEB })}>
<section className="card card--section"> <section className="card card--section">
<header className="card__header"> <header className="card__header">
<h2 className="card__title">{__('Download Directory')}</h2> <h2 className="card__title">{__('Download Directory')}</h2>
@ -430,7 +433,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
/> />
</div> </div>
</section> </section>
</React.Fragment> </div>
)} )}
</Page> </Page>
); );

View file

@ -2,6 +2,7 @@ const path = require('path');
const merge = require('webpack-merge'); const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.config.js'); const baseConfig = require('./webpack.base.config.js');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const { DefinePlugin } = require('webpack');
const STATIC_ROOT = path.resolve(__dirname, 'static/'); const STATIC_ROOT = path.resolve(__dirname, 'static/');
const DIST_ROOT = path.resolve(__dirname, 'dist/'); const DIST_ROOT = path.resolve(__dirname, 'dist/');
@ -55,6 +56,9 @@ const webConfig = {
to: `${DIST_ROOT}/web/server.js`, to: `${DIST_ROOT}/web/server.js`,
}, },
]), ]),
new DefinePlugin({
IS_WEB: JSON.stringify(true),
}),
], ],
}; };