diff --git a/Dockerfile b/Dockerfile
index dd695256..cf6389fa 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,6 +5,7 @@ ENV LANG=C.UTF-8
RUN apt-get update && \
apt-get install -y build-essential libxml2-dev python3-dev python3-pip zlib1g-dev python3-requests python3-aiohttp && \
+ python3 -m pip install --upgrade pip && \
pip3 install cellxgene
ENTRYPOINT ["cellxgene"]
diff --git a/client/configuration/eslint/eslint.js b/client/configuration/eslint/eslint.js
index 66637c26..21fbfe8d 100644
--- a/client/configuration/eslint/eslint.js
+++ b/client/configuration/eslint/eslint.js
@@ -64,6 +64,12 @@ module.exports = {
"LabeledStatement",
"WithStatement",
],
+ "import/no-extraneous-dependencies": [
+ "error",
+ {
+ devDependencies: true,
+ },
+ ],
},
overrides: [
{
diff --git a/client/configuration/webpack/obsoleteHTMLTemplate.html b/client/configuration/webpack/obsoleteHTMLTemplate.html
index 500c7db5..ddb2dd27 100644
--- a/client/configuration/webpack/obsoleteHTMLTemplate.html
+++ b/client/configuration/webpack/obsoleteHTMLTemplate.html
@@ -75,7 +75,7 @@
src="https://cellxgene.cziscience.com/s3/cellxgene/static/images/edge.png"
style="width: 80px; height: 80px;"
/>
-
Edge ≥ 15
+ Edge ≥ 79
diff --git a/client/configuration/webpack/webpack.config.dev.js b/client/configuration/webpack/webpack.config.dev.js
index e0881c0a..1295d6dc 100644
--- a/client/configuration/webpack/webpack.config.dev.js
+++ b/client/configuration/webpack/webpack.config.dev.js
@@ -31,7 +31,11 @@ const devConfig = {
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2|otf)$/i,
loader: "file-loader",
include: [nodeModules, fonts],
- query: { name: "static/assets/[name].[ext]" },
+ query: {
+ name: "static/assets/[name].[ext]",
+ // (thuang): This is needed to make sure @font url path is '/static/assets/'
+ publicPath: "/",
+ },
},
],
},
diff --git a/client/configuration/webpack/webpack.config.prod.js b/client/configuration/webpack/webpack.config.prod.js
index a2115e7f..c92e307f 100644
--- a/client/configuration/webpack/webpack.config.prod.js
+++ b/client/configuration/webpack/webpack.config.prod.js
@@ -45,7 +45,11 @@ const prodConfig = {
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2|otf)$/i,
loader: "file-loader",
include: [nodeModules, fonts],
- query: { name: "static/assets/[name]-[contenthash].[ext]" },
+ query: {
+ name: "static/assets/[name]-[contenthash].[ext]",
+ // (thuang): This is needed to make sure @font url path is '../static/assets/'
+ publicPath: "static/",
+ },
},
],
},
diff --git a/client/configuration/webpack/webpack.config.shared.js b/client/configuration/webpack/webpack.config.shared.js
index 2a8fcfdd..649844a9 100644
--- a/client/configuration/webpack/webpack.config.shared.js
+++ b/client/configuration/webpack/webpack.config.shared.js
@@ -7,7 +7,7 @@ const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");
const src = path.resolve("src");
const nodeModules = path.resolve("node_modules");
-const publicPath = "/";
+const publicPath = "";
const rawObsoleteHTMLTemplate = fs.readFileSync(
`${__dirname}/obsoleteHTMLTemplate.html`,
diff --git a/client/package-lock.json b/client/package-lock.json
index 2aa843aa..205d3600 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -13786,9 +13786,9 @@
}
},
"lodash": {
- "version": "4.17.19",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
- "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"lodash._reinterpolate": {
"version": "3.0.0",
diff --git a/client/package.json b/client/package.json
index 9ab152e1..bc7a88b9 100644
--- a/client/package.json
+++ b/client/package.json
@@ -34,7 +34,7 @@
"Safari >= 10.1",
"iOS >= 10.3",
"Firefox >= 60",
- "Edge >= 15",
+ "Edge >= 79",
"not Explorer > 0"
],
"dependencies": {
@@ -52,7 +52,7 @@
"gl-matrix": "^3.3.0",
"gl-vec3": "^1.1.3",
"is-number": "^7.0.0",
- "lodash": "^4.17.19",
+ "lodash": "^4.17.20",
"memoize-one": "^5.1.1",
"react": "^16.13.1",
"react-async": "^10.0.1",
diff --git a/client/server/development.js b/client/server/development.js
index c85b51ff..0b3e3811 100644
--- a/client/server/development.js
+++ b/client/server/development.js
@@ -1,20 +1,19 @@
-/* eslint-disable */
-// jshint esversion: 6
-var path = require("path");
-var historyApiFallback = require("connect-history-api-fallback");
-var chalk = require("chalk");
-var express = require("express");
-var favicon = require("serve-favicon");
-var webpack = require("webpack");
-var config = require("../configuration/webpack/webpack.config.dev");
-var utils = require("./utils");
+const path = require("path");
+const historyApiFallback = require("connect-history-api-fallback");
+const chalk = require("chalk");
+const express = require("express");
+const favicon = require("serve-favicon");
+const webpack = require("webpack");
+const devMiddleware = require("webpack-dev-middleware");
+const config = require("../configuration/webpack/webpack.config.dev");
+const utils = require("./utils");
process.env.NODE_ENV = "development";
const CLIENT_PORT = process.env.CXG_CLIENT_PORT;
// Set up compiler
-var compiler = webpack(config);
+const compiler = webpack(config);
compiler.plugin("invalid", () => {
utils.clearConsole();
@@ -26,12 +25,12 @@ compiler.plugin("done", (stats) => {
});
// Launch server
-var app = express();
+const app = express();
app.use(historyApiFallback({ verbose: false }));
app.use(
- require("webpack-dev-middleware")(compiler, {
+ devMiddleware(compiler, {
logLevel: "warn",
publicPath: config.output.publicPath,
})
diff --git a/client/src/actions/index.js b/client/src/actions/index.js
index a829b45b..4c39f86d 100644
--- a/client/src/actions/index.js
+++ b/client/src/actions/index.js
@@ -41,6 +41,17 @@ async function configFetch(dispatch) {
});
}
+async function userInfoFetch(dispatch) {
+ return fetchJson("userinfo").then((response) => {
+ const userinfo = { ...response.userinfo };
+ dispatch({
+ type: "userinfo load complete",
+ userinfo,
+ });
+ return userinfo;
+ });
+}
+
function prefetchEmbeddings(annoMatrix) {
/*
prefetch requests for all embeddings
@@ -62,6 +73,7 @@ const doInitialDataLoad = () =>
configFetch(dispatch),
schemaFetch(dispatch),
userColorsFetchAndLoad(dispatch),
+ userInfoFetch(dispatch),
]);
const baseDataUrl = `${globals.API.prefix}${globals.API.version}`;
diff --git a/client/src/components/autosave/filenameDialog.js b/client/src/components/autosave/filenameDialog.js
index 9b67dc17..3937ecee 100644
--- a/client/src/components/autosave/filenameDialog.js
+++ b/client/src/components/autosave/filenameDialog.js
@@ -14,6 +14,7 @@ import {
idhash: state.config?.parameters?.["annotations-user-data-idhash"] ?? null,
annotations: state.annotations,
auth: state.config?.authentication,
+ userinfo: state.userinfo,
writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
}))
class FilenameDialog extends React.Component {
@@ -91,13 +92,19 @@ class FilenameDialog extends React.Component {
};
render() {
- const { writableCategoriesEnabled, annotations, idhash, auth } = this.props;
+ const {
+ writableCategoriesEnabled,
+ annotations,
+ idhash,
+ userinfo,
+ } = this.props;
const { filenameText } = this.state;
return writableCategoriesEnabled &&
+ annotations.promptForFilename &&
!annotations.dataCollectionNameIsReadOnly &&
!annotations.dataCollectionName &&
- auth.is_authenticated ? (
+ userinfo.is_authenticated ? (