1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-23 01:39:36 +01:00

Merge pull request #201 from ascribe/modernize-build-system

Modernize build system
This commit is contained in:
Brett Sun 2016-06-10 10:26:24 +02:00 committed by GitHub
commit 6b1609e8d9
59 changed files with 1321 additions and 1188 deletions

27
.babelrc Normal file
View File

@ -0,0 +1,27 @@
{
'presets': ['react', 'es2015-no-commonjs'],
'plugins': [
'transform-object-assign',
'transform-object-rest-spread',
'transform-react-display-name',
[ 'transform-runtime', {
'polyfill': false,
'regenerator': false
} ]
],
'sourceMaps': true,
'env': {
'demo': {
'plugins': [
[ 'react-transform', {
'transforms': [{
'transform': 'react-transform-hmr',
'imports': ['react'],
'locals': ['module']
}]
} ]
]
}
}
}

16
.bootstraprc Normal file
View File

@ -0,0 +1,16 @@
---
bootstrapVersion: 3
# Phase this out, first with react-component's bootstrap overrides, and then nothing at all
preBootstrapCustomizations: ./sass/bootstrap/overrides
styleLoaders:
- style
- css?sourceMap
- postcss
- sass?sourceMap&output=expanded&precision=8
# For now, just include everything from Bootstrap
styles: true
scripts: false

View File

@ -1,3 +0,0 @@
SAUCE_USERNAME=ascribe
SAUCE_ACCESS_KEY=
SAUCE_DEFAULT_URL=

25
.env_template Normal file
View File

@ -0,0 +1,25 @@
# App settings
ONION_APP_VERSION=
ONION_API_URL=http://localhost.com:8000/api
ONION_BASE_PATH=
ONION_SERVER_URL=http://localhost.com:8000
ONION_PORT=4000
# Raven settings
RAVEN_DSN_URL=
# S3 settings
S3_ACCESS_KEY=
# Dev server configuration
ONION_DEV_HOST=localhost
ONION_DEV_PORT=3000
# Integration testing
SAUCE_USERNAME=ascribe
SAUCE_ACCESS_KEY=
SAUCE_DEFAULT_URL=

View File

@ -1,8 +1,5 @@
. build/*
gulpfile.js dist/*
node_modules node_modules/*
js/**/__tests__ js/components/ascribe_uploader/vendor/*
server.js
js/components/ascribe_uploader/vendor

View File

@ -1,61 +0,0 @@
{
"parser": "babel-eslint",
"env": {
"browser": true,
"es6": true,
},
"rules": {
"new-cap": [2, {newIsCap: true, capIsNew: false}],
"quotes": [2, "single"],
"eol-last": [0],
"no-mixed-requires": [0],
"no-underscore-dangle": [0],
"global-strict": [2, "always"],
"no-trailing-spaces": [2, { skipBlankLines: true }],
"no-console": 0,
"camelcase": [2, {"properties": "never"}],
"react/display-name": 0,
"react/jsx-boolean-value": 1,
"react/jsx-no-undef": 1,
"react/jsx-quotes": 1,
"react/jsx-sort-prop-types": 0,
"react/jsx-sort-props": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
"react/no-did-mount-set-state": [1, "allow-in-func"],
"react/no-did-update-set-state": 1,
"react/no-multi-comp": 0,
"react/no-unknown-property": 1,
"react/prop-types": 1,
"react/react-in-jsx-scope": 1,
"react/self-closing-comp": 1,
"react/sort-comp": 1,
"react/wrap-multilines": 1
},
"globals": {
"Intercom": true,
"fetch": true,
"require": true,
"File": true
},
"plugins": [
"react"
],
"ecmaFeatures": {
"jsx": 1,
"modules": 1,
"arrowFunctions",
"classes": 1,
"blockBindings": 1,
"defaultParams": 1,
"destructuring": 1,
"objectLiteralComputedProperties": 1,
"objectLiteralDuplicateProperties": 0,
"objectLiteralShorthandMethods": 1,
"objectLiteralShorthandProperties": 1,
"restParams": 1,
"spread": 1,
"superInFunctions": 1,
"templateStrings": 1
}
}

6
.eslintrc.json Normal file
View File

@ -0,0 +1,6 @@
{
"extends": "ascribe",
"rules": {
"no-console": [1, { "allow": ["error", "logGlobal"] }]
}
}

10
.gitignore vendored
View File

@ -1,3 +1,4 @@
.DS_Store
lib-cov lib-cov
*.seed *.seed
*.log *.log
@ -8,22 +9,19 @@ lib-cov
*.gz *.gz
*.sublime-project *.sublime-project
.idea .idea
spool-project.sublime-project
*.sublime-workspace *.sublime-workspace
*.sublime-workspace
webapp-dependencies.txt
pids pids
logs logs
results results
.env
build/* build/*
dist/*
gemini-coverage/* gemini-coverage/*
gemini-report/* gemini-report/*
test/gemini/screenshots/* test/gemini/screenshots/*
node_modules/* node_modules/*
.DS_Store
.env

View File

@ -170,12 +170,11 @@ A: Use `npm dedupe` to remove duplicates in npm. This might fix that you're not
Q: How can I use a local copy of SPOOL and Onion? Q: How can I use a local copy of SPOOL and Onion?
A: Easily by starting the your gulp process with the following command: A: Easily by starting the your gulp process with the following command:
``` ```
ONION_BASE_URL='/' ONION_SERVER_URL='http://localhost.com:8000/' gulp serve ONION_SERVER_URL='http://localhost.com:8000/' gulp serve
``` ```
Or, by adding these two your environment variables: Or, by adding the local server url to your environment variables:
``` ```
ONION_BASE_URL='/'
ONION_SERVER_URL='http://localhost.com:8000/' ONION_SERVER_URL='http://localhost.com:8000/'
``` ```

View File

@ -1,207 +0,0 @@
'use strict';
require("harmonize")();
var gulp = require('gulp');
var template = require('gulp-template');
var gulpif = require('gulp-if');
var sourcemaps = require('gulp-sourcemaps');
var util = require('gulp-util');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var watchify = require('watchify');
var browserify = require('browserify');
var browserSync = require('browser-sync');
var babelify = require('babelify');
var notify = require('gulp-notify');
var sass = require('gulp-sass');
var concat = require('gulp-concat');
var _ = require('lodash');
var eslint = require('gulp-eslint');
var jest = require('jest-cli');
var argv = require('yargs').argv;
var server = require('./server.js').app;
var minifyCss = require('gulp-minify-css');
var uglify = require('gulp-uglify');
var opn = require('opn');
var config = {
bootstrapDir: './node_modules/bootstrap-sass',
jestOptions: {
rootDir: 'js',
scriptPreprocessor: '../node_modules/babel-jest',
testFileExtensions: [
'es6',
'js'
],
unmockedModulePathPatterns: [
'<rootDir>/node_modules/react',
'<rootDir>/node_modules/react-tools'
],
moduleFileExtensions: [
'js',
'json',
'react',
'es6'
]
},
filesToWatch: [
'build/css/*.css',
'build/js/*.js'
]
};
var SERVER_URL = process.env.ONION_SERVER_URL || 'https://staging.ascribe.io/';
var constants = {
BASE_URL: (function () { var baseUrl = process.env.ONION_BASE_URL || '/'; return baseUrl + (baseUrl.match(/\/$/) ? '' : '/'); })(),
SERVER_URL: SERVER_URL,
API_ENDPOINT: SERVER_URL + 'api/' || 'https://staging.ascribe.io/api/',
DEBUG: !argv.production,
CREDENTIALS: 'ZGltaUBtYWlsaW5hdG9yLmNvbTowMDAwMDAwMDAw' // dimi@mailinator:0000000000
};
gulp.task('build', ['js:build', 'sass:build', 'copy'], function() {
});
gulp.task('js:build', function() {
bundle(false);
});
gulp.task('serve', ['browser-sync', 'run-server', 'sass:build', 'sass:watch', 'copy'], function() {
bundle(true);
// opens the browser window with the correct url, which is localhost.com
opn('http://www.localhost.com:3000');
});
gulp.task('jest', function(done) {
jest.runCLI({ config : config.jestOptions }, ".", function() {
done();
});
});
gulp.task('jest:watch', function(done) {
gulp.watch([ config.jestOptions.rootDir + "/**/*.js" ], [ 'jest' ]);
});
gulp.task('run-server', function() {
server.listen(4000);
});
gulp.task('browser-sync', function() {
browserSync({
files: config.filesToWatch,
proxy: 'http://localhost:4000',
port: 3000,
open: false, // does not open the browser-window anymore (handled manually)
ghostMode: false,
notify: false // stop showing the browsersync pop up
});
});
gulp.task('sass:build', function () {
gulp.src('./sass/**/main.scss')
.pipe(template(constants))
.pipe(gulpif(!argv.production, sourcemaps.init()))
.pipe(sass({
includePaths: [
config.bootstrapDir + '/assets/stylesheets'
]
}).on('error', sass.logError))
.pipe(gulpif(!argv.production, sourcemaps.write('./maps')))
// We need to set `advanced` to false, as it merges
// some of the styles wrongly
.pipe(gulpif(argv.production, minifyCss({
advanced: false
})))
.pipe(gulp.dest('./build/css'))
.pipe(browserSync.stream());
});
gulp.task('sass:watch', function () {
gulp.watch('./sass/**/*.scss', ['sass:build']);
});
gulp.task('copy', function () {
var staticAssets = [
'./fonts/**/*',
'./img/**/*'
];
gulp.src(staticAssets, {base: './'})
.pipe(gulp.dest('./build'));
gulp.src(config.bootstrapDir + '/assets/fonts/**/*')
.pipe(gulp.dest('./build/fonts'));
gulp.src('./index.html')
.pipe(template(constants))
.pipe(gulp.dest('./build'));
});
gulp.task('lint', function () {
return gulp.src(['js/**/*.js'])
// eslint() attaches the lint output to the eslint property
// of the file object so it can be used by other modules.
.pipe(eslint())
// eslint.format() outputs the lint results to the console.
// Alternatively use eslint.formatEach() (see Docs).
.pipe(eslint.format());
// To have the process exit with an error code (1) on
// lint error, return the stream and pipe to failOnError last.
});
gulp.task('lint:watch', function () {
gulp.watch('js/**/*.js', ['lint']);
});
function bundle(watch) {
var bro;
if (watch) {
bro = watchify(browserify('./js/app.js',
// Assigning debug to have sourcemaps
_.assign(watchify.args, {
debug: true
})));
bro.on('update', function() {
rebundle(bro, true);
});
} else {
bro = browserify('./js/app.js', {
debug: true
});
}
bro.transform(babelify.configure({
compact: false
}));
function rebundle(bundler, watch) {
return bundler.bundle()
.on('error', notify.onError('Error: <%= error.message %>'))
.pipe(source('app.js'))
.on('error', notify.onError('Error: <%= error.message %>'))
.pipe(buffer())
.on('error', notify.onError('Error: <%= error.message %>'))
.pipe(gulpif(!argv.production, sourcemaps.init({
loadMaps: true
}))) // loads map from browserify file
.on('error', notify.onError('Error: <%= error.message %>'))
.pipe(gulpif(!argv.production, sourcemaps.write())) // writes .map file
.on('error', notify.onError('Error: <%= error.message %>'))
.pipe(gulpif(argv.production, uglify({
mangle: true
})))
.on('error', notify.onError('Error: <%= error.message %>'))
.pipe(gulp.dest('./build/js'))
.on('error', notify.onError('Error: <%= error.message %>'))
.pipe(browserSync.stream())
.on('error', notify.onError('Error: <%= error.message %>'));
}
return rebundle(bro);
}

View File

@ -1,4 +1,4 @@
<!doctype html> <!DOCTYPE html>
<html> <html>
<head> <head>
@ -10,16 +10,7 @@
<title>ascribe</title> <title>ascribe</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="<%= BASE_URL %>static/css/main.css">
<% DEBUG && print('<link rel="stylesheet" href="' + BASE_URL + 'static/css/maps/main.css.map">') %>
<script>
window.BASE_URL = '<%= BASE_URL %>';
window.SERVER_URL = '<%= SERVER_URL %>';
window.API_ENDPOINT = '<%= API_ENDPOINT %>';
<% DEBUG && print('window.DEBUG = true'); %>
<% DEBUG && print('window.CREDENTIALS = \'' + CREDENTIALS + '\''); %>
</script>
<!-- Typekit gibson font --> <!-- Typekit gibson font -->
<script src="https://use.typekit.net/gma2yhj.js"></script> <script src="https://use.typekit.net/gma2yhj.js"></script>
<script> <script>
@ -44,8 +35,5 @@
s.src='https://widget.intercom.io/widget/{app_id}'; s.src='https://widget.intercom.io/widget/{app_id}';
var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);}if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})() var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);}if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})()
</script> </script>
<!-- actual app -->
<script src="<%= BASE_URL %>static/js/app.js"></script>
</body> </body>
</html> </html>

106
js/app.js
View File

@ -1,92 +1,66 @@
'use strict'; import 'core-js/es6';
import 'core-js/stage/4';
import 'babel/polyfill';
import 'classlist-polyfill'; import 'classlist-polyfill';
import 'isomorphic-fetch';
import React from 'react'; import React from 'react';
import { Router, Redirect } from 'react-router'; import { Router } from 'react-router';
import AppResolver from './app_resolver';
import history from './history'; import history from './history';
import fetch from 'isomorphic-fetch';
import ApiUrls from './constants/api_urls';
import AppConstants from './constants/application_constants';
import getRoutes from './routes';
import requests from './utils/requests';
import { updateApiUrls } from './constants/api_urls';
import { getDefaultSubdomainSettings, getSubdomainSettings } from './utils/constants_utils'; import { getDefaultSubdomainSettings, getSubdomainSettings } from './utils/constants_utils';
import { initLogging } from './utils/error_utils'; import { initLogging } from './utils/error_utils';
import { getSubdomain } from './utils/general_utils'; import { getSubdomain } from './utils/general_utils';
import requests from './utils/requests';
// FIXME: rename these event actions
import EventActions from './actions/event_actions'; import EventActions from './actions/event_actions';
// You can comment out the modules you don't need // You can comment out the modules you don't need
// import DebugHandler from './third_party/debug_handler'; // import DebugHandler from './third_party/debug_handler';
import FacebookHandler from './third_party/facebook_handler'; import './third_party/facebook_handler';
import GoogleAnalyticsHandler from './third_party/ga_handler'; import './third_party/ga_handler';
import IntercomHandler from './third_party/intercom_handler'; import './third_party/intercom_handler';
import NotificationsHandler from './third_party/notifications_handler'; import './third_party/notifications_handler';
import RavenHandler from './third_party/raven_handler'; import './third_party/raven_handler';
// Import global stylesheet
import '../sass/main.scss';
const AppGateway = { const AppGateway = {
start() { start() {
try { let subdomainSettings;
const subdomain = getSubdomain();
const settings = getSubdomainSettings(subdomain);
AppConstants.whitelabel = settings; try {
updateApiUrls(settings.type, subdomain); subdomainSettings = getSubdomainSettings(getSubdomain());
this.load(settings);
} catch (err) { } catch (err) {
// if there are no matching subdomains, we're routing // if there are no matching subdomains, we''ll route to the default frontend
// to the default frontend
console.logGlobal(err); console.logGlobal(err);
this.load(getDefaultSubdomainSettings()); subdomainSettings = getDefaultSubdomainSettings();
} }
this.load(subdomainSettings);
}, },
load(settings) { load(settings) {
const { subdomain, type } = settings;
let redirectRoute = (<Redirect from="/" to="/collection" />);
if (subdomain) {
// Some whitelabels have landing pages so we should not automatically redirect from / to /collection.
// Only www and cc do not have a landing page.
if (subdomain !== 'cc') {
redirectRoute = null;
}
// Adds a client specific class to the body for whitelabel styling
window.document.body.classList.add('client--' + subdomain);
}
// Send the applicationWillBoot event to the third-party stores // Send the applicationWillBoot event to the third-party stores
EventActions.applicationWillBoot(settings); EventActions.applicationWillBoot(settings);
// `history.listen` is called on every route change, which is perfect for // `history.listen` is called on every route change, which is perfect for routeDidChange
// us in that case. // events.
history.listen(EventActions.routeDidChange); history.listen(EventActions.routeDidChange);
React.render(( // Adds a client specific class to the body for whitelabel styling
<Router history={history}> window.document.body.classList.add(`client--${settings.subdomain}`);
{redirectRoute}
{getRoutes(type, subdomain)}
</Router>
), document.getElementById('main'));
// Send the applicationDidBoot event to the third-party stores
EventActions.applicationDidBoot(settings);
}
};
// Initialize pre-start components
initLogging();
AppResolver
.resolve(settings)
.then(({ apiUrls, redirectRoute, routes }) => {
// Initialize api urls and defaults for outgoing requests
requests.defaults({ requests.defaults({
urlMap: ApiUrls, urlMap: apiUrls,
http: { http: {
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
@ -96,5 +70,21 @@ requests.defaults({
} }
}); });
React.render((
<Router history={history}>
{redirectRoute}
{routes}
</Router>
), document.getElementById('main'));
// Send the applicationDidBoot event to the third-party stores
EventActions.applicationDidBoot(settings);
});
}
};
// Initialize pre-start components
initLogging();
// And bootstrap app // And bootstrap app
AppGateway.start(); AppGateway.start();

22
js/app_resolver.js Normal file
View File

@ -0,0 +1,22 @@
import React from 'react';
import { Redirect } from 'react-router';
import Routes from './routes';
import ApiUrls from './constants/api_urls';
function resolve({ subdomain, type }) {
if (type === 'wallet') {
return System.import('./components/whitelabel/wallet/wallet_app_resolver')
.then(({ default: WalletAppResolver }) => WalletAppResolver.resolve(subdomain));
} else {
return Promise.resolve({
apiUrls: ApiUrls,
redirectRoute: (<Redirect from="/" to="/collection" />),
routes: Routes
});
}
}
export default { resolve };

View File

@ -81,14 +81,14 @@ let FurtherDetailsFileuploader = React.createClass({
deleteFile={{ deleteFile={{
enabled: true, enabled: true,
method: 'DELETE', method: 'DELETE',
endpoint: AppConstants.serverUrl + 's3/delete', endpoint: `${AppConstants.serverUrl}/s3/delete`,
customHeaders: { customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken) 'X-CSRFToken': getCookie(AppConstants.csrftoken)
} }
}} }}
isReadyForFormSubmission={isReadyForFormSubmission} isReadyForFormSubmission={isReadyForFormSubmission}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: `${AppConstants.serverUrl}/s3/key/`,
fileClass: 'otherdata', fileClass: 'otherdata',
pieceId: pieceId pieceId: pieceId
}} }}
@ -96,7 +96,7 @@ let FurtherDetailsFileuploader = React.createClass({
onValidationFailed={onValidationFailed} onValidationFailed={onValidationFailed}
setIsUploadReady={setIsUploadReady} setIsUploadReady={setIsUploadReady}
session={{ session={{
endpoint: AppConstants.serverUrl + 'api/blob/otherdatas/fineuploader_session/', endpoint: `${AppConstants.serverUrl}/api/blob/otherdatas/fineuploader_session/`,
customHeaders: { customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken) 'X-CSRFToken': getCookie(AppConstants.csrftoken)
}, },
@ -109,7 +109,7 @@ let FurtherDetailsFileuploader = React.createClass({
} }
}} }}
signature={{ signature={{
endpoint: AppConstants.serverUrl + 's3/signature/', endpoint: `${AppConstants.serverUrl}/s3/signature/`,
customHeaders: { customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken) 'X-CSRFToken': getCookie(AppConstants.csrftoken)
} }

View File

@ -72,7 +72,7 @@ let CreateContractForm = React.createClass({
<InputFineUploader <InputFineUploader
submitFile={this.submitFile} submitFile={this.submitFile}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: `${AppConstants.serverUrl}/s3/key/`,
fileClass: 'contract' fileClass: 'contract'
}} }}
createBlobRoutine={{ createBlobRoutine={{

View File

@ -162,7 +162,7 @@ let RegisterPieceForm = React.createClass({
<InputFineUploader <InputFineUploader
ref={ref => this.refs.digitalWorkFineUploader = ref} ref={ref => this.refs.digitalWorkFineUploader = ref}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: `${AppConstants.serverUrl}/s3/key/`,
fileClass: 'digitalwork' fileClass: 'digitalwork'
}} }}
createBlobRoutine={{ createBlobRoutine={{
@ -191,7 +191,7 @@ let RegisterPieceForm = React.createClass({
onValidationFailed={this.handleThumbnailValidationFailed} onValidationFailed={this.handleThumbnailValidationFailed}
isReadyForFormSubmission={formSubmissionValidation.fileOptional} isReadyForFormSubmission={formSubmissionValidation.fileOptional}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: `${AppConstants.serverUrl}/s3/key/`,
fileClass: 'thumbnail' fileClass: 'thumbnail'
}} }}
validation={{ validation={{

View File

@ -121,7 +121,7 @@ const InputFineUploader = React.createClass({
setWarning={setWarning} setWarning={setWarning}
showErrorPrompt={showErrorPrompt} showErrorPrompt={showErrorPrompt}
signature={{ signature={{
endpoint: AppConstants.serverUrl + 's3/signature/', endpoint: `${AppConstants.serverUrl}/s3/signature/`,
customHeaders: { customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken) 'X-CSRFToken': getCookie(AppConstants.csrftoken)
} }
@ -129,7 +129,7 @@ const InputFineUploader = React.createClass({
deleteFile={{ deleteFile={{
enabled: true, enabled: true,
method: 'DELETE', method: 'DELETE',
endpoint: AppConstants.serverUrl + 's3/delete', endpoint: `${AppConstants.serverUrl}/s3/delete`,
customHeaders: { customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken) 'X-CSRFToken': getCookie(AppConstants.csrftoken)
} }

View File

@ -7,10 +7,15 @@ import Panel from 'react-bootstrap/lib/Panel';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import audiojs from '../../third_party/imports/audiojs';
import shmui from '../../third_party/imports/shmui';
import videojs from '../../third_party/imports/videojs';
import { escapeHTML } from '../../utils/general_utils'; import { escapeHTML } from '../../utils/general_utils';
import { extractFileExtensionFromUrl } from '../../utils/file_utils'; import { extractFileExtensionFromUrl } from '../../utils/file_utils';
import { InjectInHeadUtils } from '../../utils/inject_utils'; import { InjectInHeadUtils } from '../../utils/inject_utils';
/** /**
* This is the component that implements display-specific functionality. * This is the component that implements display-specific functionality.
* *
@ -57,12 +62,9 @@ let Image = React.createClass({
componentDidMount() { componentDidMount() {
if (this.props.url) { if (this.props.url) {
InjectInHeadUtils.inject(AppConstants.jquery.sdkUrl) shmui
.then(() => .importLib()
Q.all([ .then(() => window.jQuery('.shmui-ascribe').shmui());
InjectInHeadUtils.inject(AppConstants.shmui.cssUrl),
InjectInHeadUtils.inject(AppConstants.shmui.sdkUrl)
]).then(() => { window.jQuery('.shmui-ascribe').shmui(); }));
} }
}, },
@ -89,13 +91,9 @@ let Audio = React.createClass({
}, },
componentDidMount() { componentDidMount() {
InjectInHeadUtils.inject(AppConstants.audiojs.sdkUrl).then(this.ready); audiojs
}, .importLib()
.then(() => window.audiojs.events.ready(() => window.audiojs.createAll()));
ready() {
window.audiojs.events.ready(function() {
window.audiojs.createAll();
});
}, },
render() { render() {
@ -142,11 +140,10 @@ let Video = React.createClass({
}, },
componentDidMount() { componentDidMount() {
Q.all([ videojs
InjectInHeadUtils.inject(AppConstants.videojs.cssUrl), .importLib()
InjectInHeadUtils.inject(AppConstants.videojs.sdkUrl)])
.then(() => this.setState({ libraryLoaded: true })) .then(() => this.setState({ libraryLoaded: true }))
.fail(() => this.setState({libraryLoaded: false})); .catch(() => this.setState({ libraryLoaded: false }));
}, },
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {

View File

@ -56,10 +56,11 @@ export function AuthRedirect({ to, when }) {
* hence transitionTo cannot be used directly. * hence transitionTo cannot be used directly.
* *
* While we're getting rid of `query.redirect` explicitly in the * While we're getting rid of `query.redirect` explicitly in the
* above `else if` statement, here it's sufficient to just call * above `else if` statement, here it's sufficient to just set the
* `baseUrl` + `redirectAuthenticated`, as it gets rid of queries as well. * location to `${baseUrl}/${redirectAuthenticated}`, as this will
* get rid of queries as well.
*/ */
window.location = AppConstants.baseUrl + redirectAuthenticated; window.location = `${AppConstants.baseUrl}/${redirectAuthenticated}`;
return true; return true;
} }

View File

@ -62,7 +62,7 @@ let ContractSettingsUpdateButton = React.createClass({
ref='fineuploader' ref='fineuploader'
fileInputElement={UploadButton({ showLabel: false })} fileInputElement={UploadButton({ showLabel: false })}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: `${AppConstants.serverUrl}/s3/key/`,
fileClass: 'contract' fileClass: 'contract'
}} }}
createBlobRoutine={{ createBlobRoutine={{
@ -75,7 +75,7 @@ let ContractSettingsUpdateButton = React.createClass({
}} }}
setIsUploadReady={() =>{/* So that ReactS3FineUploader is not complaining */}} setIsUploadReady={() =>{/* So that ReactS3FineUploader is not complaining */}}
signature={{ signature={{
endpoint: AppConstants.serverUrl + 's3/signature/', endpoint: `${AppConstants.serverUrl}/s3/signature/`,
customHeaders: { customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken) 'X-CSRFToken': getCookie(AppConstants.csrftoken)
} }
@ -83,7 +83,7 @@ let ContractSettingsUpdateButton = React.createClass({
deleteFile={{ deleteFile={{
enabled: true, enabled: true,
method: 'DELETE', method: 'DELETE',
endpoint: AppConstants.serverUrl + 's3/delete', endpoint: `${AppConstants.serverUrl}/s3/delete`,
customHeaders: { customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken) 'X-CSRFToken': getCookie(AppConstants.csrftoken)
} }

View File

@ -1,7 +1,8 @@
'use strict'; 'use strict';
import React from 'react/addons'; import React from 'react/addons';
import fineUploader from 'fineUploader'; // FIXME: remove once using react-components
import fineUploader from 'exports?qq!./vendor/s3.fine-uploader';
import Q from 'q'; import Q from 'q';
import S3Fetcher from '../../fetchers/s3_fetcher'; import S3Fetcher from '../../fetchers/s3_fetcher';

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
import fineUploader from 'fineUploader'; // FIXME: remove once using react-components
import fineUploader from 'exports?qq!./vendor/s3.fine-uploader';
import MimeTypes from '../../constants/mime_types'; import MimeTypes from '../../constants/mime_types';

View File

@ -6,27 +6,27 @@ import walletConstants from './wallet_application_constants';
function getWalletApiUrls(subdomain) { function getWalletApiUrls(subdomain) {
if (subdomain === 'cyland') { if (subdomain === 'cyland') {
return { return {
'pieces_list': walletConstants.walletApiEndpoint + subdomain + '/pieces/', 'pieces_list': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/',
'piece': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/', 'piece': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/${piece_id}/',
'piece_extradata': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/extradata/', 'piece_extradata': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/${piece_id}/extradata/',
'user': walletConstants.walletApiEndpoint + subdomain + '/users/' 'user': walletConstants.walletApiEndpoint + '/' + subdomain + '/users/'
}; };
} else if (subdomain === 'ikonotv') { } else if (subdomain === 'ikonotv') {
return { return {
'pieces_list': walletConstants.walletApiEndpoint + subdomain + '/pieces/', 'pieces_list': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/',
'piece': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/', 'piece': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/${piece_id}/',
'user': walletConstants.walletApiEndpoint + subdomain + '/users/' 'user': walletConstants.walletApiEndpoint + '/' + subdomain + '/users/'
}; };
} else if (subdomain === 'lumenus' || subdomain === '23vivi' || } else if (subdomain === 'lumenus' || subdomain === '23vivi' ||
subdomain === 'polline' || subdomain === 'artcity' || subdomain === 'polline' || subdomain === 'artcity' ||
subdomain === 'demo' || subdomain === 'liquidgallery') { subdomain === 'demo' || subdomain === 'liquidgallery') {
return { return {
'editions_list': walletConstants.walletApiEndpoint + 'markets/' + subdomain + '/pieces/${piece_id}/editions/', 'editions_list': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/pieces/${piece_id}/editions/',
'edition': walletConstants.walletApiEndpoint + 'markets/' + subdomain + '/editions/${bitcoin_id}/', 'edition': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/editions/${bitcoin_id}/',
'pieces_list': walletConstants.walletApiEndpoint + 'markets/' + subdomain + '/pieces/', 'pieces_list': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/pieces/',
'piece': walletConstants.walletApiEndpoint + 'markets/' + subdomain + '/pieces/${piece_id}/', 'piece': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/pieces/${piece_id}/',
'piece_extradata': walletConstants.walletApiEndpoint + 'markets/' + subdomain + '/pieces/${piece_id}/extradata/', 'piece_extradata': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/pieces/${piece_id}/extradata/',
'user': walletConstants.walletApiEndpoint + 'markets/' + subdomain + '/users/' 'user': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/users/'
}; };
} }
return {}; return {};

View File

@ -2,8 +2,8 @@
import AppConstants from '../../../../constants/application_constants'; import AppConstants from '../../../../constants/application_constants';
let walletConstants = { const walletConstants = {
walletApiEndpoint: AppConstants.apiEndpoint + 'whitelabel/' walletApiEndpoint: `${AppConstants.apiEndpoint}/whitelabel`
}; };
export default walletConstants; export default walletConstants;

View File

@ -0,0 +1,22 @@
import React from 'react';
import { Redirect } from 'react-router';
import getWalletApiUrls from './constants/wallet_api_urls';
import getWalletRoutes from './wallet_routes';
import { updateApiUrls } from '../../../constants/api_urls';
function resolve(subdomain) {
// Most whitelabels have landing pages so we should not automatically redirect from / to /collection.
// Only cc does not have a landing page.
const redirectRoute = subdomain === 'cc' ? (<Redirect from="/" to="/collection" />) : null;
return {
redirectRoute,
apiUrls: updateApiUrls(getWalletApiUrls(subdomain)),
routes: getWalletRoutes(subdomain)
};
}
export default { resolve };

View File

@ -1,5 +1,3 @@
'use strict';
import React from 'react'; import React from 'react';
import { Route, IndexRoute } from 'react-router'; import { Route, IndexRoute } from 'react-router';
@ -17,7 +15,6 @@ import EditionContainer from '../../../components/ascribe_detail/edition_contain
import SettingsContainer from '../../../components/ascribe_settings/settings_container'; import SettingsContainer from '../../../components/ascribe_settings/settings_container';
import ContractSettings from '../../../components/ascribe_settings/contract_settings'; import ContractSettings from '../../../components/ascribe_settings/contract_settings';
import ErrorNotFoundPage from '../../../components/error_not_found_page'; import ErrorNotFoundPage from '../../../components/error_not_found_page';
import Footer from '../../../components/footer.js';
import CCRegisterPiece from './components/cc/cc_register_piece'; import CCRegisterPiece from './components/cc/cc_register_piece';
@ -54,409 +51,407 @@ import WalletApp from './wallet_app';
import { getLangText } from '../../../utils/lang_utils'; import { getLangText } from '../../../utils/lang_utils';
let ROUTES = { const ROUTES = {
'cyland': ( 'cyland': (
<Route path='/' component={WalletApp}> <Route path="/" component={WalletApp}>
<IndexRoute <IndexRoute
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(CylandLanding)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(CylandLanding)} />
<Route <Route
path='login' path="login"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} />
<Route <Route
path='logout' path="logout"
component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} />
<Route <Route
path='signup' path="signup"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} />
<Route <Route
path='password_reset' path="password_reset"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} />
<Route <Route
path='settings' path="settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} />
<Route <Route
path='contract_settings' path="contract_settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} />
<Route <Route
path='register_piece' path="register_piece"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(CylandRegisterPiece)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(CylandRegisterPiece)}
headerTitle={getLangText('+ NEW WORK')} headerTitle={getLangText('+ NEW WORK')}
aclName='acl_wallet_submit' /> aclName="acl_wallet_submit" />
<Route <Route
path='collection' path="collection"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(CylandPieceList)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(CylandPieceList)}
headerTitle={getLangText('COLLECTION')} headerTitle={getLangText('COLLECTION')}
disableOn='noPieces' /> disableOn="noPieces" />
<Route <Route
path='editions/:editionId' path="editions/:editionId"
component={EditionContainer} /> component={EditionContainer} />
<Route <Route
path='coa_verify' path="coa_verify"
component={CoaVerifyContainer} /> component={CoaVerifyContainer} />
<Route <Route
path='pieces/:pieceId' path="pieces/:pieceId"
component={CylandPieceContainer} /> component={CylandPieceContainer} />
<Route <Route
path='*' path="*"
component={ErrorNotFoundPage} /> component={ErrorNotFoundPage} />
</Route> </Route>
), ),
'cc': ( 'cc': (
<Route path='/' component={WalletApp}> <Route path="/" component={WalletApp}>
<Route <Route
path='login' path="login"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} />
<Route <Route
path='logout' path="logout"
component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} />
<Route <Route
path='signup' path="signup"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} />
<Route <Route
path='password_reset' path="password_reset"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} />
<Route <Route
path='settings' path="settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} />
<Route <Route
path='contract_settings' path="contract_settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} />
<Route <Route
path='register_piece' path="register_piece"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(CCRegisterPiece)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(CCRegisterPiece)}
headerTitle={getLangText('+ NEW WORK')} /> headerTitle={getLangText('+ NEW WORK')} />
<Route <Route
path='collection' path="collection"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(PieceList)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(PieceList)}
headerTitle={getLangText('COLLECTION')} headerTitle={getLangText('COLLECTION')}
disableOn='noPieces' /> disableOn="noPieces" />
<Route <Route
path='pieces/:pieceId' path="pieces/:pieceId"
component={PieceContainer} /> component={PieceContainer} />
<Route <Route
path='editions/:editionId' path="editions/:editionId"
component={EditionContainer} /> component={EditionContainer} />
<Route <Route
path='coa_verify' path="coa_verify"
component={CoaVerifyContainer} /> component={CoaVerifyContainer} />
<Route <Route
path='*' path="*"
component={ErrorNotFoundPage} /> component={ErrorNotFoundPage} />
</Route> </Route>
), ),
'ikonotv': ( 'ikonotv': (
<Route path='/' component={WalletApp}> <Route path="/" component={WalletApp}>
<IndexRoute <IndexRoute
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(IkonotvLanding)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(IkonotvLanding)} />
<Route <Route
path='login' path="login"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} />
<Route <Route
path='logout' path="logout"
component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} />
<Route <Route
path='signup' path="signup"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} />
<Route <Route
path='password_reset' path="password_reset"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} />
<Route <Route
path='settings' path="settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} />
<Route <Route
path='contract_settings' path="contract_settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} />
<Route <Route
path='request_loan' path="request_loan"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SendContractAgreementForm)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SendContractAgreementForm)}
headerTitle={getLangText('SEND NEW CONTRACT')} headerTitle={getLangText('SEND NEW CONTRACT')}
aclName='acl_create_contractagreement' /> aclName="acl_create_contractagreement" />
<Route <Route
path='register_piece' path="register_piece"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(IkonotvRegisterPiece)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(IkonotvRegisterPiece)}
headerTitle={getLangText('+ NEW WORK')} headerTitle={getLangText('+ NEW WORK')}
aclName='acl_wallet_submit' /> aclName="acl_wallet_submit" />
<Route <Route
path='collection' path="collection"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(IkonotvPieceList)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(IkonotvPieceList)}
headerTitle={getLangText('COLLECTION')} headerTitle={getLangText('COLLECTION')}
disableOn='noPieces' /> disableOn="noPieces" />
<Route <Route
path='contract_notifications' path="contract_notifications"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(IkonotvContractNotifications)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(IkonotvContractNotifications)} />
<Route <Route
path='pieces/:pieceId' path="pieces/:pieceId"
component={IkonotvPieceContainer} /> component={IkonotvPieceContainer} />
<Route <Route
path='editions/:editionId' path="editions/:editionId"
component={EditionContainer} /> component={EditionContainer} />
<Route <Route
path='coa_verify' path="coa_verify"
component={CoaVerifyContainer} /> component={CoaVerifyContainer} />
<Route <Route
path='*' path="*"
component={ErrorNotFoundPage} /> component={ErrorNotFoundPage} />
</Route> </Route>
), ),
'lumenus': ( 'lumenus': (
<Route path='/' component={WalletApp}> <Route path="/" component={WalletApp}>
<IndexRoute <IndexRoute
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LumenusLanding)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LumenusLanding)} />
<Route <Route
path='login' path="login"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} />
<Route <Route
path='logout' path="logout"
component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} />
<Route <Route
path='signup' path="signup"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} />
<Route <Route
path='password_reset' path="password_reset"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} />
<Route <Route
path='settings' path="settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} />
<Route <Route
path='contract_settings' path="contract_settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} />
<Route <Route
path='register_piece' path="register_piece"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)}
headerTitle={getLangText('+ NEW WORK')} headerTitle={getLangText('+ NEW WORK')}
aclName='acl_wallet_submit' /> aclName="acl_wallet_submit" />
<Route <Route
path='collection' path="collection"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketPieceList)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketPieceList)}
headerTitle={getLangText('COLLECTION')} headerTitle={getLangText('COLLECTION')}
disableOn='noPieces' /> disableOn="noPieces" />
<Route <Route
path='pieces/:pieceId' path="pieces/:pieceId"
component={MarketPieceContainer} /> component={MarketPieceContainer} />
<Route <Route
path='editions/:editionId' path="editions/:editionId"
component={MarketEditionContainer} /> component={MarketEditionContainer} />
<Route <Route
path='coa_verify' path="coa_verify"
component={CoaVerifyContainer} /> component={CoaVerifyContainer} />
<Route <Route
path='*' path="*"
component={ErrorNotFoundPage} /> component={ErrorNotFoundPage} />
</Route> </Route>
), ),
'23vivi': ( '23vivi': (
<Route path='/' component={WalletApp}> <Route path="/" component={WalletApp}>
<IndexRoute component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(Vivi23Landing)} /> <IndexRoute component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(Vivi23Landing)} />
<Route <Route
path='login' path="login"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)}
footer={MarketFooter} /> footer={MarketFooter} />
<Route <Route
path='logout' path="logout"
component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)}
footer={MarketFooter} /> footer={MarketFooter} />
<Route <Route
path='signup' path="signup"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)}
footer={MarketFooter} /> footer={MarketFooter} />
<Route <Route
path='password_reset' path="password_reset"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)}
footer={MarketFooter} /> footer={MarketFooter} />
<Route <Route
path='settings' path="settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)}
footer={MarketFooter} /> footer={MarketFooter} />
<Route <Route
path='contract_settings' path="contract_settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)}
footer={MarketFooter} /> footer={MarketFooter} />
<Route <Route
path='register_piece' path="register_piece"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)}
headerTitle={getLangText('+ NEW WORK')} headerTitle={getLangText('+ NEW WORK')}
aclName='acl_wallet_submit' aclName="acl_wallet_submit"
footer={MarketFooter} /> footer={MarketFooter} />
<Route <Route
path='collection' path="collection"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(Vivi23PieceList)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(Vivi23PieceList)}
headerTitle={getLangText('COLLECTION')} headerTitle={getLangText('COLLECTION')}
disableOn='noPieces' disableOn="noPieces"
footer={MarketFooter} /> footer={MarketFooter} />
<Route <Route
path='pieces/:pieceId' path="pieces/:pieceId"
component={MarketPieceContainer} component={MarketPieceContainer}
footer={MarketFooter} /> footer={MarketFooter} />
<Route <Route
path='editions/:editionId' path="editions/:editionId"
component={MarketEditionContainer} component={MarketEditionContainer}
footer={MarketFooter} /> footer={MarketFooter} />
<Route <Route
path='coa_verify' path="coa_verify"
component={CoaVerifyContainer} component={CoaVerifyContainer}
footer={MarketFooter} /> footer={MarketFooter} />
<Route <Route
path='*' path="*"
component={ErrorNotFoundPage} component={ErrorNotFoundPage}
footer={MarketFooter} /> footer={MarketFooter} />
</Route> </Route>
), ),
'polline': ( 'polline': (
<Route path='/' component={WalletApp}> <Route path="/" component={WalletApp}>
<IndexRoute component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PollineLanding)} /> <IndexRoute component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PollineLanding)} />
<Route <Route
path='login' path="login"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} />
<Route <Route
path='logout' path="logout"
component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} />
<Route <Route
path='signup' path="signup"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} />
<Route <Route
path='password_reset' path="password_reset"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} />
<Route <Route
path='settings' path="settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} />
<Route <Route
path='contract_settings' path="contract_settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} />
<Route <Route
path='register_piece' path="register_piece"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)}
headerTitle={getLangText('+ NEW WORK')} headerTitle={getLangText('+ NEW WORK')}
aclName='acl_wallet_submit' /> aclName="acl_wallet_submit" />
<Route <Route
path='collection' path="collection"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketPieceList)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketPieceList)}
headerTitle={getLangText('COLLECTION')} headerTitle={getLangText('COLLECTION')}
disableOn='noPieces' /> disableOn="noPieces" />
<Route path='pieces/:pieceId' component={MarketPieceContainer} /> <Route path="pieces/:pieceId" component={MarketPieceContainer} />
<Route path='editions/:editionId' component={MarketEditionContainer} /> <Route path="editions/:editionId" component={MarketEditionContainer} />
<Route path='verify' component={CoaVerifyContainer} /> <Route path="verify" component={CoaVerifyContainer} />
<Route path='*' component={ErrorNotFoundPage} /> <Route path="*" component={ErrorNotFoundPage} />
</Route> </Route>
), ),
'artcity': ( 'artcity': (
<Route path='/' component={WalletApp}> <Route path="/" component={WalletApp}>
<IndexRoute component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(ArtcityLanding)} /> <IndexRoute component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(ArtcityLanding)} />
<Route <Route
path='login' path="login"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} />
<Route <Route
path='logout' path="logout"
component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} />
<Route <Route
path='signup' path="signup"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} />
<Route <Route
path='password_reset' path="password_reset"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} />
<Route <Route
path='settings' path="settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} />
<Route <Route
path='contract_settings' path="contract_settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} />
<Route <Route
path='register_piece' path="register_piece"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)}
headerTitle={getLangText('+ NEW WORK')} headerTitle={getLangText('+ NEW WORK')}
aclName='acl_wallet_submit' /> aclName="acl_wallet_submit" />
<Route <Route
path='collection' path="collection"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketPieceList)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketPieceList)}
headerTitle={getLangText('COLLECTION')} headerTitle={getLangText('COLLECTION')}
disableOn='noPieces' /> disableOn="noPieces" />
<Route path='pieces/:pieceId' component={MarketPieceContainer} /> <Route path="pieces/:pieceId" component={MarketPieceContainer} />
<Route path='editions/:editionId' component={MarketEditionContainer} /> <Route path="editions/:editionId" component={MarketEditionContainer} />
<Route path='verify' component={CoaVerifyContainer} /> <Route path="verify" component={CoaVerifyContainer} />
<Route path='*' component={ErrorNotFoundPage} /> <Route path="*" component={ErrorNotFoundPage} />
</Route> </Route>
), ),
'demo': ( 'demo': (
<Route path='/' component={WalletApp}> <Route path="/" component={WalletApp}>
<IndexRoute component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(MarketLanding)} /> <IndexRoute component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(MarketLanding)} />
<Route <Route
path='login' path="login"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} />
<Route <Route
path='logout' path="logout"
component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} />
<Route <Route
path='signup' path="signup"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} />
<Route <Route
path='password_reset' path="password_reset"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} />
<Route <Route
path='settings' path="settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} />
<Route <Route
path='contract_settings' path="contract_settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} />
<Route <Route
path='register_piece' path="register_piece"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)}
headerTitle={getLangText('+ NEW WORK')} headerTitle={getLangText('+ NEW WORK')}
aclName='acl_wallet_submit' /> aclName="acl_wallet_submit" />
<Route <Route
path='collection' path="collection"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketPieceList)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketPieceList)}
headerTitle={getLangText('COLLECTION')} headerTitle={getLangText('COLLECTION')}
disableOn='noPieces' /> disableOn="noPieces" />
<Route path='pieces/:pieceId' component={MarketPieceContainer} /> <Route path="pieces/:pieceId" component={MarketPieceContainer} />
<Route path='editions/:editionId' component={MarketEditionContainer} /> <Route path="editions/:editionId" component={MarketEditionContainer} />
<Route path='verify' component={CoaVerifyContainer} /> <Route path="verify" component={CoaVerifyContainer} />
<Route path='*' component={ErrorNotFoundPage} /> <Route path="*" component={ErrorNotFoundPage} />
</Route> </Route>
), ),
'liquidgallery': ( 'liquidgallery': (
<Route path='/' component={WalletApp}> <Route path="/" component={WalletApp}>
<IndexRoute component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(MarketLanding)} /> <IndexRoute component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(MarketLanding)} />
<Route <Route
path='login' path="login"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} />
<Route <Route
path='logout' path="logout"
component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/', when: 'loggedOut' }))(LogoutContainer)} />
<Route <Route
path='signup' path="signup"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} />
<Route <Route
path='password_reset' path="password_reset"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} />
<Route <Route
path='settings' path="settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} />
<Route <Route
path='contract_settings' path="contract_settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} />
<Route <Route
path='register_piece' path="register_piece"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketRegisterPiece)}
headerTitle={getLangText('+ NEW WORK')} headerTitle={getLangText('+ NEW WORK')}
aclName='acl_wallet_submit' /> aclName="acl_wallet_submit" />
<Route <Route
path='collection' path="collection"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketPieceList)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(MarketPieceList)}
headerTitle={getLangText('COLLECTION')} headerTitle={getLangText('COLLECTION')}
disableOn='noPieces' /> disableOn="noPieces" />
<Route path='pieces/:pieceId' component={MarketPieceContainer} /> <Route path="pieces/:pieceId" component={MarketPieceContainer} />
<Route path='editions/:editionId' component={MarketEditionContainer} /> <Route path="editions/:editionId" component={MarketEditionContainer} />
<Route path='verify' component={CoaVerifyContainer} /> <Route path="verify" component={CoaVerifyContainer} />
<Route path='*' component={ErrorNotFoundPage} /> <Route path="*" component={ErrorNotFoundPage} />
</Route> </Route>
) )
}; };
function getRoutes(commonRoutes, subdomain) { export default function getWalletRoutes(subdomain) {
if (subdomain in ROUTES) { if (subdomain in ROUTES) {
return ROUTES[subdomain]; return (ROUTES[subdomain]);
} else { } else {
throw new Error('Subdomain wasn\'t specified in the wallet app.'); throw new Error("Subdomain wasn't specified in the wallet app.");
} }
} }
export default getRoutes;

View File

@ -1,88 +1,80 @@
'use strict';
import AppConstants from './application_constants'; import AppConstants from './application_constants';
import getWalletApiUrls from '../components/whitelabel/wallet/constants/wallet_api_urls';
import { update } from '../utils/general_utils'; const ApiUrls = {
'applications': AppConstants.apiEndpoint + '/applications/',
'application_token_refresh': AppConstants.apiEndpoint + '/applications/refresh_token/',
let ApiUrls = { 'blob_digitalworks': AppConstants.apiEndpoint + '/blob/digitalworks/',
'applications': AppConstants.apiEndpoint + 'applications/', 'blob_otherdatas': AppConstants.apiEndpoint + '/blob/otherdatas/',
'application_token_refresh': AppConstants.apiEndpoint + 'applications/refresh_token/', 'blob_contracts': AppConstants.apiEndpoint + '/blob/contracts/',
'blob_digitalworks': AppConstants.apiEndpoint + 'blob/digitalworks/', 'blob_thumbnails': AppConstants.apiEndpoint + '/blob/thumbnails/',
'blob_otherdatas': AppConstants.apiEndpoint + 'blob/otherdatas/', 'coa': AppConstants.apiEndpoint + '/coa/${id}/',
'blob_contracts': AppConstants.apiEndpoint + 'blob/contracts/', 'coa_create': AppConstants.apiEndpoint + '/coa/',
'blob_thumbnails': AppConstants.apiEndpoint + 'blob/thumbnails/', 'coa_verify': AppConstants.apiEndpoint + '/coa/verify_coa/',
'coa': AppConstants.apiEndpoint + 'coa/${id}/', 'edition': AppConstants.apiEndpoint + '/editions/${bitcoin_id}/',
'coa_create': AppConstants.apiEndpoint + 'coa/', 'edition_delete': AppConstants.apiEndpoint + '/editions/${edition_id}/',
'coa_verify': AppConstants.apiEndpoint + 'coa/verify_coa/', 'edition_remove_from_collection': AppConstants.apiEndpoint + '/ownership/shares/editions/${edition_id}/',
'edition': AppConstants.apiEndpoint + 'editions/${bitcoin_id}/', 'editions': AppConstants.apiEndpoint + '/editions/', // this should be moved to the one below
'edition_delete': AppConstants.apiEndpoint + 'editions/${edition_id}/', 'editions_list': AppConstants.apiEndpoint + '/pieces/${piece_id}/editions/',
'edition_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/editions/${edition_id}/', 'licenses': AppConstants.apiEndpoint + '/ownership/licenses/',
'editions': AppConstants.apiEndpoint + 'editions/', // this should be moved to the one below 'note_private_edition': AppConstants.apiEndpoint + '/note/private/editions/',
'editions_list': AppConstants.apiEndpoint + 'pieces/${piece_id}/editions/', 'note_private_piece': AppConstants.apiEndpoint + '/note/private/pieces/',
'licenses': AppConstants.apiEndpoint + 'ownership/licenses/', 'note_public_edition': AppConstants.apiEndpoint + '/note/public/editions/',
'note_private_edition': AppConstants.apiEndpoint + 'note/private/editions/', 'note_public_piece': AppConstants.apiEndpoint + '/note/public/pieces/',
'note_private_piece': AppConstants.apiEndpoint + 'note/private/pieces/', 'notification_piecelist': AppConstants.apiEndpoint + '/notifications/pieces/',
'note_public_edition': AppConstants.apiEndpoint + 'note/public/editions/', 'notification_piece': AppConstants.apiEndpoint + '/notifications/pieces/${piece_id}/',
'note_public_piece': AppConstants.apiEndpoint + 'note/public/pieces/', 'notification_editionlist': AppConstants.apiEndpoint + '/notifications/editions/',
'notification_piecelist': AppConstants.apiEndpoint + 'notifications/pieces/', 'notification_edition': AppConstants.apiEndpoint + '/notifications/editions/${edition_id}/',
'notification_piece': AppConstants.apiEndpoint + 'notifications/pieces/${piece_id}/', 'notification_contractagreementlist': AppConstants.apiEndpoint + '/notifications/contract_agreements/',
'notification_editionlist': AppConstants.apiEndpoint + 'notifications/editions/', 'ownership_contract_agreements': AppConstants.apiEndpoint + '/ownership/contract_agreements/',
'notification_edition': AppConstants.apiEndpoint + 'notifications/editions/${edition_id}/', 'ownership_contract_agreements_confirm': AppConstants.apiEndpoint + '/ownership/contract_agreements/${contract_agreement_id}/accept/',
'notification_contractagreementlist': AppConstants.apiEndpoint + 'notifications/contract_agreements/', 'ownership_contract_agreements_deny': AppConstants.apiEndpoint + '/ownership/contract_agreements/${contract_agreement_id}/reject/',
'ownership_contract_agreements': AppConstants.apiEndpoint + 'ownership/contract_agreements/', 'ownership_consigns': AppConstants.apiEndpoint + '/ownership/consigns/',
'ownership_contract_agreements_confirm': AppConstants.apiEndpoint + 'ownership/contract_agreements/${contract_agreement_id}/accept/', 'ownership_consigns_confirm': AppConstants.apiEndpoint + '/ownership/consigns/confirm/',
'ownership_contract_agreements_deny': AppConstants.apiEndpoint + 'ownership/contract_agreements/${contract_agreement_id}/reject/', 'ownership_consigns_deny': AppConstants.apiEndpoint + '/ownership/consigns/deny/',
'ownership_consigns': AppConstants.apiEndpoint + 'ownership/consigns/', 'ownership_consigns_withdraw': AppConstants.apiEndpoint + '/ownership/consigns/withdraw/',
'ownership_consigns_confirm': AppConstants.apiEndpoint + 'ownership/consigns/confirm/', 'ownership_loans_pieces': AppConstants.apiEndpoint + '/ownership/loans/pieces/',
'ownership_consigns_deny': AppConstants.apiEndpoint + 'ownership/consigns/deny/', 'ownership_loans_pieces_confirm': AppConstants.apiEndpoint + '/ownership/loans/pieces/confirm/',
'ownership_consigns_withdraw': AppConstants.apiEndpoint + 'ownership/consigns/withdraw/', 'ownership_loans_pieces_deny': AppConstants.apiEndpoint + '/ownership/loans/pieces/deny/',
'ownership_loans_pieces': AppConstants.apiEndpoint + 'ownership/loans/pieces/', 'ownership_loans_pieces_request': AppConstants.apiEndpoint + '/ownership/loans/pieces/request/',
'ownership_loans_pieces_confirm': AppConstants.apiEndpoint + 'ownership/loans/pieces/confirm/', 'ownership_loans_pieces_request_confirm': AppConstants.apiEndpoint + '/ownership/loans/pieces/request_confirm/',
'ownership_loans_pieces_deny': AppConstants.apiEndpoint + 'ownership/loans/pieces/deny/', 'ownership_loans_pieces_request_deny': AppConstants.apiEndpoint + '/ownership/loans/pieces/request_deny/',
'ownership_loans_pieces_request': AppConstants.apiEndpoint + 'ownership/loans/pieces/request/', 'ownership_loans_editions': AppConstants.apiEndpoint + '/ownership/loans/editions/',
'ownership_loans_pieces_request_confirm': AppConstants.apiEndpoint + 'ownership/loans/pieces/request_confirm/', 'ownership_loans_confirm': AppConstants.apiEndpoint + '/ownership/loans/editions/confirm/',
'ownership_loans_pieces_request_deny': AppConstants.apiEndpoint + 'ownership/loans/pieces/request_deny/', 'ownership_loans_deny': AppConstants.apiEndpoint + '/ownership/loans/editions/deny/',
'ownership_loans_editions': AppConstants.apiEndpoint + 'ownership/loans/editions/', 'ownership_shares_editions': AppConstants.apiEndpoint + '/ownership/shares/editions/',
'ownership_loans_confirm': AppConstants.apiEndpoint + 'ownership/loans/editions/confirm/', 'ownership_shares_pieces': AppConstants.apiEndpoint + '/ownership/shares/pieces/',
'ownership_loans_deny': AppConstants.apiEndpoint + 'ownership/loans/editions/deny/', 'ownership_transfers': AppConstants.apiEndpoint + '/ownership/transfers/',
'ownership_shares_editions': AppConstants.apiEndpoint + 'ownership/shares/editions/', 'ownership_transfers_withdraw': AppConstants.apiEndpoint + '/ownership/transfers/withdraw/',
'ownership_shares_pieces': AppConstants.apiEndpoint + 'ownership/shares/pieces/', 'ownership_unconsigns': AppConstants.apiEndpoint + '/ownership/unconsigns/',
'ownership_transfers': AppConstants.apiEndpoint + 'ownership/transfers/', 'ownership_unconsigns_deny': AppConstants.apiEndpoint + '/ownership/unconsigns/deny/',
'ownership_transfers_withdraw': AppConstants.apiEndpoint + 'ownership/transfers/withdraw/', 'ownership_unconsigns_request': AppConstants.apiEndpoint + '/ownership/unconsigns/request/',
'ownership_unconsigns': AppConstants.apiEndpoint + 'ownership/unconsigns/', 'ownership_contract': AppConstants.apiEndpoint + '/ownership/contracts/${contract_id}/',
'ownership_unconsigns_deny': AppConstants.apiEndpoint + 'ownership/unconsigns/deny/', 'ownership_contract_list': AppConstants.apiEndpoint + '/ownership/contracts/',
'ownership_unconsigns_request': AppConstants.apiEndpoint + 'ownership/unconsigns/request/', 'piece': AppConstants.apiEndpoint + '/pieces/${piece_id}/',
'ownership_contract': AppConstants.apiEndpoint + 'ownership/contracts/${contract_id}/', 'piece_extradata': AppConstants.apiEndpoint + '/pieces/${piece_id}/extradata/',
'ownership_contract_list': AppConstants.apiEndpoint + 'ownership/contracts/', 'pieces_list': AppConstants.apiEndpoint + '/pieces/',
'piece': AppConstants.apiEndpoint + 'pieces/${piece_id}/', 'piece_remove_from_collection': AppConstants.apiEndpoint + '/ownership/shares/pieces/${piece_id}/',
'piece_extradata': AppConstants.apiEndpoint + 'pieces/${piece_id}/extradata/', 'user': AppConstants.apiEndpoint + '/users/',
'pieces_list': AppConstants.apiEndpoint + 'pieces/', 'users_login': AppConstants.apiEndpoint + '/users/login/',
'piece_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/pieces/${piece_id}/', 'users_logout': AppConstants.apiEndpoint + '/users/logout/',
'user': AppConstants.apiEndpoint + 'users/', 'users_password_reset': AppConstants.apiEndpoint + '/users/reset_password/',
'users_login': AppConstants.apiEndpoint + 'users/login/', 'users_password_reset_request': AppConstants.apiEndpoint + '/users/request_reset_password/',
'users_logout': AppConstants.apiEndpoint + 'users/logout/', 'users_signup': AppConstants.apiEndpoint + '/users/',
'users_password_reset': AppConstants.apiEndpoint + 'users/reset_password/', 'users_username': AppConstants.apiEndpoint + '/users/username/',
'users_password_reset_request': AppConstants.apiEndpoint + 'users/request_reset_password/', 'users_profile': AppConstants.apiEndpoint + '/users/profile/',
'users_signup': AppConstants.apiEndpoint + 'users/', 'wallet_settings': AppConstants.apiEndpoint + '/users/wallet_settings/',
'users_username': AppConstants.apiEndpoint + 'users/username/', 'webhook': AppConstants.apiEndpoint + '/webhooks/${webhook_id}/',
'users_profile': AppConstants.apiEndpoint + 'users/profile/', 'webhooks': AppConstants.apiEndpoint + '/webhooks/',
'wallet_settings': AppConstants.apiEndpoint + 'users/wallet_settings/', 'webhooks_events': AppConstants.apiEndpoint + '/webhooks/events/',
'webhook': AppConstants.apiEndpoint + 'webhooks/${webhook_id}/', 'whitelabel_settings': AppConstants.apiEndpoint + '/whitelabel/settings/${subdomain}/',
'webhooks': AppConstants.apiEndpoint + 'webhooks/', 'delete_s3_file': AppConstants.serverUrl + '/s3/delete/',
'webhooks_events': AppConstants.apiEndpoint + 'webhooks/events/', 'sign_url_s3': AppConstants.serverUrl + '/s3/sign_url/'
'whitelabel_settings': AppConstants.apiEndpoint + 'whitelabel/settings/${subdomain}/',
'delete_s3_file': AppConstants.serverUrl + 's3/delete/',
'sign_url_s3': AppConstants.serverUrl + 's3/sign_url/'
}; };
export function updateApiUrls(type, subdomain) { export function updateApiUrls(updatedApiUrls) {
if (type === 'wallet') { return Object.assign(ApiUrls, updatedApiUrls);
update(getWalletApiUrls(subdomain));
}
} }
export default ApiUrls; export default ApiUrls;

View File

@ -1,19 +1,13 @@
'use strict'; 'use strict';
//const baseUrl = 'http://localhost:8000/api/'; const apiEndpoint = process.env.API_URL;
const serverUrl = process.env.SERVER_URL;
//FIXME: referring to a global variable in `window` is not const baseUrl = process.env.APP_BASE_PATH;
// super pro. What if we render stuff on the server?
// - super-bro - Senor Developer, 14th July 2015
//const baseUrl = window.BASE_URL;
const apiEndpoint = window.API_ENDPOINT;
const serverUrl = window.SERVER_URL;
const baseUrl = window.BASE_URL;
const constants = { const constants = {
apiEndpoint, apiEndpoint,
serverUrl,
baseUrl, baseUrl,
serverUrl,
'aclList': ['acl_coa', 'acl_consign', 'acl_delete', 'acl_download', 'acl_edit', 'acl_create_editions', 'acl_view_editions', 'aclList': ['acl_coa', 'acl_consign', 'acl_delete', 'acl_download', 'acl_edit', 'acl_create_editions', 'acl_view_editions',
'acl_loan', 'acl_loan_request', 'acl_share', 'acl_transfer', 'acl_unconsign', 'acl_unshare', 'acl_view', 'acl_loan', 'acl_loan_request', 'acl_share', 'acl_transfer', 'acl_unconsign', 'acl_unshare', 'acl_view',
'acl_withdraw_transfer', 'acl_wallet_submit'], 'acl_withdraw_transfer', 'acl_wallet_submit'],
@ -104,12 +98,8 @@ const constants = {
'jquery': { 'jquery': {
'sdkUrl': 'https://code.jquery.com/jquery-2.1.4.min.js' 'sdkUrl': 'https://code.jquery.com/jquery-2.1.4.min.js'
}, },
'shmui': {
'sdkUrl': baseUrl + 'static/thirdparty/shmui/jquery.shmui.js',
'cssUrl': baseUrl + 'static/thirdparty/shmui/shmui.css'
},
'audiojs': { 'audiojs': {
'sdkUrl': baseUrl + 'static/thirdparty/audiojs/audiojs/audio.min.js' 'sdkUrl': baseUrl + '/static/third_party/audiojs/audio.min.js'
}, },
'videojs': { 'videojs': {
'sdkUrl': '//vjs.zencdn.net/4.12/video.js', 'sdkUrl': '//vjs.zencdn.net/4.12/video.js',

View File

@ -6,11 +6,8 @@ import createBrowserHistory from 'history/lib/createBrowserHistory';
import AppConstants from './constants/application_constants'; import AppConstants from './constants/application_constants';
// Remove the trailing slash if present
const baseUrl = AppConstants.baseUrl.replace(/\/$/, '');
const history = useBasename(useQueries(createBrowserHistory))({ const history = useBasename(useQueries(createBrowserHistory))({
basename: baseUrl basename: AppConstants.baseUrl
}); });
history.locationQueue = []; history.locationQueue = [];

View File

@ -1,10 +1,6 @@
'use strict';
import React from 'react'; import React from 'react';
import { Route } from 'react-router'; import { Route } from 'react-router';
import getWalletRoutes from './components/whitelabel/wallet/wallet_routes';
import AscribeApp from './components/ascribe_app'; import AscribeApp from './components/ascribe_app';
import PieceList from './components/piece_list'; import PieceList from './components/piece_list';
@ -31,62 +27,56 @@ import { ProxyHandler, AuthRedirect } from './components/ascribe_routes/proxy_ha
import { getLangText } from './utils/lang_utils'; import { getLangText } from './utils/lang_utils';
const COMMON_ROUTES = ( const Routes = (
<Route path='/' component={AscribeApp}> <Route path="/" component={AscribeApp}>
<Route <Route
path='login' path="login"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)} component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(LoginContainer)}
footer={Footer} /> footer={Footer} />
<Route <Route
path='register_piece' path="register_piece"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(RegisterPiece)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(RegisterPiece)}
headerTitle={getLangText('+ NEW WORK')} headerTitle={getLangText('+ NEW WORK')}
footer={Footer} /> footer={Footer} />
<Route <Route
path='collection' path="collection"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(PieceList)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(PieceList)}
headerTitle={getLangText('COLLECTION')} headerTitle={getLangText('COLLECTION')}
disableOn='noPieces' disableOn="noPieces"
footer={Footer} /> footer={Footer} />
<Route <Route
path='signup' path="signup"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)} component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(SignupContainer)}
footer={Footer} /> footer={Footer} />
<Route <Route
path='logout' path="logout"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(LogoutContainer)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(LogoutContainer)}
footer={Footer} /> footer={Footer} />
<Route path='pieces/:pieceId' component={PieceContainer} <Route path="pieces/:pieceId" component={PieceContainer}
footer={Footer} /> footer={Footer} />
<Route path='editions/:editionId' component={EditionContainer} <Route path="editions/:editionId" component={EditionContainer}
footer={Footer} /> footer={Footer} />
<Route <Route
path='password_reset' path="password_reset"
component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)} component={ProxyHandler(AuthRedirect({ to: '/collection', when: 'loggedIn' }))(PasswordResetContainer)}
footer={Footer} /> footer={Footer} />
<Route <Route
path='settings' path="settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(SettingsContainer)}
footer={Footer} /> footer={Footer} />
<Route <Route
path='contract_settings' path="contract_settings"
component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)} component={ProxyHandler(AuthRedirect({ to: '/login', when: 'loggedOut' }))(ContractSettings)}
footer={Footer} /> footer={Footer} />
<Route path='coa_verify' component={CoaVerifyContainer} <Route
path="coa_verify"
component={CoaVerifyContainer}
footer={Footer} /> footer={Footer} />
<Route path='*' component={ErrorNotFoundPage} <Route
path="*"
component={ErrorNotFoundPage}
footer={Footer} /> footer={Footer} />
</Route> </Route>
); );
export default Routes;
function getRoutes(type, subdomain) {
if (type === 'wallet') {
return getWalletRoutes(COMMON_ROUTES, subdomain);
} else {
return COMMON_ROUTES;
}
}
export default getRoutes;

18
js/third_party/imports/audiojs.js vendored Normal file
View File

@ -0,0 +1,18 @@
import AppConstants from '../../constants/application_constants';
import { InjectInHeadUtils } from '../../utils/inject_utils';
/**
* Imports audiojs from the copied directory.
*
* Unfortunately, audiojs' package structure and the way it currently loads image assets prevents us
* from using System.import.
*
* @return {Promise} Promise that resolves with [audio.min.js] on success.
*/
function importLib() {
return InjectInHeadUtils.inject(AppConstants.audiojs.sdkUrl);
}
export default { importLib };

19
js/third_party/imports/shmui.js vendored Normal file
View File

@ -0,0 +1,19 @@
import AppConstants from '../../constants/application_constants';
import { InjectInHeadUtils } from '../../utils/inject_utils';
/**
* Imports shmui and its dependencies (jQuery)
*
* @return {Promise} Promise that resolves with [jquery.shmui.js, shmui.css] on success.
*/
function importLib() {
return InjectInHeadUtils.inject(AppConstants.jquery.sdkUrl)
.then(() => Promise.all([
System.import('shmui/jquery.shmui'),
System.import('shmui/shmui.css')
]));
}
export default { importLib };

18
js/third_party/imports/videojs.js vendored Normal file
View File

@ -0,0 +1,18 @@
import AppConstants from '../../constants/application_constants';
import { InjectInHeadUtils } from '../../utils/inject_utils';
/**
* Imports videojs and its stylesheet.
*
* @return {Promise} Promise that resolves with [video.js, video-js.css] on success.
*/
function importLib() {
return Promise.all([
InjectInHeadUtils.inject(AppConstants.videojs.cssUrl),
InjectInHeadUtils.inject(AppConstants.videojs.sdkUrl)
]);
}
export default { importLib };

View File

@ -1,21 +1,30 @@
{ {
"name": "Onion", "name": "Onion",
"version": "0.0.1", "version": "0.0.2",
"description": "Das neue web client for Ascribe. Onions make you cry", "description": "Das neue web client for Ascribe. Onions make you cry",
"homepapge": "https://www.ascribe.io",
"license": "Copyright",
"author": "Ascribe",
"main": "js/app.js", "main": "js/app.js",
"private": true,
"engines": { "engines": {
"node": "0.10.x" "node": "6.2.0"
}, },
"scripts": { "scripts": {
"lint": "eslint ./js",
"preinstall": "export SAUCE_CONNECT_DOWNLOAD_ON_INSTALL=true", "preinstall": "export SAUCE_CONNECT_DOWNLOAD_ON_INSTALL=true",
"postinstall": "npm run build", "postinstall": "npm run build",
"build": "gulp build --production", "build": "rimraf ./dist && NODE_ENV=production webpack -p",
"start": "node server.js", "start": "NODE_ENV=production node server.js",
"build:dev": "rimraf ./build && NODE_ENV=development webpack",
"build:extract": "rimraf ./build && NODE_ENV=extract webpack",
"clean": "rimraf ./build ./dist",
"start:dev": "NODE_ENV=development node server.dev.js",
"lint": "eslint ./",
"test": "npm run sauce-test", "test": "npm run sauce-test",
"sauce-test": "mocha ./test/integration/tests/", "sauce-test": "mocha ./test/integration/tests/",
"tunnel": "node ./test/integration/tunnel.js", "sauce-tunnel": "node ./test/integration/tunnel.js",
"vi-clean": "rm -rf ./gemini-report", "vi-clean": "rm -rf ./gemini-report",
"vi-phantom": "phantomjs --webdriver=4444", "vi-phantom": "phantomjs --webdriver=4444",
@ -31,78 +40,61 @@
"vi-test:lumenus": "npm run -s vi-test:base -- --browser LumenusDesktop --browser LumenusMobile", "vi-test:lumenus": "npm run -s vi-test:base -- --browser LumenusDesktop --browser LumenusMobile",
"vi-test:23vivi": "npm run -s vi-test:base -- --browser 23viviDesktop --browser 23viviMobile" "vi-test:23vivi": "npm run -s vi-test:base -- --browser 23viviDesktop --browser 23viviMobile"
}, },
"browser": {
"fineUploader": "./js/components/ascribe_uploader/vendor/s3.fine-uploader.js"
},
"browserify-shim": {
"fineUploader": "qq"
},
"browserify": {
"transform": [
[
"babelify",
{
"compact": false
}
],
"browserify-shim"
]
},
"author": "Ascribe",
"license": "Copyright",
"private": true,
"devDependencies": { "devDependencies": {
"babel-eslint": "^3.1.11",
"babel-jest": "^5.2.0", "babel-jest": "^5.2.0",
"chai": "^3.4.1", "chai": "^3.4.1",
"chai-as-promised": "^5.1.0", "chai-as-promised": "^5.1.0",
"colors": "^1.1.2", "colors": "^1.1.2",
"dotenv": "^1.2.0", "dotenv": "^1.2.0",
"gemini": "^2.1.0", "gemini": "^4.3.0",
"jest-cli": "^0.4.0", "jest-cli": "^0.4.0",
"mocha": "^2.3.4", "mocha": "^2.3.4",
"phantomjs2": "^2.0.2", "rimraf": "^2.5.2",
"sauce-connect-launcher": "^0.13.0", "sauce-connect-launcher": "^0.13.0",
"wd": "^0.4.0" "wd": "^0.4.0"
}, },
"dependencies": { "dependencies": {
"alt": "^0.16.5", "alt": "^0.16.5",
"audiojs": "vrde/audiojs", "audiojs": "vrde/audiojs",
"babel": "^5.6.14", "autoprefixer": "^6.3.6",
"babelify": "^6.1.2", "babel-cli": "^6.9.0",
"bootstrap-sass": "^3.3.4", "babel-eslint": "^6.0.4",
"browser-sync": "^2.7.5", "babel-loader": "^6.2.4",
"browserify": "^9.0.8", "babel-plugin-react-transform": "^2.0.2",
"browserify-shim": "^3.8.10", "babel-plugin-transform-object-assign": "^6.8.0",
"babel-plugin-transform-object-rest-spread": "^6.8.0",
"babel-plugin-transform-react-display-name": "^6.8.0",
"babel-plugin-transform-runtime": "^6.9.0",
"babel-preset-es2015-no-commonjs": "0.0.2",
"babel-preset-react": "^6.5.0",
"babel-runtime": "^6.9.0",
"bootstrap-loader": "^1.0.10",
"bootstrap-sass": "^3.3.6",
"camelcase": "^1.2.1", "camelcase": "^1.2.1",
"classlist-polyfill": "^1.0.2", "classlist-polyfill": "^1.0.2",
"classnames": "^1.2.2", "classnames": "^1.2.2",
"compression": "^1.4.4", "compression": "^1.6.2",
"copy-webpack-plugin": "^3.0.1",
"core-js": "^2.4.0",
"css-loader": "^0.23.1",
"decamelize": "^1.1.1", "decamelize": "^1.1.1",
"envify": "^3.4.0", "dotenv": "^1.2.0",
"eslint": "^0.22.1", "eslint": "^2.11.1",
"eslint-plugin-react": "^2.5.0", "eslint-config-ascribe": "^1.0.1",
"express": "^4.12.4", "eslint-plugin-import": "^1.8.1",
"gulp": "^3.8.11", "eslint-plugin-jsx-a11y": "^1.2.2",
"gulp-concat": "^2.5.2", "eslint-plugin-react": "^5.1.1",
"gulp-eslint": "^0.13.2", "exports-loader": "^0.6.3",
"gulp-if": "^1.2.5", "express": "^4.13.4",
"gulp-minify-css": "^1.1.6", "extract-text-webpack-plugin": "^1.0.1",
"gulp-notify": "^2.2.0", "file-loader": "^0.8.5",
"gulp-sass": "^2.1.1",
"gulp-sourcemaps": "^1.5.2",
"gulp-template": "~3.0.0",
"gulp-uglify": "^1.2.0",
"gulp-util": "^3.0.4",
"harmonize": "^1.4.2",
"history": "1.17.0", "history": "1.17.0",
"html-webpack-plugin": "^2.19.0",
"invariant": "^2.1.1", "invariant": "^2.1.1",
"isomorphic-fetch": "^2.0.2", "isomorphic-fetch": "^2.0.2",
"jest-cli": "^0.4.0",
"lodash": "^3.9.3",
"moment": "^2.10.6", "moment": "^2.10.6",
"object-assign": "^2.0.0", "node-sass": "^3.7.0",
"opn": "^3.0.2", "postcss-loader": "^0.9.1",
"q": "^1.4.1", "q": "^1.4.1",
"query-string": "^3.0.0", "query-string": "^3.0.0",
"raven-js": "^1.1.19", "raven-js": "^1.1.19",
@ -113,15 +105,18 @@
"react-router-bootstrap": "^0.19.0", "react-router-bootstrap": "^0.19.0",
"react-star-rating": "~1.3.2", "react-star-rating": "~1.3.2",
"react-textarea-autosize": "^2.5.2", "react-textarea-autosize": "^2.5.2",
"reactify": "^1.1.0", "react-transform-hmr": "^1.0.4",
"remove-trailing-slash": "^0.1.0",
"resolve-url-loader": "^1.4.3",
"sass-loader": "^3.2.0",
"shallow-equals": "0.0.0", "shallow-equals": "0.0.0",
"shmui": "^0.1.0", "shmui": "^0.1.0",
"spark-md5": "~1.0.0", "spark-md5": "~1.0.0",
"uglifyjs": "^2.4.10", "style-loader": "^0.13.1",
"vinyl-buffer": "^1.0.0", "url-loader": "^0.5.7",
"vinyl-source-stream": "^1.1.0", "webpack": "^2.1.0-beta.7",
"watchify": "^3.1.2", "webpack-combine-loaders": "^2.0.0",
"yargs": "^3.10.0" "webpack-dev-server": "^2.1.0-beta.0"
}, },
"jest": { "jest": {
"scriptPreprocessor": "node_modules/babel-jest", "scriptPreprocessor": "node_modules/babel-jest",

View File

@ -13,11 +13,11 @@
@font-face { @font-face {
font-family: 'ascribe-font'; font-family: 'ascribe-font';
src:url('#{$BASE_URL}static/fonts/ascribe-font.eot?q6qoae'); src:url('../fonts/ascribe-font.eot?q6qoae');
src:url('#{$BASE_URL}static/fonts/ascribe-font.eot?q6qoae#iefix') format('embedded-opentype'), src:url('../fonts/ascribe-font.eot?q6qoae#iefix') format('embedded-opentype'),
url('#{$BASE_URL}static/fonts/ascribe-font.ttf?q6qoae') format('truetype'), url('../fonts/ascribe-font.ttf?q6qoae') format('truetype'),
url('#{$BASE_URL}static/fonts/ascribe-font.woff?q6qoae') format('woff'), url('../fonts/ascribe-font.woff?q6qoae') format('woff'),
url('#{$BASE_URL}static/fonts/ascribe-font.svg?q6qoae#ascribe-font') format('svg'); url('../fonts/ascribe-font.svg?q6qoae#ascribe-font') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }

View File

@ -0,0 +1,2 @@
@import '../ascribe_variables';
@import '../variables';

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,9 @@
// If you import a new .scss file, make sure to restart gulp // If you import a new .scss file, make sure to restart gulp
// otherwise it will not be included // otherwise it will not be included
$BASE_URL: '<%= BASE_URL %>';
@import 'mixins'; @import 'mixins';
@import 'ascribe_variables'; @import 'ascribe_variables';
@import 'variables'; @import 'variables';
@import '../node_modules/bootstrap-sass/assets/stylesheets/bootstrap';
@import '../node_modules/react-star-rating/dist/css/react-star-rating.min'; @import '../node_modules/react-star-rating/dist/css/react-star-rating.min';
@import '../node_modules/react-datepicker/dist/react-datepicker'; @import '../node_modules/react-datepicker/dist/react-datepicker';
@import 'glyphicons-social'; @import 'glyphicons-social';

44
server.dev.js Normal file
View File

@ -0,0 +1,44 @@
/* eslint-disable strict, no-console */
/* eslint-disable import/no-extraneous-dependencies, import/newline-after-import */
'use strict';
const removeTrailingSlash = require('remove-trailing-slash');
const WebpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');
const config = require('./webpack.config.js');
require('dotenv').load({ silent: true });
const HOST = process.env.ONION_DEV_HOST || 'localhost';
const PORT = process.env.ONION_DEV_PORT || 3000;
const BASE_PATH = removeTrailingSlash(process.env.ONION_BASE_PATH || '/');
// Enable server hot reloading
// React hot reloading is enabled through .babelrc and babel-react-transform
config.entry.unshift(`webpack-dev-server/client?http://${HOST}:${PORT}/`,
'webpack/hot/dev-server');
config.plugins.push(new webpack.HotModuleReplacementPlugin());
// Configure server
const compiler = webpack(config);
const server = new WebpackDevServer(compiler, {
publicPath: config.output.publicPath,
historyApiFallback: {
index: config.output.publicPath
},
hot: true,
noInfo: true,
stats: { colors: true }
});
// Start server
server.listen(PORT, HOST, (err) => {
if (err) {
console.error(`Onion dev server ran into ${err} while starting on ${HOST}:${PORT} ` +
`with basePath set to ${BASE_PATH || '/'}. Shutting down...`);
server.close();
}
console.log(`Onion server running on ${HOST}:${PORT} with basePath set to ${BASE_PATH || '/'}.`);
});

View File

@ -1,30 +1,28 @@
var argv = require('yargs').argv; /* eslint-disable strict, no-console */
var express = require('express'); 'use strict';
var compression = require('compression');
var baseUrl = (function () { var baseUrl = process.env.ONION_BASE_URL || '/'; return baseUrl + (baseUrl.match(/\/$/) ? '' : '/'); })(); const path = require('path');
const express = require('express');
const compression = require('compression');
const removeTrailingSlash = require('remove-trailing-slash');
var app = express(); const BASE_PATH = removeTrailingSlash(process.env.ONION_BASE_PATH || '/');
const PORT = process.env.ONION_PORT || 4000;
const app = express();
app.use(compression()); app.use(compression());
app.use(path.resolve(BASE_PATH, '/static'), express.static(path.resolve(__dirname, 'dist')));
app.use(baseUrl + 'static/js', express.static(__dirname + '/build/js')); app.get(/.*/, (req, res) => {
app.use(baseUrl + 'static/img', express.static(__dirname + '/build/img'));
app.use(baseUrl + 'static/css', express.static(__dirname + '/build/css'));
app.use(baseUrl + 'static/fonts', express.static(__dirname + '/build/fonts'));
app.use(baseUrl + 'static/thirdparty', express.static(__dirname + '/node_modules'));
app.get(/.*/, function(req, res) {
console.log('%s %s', req.method, req.path); console.log('%s %s', req.method, req.path);
res.sendFile(__dirname + '/build/index.html'); res.sendFile(path.resolve(__dirname, 'dist/index.html'));
}); });
if (require.main === module) { if (require.main === module) {
var port = process.env.PORT || 4000; console.log(`Starting Onion server on port ${PORT} with basePath set to ${BASE_PATH || '/'}`);
console.log('Starting Onion server on port', port, app.listen(PORT);
'baseUrl is set to', baseUrl);
app.listen(port);
} }
module.exports.app = app; module.exports.app = app;

View File

@ -1,36 +0,0 @@
{
"parser": "babel-eslint",
"env": {
"mocha": true,
"node": true
},
"rules": {
"new-cap": [2, {newIsCap: true, capIsNew: false}],
"quotes": [2, "single"],
"eol-last": [0],
"no-mixed-requires": [0],
"no-underscore-dangle": [0],
"global-strict": [2, "always"],
"no-trailing-spaces": [2, { skipBlankLines: true }],
"no-console": 0,
"camelcase": [2, {"properties": "never"}],
},
"globals": {},
"plugins": [],
"ecmaFeatures": {
"modules": 1,
"arrowFunctions",
"classes": 1,
"blockBindings": 1,
"defaultParams": 1,
"destructuring": 1,
"objectLiteralComputedProperties": 1,
"objectLiteralDuplicateProperties": 0,
"objectLiteralShorthandMethods": 1,
"objectLiteralShorthandProperties": 1,
"restParams": 1,
"spread": 1,
"superInFunctions": 1,
"templateStrings": 1
}
}

13
test/.eslintrc.json Normal file
View File

@ -0,0 +1,13 @@
{
"env": {
"node": true
},
"rules": {
"func-names": [0],
"no-console": [0],
"no-process-exit": [0],
"strict": [0],
"import/newline-after-import": [0],
"import/no-extraneous-dependencies": [2, { "devDependencies": true, "optionalDependencies": false }]
}
}

View File

@ -0,0 +1,8 @@
{
"globals": {
"gemini": true
},
"rules": {
"max-len": [2, { "code": 125 }]
}
}

View File

@ -23,30 +23,41 @@ Then, install [PhantomJS2](https://www.npmjs.com/package/phantomjs2):
```bash ```bash
# Until phantomjs2 is updated for the new 2.1 version of PhantomJS, use the following (go to https://bitbucket.org/ariya/phantomjs/downloads to find a build for your OS) # Until phantomjs2 is updated for the new 2.1 version of PhantomJS, use the following (go to https://bitbucket.org/ariya/phantomjs/downloads to find a build for your OS)
npm install -g phantomjs2 --phantomjs_downloadurl=https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-macosx.zip npm install phantomjs2 --phantomjs_downloadurl=https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-macosx.zip
npm install --save-dev phantomjs2 --phantomjs_downloadurl=https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-macosx.zip
# If using OSX, you may have to install upx and decompress the binary downloaded by npm manually: # If using OSX, you may have to install upx and decompress the binary downloaded by npm manually:
brew install upx brew install upx
# Navigate to the binary, ie. /Users/Brett/.nvm/versions/node/v5.4.0/lib/node_modules/phantomjs2/lib/phantom/bin/phantomjs # Navigate to the binary, ie. node_modules/phantomjs2/lib/phantom/bin/ (for the local installation)
upx -d phantomjs upx -d phantomjs
# If using Linux or OSX, you may also have to convert the binary using `dos2unix`:
brew install dos2unix
dos2unix phantomjs
``` ```
Finally, [install Gemini globally and locally with npm](https://github.com/gemini-testing/gemini/blob/master/README.md#installation). Finally, [install Gemini locally with npm](https://github.com/gemini-testing/gemini/blob/master/README.md#installation), if it
hasn't already been installed through `package.json`.
Running Tests Running Tests
============= =============
Run Spool and Onion (on localhost.com:3000).
Run PhantomJS: Run PhantomJS:
```bash ```bash
npm run vi-phantom npm run vi-phantom
``` ```
And then run Gemini tests: Gather your initial baseline (on the master branch, for example):
```bash
npm run vi-update
```
And then run Gemini tests (on your current branch with changes, to test for regressions):
```bash ```bash
npm run vi-test npm run vi-test
@ -61,8 +72,8 @@ npm run vi-test:whitelabel
npm run vi-test:cyland npm run vi-test:cyland
``` ```
If you've made changes and want them to be the new baseline (ie. it's a correct change--**make sure** to test there are Later on, if you've made changes and want them to be the new baseline (ie. it's a correct change--**make sure** to test
no regressions first!), use there are no regressions first!), use:
```bash ```bash
npm run vi-update npm run vi-update

View File

@ -1,3 +1,4 @@
/* eslint-disable strict, no-console */
'use strict'; 'use strict';
const MAIN_USER = { const MAIN_USER = {

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const gemini = require('gemini');
const environment = require('../environment'); const environment = require('../environment');
const MAIN_USER = environment.MAIN_USER; const MAIN_USER = environment.MAIN_USER;
const TIMEOUTS = environment.TIMEOUTS; const TIMEOUTS = environment.TIMEOUTS;
@ -12,7 +11,7 @@ gemini.suite('Authenticated', (suite) => {
suite suite
.setUrl('/collection') .setUrl('/collection')
.setCaptureElements('.ascribe-body') .setCaptureElements('.ascribe-body')
.before((actions, find) => { .before((actions) => {
// This will be called before every nested suite begins unless that suite // This will be called before every nested suite begins unless that suite
// also defines a `.before()` // also defines a `.before()`
actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL);
@ -30,18 +29,20 @@ gemini.suite('Authenticated', (suite) => {
actions.sendKeys(find('.ascribe-login-wrapper input[name=password]'), MAIN_USER.password); actions.sendKeys(find('.ascribe-login-wrapper input[name=password]'), MAIN_USER.password);
actions.click(find('.ascribe-login-wrapper button[type=submit]')); actions.click(find('.ascribe-login-wrapper button[type=submit]'));
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.LONG);
}); });
}); });
gemini.suite('Authenticated', (authenticatedSuite) => {
gemini.suite('Header-desktop', (headerSuite) => { gemini.suite('Header-desktop', (headerSuite) => {
headerSuite headerSuite
.setCaptureElements('nav.navbar .container') .setCaptureElements('nav.navbar .container')
// Ignore Cyland's logo as it's a gif // Ignore Cyland's logo as it's a gif
.ignoreElements('.client--cyland img.img-brand') .ignoreElements('.client--cyland img.img-brand')
.skip(/Mobile/) .skip(/Mobile/)
.before((actions, find) => { .before((actions) => {
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.LONG);
actions.waitForElementToShow('#header-notification-dropdown', TIMEOUTS.SUPER_DUPER_EXTRA_LONG);
}) })
.capture('desktop header'); .capture('desktop header');
@ -69,14 +70,15 @@ gemini.suite('Authenticated', (suite) => {
// Ignore Cyland's logo as it's a gif // Ignore Cyland's logo as it's a gif
.ignoreElements('.client--cyland img.img-brand') .ignoreElements('.client--cyland img.img-brand')
.skip(/Desktop/) .skip(/Desktop/)
.before((actions, find) => { .before((actions) => {
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.LONG);
}) })
.capture('mobile header') .capture('mobile header')
.capture('expanded mobile header', (actions, find) => { .capture('expanded mobile header', (actions, find) => {
actions.click(find('nav.navbar .navbar-toggle')); actions.click(find('nav.navbar .navbar-toggle'));
// Wait for the header to expand // Wait for the header to expand
actions.wait(500); actions.wait(500);
actions.waitForElementToShow('#header-notification-dropdown', TIMEOUTS.SUPER_DUPER_EXTRA_LONG);
}) })
.capture('expanded user dropdown', (actions, find) => { .capture('expanded user dropdown', (actions, find) => {
actions.click(find('#nav-route-user-dropdown')); actions.click(find('#nav-route-user-dropdown'));
@ -89,7 +91,7 @@ gemini.suite('Authenticated', (suite) => {
gemini.suite('Collection', (collectionSuite) => { gemini.suite('Collection', (collectionSuite) => {
collectionSuite collectionSuite
.setCaptureElements('.ascribe-accordion-list') .setCaptureElements('.ascribe-accordion-list')
.before((actions, find) => { .before((actions) => {
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL);
// Wait for the images to load // Wait for the images to load
// FIXME: unfortuntately gemini doesn't support ignoring multiple elements from a single selector // FIXME: unfortuntately gemini doesn't support ignoring multiple elements from a single selector
@ -102,7 +104,7 @@ gemini.suite('Authenticated', (suite) => {
actions.click(find('.ascribe-accordion-list-item .ascribe-accordion-list-item-edition-widget')); actions.click(find('.ascribe-accordion-list-item .ascribe-accordion-list-item-edition-widget'));
// Wait for editions to load // Wait for editions to load
actions.waitForElementToShow('.ascribe-accordion-list-item-table', TIMEOUTS.LONG); actions.waitForElementToShow('.ascribe-accordion-list-item-table', TIMEOUTS.LONG);
}) });
gemini.suite('Collection placeholder', (collectionPlaceholderSuite) => { gemini.suite('Collection placeholder', (collectionPlaceholderSuite) => {
collectionPlaceholderSuite collectionPlaceholderSuite
@ -133,8 +135,9 @@ gemini.suite('Authenticated', (suite) => {
.capture('piece list toolbar') .capture('piece list toolbar')
.capture('piece list toolbar search filled', (actions, find) => { .capture('piece list toolbar search filled', (actions, find) => {
actions.sendKeys(find('.ascribe-piece-list-toolbar .search-bar input[type="text"]'), 'search text'); actions.sendKeys(find('.ascribe-piece-list-toolbar .search-bar input[type="text"]'), 'search text');
actions.waitForElementToShow('.ascribe-piece-list-toolbar .search-bar .icon-ascribe-search', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-piece-list-toolbar .search-bar .icon-ascribe-search',
}) TIMEOUTS.NORMAL);
});
gemini.suite('Order widget dropdown', (pieceListToolbarOrderWidgetSuite) => { gemini.suite('Order widget dropdown', (pieceListToolbarOrderWidgetSuite) => {
pieceListToolbarOrderWidgetSuite pieceListToolbarOrderWidgetSuite
@ -164,13 +167,14 @@ gemini.suite('Authenticated', (suite) => {
gemini.suite('Register work', (registerSuite) => { gemini.suite('Register work', (registerSuite) => {
registerSuite registerSuite
.setUrl('/register_piece') .setUrl('/register_piece')
.before((actions, find) => { .before((actions) => {
// The editions options are only rendered after the whitelabel is fetched, so // The editions options are only rendered after the whitelabel is fetched, so
// we have to wait for it here // we have to wait for it here
// We have to check for the sibling checkbox class as the input itself is hidden // We have to check for the sibling checkbox class as the input itself is hidden
actions.waitForElementToShow('.ascribe-form input[name="num_editions-checkbox"] ~ .checkbox', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-form input[name="num_editions-checkbox"] ~ .checkbox',
TIMEOUTS.NORMAL);
}) })
.capture('register work', (actions, find) => { .capture('register work', (actions) => {
// The uploader options are only rendered after the user is fetched, so // The uploader options are only rendered after the user is fetched, so
// we also have to wait for it here // we also have to wait for it here
actions.waitForElementToShow('.file-drag-and-drop-dialog .present-options', TIMEOUTS.NORMAL); actions.waitForElementToShow('.file-drag-and-drop-dialog .present-options', TIMEOUTS.NORMAL);
@ -203,20 +207,21 @@ gemini.suite('Authenticated', (suite) => {
gemini.suite('User settings', (userSettingsSuite) => { gemini.suite('User settings', (userSettingsSuite) => {
userSettingsSuite userSettingsSuite
.setUrl('/settings') .setUrl('/settings')
.before((actions, find) => { .before((actions) => {
// This will be called before every nested suite begins unless that suite // This will be called before every nested suite begins unless that suite
// also defines a `.before()` // also defines a `.before()`
actions.waitForElementToShow('.settings-container', TIMEOUTS.NORMAL); actions.waitForElementToShow('.settings-container', TIMEOUTS.NORMAL);
}) })
.capture('user settings'); .capture('user settings');
}); });
});
// Suite just to log out after suites have run // Suite just to log out after suites have run
gemini.suite('Log out', (logoutSuite) => { gemini.suite('Log out', (logoutSuite) => {
logoutSuite logoutSuite
.setUrl('/logout') .setUrl('/logout')
.ignoreElements('.ascribe-body') .ignoreElements('.ascribe-body')
.capture('logout', (actions, find) => { .capture('logout', (actions) => {
actions.waitForElementToShow('.ascribe-login-wrapper', TIMEOUTS.LONG); actions.waitForElementToShow('.ascribe-login-wrapper', TIMEOUTS.LONG);
}); });
}); });

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const gemini = require('gemini');
const environment = require('../environment'); const environment = require('../environment');
const MAIN_USER = environment.MAIN_USER; const MAIN_USER = environment.MAIN_USER;
const TIMEOUTS = environment.TIMEOUTS; const TIMEOUTS = environment.TIMEOUTS;
@ -12,7 +11,7 @@ gemini.suite('Basic', (suite) => {
suite suite
.setUrl('/login') .setUrl('/login')
.setCaptureElements('.ascribe-body') .setCaptureElements('.ascribe-body')
.before((actions, find) => { .before((actions) => {
// This will be called before every nested suite begins unless that suite // This will be called before every nested suite begins unless that suite
// also defines a `.before()` // also defines a `.before()`
actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL);
@ -22,7 +21,7 @@ gemini.suite('Basic', (suite) => {
headerSuite headerSuite
.setCaptureElements('nav.navbar .container') .setCaptureElements('nav.navbar .container')
.skip(/Mobile/) .skip(/Mobile/)
.capture('desktop header', (actions, find) => { .capture('desktop header', (actions) => {
actions.waitForElementToShow('nav.navbar .container', TIMEOUTS.NORMAL); actions.waitForElementToShow('nav.navbar .container', TIMEOUTS.NORMAL);
}) })
.capture('hover on active item', (actions, find) => { .capture('hover on active item', (actions, find) => {
@ -40,7 +39,7 @@ gemini.suite('Basic', (suite) => {
headerMobileSuite headerMobileSuite
.setCaptureElements('nav.navbar .container') .setCaptureElements('nav.navbar .container')
.skip(/Desktop/) .skip(/Desktop/)
.capture('mobile header', (actions, find) => { .capture('mobile header', (actions) => {
actions.waitForElementToShow('nav.navbar .container', TIMEOUTS.NORMAL); actions.waitForElementToShow('nav.navbar .container', TIMEOUTS.NORMAL);
}) })
.capture('expanded mobile header', (actions, find) => { .capture('expanded mobile header', (actions, find) => {
@ -56,7 +55,7 @@ gemini.suite('Basic', (suite) => {
gemini.suite('Footer', (footerSuite) => { gemini.suite('Footer', (footerSuite) => {
footerSuite footerSuite
.setCaptureElements('.ascribe-footer') .setCaptureElements('.ascribe-footer')
.capture('footer', (actions, find) => { .capture('footer', (actions) => {
actions.waitForElementToShow('.ascribe-footer', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-footer', TIMEOUTS.NORMAL);
}) })
.capture('hover on footer item', (actions, find) => { .capture('hover on footer item', (actions, find) => {
@ -64,14 +63,14 @@ gemini.suite('Basic', (suite) => {
actions.mouseMove(footerItem); actions.mouseMove(footerItem);
}) })
.capture('hover on footer social item', (actions, find) => { .capture('hover on footer social item', (actions, find) => {
const footerSocialItem = find('.ascribe-footer a.social') const footerSocialItem = find('.ascribe-footer a.social');
actions.mouseMove(footerSocialItem); actions.mouseMove(footerSocialItem);
}); });
}); });
gemini.suite('Login', (loginSuite) => { gemini.suite('Login', (loginSuite) => {
loginSuite loginSuite
.capture('login', (actions, find) => { .capture('login', (actions) => {
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
}) })
.capture('hover on login submit', (actions, find) => { .capture('hover on login submit', (actions, find) => {
@ -97,7 +96,7 @@ gemini.suite('Basic', (suite) => {
gemini.suite('Sign up', (signUpSuite) => { gemini.suite('Sign up', (signUpSuite) => {
signUpSuite signUpSuite
.setUrl('/signup') .setUrl('/signup')
.capture('sign up', (actions, find) => { .capture('sign up', (actions) => {
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
}) })
.capture('sign up form filled with focus', (actions, find) => { .capture('sign up form filled with focus', (actions, find) => {
@ -113,7 +112,7 @@ gemini.suite('Basic', (suite) => {
gemini.suite('Password reset', (passwordResetSuite) => { gemini.suite('Password reset', (passwordResetSuite) => {
passwordResetSuite passwordResetSuite
.setUrl('/password_reset') .setUrl('/password_reset')
.capture('password reset', (actions, find) => { .capture('password reset', (actions) => {
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
}) })
.capture('password reset form filled with focus', (actions, find) => { .capture('password reset form filled with focus', (actions, find) => {
@ -127,12 +126,13 @@ gemini.suite('Basic', (suite) => {
gemini.suite('Coa verify', (coaVerifySuite) => { gemini.suite('Coa verify', (coaVerifySuite) => {
coaVerifySuite coaVerifySuite
.setUrl('/coa_verify') .setUrl('/coa_verify')
.capture('coa verify', (actions, find) => { .capture('coa verify', (actions) => {
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
}) })
.capture('coa verify form filled with focus', (actions, find) => { .capture('coa verify form filled with focus', (actions, find) => {
actions.sendKeys(find('.ascribe-form input[name="message"]'), 'sample text'); actions.sendKeys(find('.ascribe-form input[name="message"]'), 'sample text');
actions.sendKeys(find('.ascribe-form .ascribe-property-wrapper:nth-of-type(2) textarea'), 'sample signature'); actions.sendKeys(find('.ascribe-form .ascribe-property-wrapper:nth-of-type(2) textarea'),
'sample signature');
}) })
.capture('coa verify form filled', (actions, find) => { .capture('coa verify form filled', (actions, find) => {
actions.click(find('.ascribe-login-header')); actions.click(find('.ascribe-login-header'));

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const gemini = require('gemini');
const environment = require('../environment'); const environment = require('../environment');
const MAIN_USER = environment.MAIN_USER; const MAIN_USER = environment.MAIN_USER;
const TIMEOUTS = environment.TIMEOUTS; const TIMEOUTS = environment.TIMEOUTS;
@ -13,24 +12,24 @@ const editionUrl = `/editions/${environment.MAIN_EDITION_ID}`;
* Tests include accessing the piece / edition as the owner or as another user * Tests include accessing the piece / edition as the owner or as another user
* (we can just use an anonymous user in this case). * (we can just use an anonymous user in this case).
*/ */
gemini.suite('Work detail', (suite) => { gemini.suite('Basic work detail', (suite) => {
suite suite
.setCaptureElements('.ascribe-body') .setCaptureElements('.ascribe-body')
.before((actions, find) => { .before((actions) => {
// This will be called before every nested suite begins unless that suite
// also defines a `.before()`
actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL);
// Wait for the social media buttons to appear // Wait for the social media buttons to appear
actions.waitForElementToShow('.ascribe-social-button-list .fb-share-button iframe', TIMEOUTS.SUPER_DUPER_EXTRA_LONG); actions.waitForElementToShow('.ascribe-social-button-list .fb-share-button iframe',
actions.waitForElementToShow('.ascribe-social-button-list .twitter-share-button', TIMEOUTS.SUPER_DUPER_EXTRA_LONG); TIMEOUTS.SUPER_DUPER_EXTRA_LONG);
actions.waitForElementToShow('.ascribe-social-button-list .twitter-share-button',
TIMEOUTS.SUPER_DUPER_EXTRA_LONG);
actions.waitForElementToShow('.ascribe-media-player', TIMEOUTS.LONG); actions.waitForElementToShow('.ascribe-media-player', TIMEOUTS.LONG);
}); });
gemini.suite('Basic piece', (basicPieceSuite) => { gemini.suite('Basic piece', (basicPieceSuite) => {
basicPieceSuite basicPieceSuite
.setUrl(pieceUrl) .setUrl(pieceUrl)
.capture('basic piece') .capture('basic piece');
gemini.suite('Shmui', (shmuiSuite) => { gemini.suite('Shmui', (shmuiSuite) => {
shmuiSuite. shmuiSuite.
@ -49,25 +48,42 @@ gemini.suite('Work detail', (suite) => {
.setUrl(editionUrl) .setUrl(editionUrl)
.capture('basic edition'); .capture('basic edition');
}); });
});
gemini.suite('Authenticated work detail', (suite) => {
suite
.setCaptureElements('.ascribe-body')
.before((actions) => {
actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL);
});
// Suite just to log us in before any other suites run
gemini.suite('Login', (loginSuite) => { gemini.suite('Login', (loginSuite) => {
loginSuite loginSuite
.setUrl('/login') .setUrl('/login')
.ignoreElements('.ascribe-body') .ignoreElements('.ascribe-body')
.before((actions, find) => {
actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL);
})
.capture('logged in', (actions, find) => { .capture('logged in', (actions, find) => {
console.log('logging in');
actions.sendKeys(find('.ascribe-login-wrapper input[name=email]'), MAIN_USER.email); actions.sendKeys(find('.ascribe-login-wrapper input[name=email]'), MAIN_USER.email);
actions.sendKeys(find('.ascribe-login-wrapper input[name=password]'), MAIN_USER.password); actions.sendKeys(find('.ascribe-login-wrapper input[name=password]'), MAIN_USER.password);
actions.click(find('.ascribe-login-wrapper button[type=submit]')); actions.click(find('.ascribe-login-wrapper button[type=submit]'));
actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-accordion-list:not(.ascribe-loading-position)', TIMEOUTS.LONG);
}); });
}); });
gemini.suite('Authenticated', (authenticatedSuite) => {
authenticatedSuite
.before((actions) => {
// Wait for the social media buttons to appear
actions.waitForElementToShow('.ascribe-social-button-list .fb-share-button iframe',
TIMEOUTS.SUPER_DUPER_EXTRA_LONG);
actions.waitForElementToShow('.ascribe-social-button-list .twitter-share-button',
TIMEOUTS.SUPER_DUPER_EXTRA_LONG);
actions.waitForElementToShow('.ascribe-media-player', TIMEOUTS.LONG);
});
gemini.suite('Authorized piece', (authorizedPieceSuite) => { gemini.suite('Authorized piece', (authorizedPieceSuite) => {
console.log('authorized piece');
authorizedPieceSuite authorizedPieceSuite
.setUrl(pieceUrl) .setUrl(pieceUrl)
.capture('authorized piece'); .capture('authorized piece');
@ -76,13 +92,14 @@ gemini.suite('Work detail', (suite) => {
gemini.suite('Authorized edition', (authorizedEditionSuite) => { gemini.suite('Authorized edition', (authorizedEditionSuite) => {
authorizedEditionSuite authorizedEditionSuite
.setUrl(editionUrl) .setUrl(editionUrl)
.capture('authorized edition') .capture('authorized edition');
}); });
gemini.suite('Detail action buttons', (detailActionButtonSuite) => { gemini.suite('Detail action buttons', (detailActionButtonSuite) => {
detailActionButtonSuite detailActionButtonSuite
.setUrl(editionUrl) .setUrl(editionUrl)
.capture('hover on action button', (actions, find) => { .capture('hover on action button', (actions, find) => {
console.log('hover on action button');
actions.mouseMove(find('.ascribe-detail-property .ascribe-button-list button.btn-default')); actions.mouseMove(find('.ascribe-detail-property .ascribe-button-list button.btn-default'));
}) })
.capture('hover on delete button', (actions, find) => { .capture('hover on delete button', (actions, find) => {
@ -102,13 +119,19 @@ gemini.suite('Work detail', (suite) => {
.setCaptureElements('.modal-dialog') .setCaptureElements('.modal-dialog')
.capture('open email form', (actions, find) => { .capture('open email form', (actions, find) => {
// Add class names to make the action buttons easier to select // Add class names to make the action buttons easier to select
actions.executeJS(function (window) { // eslint-disable-next-line prefer-arrow-callback
var actionButtons = window.document.querySelectorAll('.ascribe-detail-property .ascribe-button-list button.btn-default'); actions.executeJS(function addButtonTypeAsClass(window) {
for (var ii = 0; ii < actionButtons.length; ++ii) { /* eslint-disable no-var, prefer-template */
var actionButtonsSelector = '.ascribe-detail-property .ascribe-button-list button.btn-default';
var actionButtons = window.document.querySelectorAll(actionButtonsSelector);
var ii = 0;
for (; ii < actionButtons.length; ++ii) {
if (actionButtons[ii].textContent) { if (actionButtons[ii].textContent) {
actionButtons[ii].className += ' ascribe-action-button-' + actionButtons[ii].textContent.toLowerCase(); actionButtons[ii].className += ' ascribe-action-button-' +
actionButtons[ii].textContent.toLowerCase();
} }
} }
/* eslint-enable no-var */
}); });
actions.click(find('.ascribe-detail-property .ascribe-button-list button.ascribe-action-button-email')); actions.click(find('.ascribe-detail-property .ascribe-button-list button.ascribe-action-button-email'));
@ -116,16 +139,18 @@ gemini.suite('Work detail', (suite) => {
actions.wait(1000); actions.wait(1000);
}); });
}); });
});
// Suite just to log out after suites have run // Suite just to log out after suites have run
gemini.suite('Log out', (logoutSuite) => { gemini.suite('Log out', (logoutSuite) => {
logoutSuite logoutSuite
.setUrl('/logout') .setUrl('/logout')
.ignoreElements('.ascribe-body') .ignoreElements('.ascribe-body')
.before((actions, find) => { .before((actions) => {
console.log('before log out');
actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL);
}) })
.capture('logout', (actions, find) => { .capture('logout', (actions) => {
actions.waitForElementToShow('.ascribe-login-wrapper', TIMEOUTS.LONG); actions.waitForElementToShow('.ascribe-login-wrapper', TIMEOUTS.LONG);
}); });
}); });

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const gemini = require('gemini');
const environment = require('../../environment'); const environment = require('../../environment');
const TIMEOUTS = environment.TIMEOUTS; const TIMEOUTS = environment.TIMEOUTS;
@ -10,7 +9,7 @@ const TIMEOUTS = environment.TIMEOUTS;
gemini.suite('23vivi', (suite) => { gemini.suite('23vivi', (suite) => {
suite suite
.setCaptureElements('.ascribe-body') .setCaptureElements('.ascribe-body')
.before((actions, find) => { .before((actions) => {
// This will be called before every nested suite begins // This will be called before every nested suite begins
actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL);
}); });
@ -18,7 +17,7 @@ gemini.suite('23vivi', (suite) => {
gemini.suite('Landing', (landingSuite) => { gemini.suite('Landing', (landingSuite) => {
landingSuite landingSuite
.setUrl('/') .setUrl('/')
.capture('landing', (actions, find) => { .capture('landing', (actions) => {
// Wait for the logo to appear // Wait for the logo to appear
actions.waitForElementToShow('.vivi23-landing--header-logo', TIMEOUTS.LONG); actions.waitForElementToShow('.vivi23-landing--header-logo', TIMEOUTS.LONG);
}); });

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const gemini = require('gemini');
const environment = require('../../environment'); const environment = require('../../environment');
const TIMEOUTS = environment.TIMEOUTS; const TIMEOUTS = environment.TIMEOUTS;
@ -10,7 +9,7 @@ const TIMEOUTS = environment.TIMEOUTS;
gemini.suite('Cyland', (suite) => { gemini.suite('Cyland', (suite) => {
suite suite
.setCaptureElements('.ascribe-body') .setCaptureElements('.ascribe-body')
.before((actions, find) => { .before((actions) => {
// This will be called before every nested suite begins // This will be called before every nested suite begins
actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL);
}); });
@ -20,7 +19,7 @@ gemini.suite('Cyland', (suite) => {
.setUrl('/') .setUrl('/')
// Ignore Cyland's logo as it's a gif // Ignore Cyland's logo as it's a gif
.ignoreElements('.cyland-landing img') .ignoreElements('.cyland-landing img')
.capture('landing', (actions, find) => { .capture('landing', (actions) => {
actions.waitForElementToShow('.cyland-landing img', TIMEOUTS.LONG); actions.waitForElementToShow('.cyland-landing img', TIMEOUTS.LONG);
}); });
}); });

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const gemini = require('gemini');
const environment = require('../../environment'); const environment = require('../../environment');
const MAIN_USER = environment.MAIN_USER; const MAIN_USER = environment.MAIN_USER;
const TIMEOUTS = environment.TIMEOUTS; const TIMEOUTS = environment.TIMEOUTS;
@ -11,7 +10,7 @@ const TIMEOUTS = environment.TIMEOUTS;
gemini.suite('Ikonotv', (suite) => { gemini.suite('Ikonotv', (suite) => {
suite suite
.setCaptureElements('.ascribe-body') .setCaptureElements('.ascribe-body')
.before((actions, find) => { .before((actions) => {
// This will be called before every nested suite begins // This will be called before every nested suite begins
actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL);
}); });
@ -22,9 +21,11 @@ gemini.suite('Ikonotv', (suite) => {
// Gemini complains if we try to capture the entire app for Ikonotv's landing page for some reason // Gemini complains if we try to capture the entire app for Ikonotv's landing page for some reason
.setCaptureElements('.ikonotv-landing') .setCaptureElements('.ikonotv-landing')
.setTolerance(5) .setTolerance(5)
.capture('landing', (actions, find) => { .capture('landing', (actions) => {
// Stop background animation // Stop background animation
actions.executeJS(function (window) { // eslint-disable-next-line prefer-arrow-callback
actions.executeJS(function removeBackgroundAnimation(window) {
/* eslint-disable no-var */
var landingBackground = window.document.querySelector('.client--ikonotv .route--landing'); var landingBackground = window.document.querySelector('.client--ikonotv .route--landing');
landingBackground.style.animation = 'none'; landingBackground.style.animation = 'none';
landingBackground.style.webkitAnimation = 'none'; landingBackground.style.webkitAnimation = 'none';
@ -37,10 +38,10 @@ gemini.suite('Ikonotv', (suite) => {
// Ikono needs its own set of tests for some pre-authorization pages to wait for // Ikono needs its own set of tests for some pre-authorization pages to wait for
// its logo to appear // its logo to appear
gemini.suite('Ikonotv basic', (suite) => { gemini.suite('Ikonotv basic', (basicSuite) => {
suite basicSuite
.setCaptureElements('.ascribe-app') .setCaptureElements('.ascribe-app')
.before((actions, find) => { .before((actions) => {
// This will be called before every nested suite begins unless that suite // This will be called before every nested suite begins unless that suite
// also defines a `.before()` // also defines a `.before()`
actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL);

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const gemini = require('gemini');
const environment = require('../../environment'); const environment = require('../../environment');
const TIMEOUTS = environment.TIMEOUTS; const TIMEOUTS = environment.TIMEOUTS;
@ -10,7 +9,7 @@ const TIMEOUTS = environment.TIMEOUTS;
gemini.suite('Lumenus', (suite) => { gemini.suite('Lumenus', (suite) => {
suite suite
.setCaptureElements('.ascribe-body') .setCaptureElements('.ascribe-body')
.before((actions, find) => { .before((actions) => {
// This will be called before every nested suite begins // This will be called before every nested suite begins
actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-app', TIMEOUTS.NORMAL);
}); });
@ -18,7 +17,7 @@ gemini.suite('Lumenus', (suite) => {
gemini.suite('Landing', (landingSuite) => { gemini.suite('Landing', (landingSuite) => {
landingSuite landingSuite
.setUrl('/') .setUrl('/')
.capture('landing', (actions, find) => { .capture('landing', (actions) => {
// Wait for the logo to appear // Wait for the logo to appear
actions.waitForElementToShow('.wp-landing-wrapper img', TIMEOUTS.LONG); actions.waitForElementToShow('.wp-landing-wrapper img', TIMEOUTS.LONG);
}); });

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const gemini = require('gemini');
const environment = require('../../environment'); const environment = require('../../environment');
const MAIN_USER = environment.MAIN_USER; const MAIN_USER = environment.MAIN_USER;
const TIMEOUTS = environment.TIMEOUTS; const TIMEOUTS = environment.TIMEOUTS;
@ -11,7 +10,7 @@ const TIMEOUTS = environment.TIMEOUTS;
gemini.suite('Whitelabel basic', (suite) => { gemini.suite('Whitelabel basic', (suite) => {
suite suite
.setCaptureElements('.ascribe-wallet-app > .container') .setCaptureElements('.ascribe-wallet-app > .container')
.before((actions, find) => { .before((actions) => {
// This will be called before every nested suite begins unless that suite // This will be called before every nested suite begins unless that suite
// also defines a `.before()` // also defines a `.before()`
// FIXME: use a more generic class for this, like just '.ascribe-app' // FIXME: use a more generic class for this, like just '.ascribe-app'
@ -26,7 +25,7 @@ gemini.suite('Whitelabel basic', (suite) => {
.setUrl('/login') .setUrl('/login')
// See Ikono // See Ikono
.skip(/Ikono/) .skip(/Ikono/)
.capture('login', (actions, find) => { .capture('login', (actions) => {
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
// For some reason, the screenshots seem to keep catching the whitelabel login form // For some reason, the screenshots seem to keep catching the whitelabel login form
// on a refresh and without fonts loaded (maybe because they're the first tests run // on a refresh and without fonts loaded (maybe because they're the first tests run
@ -59,7 +58,7 @@ gemini.suite('Whitelabel basic', (suite) => {
.setUrl('/signup') .setUrl('/signup')
// See Ikono // See Ikono
.skip(/Ikono/) .skip(/Ikono/)
.capture('sign up', (actions, find) => { .capture('sign up', (actions) => {
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
// Wait in case the form reloads due to other assets loading // Wait in case the form reloads due to other assets loading
actions.wait(500); actions.wait(500);
@ -77,7 +76,7 @@ gemini.suite('Whitelabel basic', (suite) => {
gemini.suite('Password reset', (passwordResetSuite) => { gemini.suite('Password reset', (passwordResetSuite) => {
passwordResetSuite passwordResetSuite
.setUrl('/password_reset') .setUrl('/password_reset')
.capture('password reset', (actions, find) => { .capture('password reset', (actions) => {
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
// Wait in case the form reloads due to other assets loading // Wait in case the form reloads due to other assets loading
actions.wait(500); actions.wait(500);
@ -93,14 +92,15 @@ gemini.suite('Whitelabel basic', (suite) => {
gemini.suite('Coa verify', (coaVerifySuite) => { gemini.suite('Coa verify', (coaVerifySuite) => {
coaVerifySuite coaVerifySuite
.setUrl('/coa_verify') .setUrl('/coa_verify')
.capture('coa verify', (actions, find) => { .capture('coa verify', (actions) => {
actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL); actions.waitForElementToShow('.ascribe-form', TIMEOUTS.NORMAL);
// Wait in case the form reloads due to other assets loading // Wait in case the form reloads due to other assets loading
actions.wait(500); actions.wait(500);
}) })
.capture('coa verify form filled with focus', (actions, find) => { .capture('coa verify form filled with focus', (actions, find) => {
actions.sendKeys(find('.ascribe-form input[name="message"]'), 'sample text'); actions.sendKeys(find('.ascribe-form input[name="message"]'), 'sample text');
actions.sendKeys(find('.ascribe-form .ascribe-property-wrapper:nth-of-type(2) textarea'), 'sample signature'); actions.sendKeys(find('.ascribe-form .ascribe-property-wrapper:nth-of-type(2) textarea'),
'sample signature');
}) })
.capture('coa verify form filled', (actions, find) => { .capture('coa verify form filled', (actions, find) => {
actions.click(find('.ascribe-login-header')); actions.click(find('.ascribe-login-header'));

View File

@ -0,0 +1,9 @@
{
"env": {
"mocha": true
},
"rules": {
"max-len": [2, { "code": 125 }],
"prefer-arrow-callback": [0]
}
}

View File

@ -1,10 +1,10 @@
# TL;DR # TL;DR
Copy the contents of `.env-template` to `.env` and [fill up the missing keys with Copy the contents of `.env_template` to `.env` and [fill up the missing keys with
information from your SauceLabs account](#how-to-set-up-your-env-config-file). information from your SauceLabs account](#how-to-set-up-your-env-config-file).
```bash ```bash
$ npm install $ npm install
$ npm run tunnel $ npm run sauce-tunnel
$ npm test && git commit $ npm test && git commit
``` ```
@ -99,7 +99,7 @@ On the JavaScript side, we use:
## How to set up your `.env` config file ## How to set up your `.env` config file
In the root of this repository there is a file called `.env-template`. Create a In the root of this repository there is a file called `.env_template`. Create a
copy and call it `.env`. This file will store some values we need to connect to copy and call it `.env`. This file will store some values we need to connect to
Saucelabs. Saucelabs.

View File

@ -1,20 +1,20 @@
'use strict'; 'use strict';
const config = require('./config');
const colors = require('colors'); const colors = require('colors');
const sauceConnectLauncher = require('sauce-connect-launcher'); const sauceConnectLauncher = require('sauce-connect-launcher');
const config = require('./config');
let globalSauceProcess; let globalSauceProcess;
if (!process.env.SAUCE_USERNAME) { if (!process.env.SAUCE_USERNAME) {
console.log(colors.red('SAUCE_USERNAME is missing. Please check the README.md file.')); console.log(colors.red('SAUCE_USERNAME is missing. Please check the README.md file.'));
process.exit(1); //eslint-disable-line no-process-exit process.exit(1);
} }
if (!process.env.SAUCE_ACCESS_KEY) { if (!process.env.SAUCE_ACCESS_KEY) {
console.log(colors.red('SAUCE_ACCESS_KEY is missing. Please check the README.md file.')); console.log(colors.red('SAUCE_ACCESS_KEY is missing. Please check the README.md file.'));
process.exit(1); //eslint-disable-line no-process-exit process.exit(1);
} }

View File

@ -40,9 +40,9 @@ function testSuite(browserName, version, platform) {
}); });
it('should contain "Log in" in the title', function () { it('should contain "Log in" in the title', function () {
return browser. return browser
waitForElementByCss('.ascribe-login-wrapper', asserters.isDisplayed, 2000) .waitForElementByCss('.ascribe-login-wrapper', asserters.isDisplayed, 2000)
title().should.become('Log in'); .title().should.become('Log in');
}); });
}); });
} }

View File

@ -1,15 +1,17 @@
'use strict'; 'use strict';
const config = require('./config'); //eslint-disable-line no-unused-vars
const colors = require('colors'); const colors = require('colors');
const sauceConnectLauncher = require('sauce-connect-launcher'); const sauceConnectLauncher = require('sauce-connect-launcher');
const config = require('./config'); // eslint-disable-line no-unused-vars
function connect() { function connect() {
console.log(colors.yellow('Setting up tunnel from Saucelabs to your lovely computer, will take a while.')); console.log(
colors.yellow('Setting up tunnel from Saucelabs to your lovely computer, will take a while.')
);
// Creating the tunnel takes a bit of time. For this case we can safely disable Mocha timeouts. // Creating the tunnel takes a bit of time. For this case we can safely disable Mocha timeouts.
sauceConnectLauncher(function (err) { sauceConnectLauncher((err) => {
if (err) { if (err) {
console.error(err.message); console.error(err.message);
return; return;

223
webpack.config.js Normal file
View File

@ -0,0 +1,223 @@
/* eslint-disable strict, no-console, object-shorthand, prefer-template */
/* eslint-disable import/no-extraneous-dependencies, import/newline-after-import */
'use strict';
const path = require('path');
const removeTrailingSlash = require('remove-trailing-slash');
const webpack = require('webpack');
const autoPrefixer = require('autoprefixer');
const combineLoaders = require('webpack-combine-loaders');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
require('dotenv').load({ silent: true });
const EXTRACT = process.env.NODE_ENV === 'extract';
const PRODUCTION = process.env.NODE_ENV === 'production';
const PATHS = {
APP: path.resolve(__dirname, 'js/app.js'),
BUILD: path.resolve(__dirname, 'build'),
DIST: path.resolve(__dirname, 'dist'),
NODE_MODULES: path.resolve(__dirname, 'node_modules'),
};
/** EXTERNAL DEFINITIONS INJECTED INTO APP **/
const DEFINITIONS = {
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
APP_VERSION: JSON.stringify(process.env.ONION_APP_VERSION || 'dev'),
API_URL: JSON.stringify(
removeTrailingSlash(process.env.ONION_API_URL || 'https://staging.ascribe.io/api')
),
APP_BASE_PATH: JSON.stringify(
removeTrailingSlash(process.env.ONION_BASE_PATH || '')
),
SERVER_URL: JSON.stringify(
removeTrailingSlash(process.env.ONION_SERVER_URL || 'https://staging.ascribe.io')
),
RAVEN_DSN_URL: JSON.stringify(process.env.RAVEN_DSN_URL || ''),
S3_ACCESS_KEY: JSON.stringify(process.env.S3_ACCESS_KEY || ''),
},
};
/** PLUGINS **/
const PLUGINS = [
new webpack.DefinePlugin(DEFINITIONS),
new webpack.NoErrorsPlugin(),
// Handle any dependencies that don't play nicely with System.import resolution
new CopyWebpackPlugin([
{
from: path.resolve(PATHS.NODE_MODULES, 'audiojs/audiojs'),
to: 'third_party/audiojs'
},
]),
// Generate index.html for app with link and style tags addded
new HtmlWebpackPlugin({
filename: 'index.html',
minify: PRODUCTION ? {
collapseWhitespace: true,
minifyJS: true,
removeComments: true,
removeRedundantAttributes: true
} : false,
template: path.resolve(__dirname, 'index_template.html'),
}),
];
const PROD_PLUGINS = [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
output: {
comments: false
}
}),
new webpack.LoaderOptionsPlugin({
debug: false,
minimize: true
}),
];
if (PRODUCTION || EXTRACT) {
// Extract stylesheets out of bundle
PLUGINS.push(
new ExtractTextPlugin(PRODUCTION ? 'css/styles.min.css' : 'css/styles.css', {
allChunks: true
})
);
}
if (PRODUCTION) {
PLUGINS.push(...PROD_PLUGINS);
}
/** LOADERS **/
const JS_LOADER = combineLoaders([
{
loader: 'babel',
query: {
cacheDirectory: true,
},
},
]);
const CSS_LOADER = combineLoaders([
{
loader: 'css',
query: {
sourceMap: true,
},
},
{ loader: 'postcss' },
]);
const SASS_LOADER = `${CSS_LOADER}!` + combineLoaders([
{
loader: 'sass',
query: {
precision: '8', // See https://github.com/twbs/bootstrap-sass#sass-number-precision
outputStyle: 'expanded',
sourceMap: true,
},
}
]);
const URL_FONT_LOADER = combineLoaders([
{
loader: 'url',
query: {
limit: 10000,
name: 'fonts/[name].[ext]',
},
},
]);
const FILE_FONT_LOADER = combineLoaders([
{
loader: 'file',
query: {
name: 'fonts/[name].[ext]',
},
},
]);
const LOADERS = [
{
test: /\.jsx?$/,
exclude: [PATHS.NODE_MODULES],
loader: JS_LOADER,
},
{
test: /\.s[ac]ss$/,
exclude: [PATHS.NODE_MODULES],
loader: PRODUCTION || EXTRACT ? ExtractTextPlugin.extract('style', SASS_LOADER)
: `style!${SASS_LOADER}`,
},
// Dependencies
// Shmui
{
test: /\.css$/,
include: [path.resolve(PATHS.NODE_MODULES, 'shmui')],
loader: `style!${CSS_LOADER}`,
},
// Fonts
// woffs and svgs are typically smaller should we can try to load them as a url
{
test: /\.(woff2?|svg)$/,
loader: URL_FONT_LOADER,
},
{
test: /\.(ttf|eot)$/,
loader: FILE_FONT_LOADER,
},
];
/** EXPORTED WEBPACK CONFIG **/
const config = {
entry: [
PRODUCTION || EXTRACT ? 'bootstrap-loader/extractStyles' : 'bootstrap-loader',
PATHS.APP
],
output: {
filename: PRODUCTION ? 'js/bundle.min.js' : 'js/bundle.js',
path: PRODUCTION ? PATHS.DIST : PATHS.BUILD,
publicPath: '/static/',
},
debug: !PRODUCTION,
devtool: PRODUCTION ? '#source-map' : '#inline-source-map',
resolve: {
extensions: ['', '.js', '.jsx'],
modules: ['node_modules'], // Don't use absolute path here to allow recursive matching
},
plugins: PLUGINS,
module: {
loaders: LOADERS,
},
postcss: [autoPrefixer()],
};
module.exports = config;