so far
This commit is contained in:
parent
498618e39b
commit
575db85477
23 changed files with 625 additions and 359 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,6 +6,7 @@ dist
|
|||
/app/node_modules
|
||||
/build/venv
|
||||
/lbry-app-venv
|
||||
/lbry-venv
|
||||
/daemon/build
|
||||
/daemon/venv
|
||||
/daemon/requirements.txt
|
||||
|
|
|
@ -62,9 +62,9 @@ function getPidsForProcessName(name) {
|
|||
}
|
||||
|
||||
function createWindow () {
|
||||
win = new BrowserWindow({backgroundColor: '#155B4A'}) //$color-primary
|
||||
win = new BrowserWindow({backgroundColor: '#155B4A', minWidth: 800, minHeight: 1000 }) //$color-primary
|
||||
win.maximize()
|
||||
//win.webContents.openDevTools()
|
||||
win.webContents.openDevTools();
|
||||
win.loadURL(`file://${__dirname}/dist/index.html`)
|
||||
win.on('closed', () => {
|
||||
win = null
|
||||
|
|
4
doitagain.sh
Executable file
4
doitagain.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
rm -rf ~/.lbrynet/
|
||||
rm -rf ~/.lbryum/
|
||||
./node_modules/.bin/electron app
|
49
ui/js/app.js
49
ui/js/app.js
|
@ -47,12 +47,6 @@ var App = React.createClass({
|
|||
_upgradeDownloadItem: null,
|
||||
_isMounted: false,
|
||||
_version: null,
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
address: window.location.search
|
||||
};
|
||||
},
|
||||
getUpdateUrl: function() {
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
|
@ -87,54 +81,19 @@ var App = React.createClass({
|
|||
pageArgs: pageArgs === undefined ? null : pageArgs
|
||||
};
|
||||
},
|
||||
updateRegistrationStatus: function() {
|
||||
if (localStorage.getItem('accessToken')) {
|
||||
this.setState({
|
||||
registrationCheckComplete: true,
|
||||
});
|
||||
} else {
|
||||
lbry.status().then(({installation_id}) => {
|
||||
installation_id += parseInt(Date.now(), 10); // temp
|
||||
installation_id += "X".repeat(96 - installation_id.length); // temp
|
||||
lbryio.call('user_install', 'exists', {app_id: installation_id}).then((userExists) => {
|
||||
// TODO: deal with case where user exists already with the same app ID, but we have no access token.
|
||||
// Possibly merge in to the existing user with the same app ID.
|
||||
lbryio.call('user', 'new', {
|
||||
language: 'en',
|
||||
app_id: installation_id,
|
||||
}, 'post').then(({ID}) => {
|
||||
localStorage.setItem('accessToken', ID);
|
||||
localStorage.setItem('appId', installation_id);
|
||||
this.setState({
|
||||
registrationCheckComplete: true,
|
||||
justRegistered: true,
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
},
|
||||
getInitialState: function() {
|
||||
var match, param, val, viewingPage, pageArgs,
|
||||
drawerOpenRaw = sessionStorage.getItem('drawerOpen');
|
||||
|
||||
return Object.assign(this.getViewingPageAndArgs(this.props.address), {
|
||||
return Object.assign(this.getViewingPageAndArgs(window.location.search), {
|
||||
drawerOpen: drawerOpenRaw !== null ? JSON.parse(drawerOpenRaw) : true,
|
||||
errorInfo: null,
|
||||
modal: null,
|
||||
downloadProgress: null,
|
||||
downloadComplete: false,
|
||||
registrationCheckComplete: null,
|
||||
justRegistered: false,
|
||||
});
|
||||
},
|
||||
componentWillMount: function() {
|
||||
if (!localStorage.getItem('accessToken') && window.location.search != '?discover') {
|
||||
// User isn't registered but somehow made it to a page other than Discover, so send them to
|
||||
// Discover to get them registered and show them the welcome screen.
|
||||
window.location.search = '?discover';
|
||||
}
|
||||
|
||||
document.addEventListener('unhandledError', (event) => {
|
||||
this.alertError(event.detail);
|
||||
});
|
||||
|
@ -172,8 +131,6 @@ var App = React.createClass({
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.updateRegistrationStatus();
|
||||
},
|
||||
openDrawer: function() {
|
||||
sessionStorage.setItem('drawerOpen', true);
|
||||
|
@ -337,10 +294,6 @@ var App = React.createClass({
|
|||
}
|
||||
},
|
||||
render: function() {
|
||||
if (!this.state.registrationCheckComplete) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var mainContent = this.getMainContent(),
|
||||
headerLinks = this.getHeaderLinks(),
|
||||
searchQuery = this.state.viewingPage == 'discover' && this.state.pageArgs ? this.state.pageArgs : '';
|
||||
|
|
196
ui/js/component/auth.js
Normal file
196
ui/js/component/auth.js
Normal file
|
@ -0,0 +1,196 @@
|
|||
import React from 'react';
|
||||
import lbryio from '../lbryio.js';
|
||||
|
||||
import ModalPage from './modal-page.js';
|
||||
import {Link} from '../component/link.js';
|
||||
import FormField from '../component/form.js';
|
||||
import Notice from '../component/notice.js'
|
||||
|
||||
|
||||
const SubmitEmailStage = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
rewardType: null,
|
||||
email: '',
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
handleEmailChanged: function(event) {
|
||||
this.setState({
|
||||
email: event.target.value,
|
||||
});
|
||||
},
|
||||
handleSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.setState({
|
||||
submitting: true,
|
||||
});
|
||||
lbryio.call('user_email', 'new', {email: this.state.email}, 'post').then(() => {
|
||||
this.props.onEmailSaved();
|
||||
}, (error) => {
|
||||
if (this._emailField) {
|
||||
this._emailField.showError(error.message)
|
||||
}
|
||||
this.setState({ submitting: false });
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<section>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<FormField row={true} ref={(field) => { this._emailField = field }} type="text" label="Email" placeholder="webmaster@toplbryfan.com"
|
||||
name="email" value={this.state.email}
|
||||
onChange={this.handleEmailChanged} />
|
||||
<div className="form-row">
|
||||
<Link button="primary" label="Next" disabled={this.state.submitting} onClick={this.handleSubmit} />
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const ConfirmEmailStage = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
rewardType: null,
|
||||
code: '',
|
||||
submitting: false,
|
||||
errorMessage: null,
|
||||
};
|
||||
},
|
||||
handleCodeChanged: function(event) {
|
||||
this.setState({
|
||||
code: event.target.value,
|
||||
});
|
||||
},
|
||||
handleSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
this.setState({
|
||||
submitting: true,
|
||||
});
|
||||
|
||||
lbryio.call('user_email', 'confirm', {verification_token: this.state.code}, 'post').then(() => {
|
||||
rewards.claimReward('confirm_email').then(() => {
|
||||
this.props.onDone();
|
||||
}, (err) => {l
|
||||
this.props.onDone();
|
||||
});
|
||||
}, (error) => {
|
||||
if (this._codeField) {
|
||||
this._codeField.showError(error.message)
|
||||
this.setState({ submitting: false })
|
||||
}
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<section>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<FormField row={true} label="Verification Code" ref={(field) => { this._codeField = field }} type="text"
|
||||
name="code" placeholder="a94bXXXXXXXXXXXXXX" value={this.state.code} onChange={this.handleCodeChanged}
|
||||
helper="A verification code is required to access this version."/>
|
||||
<div className="form-row">
|
||||
<Link button="primary" label="Verify" disabled={this.state.submitting} onClick={this.handleSubmit} />
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const ErrorStage = React.createClass({
|
||||
render: function() {
|
||||
// <section><Link button="primary" label="OK" onClick={this.props.onDone} /></section>
|
||||
return (
|
||||
<section>
|
||||
<p>An error was encountered that we cannot continue from.</p>
|
||||
<p>At least we're earning the name beta.</p>
|
||||
<Link button="alt" label="Try Reload" onClick={() => { window.location.reload() } } />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const PendingStage = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<section>
|
||||
<p>Preparing for first access <span className="busy-indicator"></span></p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export const AuthOverlay = React.createClass({
|
||||
_stages: {
|
||||
pending: PendingStage,
|
||||
error: ErrorStage,
|
||||
email: SubmitEmailStage,
|
||||
confirm: ConfirmEmailStage,
|
||||
},
|
||||
propTypes: {
|
||||
// onDone: React.PropTypes.func.isRequired,
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {
|
||||
stage: "pending",
|
||||
stageProps: {}
|
||||
};
|
||||
},
|
||||
componentWillMount: function() {
|
||||
lbryio.authenticate().then(function(user) {
|
||||
console.log(user);
|
||||
if (!user.HasVerifiedEmail) {
|
||||
this.setState({
|
||||
stage: "email",
|
||||
stageProps: {
|
||||
onEmailSaved: function() {
|
||||
this.setState({
|
||||
stage: "confirm"
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.setState({ stage: null })
|
||||
}
|
||||
}.bind(this)).catch((err) => {
|
||||
this.setState({
|
||||
stage: "error",
|
||||
stageProps: { errorText: err.message }
|
||||
})
|
||||
document.dispatchEvent(new CustomEvent('unhandledError', {
|
||||
detail: {
|
||||
message: err.message,
|
||||
data: err.stack
|
||||
}
|
||||
}));
|
||||
})
|
||||
},
|
||||
// handleStageDone: function() {
|
||||
// if (this.state.stageNum >= this._stages.length - 1) {
|
||||
// this.props.onDone();
|
||||
// } else {
|
||||
// this.setState({
|
||||
// stageNum: this.state.stageNum + 1,
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
|
||||
// <Content onDone={this.handleStageDone} />
|
||||
render: function() {
|
||||
console.log(lbryio.user);
|
||||
if (!this.state.stage || lbryio.user && lbryio.user.HasVerifiedEmail) {
|
||||
return null;
|
||||
}
|
||||
const StageContent = this._stages[this.state.stage];
|
||||
return (
|
||||
<ModalPage isOpen={true} contentLabel="Authentication" {...this.props}>
|
||||
<h1>LBRY Early Access</h1>
|
||||
<StageContent {...this.state.stageProps} />
|
||||
</ModalPage>
|
||||
);
|
||||
}
|
||||
});
|
|
@ -6,6 +6,8 @@ var requiredFieldWarningStyle = {
|
|||
transition: 'opacity 400ms ease-in',
|
||||
};
|
||||
|
||||
var formFieldCounter = 0;
|
||||
|
||||
var FormField = React.createClass({
|
||||
_fieldRequiredText: 'This field is required',
|
||||
_type: null,
|
||||
|
@ -13,11 +15,12 @@ var FormField = React.createClass({
|
|||
|
||||
propTypes: {
|
||||
type: React.PropTypes.string.isRequired,
|
||||
row: React.PropTypes.bool,
|
||||
hidden: React.PropTypes.bool,
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {
|
||||
adviceState: 'hidden',
|
||||
errorState: 'hidden',
|
||||
adviceText: null,
|
||||
}
|
||||
},
|
||||
|
@ -33,25 +36,25 @@ var FormField = React.createClass({
|
|||
this._element = this.props.type;
|
||||
}
|
||||
},
|
||||
showAdvice: function(text) {
|
||||
showError: function(text) {
|
||||
this.setState({
|
||||
adviceState: 'shown',
|
||||
errorState: 'shown',
|
||||
adviceText: text,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
adviceState: 'fading',
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
adviceState: 'hidden',
|
||||
});
|
||||
}, 450);
|
||||
}, 5000);
|
||||
// setTimeout(() => {
|
||||
// this.setState({
|
||||
// errorState: 'fading',
|
||||
// });
|
||||
// setTimeout(() => {
|
||||
// this.setState({
|
||||
// errorState: 'hidden',
|
||||
// });
|
||||
// }, 450);
|
||||
// }, 5000);
|
||||
},
|
||||
warnRequired: function() {
|
||||
this.showAdvice(this._fieldRequiredText);
|
||||
this.showError(this._fieldRequiredText);
|
||||
},
|
||||
focus: function() {
|
||||
this.refs.field.focus();
|
||||
|
@ -70,43 +73,39 @@ var FormField = React.createClass({
|
|||
},
|
||||
render: function() {
|
||||
// Pass all unhandled props to the field element
|
||||
const otherProps = Object.assign({}, this.props);
|
||||
const otherProps = Object.assign({}, this.props),
|
||||
hasError = this.state.errorState != 'hidden';
|
||||
delete otherProps.type;
|
||||
delete otherProps.hidden;
|
||||
delete otherProps.label;
|
||||
delete otherProps.row;
|
||||
delete otherProps.helper;
|
||||
|
||||
return (
|
||||
!this.props.hidden
|
||||
? <div className="form-field-container">
|
||||
<this._element type={this._type} className="form-field" name={this.props.name} ref="field" placeholder={this.props.placeholder}
|
||||
className={'form-field--' + this.props.type + ' ' + (this.props.className || '')}
|
||||
{...otherProps}>
|
||||
{this.props.children}
|
||||
</this._element>
|
||||
<FormFieldAdvice field={this.refs.field} state={this.state.adviceState}>{this.state.adviceText}</FormFieldAdvice>
|
||||
</div>
|
||||
: null
|
||||
);
|
||||
}
|
||||
});
|
||||
++formFieldCounter;
|
||||
const elementId = "form-field-" + formFieldCounter
|
||||
|
||||
var FormFieldAdvice = React.createClass({
|
||||
propTypes: {
|
||||
state: React.PropTypes.string.isRequired,
|
||||
},
|
||||
render: function() {
|
||||
if (this.props.hidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const field = <div className="form-field">
|
||||
{ this.props.label ?
|
||||
<div className={"form-field__label " + (hasError ? 'form-field__label--error' : '')}>
|
||||
<label htmlFor={elementId}>{this.props.label}</label>
|
||||
</div> : ''
|
||||
}
|
||||
<this._element id={elementId} type={this._type} name={this.props.name} ref="field" placeholder={this.props.placeholder}
|
||||
className={'form-field__input form-field__input-' + this.props.type + ' ' + (this.props.className || '') + (hasError ? 'form-field__input--error' : '')}
|
||||
{...otherProps}>
|
||||
{this.props.children}
|
||||
</this._element>
|
||||
{ !hasError && this.props.helper ? <div className="form-field__helper">{this.props.helper}</div> : '' }
|
||||
{ hasError ? <div className="form-field__error">{this.state.adviceText}</div> : '' }
|
||||
</div>
|
||||
return (
|
||||
this.props.state != 'hidden'
|
||||
? <div className="form-field-advice-container">
|
||||
<div className={'form-field-advice' + (this.props.state == 'fading' ? ' form-field-advice--fading' : '')}>
|
||||
<Icon icon="icon-caret-up" className="form-field-advice__arrow" />
|
||||
<div className="form-field-advice__content-container">
|
||||
<span className="form-field-advice__content">
|
||||
{this.props.children}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
: null
|
||||
this.props.row ?
|
||||
<div className="form-row">{field}</div> :
|
||||
field
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -13,11 +13,12 @@ var SplashScreen = React.createClass({
|
|||
isLagging: false,
|
||||
}
|
||||
},
|
||||
updateStatus: function(was_lagging=false) {
|
||||
lbry.getDaemonStatus(this._updateStatusCallback);
|
||||
updateStatus: function() {
|
||||
lbry.status().then(this._updateStatusCallback);
|
||||
},
|
||||
_updateStatusCallback: function(status) {
|
||||
if (status.code == 'started') {
|
||||
const startupStatus = status.startup_status
|
||||
if (startupStatus.code == 'started') {
|
||||
// Wait until we are able to resolve a name before declaring
|
||||
// that we are done.
|
||||
// TODO: This is a hack, and the logic should live in the daemon
|
||||
|
@ -34,20 +35,28 @@ var SplashScreen = React.createClass({
|
|||
return;
|
||||
}
|
||||
this.setState({
|
||||
details: status.message + (status.is_lagging ? '' : '...'),
|
||||
isLagging: status.is_lagging,
|
||||
details: startupStatus.message + (startupStatus.is_lagging ? '' : '...'),
|
||||
isLagging: startupStatus.is_lagging,
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.updateStatus(status.is_lagging);
|
||||
this.updateStatus();
|
||||
}, 500);
|
||||
},
|
||||
componentDidMount: function() {
|
||||
lbry.connect((connected) => {
|
||||
this.updateStatus();
|
||||
});
|
||||
lbry.connect().then((isConnected) => {
|
||||
if (isConnected) {
|
||||
this.updateStatus();
|
||||
} else {
|
||||
this.setState({
|
||||
isLagging: true,
|
||||
message: "Failed to connect to LBRY",
|
||||
details: "LBRY was unable to start and connect properly."
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
render: function() {
|
||||
return <LoadScreen message={this.props.message} details={this.state.details} isWarning={this.state.isLagging} />;
|
||||
return <LoadScreen message={this.props.message} details={this.state.details} isWarning={this.state.isLagging} />
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -118,28 +118,36 @@ lbry.call = function (method, params, callback, errorCallback, connectFailedCall
|
|||
|
||||
|
||||
//core
|
||||
lbry.connect = function(callback)
|
||||
{
|
||||
// Check every half second to see if the daemon is accepting connections
|
||||
// Once this returns True, can call getDaemonStatus to see where
|
||||
// we are in the startup process
|
||||
function checkDaemonStarted(tryNum=0) {
|
||||
lbry.isDaemonAcceptingConnections(function (runningStatus) {
|
||||
if (runningStatus) {
|
||||
lbry.isConnected = true;
|
||||
callback(true);
|
||||
} else {
|
||||
if (tryNum <= 600) { // Move # of tries into constant or config option
|
||||
setTimeout(function () {
|
||||
checkDaemonStarted(tryNum + 1);
|
||||
}, 500);
|
||||
} else {
|
||||
callback(false);
|
||||
}
|
||||
lbry._connectPromise = null;
|
||||
lbry.connect = function() {
|
||||
if (lbry._connectPromise === null) {
|
||||
|
||||
lbry._connectPromise = new Promise((resolve, reject) => {
|
||||
|
||||
// Check every half second to see if the daemon is accepting connections
|
||||
function checkDaemonStarted(tryNum = 0) {
|
||||
lbry.isDaemonAcceptingConnections(function (runningStatus) {
|
||||
if (runningStatus) {
|
||||
resolve(true);
|
||||
}
|
||||
else {
|
||||
if (tryNum <= 600) { // Move # of tries into constant or config option
|
||||
setTimeout(function () {
|
||||
checkDaemonStarted(tryNum + 1);
|
||||
}, tryNum < 100 ? 200 : 1000);
|
||||
}
|
||||
else {
|
||||
reject(new Error("Unable to connect to LBRY"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
checkDaemonStarted();
|
||||
});
|
||||
}
|
||||
checkDaemonStarted();
|
||||
|
||||
return lbry._connectPromise;
|
||||
}
|
||||
|
||||
lbry.isDaemonAcceptingConnections = function (callback) {
|
||||
|
@ -147,10 +155,6 @@ lbry.isDaemonAcceptingConnections = function (callback) {
|
|||
lbry.call('status', {}, () => callback(true), null, () => callback(false))
|
||||
};
|
||||
|
||||
lbry.getDaemonStatus = function (callback) {
|
||||
lbry.call('daemon_status', {}, callback);
|
||||
};
|
||||
|
||||
lbry.checkFirstRun = function(callback) {
|
||||
lbry.call('is_first_run', {}, callback);
|
||||
}
|
||||
|
@ -430,6 +434,10 @@ lbry.getClientSettings = function() {
|
|||
|
||||
lbry.getClientSetting = function(setting) {
|
||||
var localStorageVal = localStorage.getItem('setting_' + setting);
|
||||
if (setting == 'showDeveloperMenu')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return (localStorageVal === null ? lbry.defaultClientSettings[setting] : JSON.parse(localStorageVal));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
import {getLocal, setLocal} from './utils.js';
|
||||
import lbry from './lbry.js';
|
||||
|
||||
const querystring = require('querystring');
|
||||
|
||||
const lbryio = {};
|
||||
const lbryio = {
|
||||
_accessToken: getLocal('accessToken'),
|
||||
_authenticationPromise: null,
|
||||
_user : null
|
||||
};
|
||||
|
||||
const CONNECTION_STRING = 'https://apidev.lbry.tech/';
|
||||
const CONNECTION_STRING = 'http://localhost:8080/';
|
||||
|
||||
const mocks = {
|
||||
'reward_type.get': ({name}) => {
|
||||
|
@ -37,25 +44,18 @@ lbryio.call = function(resource, action, params={}, method='get') {
|
|||
|
||||
/* end temp */
|
||||
|
||||
console.log('about to create xhr object');
|
||||
const xhr = new XMLHttpRequest;
|
||||
|
||||
xhr.addEventListener('error', function (error) {
|
||||
console.log('received XHR error:', error);
|
||||
reject(error);
|
||||
xhr.addEventListener('error', function (event) {
|
||||
reject(new Error("Something went wrong making an internal API call."));
|
||||
});
|
||||
|
||||
|
||||
console.log('about to add timeout listener');
|
||||
xhr.addEventListener('timeout', function() {
|
||||
console.log('XHR timed out');
|
||||
|
||||
reject(new Error('XMLHttpRequest connection timed out'));
|
||||
});
|
||||
|
||||
console.log('about to create load listener');
|
||||
xhr.addEventListener('load', function() {
|
||||
console.log('loaded');
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
|
||||
if (!response.success) {
|
||||
|
@ -78,14 +78,12 @@ lbryio.call = function(resource, action, params={}, method='get') {
|
|||
}
|
||||
});
|
||||
|
||||
console.log('about to call xhr.open');
|
||||
|
||||
// For social media auth:
|
||||
//const accessToken = localStorage.getItem('accessToken');
|
||||
//const fullParams = {...params, ... accessToken ? {access_token: accessToken} : {}};
|
||||
|
||||
// Temp app ID based auth:
|
||||
const fullParams = {app_id: localStorage.getItem('appId'), ...params};
|
||||
const fullParams = {app_id: lbryio._accessToken, ...params};
|
||||
|
||||
if (method == 'get') {
|
||||
console.info('GET ', CONNECTION_STRING + resource + '/' + action, ' | params:', fullParams);
|
||||
|
@ -100,4 +98,69 @@ lbryio.call = function(resource, action, params={}, method='get') {
|
|||
});
|
||||
};
|
||||
|
||||
lbryio.setAccessToken = (token) => {
|
||||
setLocal('accessToken', token)
|
||||
lbryio._accessToken = token
|
||||
}
|
||||
|
||||
lbryio.authenticate = () => {
|
||||
if (lbryio._authenticationPromise === null) {
|
||||
lbryio._authenticationPromise = new Promise((resolve, reject) => {
|
||||
lbry.status().then(({installation_id}) => {
|
||||
|
||||
//temp hack for installation_ids being wrong
|
||||
installation_id += "Y".repeat(96 - installation_id.length)
|
||||
|
||||
function setCurrentUser() {
|
||||
lbryio.call('user', 'me').then((data) => {
|
||||
lbryio.user = data
|
||||
resolve(data)
|
||||
}).catch(function(err) {
|
||||
lbryio.setAccessToken(null);
|
||||
reject(err);
|
||||
})
|
||||
}
|
||||
|
||||
if (!lbryio._accessToken) {
|
||||
lbryio.call('user', 'new', {
|
||||
language: 'en',
|
||||
app_id: installation_id,
|
||||
}, 'post').then(function(responseData) {
|
||||
if (!responseData.ID) {
|
||||
reject(new Error("Received invalid authentication response."));
|
||||
}
|
||||
lbryio.setAccessToken(installation_id)
|
||||
setCurrentUser()
|
||||
}).catch(function(error) {
|
||||
|
||||
/*
|
||||
until we have better error code format, assume all errors are duplicate application id
|
||||
if we're wrong, this will be caught by later attempts to make a valid call
|
||||
*/
|
||||
lbryio.setAccessToken(installation_id)
|
||||
setCurrentUser()
|
||||
})
|
||||
} else {
|
||||
setCurrentUser()
|
||||
}
|
||||
// if (!lbryio._
|
||||
//(data) => {
|
||||
// resolve(data)
|
||||
// localStorage.setItem('accessToken', ID);
|
||||
// localStorage.setItem('appId', installation_id);
|
||||
// this.setState({
|
||||
// registrationCheckComplete: true,
|
||||
// justRegistered: true,
|
||||
// });
|
||||
//});
|
||||
// lbryio.call('user_install', 'exists', {app_id: installation_id}).then((userExists) => {
|
||||
// // TODO: deal with case where user exists already with the same app ID, but we have no access token.
|
||||
// // Possibly merge in to the existing user with the same app ID.
|
||||
// })
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
return lbryio._authenticationPromise;
|
||||
}
|
||||
|
||||
export default lbryio;
|
||||
|
|
|
@ -5,6 +5,8 @@ import lbryio from './lbryio.js';
|
|||
import lighthouse from './lighthouse.js';
|
||||
import App from './app.js';
|
||||
import SplashScreen from './component/splash.js';
|
||||
import {AuthOverlay} from './component/auth.js';
|
||||
import {Welcome} from './component/welcome.js';
|
||||
|
||||
const {remote} = require('electron');
|
||||
const contextMenu = remote.require('./menu/context-menu');
|
||||
|
@ -20,30 +22,21 @@ window.addEventListener('contextmenu', (event) => {
|
|||
let init = function() {
|
||||
window.lbry = lbry;
|
||||
window.lighthouse = lighthouse;
|
||||
let canvas = document.getElementById('canvas');
|
||||
|
||||
var canvas = document.getElementById('canvas');
|
||||
if (window.sessionStorage.getItem('loaded') == 'y') {
|
||||
ReactDOM.render(<App/>, canvas)
|
||||
lbry.connect().then(function(isConnected) {
|
||||
lbryio.authenticate() //start auth process as soon as soon as we can get an install ID
|
||||
})
|
||||
|
||||
async function onDaemonReady() {
|
||||
window.sessionStorage.setItem('loaded', 'y'); //once we've made it here once per session, we don't need to show splash again
|
||||
ReactDOM.render(<div><AuthOverlay /><App /></div>, canvas)
|
||||
}
|
||||
|
||||
if (window.sessionStorage.getItem('loaded') == 'y' && false) {
|
||||
onDaemonReady();
|
||||
} else {
|
||||
ReactDOM.render(
|
||||
(
|
||||
<SplashScreen message="Connecting" onLoadDone={function() {
|
||||
// There are a couple of conditions where we want to preempt loading the app and send the user to a
|
||||
// different page. TODO: Find a better place for this logic.
|
||||
|
||||
if (!localStorage.getItem('claimCodeDone') && ['', '?', 'discover'].includes(window.location.search)) {
|
||||
lbry.getBalance((balance) => {
|
||||
if (balance <= 0) {
|
||||
window.location.href = '?claim';
|
||||
} else {
|
||||
ReactDOM.render(<App/>, canvas);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ReactDOM.render(<App/>, canvas);
|
||||
}
|
||||
}}/>
|
||||
), canvas);
|
||||
ReactDOM.render(<SplashScreen message="Connecting" onLoadDone={onDaemonReady} />, canvas);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import {FileTile} from '../component/file-tile.js';
|
|||
import {Link} from '../component/link.js';
|
||||
import {ToolTip} from '../component/tooltip.js';
|
||||
import {BusyMessage} from '../component/common.js';
|
||||
import {Welcome} from '../component/welcome.js';
|
||||
|
||||
var fetchResultsStyle = {
|
||||
color: '#888',
|
||||
|
@ -174,13 +173,12 @@ var DiscoverPage = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
//{ !this.props.query && !this.state.searching ? <FeaturedContent /> : null }
|
||||
return (
|
||||
<main>
|
||||
{ this.state.searching ? <SearchActive /> : null }
|
||||
{ !this.state.searching && this.props.query && this.state.results.length ? <SearchResults results={this.state.results} /> : null }
|
||||
{ !this.state.searching && this.props.query && !this.state.results.length ? <SearchNoResults query={this.props.query} /> : null }
|
||||
{ !this.props.query && !this.state.searching ? <FeaturedContent /> : null }
|
||||
<Welcome isOpen={this.props.showWelcome && !this.state.welcomeComplete} onDone={this.handleWelcomeDone} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -162,7 +162,7 @@ var PublishPage = React.createClass({
|
|||
}
|
||||
|
||||
if (!lbry.nameIsValid(rawName, false)) {
|
||||
this.refs.name.showAdvice('LBRY names must contain only letters, numbers and dashes.');
|
||||
this.refs.name.showError('LBRY names must contain only letters, numbers and dashes.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ const MESSAGES = {
|
|||
const rewards = {};
|
||||
|
||||
rewards.claimReward = function(type) {
|
||||
console.log('top of claimReward')
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log('top of promise body')
|
||||
lbry.get_new_address().then((address) => {
|
||||
|
@ -30,7 +29,7 @@ rewards.claimReward = function(type) {
|
|||
document.dispatchEvent(new CustomEvent('globalNotice', {
|
||||
detail: {
|
||||
message: MESSAGES[type],
|
||||
isError: false,
|
||||
isError: false,
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -41,7 +40,7 @@ rewards.claimReward = function(type) {
|
|||
document.dispatchEvent(new CustomEvent('globalNotice', {
|
||||
detail: {
|
||||
message: `Failed to claim reward: ${error.message}`,
|
||||
isError: true,
|
||||
isError: true,
|
||||
},
|
||||
}));
|
||||
document.dispatchEvent(new CustomEvent('rewardFailed', error));
|
||||
|
|
|
@ -12,4 +12,4 @@ export function getLocal(key) {
|
|||
*/
|
||||
export function setLocal(key, value) {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
}
|
66
ui/scss/_form.scss
Normal file
66
ui/scss/_form.scss
Normal file
|
@ -0,0 +1,66 @@
|
|||
@import "global";
|
||||
|
||||
/* this probably shouldn't exist but it does so here we are - Jeremy */
|
||||
|
||||
|
||||
textarea,
|
||||
select,
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="number"],
|
||||
input[type="search"],
|
||||
input[type="date"] {
|
||||
@include placeholder {
|
||||
color: lighten($color-text-dark, 60%);
|
||||
}
|
||||
transition: all $transition-standard;
|
||||
cursor: pointer;
|
||||
padding-left: 1px;
|
||||
padding-right: 1px;
|
||||
box-sizing: border-box;
|
||||
-webkit-appearance: none;
|
||||
&[readonly] {
|
||||
background-color: #bbb;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="number"],
|
||||
input[type="search"],
|
||||
input[type="date"] {
|
||||
border-bottom: 2px solid $color-form-border;
|
||||
line-height: $spacing-vertical - 4;
|
||||
height: $spacing-vertical * 1.5;
|
||||
&.form-field__input--error {
|
||||
border-color: $color-error;
|
||||
}
|
||||
}
|
||||
|
||||
textarea:focus,
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="email"]:focus,
|
||||
input[type="number"]:focus,
|
||||
input[type="search"]:focus,
|
||||
input[type="date"]:focus {
|
||||
border-color: $color-primary;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border: 2px solid $color-form-border;
|
||||
}
|
||||
|
||||
.form-row
|
||||
{
|
||||
+ .form-row
|
||||
{
|
||||
margin-top: $spacing-vertical;
|
||||
}
|
||||
+ .form-row-submit
|
||||
{
|
||||
margin-top: $spacing-vertical;
|
||||
}
|
||||
}
|
|
@ -9,14 +9,15 @@ $color-primary: #155B4A;
|
|||
$color-light-alt: hsl(hue($color-primary), 15, 85);
|
||||
$color-text-dark: #000;
|
||||
$color-help: rgba(0,0,0,.6);
|
||||
$color-notice: #921010;
|
||||
$color-warning: #ffffff;
|
||||
$color-notice: #8a6d3b;
|
||||
$color-error: #a94442;
|
||||
$color-load-screen-text: #c3c3c3;
|
||||
$color-canvas: #f5f5f5;
|
||||
$color-bg: #ffffff;
|
||||
$color-bg-alt: #D9D9D9;
|
||||
$color-money: #216C2A;
|
||||
$color-meta-light: #505050;
|
||||
$color-form-border: rgba(160,160,160,.5);
|
||||
|
||||
$font-size: 16px;
|
||||
$font-line-height: 1.3333;
|
||||
|
@ -29,6 +30,9 @@ $height-header: $spacing-vertical * 2.5;
|
|||
$height-button: $spacing-vertical * 1.5;
|
||||
|
||||
$default-box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
|
||||
//$focus-box-shadow: 2px 4px 4px 0 rgba(0,0,0,.14),2px 5px 3px -2px rgba(0,0,0,.2),2px 3px 7px 0 rgba(0,0,0,.12);
|
||||
|
||||
$transition-standard: .225s ease;
|
||||
|
||||
$blur-intensity: 8px;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
&:hover
|
||||
{
|
||||
opacity: $hover-opacity;
|
||||
transition: opacity .225s ease;
|
||||
transition: opacity $transition-standard;
|
||||
text-decoration: underline;
|
||||
.icon {
|
||||
text-decoration: none;
|
||||
|
@ -76,11 +76,6 @@ sup, sub {
|
|||
sup { top: -0.4em; }
|
||||
sub { top: 0.4em; }
|
||||
|
||||
label {
|
||||
cursor: default;
|
||||
display: block;
|
||||
}
|
||||
|
||||
code {
|
||||
font: 0.8em Consolas, 'Lucida Console', 'Source Sans', monospace;
|
||||
background-color: #eee;
|
||||
|
@ -104,23 +99,6 @@ p
|
|||
opacity: 0.7;
|
||||
}
|
||||
|
||||
input[type="text"], input[type="search"], textarea
|
||||
{
|
||||
@include placeholder {
|
||||
color: lighten($color-text-dark, 60%);
|
||||
}
|
||||
border: 2px solid rgba(160,160,160,.5);
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
box-sizing: border-box;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
input[type="text"], input[type="search"]
|
||||
{
|
||||
line-height: $spacing-vertical - 4;
|
||||
height: $spacing-vertical * 1.5;
|
||||
}
|
||||
|
||||
.truncated-text {
|
||||
display: inline-block;
|
||||
}
|
||||
|
@ -144,75 +122,6 @@ input[type="text"], input[type="search"]
|
|||
}
|
||||
}
|
||||
|
||||
.button-set-item {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
+ .button-set-item
|
||||
{
|
||||
margin-left: $padding-button;
|
||||
}
|
||||
}
|
||||
|
||||
.button-block, .faux-button-block
|
||||
{
|
||||
display: inline-block;
|
||||
height: $height-button;
|
||||
line-height: $height-button;
|
||||
text-decoration: none;
|
||||
border: 0 none;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
text-transform: uppercase;
|
||||
.icon
|
||||
{
|
||||
top: 0em;
|
||||
}
|
||||
.icon:first-child
|
||||
{
|
||||
padding-right: 5px;
|
||||
}
|
||||
.icon:last-child
|
||||
{
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
.button-block
|
||||
{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button__content {
|
||||
margin: 0 $padding-button;
|
||||
}
|
||||
|
||||
.button-primary
|
||||
{
|
||||
color: white;
|
||||
background-color: $color-primary;
|
||||
box-shadow: $default-box-shadow;
|
||||
}
|
||||
.button-alt
|
||||
{
|
||||
background-color: $color-bg-alt;
|
||||
box-shadow: $default-box-shadow;
|
||||
}
|
||||
|
||||
.button-text
|
||||
{
|
||||
@include text-link();
|
||||
display: inline-block;
|
||||
|
||||
.button__content {
|
||||
margin: 0 $padding-text-link;
|
||||
}
|
||||
}
|
||||
.button-text-help
|
||||
{
|
||||
@include text-link(#aaa);
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.icon:only-child {
|
||||
position: relative;
|
||||
top: 0.16em;
|
||||
|
@ -235,87 +144,6 @@ input[type="text"], input[type="search"]
|
|||
font-style: italic;
|
||||
}
|
||||
|
||||
.form-row
|
||||
{
|
||||
+ .form-row
|
||||
{
|
||||
margin-top: $spacing-vertical / 2;
|
||||
}
|
||||
.help
|
||||
{
|
||||
margin-top: $spacing-vertical / 2;
|
||||
}
|
||||
+ .form-row-submit
|
||||
{
|
||||
margin-top: $spacing-vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.form-field-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.form-field--text {
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
.form-field--text-number {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.form-field-advice-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-field-advice {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
transition: opacity 400ms ease-in;
|
||||
}
|
||||
|
||||
.form-field-advice--fading {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.form-field-advice__arrow {
|
||||
text-align: left;
|
||||
padding-left: 18px;
|
||||
|
||||
font-size: 22px;
|
||||
line-height: 0.3;
|
||||
color: darken($color-primary, 5%);
|
||||
}
|
||||
|
||||
|
||||
.form-field-advice__content-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.form-field-advice__content {
|
||||
display: inline-block;
|
||||
|
||||
padding: 5px;
|
||||
border-radius: 2px;
|
||||
|
||||
background-color: darken($color-primary, 5%);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.form-field-label {
|
||||
width: 118px;
|
||||
text-align: right;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
.sort-section {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
|
@ -350,7 +178,6 @@ input[type="text"], input[type="search"]
|
|||
background: rgb(255, 255, 255);
|
||||
overflow: auto;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
padding: 36px;
|
||||
max-width: 250px;
|
||||
}
|
||||
|
|
|
@ -3,20 +3,20 @@ body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fiel
|
|||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
input:focus, textarea:focus
|
||||
:focus
|
||||
{
|
||||
outline: 0;
|
||||
outline: 0;
|
||||
}
|
||||
table
|
||||
table
|
||||
{
|
||||
border-collapse: collapse;
|
||||
border-spacing:0;
|
||||
}
|
||||
fieldset, img, iframe
|
||||
fieldset, img, iframe
|
||||
{
|
||||
border: 0;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6
|
||||
h1, h2, h3, h4, h5, h6
|
||||
{
|
||||
font-weight:normal;
|
||||
}
|
||||
|
@ -25,11 +25,12 @@ ol, ul
|
|||
list-style-position: inside;
|
||||
> li { list-style-position: inside; }
|
||||
}
|
||||
input, textarea, select
|
||||
input, textarea, select
|
||||
{
|
||||
font-family:inherit;
|
||||
font-size:inherit;
|
||||
font-weight:inherit;
|
||||
font-family:inherit;
|
||||
font-size:inherit;
|
||||
font-weight:inherit;
|
||||
border: 0 none;
|
||||
}
|
||||
img {
|
||||
width: auto\9;
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
@import "_reset";
|
||||
@import "_grid";
|
||||
@import "_icons";
|
||||
@import "_form";
|
||||
@import "_mediaelement";
|
||||
@import "_canvas";
|
||||
@import "_gui";
|
||||
@import "component/_table";
|
||||
@import "component/_button.scss";
|
||||
@import "component/_file-actions.scss";
|
||||
@import "component/_file-tile.scss";
|
||||
@import "component/_form-field.scss";
|
||||
@import "component/_menu.scss";
|
||||
@import "component/_tooltip.scss";
|
||||
@import "component/_load-screen.scss";
|
||||
|
|
78
ui/scss/component/_button.scss
Normal file
78
ui/scss/component/_button.scss
Normal file
|
@ -0,0 +1,78 @@
|
|||
@import "../global";
|
||||
|
||||
$button-focus-shift: 12%;
|
||||
|
||||
.button-set-item {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
+ .button-set-item
|
||||
{
|
||||
margin-left: $padding-button;
|
||||
}
|
||||
}
|
||||
|
||||
.button-block, .faux-button-block
|
||||
{
|
||||
display: inline-block;
|
||||
height: $height-button;
|
||||
line-height: $height-button;
|
||||
text-decoration: none;
|
||||
border: 0 none;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
text-transform: uppercase;
|
||||
.icon
|
||||
{
|
||||
top: 0em;
|
||||
}
|
||||
.icon:first-child
|
||||
{
|
||||
padding-right: 5px;
|
||||
}
|
||||
.icon:last-child
|
||||
{
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
.button-block
|
||||
{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button__content {
|
||||
margin: 0 $padding-button;
|
||||
}
|
||||
|
||||
.button-primary
|
||||
{
|
||||
$color-button-text: white;
|
||||
color: darken($color-button-text, $button-focus-shift * 0.5);
|
||||
background-color: $color-primary;
|
||||
box-shadow: $default-box-shadow;
|
||||
&:focus {
|
||||
color: $color-button-text;
|
||||
//box-shadow: $focus-box-shadow;
|
||||
background-color: mix(black, $color-primary, $button-focus-shift)
|
||||
}
|
||||
}
|
||||
.button-alt
|
||||
{
|
||||
background-color: $color-bg-alt;
|
||||
box-shadow: $default-box-shadow;
|
||||
}
|
||||
|
||||
.button-text
|
||||
{
|
||||
@include text-link();
|
||||
display: inline-block;
|
||||
|
||||
.button__content {
|
||||
margin: 0 $padding-text-link;
|
||||
}
|
||||
}
|
||||
.button-text-help
|
||||
{
|
||||
@include text-link(#aaa);
|
||||
font-size: 0.8em;
|
||||
}
|
36
ui/scss/component/_form-field.scss
Normal file
36
ui/scss/component/_form-field.scss
Normal file
|
@ -0,0 +1,36 @@
|
|||
@import "../global";
|
||||
|
||||
.form-field {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.form-field__label {
|
||||
margin-top: $spacing-vertical * 2/3;
|
||||
margin-bottom: $spacing-vertical * 1/3;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.form-field__label--error {
|
||||
color: $color-error;
|
||||
}
|
||||
|
||||
.form-field__input-text {
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
.form-field__input-text-number {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.form-field__error, .form-field__helper {
|
||||
margin-top: $spacing-vertical * 1/3;
|
||||
font-size: 0.8em;
|
||||
transition: opacity $transition-standard;
|
||||
}
|
||||
|
||||
.form-field__error {
|
||||
color: $color-error;
|
||||
}
|
||||
.form-field__helper {
|
||||
color: $color-help;
|
||||
}
|
|
@ -23,7 +23,7 @@
|
|||
}
|
||||
|
||||
.load-screen__details--warning {
|
||||
color: $color-warning;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.load-screen__cancel-link {
|
||||
|
|
|
@ -12,6 +12,24 @@
|
|||
z-index: 9999;
|
||||
}
|
||||
|
||||
.modal-page {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
border: 1px solid rgb(204, 204, 204);
|
||||
background: rgb(255, 255, 255);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
.modal-page {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
|
@ -31,3 +49,13 @@
|
|||
right: 25px;
|
||||
bottom: 25px;
|
||||
}
|
||||
*/
|
||||
|
||||
.modal-page__content {
|
||||
h1, h2 {
|
||||
margin-bottom: $spacing-vertical / 2;
|
||||
}
|
||||
h3, h4 {
|
||||
margin-bottom: $spacing-vertical / 4;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue