kopia lustrzana https://github.com/OpenDroneMap/WebODM
Automated task hints based on GPS location of photos
rodzic
c27d0583df
commit
04b873a0b6
|
@ -84,8 +84,7 @@ module.exports = {
|
|||
"PluginsAPI": "PluginsAPI",
|
||||
"leaflet": "leaflet",
|
||||
"ReactDOM": "ReactDOM",
|
||||
"React": "React",
|
||||
"gettext": "gettext"
|
||||
"React": "React"
|
||||
},
|
||||
|
||||
watchOptions: {
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
Route
|
||||
} from 'react-router-dom';
|
||||
import $ from 'jquery';
|
||||
import { _ } from './classes/gettext';
|
||||
|
||||
class Dashboard extends React.Component {
|
||||
constructor(){
|
||||
|
@ -22,7 +23,7 @@ class Dashboard extends React.Component {
|
|||
}
|
||||
|
||||
addNewProject(project){
|
||||
if (!project.name) return (new $.Deferred()).reject("Name field is required");
|
||||
if (!project.name) return (new $.Deferred()).reject(_("Name field is required"));
|
||||
|
||||
return $.ajax({
|
||||
url: `/api/projects/`,
|
||||
|
@ -57,7 +58,7 @@ class Dashboard extends React.Component {
|
|||
className="btn btn-primary btn-sm"
|
||||
onClick={this.handleAddProject}>
|
||||
<i className="glyphicon glyphicon-plus"></i>
|
||||
Add Project
|
||||
{_("Add Project")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -92,7 +93,7 @@ $(function(){
|
|||
// Do nothing
|
||||
}
|
||||
});
|
||||
return found ? "Your changes will be lost. Are you sure you want to leave?" : undefined;
|
||||
return found ? _("Your changes will be lost. Are you sure you want to leave?") : undefined;
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ if (!window.PluginsAPI){
|
|||
'leaflet': { loader: 'globals-loader', exports: 'L' },
|
||||
'ReactDOM': { loader: 'globals-loader', exports: 'ReactDOM' },
|
||||
'React': { loader: 'globals-loader', exports: 'React' },
|
||||
'gettext': { loader: 'globals-loader', exports: 'gettext' },
|
||||
'SystemJS': { loader: 'globals-loader', exports: 'SystemJS' }
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
import React from 'react';
|
||||
import ErrorMessage from './ErrorMessage';
|
||||
import FormDialog from './FormDialog';
|
||||
import PropTypes from 'prop-types';
|
||||
import $ from 'jquery';
|
||||
import { _ } from '../classes/gettext';
|
||||
|
||||
class EditProjectDialog extends React.Component {
|
||||
static defaultProps = {
|
||||
projectName: "",
|
||||
projectDescr: "",
|
||||
title: "New Project",
|
||||
saveLabel: "Create Project",
|
||||
savingLabel: "Creating project...",
|
||||
title: _("New Project"),
|
||||
saveLabel: _("Create Project"),
|
||||
savingLabel: _("Creating project..."),
|
||||
saveIcon: "glyphicon glyphicon-plus",
|
||||
deleteWarning: "All tasks, images and models associated with this project will be permanently deleted. Are you sure you want to continue?",
|
||||
deleteWarning: _("All tasks, images and models associated with this project will be permanently deleted. Are you sure you want to continue?"),
|
||||
show: false
|
||||
};
|
||||
|
||||
|
@ -83,13 +82,13 @@ class EditProjectDialog extends React.Component {
|
|||
onShow={this.onShow}
|
||||
ref={(domNode) => { this.dialog = domNode; }}>
|
||||
<div className="form-group">
|
||||
<label className="col-sm-2 control-label">Name</label>
|
||||
<label className="col-sm-2 control-label">{_("Name")}</label>
|
||||
<div className="col-sm-10">
|
||||
<input type="text" className="form-control" ref={(domNode) => { this.nameInput = domNode; }} value={this.state.name} onChange={this.handleChange('name')} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="col-sm-2 control-label">Description (optional)</label>
|
||||
<label className="col-sm-2 control-label">{_("Description (optional)")}</label>
|
||||
<div className="col-sm-10">
|
||||
<textarea className="form-control" rows="3" value={this.state.descr} onChange={this.handleChange('descr')} />
|
||||
</div>
|
||||
|
|
|
@ -25,20 +25,18 @@ class EditTaskForm extends React.Component {
|
|||
onFormChanged: PropTypes.func,
|
||||
inReview: PropTypes.bool,
|
||||
task: PropTypes.object,
|
||||
suggestedTaskName: PropTypes.string,
|
||||
suggestedTaskName: PropTypes.oneOfType([PropTypes.string, PropTypes.func])
|
||||
};
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
|
||||
this.namePlaceholder = "Task of " + (new Date()).toISOString();
|
||||
|
||||
this.state = {
|
||||
error: "",
|
||||
presetError: "",
|
||||
presetActionPerforming: false,
|
||||
|
||||
name: props.suggestedTaskName ? props.suggestedTaskName : (props.task !== null ? (props.task.name || "") : ""),
|
||||
namePlaceholder: typeof props.suggestedTaskName === "string" ? props.suggestedTaskName : (props.task !== null ? (props.task.name || "") : "Task of " + (new Date()).toISOString()),
|
||||
name: typeof props.suggestedTaskName === "string" ? props.suggestedTaskName : (props.task !== null ? (props.task.name || "") : ""),
|
||||
loadedProcessingNodes: false,
|
||||
loadedPresets: false,
|
||||
|
||||
|
@ -47,7 +45,9 @@ class EditTaskForm extends React.Component {
|
|||
selectedPreset: null,
|
||||
presets: [],
|
||||
|
||||
editingPreset: false
|
||||
editingPreset: false,
|
||||
|
||||
loadingTaskName: false
|
||||
};
|
||||
|
||||
this.handleNameChange = this.handleNameChange.bind(this);
|
||||
|
@ -272,6 +272,23 @@ class EditTaskForm extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
loadSuggestedName = () => {
|
||||
if (typeof this.props.suggestedTaskName === "function"){
|
||||
this.setState({loadingTaskName: true});
|
||||
|
||||
this.props.suggestedTaskName().then(name => {
|
||||
if (this.state.loadingTaskName){
|
||||
this.setState({loadingTaskName: false, name});
|
||||
}else{
|
||||
// User started typing its own name
|
||||
}
|
||||
}).catch(e => {
|
||||
// Do Nothing
|
||||
this.setState({loadingTaskName: false});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleSelectPreset(e){
|
||||
this.selectPresetById(e.target.value);
|
||||
}
|
||||
|
@ -284,6 +301,7 @@ class EditTaskForm extends React.Component {
|
|||
componentDidMount(){
|
||||
this.loadProcessingNodes();
|
||||
this.loadPresets();
|
||||
this.loadSuggestedName();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState){
|
||||
|
@ -304,7 +322,7 @@ class EditTaskForm extends React.Component {
|
|||
}
|
||||
|
||||
handleNameChange(e){
|
||||
this.setState({name: e.target.value});
|
||||
this.setState({name: e.target.value, loadingTaskName: false});
|
||||
}
|
||||
|
||||
selectNodeByKey(key){
|
||||
|
@ -579,10 +597,13 @@ class EditTaskForm extends React.Component {
|
|||
<div className="form-group">
|
||||
<label className="col-sm-2 control-label">Name</label>
|
||||
<div className="col-sm-10">
|
||||
{this.state.loadingTaskName ?
|
||||
<i className="fa fa-circle-notch fa-spin fa-fw name-loading"></i>
|
||||
: ""}
|
||||
<input type="text"
|
||||
onChange={this.handleNameChange}
|
||||
className="form-control"
|
||||
placeholder={this.namePlaceholder}
|
||||
placeholder={this.state.namePlaceholder}
|
||||
value={this.state.name}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -4,6 +4,7 @@ import ErrorMessage from './ErrorMessage';
|
|||
import EditTaskForm from './EditTaskForm';
|
||||
import PropTypes from 'prop-types';
|
||||
import $ from 'jquery';
|
||||
import { _ } from '../classes/gettext';
|
||||
|
||||
class EditTaskPanel extends React.Component {
|
||||
static defaultProps = {
|
||||
|
@ -52,7 +53,7 @@ class EditTaskPanel extends React.Component {
|
|||
this.setState({saving: false});
|
||||
this.props.onSave(json);
|
||||
}).fail(() => {
|
||||
this.setState({saving: false, error: "Could not update task information. Plese try again."});
|
||||
this.setState({saving: false, error: _("Could not update task information. Plese try again.")});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -71,14 +72,14 @@ class EditTaskPanel extends React.Component {
|
|||
task={this.props.task}
|
||||
/>
|
||||
<div className="actions">
|
||||
<button type="button" className="btn btn-sm btn-default" onClick={this.handleCancel} disabled={this.state.saving}>Cancel</button>
|
||||
<button type="button" className="btn btn-sm btn-default" onClick={this.handleCancel} disabled={this.state.saving}>{_("Cancel")}</button>
|
||||
<button type="button" className="btn btn-sm btn-primary save" onClick={this.handleSave} disabled={this.state.saving || !this.state.editTaskFormLoaded}>
|
||||
{this.state.saving ?
|
||||
<span>
|
||||
<i className="fa fa-circle-notch fa-spin"></i> Saving...
|
||||
<i className="fa fa-circle-notch fa-spin"></i> {_("Saving...")}
|
||||
</span>
|
||||
: <span>
|
||||
<i className="fa fa-edit"></i> Save
|
||||
<i className="fa fa-edit"></i> {_("Save")}
|
||||
</span>}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -3,14 +3,15 @@ import ErrorMessage from './ErrorMessage';
|
|||
import '../css/FormDialog.scss';
|
||||
import PropTypes from 'prop-types';
|
||||
import $ from 'jquery';
|
||||
import { _ } from '../classes/gettext';
|
||||
|
||||
class FormDialog extends React.Component {
|
||||
static defaultProps = {
|
||||
title: "Title",
|
||||
saveLabel: "Save",
|
||||
savingLabel: "Saving...",
|
||||
title: _("Title"),
|
||||
saveLabel: _("Save"),
|
||||
savingLabel: _("Saving..."),
|
||||
saveIcon: "glyphicon glyphicon-plus",
|
||||
deleteWarning: "Are you sure?",
|
||||
deleteWarning: _("Are you sure?"),
|
||||
show: false
|
||||
};
|
||||
|
||||
|
@ -105,7 +106,7 @@ class FormDialog extends React.Component {
|
|||
if (this.props.getFormData) formData = this.props.getFormData();
|
||||
|
||||
this.props.saveAction(formData).fail(e => {
|
||||
this.setState({error: e.message || (e.responseJSON || {}).detail || e.responseText || "Could not apply changes"});
|
||||
this.setState({error: e.message || (e.responseJSON || {}).detail || e.responseText || _("Could not apply changes")});
|
||||
}).always(() => {
|
||||
this.setState({saving: false});
|
||||
}).done(() => {
|
||||
|
@ -119,7 +120,7 @@ class FormDialog extends React.Component {
|
|||
this.setState({deleting: true});
|
||||
this.props.deleteAction()
|
||||
.fail(e => {
|
||||
if (this._mounted) this.setState({error: e.message || (e.responseJSON || {}).detail || e.responseText || "Could not delete item"});
|
||||
if (this._mounted) this.setState({error: e.message || (e.responseJSON || {}).detail || e.responseText || _("Could not delete item")});
|
||||
}).always(() => {
|
||||
if (this._mounted) this.setState({deleting: false});
|
||||
});
|
||||
|
@ -147,7 +148,7 @@ class FormDialog extends React.Component {
|
|||
</div>
|
||||
<div className="modal-footer">
|
||||
<div className="pull-right">
|
||||
<button type="button" className="btn btn-default" onClick={this.hide} disabled={this.state.saving}>Cancel</button>
|
||||
<button type="button" className="btn btn-default" onClick={this.hide} disabled={this.state.saving}>{_("Cancel")}</button>
|
||||
<button type="button" className="btn btn-primary save" onClick={this.handleSave} disabled={this.state.saving}>
|
||||
{this.state.saving ?
|
||||
<span>
|
||||
|
@ -166,10 +167,10 @@ class FormDialog extends React.Component {
|
|||
onClick={this.handleDelete}>
|
||||
{this.state.deleting ?
|
||||
<span>
|
||||
<i className="fa fa-circle-notch fa-spin"></i> Deleting...
|
||||
<i className="fa fa-circle-notch fa-spin"></i> {_("Deleting...")}
|
||||
</span>
|
||||
: <span>
|
||||
<i className="glyphicon glyphicon-trash"></i> Delete
|
||||
<i className="glyphicon glyphicon-trash"></i> {_("Delete")}
|
||||
</span>}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -19,7 +19,7 @@ class NewTaskPanel extends React.Component {
|
|||
filesCount: PropTypes.number,
|
||||
showResize: PropTypes.bool,
|
||||
getFiles: PropTypes.func,
|
||||
suggestedTaskName: PropTypes.string,
|
||||
suggestedTaskName: PropTypes.oneOfType([PropTypes.string, PropTypes.func])
|
||||
};
|
||||
|
||||
constructor(props){
|
||||
|
|
|
@ -6,7 +6,7 @@ import ProjectListItem from './ProjectListItem';
|
|||
import Paginated from './Paginated';
|
||||
import Paginator from './Paginator';
|
||||
import ErrorMessage from './ErrorMessage';
|
||||
import { Route } from 'react-router-dom';
|
||||
import { _, interpolate } from '../classes/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class ProjectList extends Paginated {
|
||||
|
@ -53,14 +53,14 @@ class ProjectList extends Paginated {
|
|||
this.updatePagination(this.PROJECTS_PER_PAGE, json.count);
|
||||
}else{
|
||||
this.setState({
|
||||
error: `Invalid JSON response: ${JSON.stringify(json)}`,
|
||||
error: interpolate(_("Invalid JSON response: %(error)s"), {error: JSON.stringify(json)}),
|
||||
loading: false
|
||||
});
|
||||
}
|
||||
})
|
||||
.fail((jqXHR, textStatus, errorThrown) => {
|
||||
this.setState({
|
||||
error: `Could not load projects list: ${textStatus}`,
|
||||
error: interpolate(_("Could not load projects list: %(error)s"), {error: textStatus}),
|
||||
loading: false
|
||||
});
|
||||
})
|
||||
|
|
|
@ -12,6 +12,8 @@ import csrf from '../django/csrf';
|
|||
import HistoryNav from '../classes/HistoryNav';
|
||||
import PropTypes from 'prop-types';
|
||||
import ResizeModes from '../classes/ResizeModes';
|
||||
import exifr from 'exifr/dist/mini.esm';
|
||||
import { _, interpolate } from '../classes/gettext';
|
||||
import $ from 'jquery';
|
||||
|
||||
class ProjectListItem extends React.Component {
|
||||
|
@ -180,7 +182,7 @@ class ProjectListItem extends React.Component {
|
|||
file.retries++;
|
||||
this.dz.processQueue();
|
||||
}else{
|
||||
throw new Error(`Cannot upload ${file.name}, exceeded max retries (${MAX_RETRIES})`);
|
||||
throw new Error(interpolate(_('Cannot upload %(filename)s, exceeded max retries (%(max_retries)s)'), {filename: file.name, max_retries: MAX_RETRIES}));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -229,17 +231,17 @@ class ProjectListItem extends React.Component {
|
|||
if (task && task.id){
|
||||
this.newTaskAdded();
|
||||
}else{
|
||||
this.setUploadState({error: `Cannot create new task. Invalid response from server: ${JSON.stringify(task)}`});
|
||||
this.setUploadState({error: interpolate(_('Cannot create new task. Invalid response from server: %(error)s'), { error: JSON.stringify(task) }) });
|
||||
}
|
||||
}).fail(() => {
|
||||
this.setUploadState({error: "Cannot create new task. Please try again later."});
|
||||
this.setUploadState({error: _("Cannot create new task. Please try again later.")});
|
||||
});
|
||||
}else if (this.dz.getQueuedFiles() === 0){
|
||||
// Done but didn't upload all?
|
||||
this.setUploadState({
|
||||
totalCount: this.state.upload.totalCount - remainingFilesCount,
|
||||
uploading: false,
|
||||
error: `${remainingFilesCount} files cannot be uploaded. As a reminder, only images (.jpg, .tif, .png) and GCP files (.txt) can be uploaded. Try again.`
|
||||
error: interpolate(_('%(count)s files cannot be uploaded. As a reminder, only images (.jpg, .tif, .png) and GCP files (.txt) can be uploaded. Try again.'), { count: remainingFilesCount })
|
||||
});
|
||||
}
|
||||
})
|
||||
|
@ -341,11 +343,11 @@ class ProjectListItem extends React.Component {
|
|||
this.dz.options.url = `/api/projects/${this.state.data.id}/tasks/${task.id}/upload/`;
|
||||
this.dz.processQueue();
|
||||
}else{
|
||||
this.setState({error: `Cannot create new task. Invalid response from server: ${JSON.stringify(task)}`});
|
||||
this.setState({error: interpolate(_('Cannot create new task. Invalid response from server: %(error)s'), { error: JSON.stringify(task) }) });
|
||||
this.handleTaskCanceled();
|
||||
}
|
||||
}).fail(() => {
|
||||
this.setState({error: "Cannot create new task. Please try again later."});
|
||||
this.setState({error: _("Cannot create new task. Please try again later.")});
|
||||
this.handleTaskCanceled();
|
||||
});
|
||||
}
|
||||
|
@ -393,6 +395,68 @@ class ProjectListItem extends React.Component {
|
|||
this.setState({importing: false});
|
||||
}
|
||||
|
||||
handleTaskTitleHint = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.state.upload.files.length > 0){
|
||||
|
||||
// Find first image in list
|
||||
let f = null;
|
||||
for (let i = 0; i < this.state.upload.files.length; i++){
|
||||
if (this.state.upload.files[i].type.indexOf("image") === 0){
|
||||
f = this.state.upload.files[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!f){
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse EXIF
|
||||
const options = {
|
||||
ifd0: false,
|
||||
exif: [0x9003],
|
||||
gps: [0x0001, 0x0002, 0x0003, 0x0004],
|
||||
interop: false,
|
||||
ifd1: false // thumbnail
|
||||
};
|
||||
exifr.parse(f, options).then(gps => {
|
||||
if (!gps.latitude || !gps.longitude){
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
let dateTime = gps["36867"];
|
||||
|
||||
// Try to parse the date from EXIF to JS
|
||||
const parts = dateTime.split(" ");
|
||||
if (parts.length == 2){
|
||||
let [ d, t ] = parts;
|
||||
d = d.replace(":", "-");
|
||||
const tm = Date.parse(`${d} ${t}`);
|
||||
if (!isNaN(tm)){
|
||||
dateTime = new Date(tm).toLocaleDateString();
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to file modified date if
|
||||
// no exif info is available
|
||||
if (!dateTime) dateTime = f.lastModifiedDate.toLocaleDateString();
|
||||
|
||||
// Query nominatim OSM
|
||||
$.ajax({
|
||||
url: `https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${gps.latitude}&lon=${gps.longitude}`,
|
||||
contentType: 'application/json',
|
||||
type: 'GET'
|
||||
}).done(json => {
|
||||
if (json.name) resolve(`${json.name} - ${dateTime}`);
|
||||
else reject(new Error("Invalid json"));
|
||||
}).fail(reject);
|
||||
}).catch(reject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { refreshing, data } = this.state;
|
||||
const numTasks = data.tasks.length;
|
||||
|
@ -405,9 +469,9 @@ class ProjectListItem extends React.Component {
|
|||
|
||||
<EditProjectDialog
|
||||
ref={(domNode) => { this.editProjectDialog = domNode; }}
|
||||
title="Edit Project"
|
||||
saveLabel="Save Changes"
|
||||
savingLabel="Saving changes..."
|
||||
title={_("Edit Project")}
|
||||
saveLabel={_("Save Changes")}
|
||||
savingLabel={_("Saving changes...")}
|
||||
saveIcon="far fa-edit"
|
||||
projectName={data.name}
|
||||
projectDescr={data.description}
|
||||
|
@ -425,12 +489,12 @@ class ProjectListItem extends React.Component {
|
|||
onClick={this.handleUpload}
|
||||
ref={this.setRef("uploadButton")}>
|
||||
<i className="glyphicon glyphicon-upload"></i>
|
||||
Select Images and GCP
|
||||
{_("Select Images and GCP")}
|
||||
</button>
|
||||
<button type="button"
|
||||
className="btn btn-default btn-sm"
|
||||
onClick={this.handleImportTask}>
|
||||
<i className="glyphicon glyphicon-import"></i> Import
|
||||
<i className="glyphicon glyphicon-import"></i> {_("Import")}
|
||||
</button>
|
||||
{this.state.buttons.map((button, i) => <React.Fragment key={i}>{button}</React.Fragment>)}
|
||||
</div>
|
||||
|
@ -445,7 +509,7 @@ class ProjectListItem extends React.Component {
|
|||
</button>
|
||||
|
||||
<button type="button" className="btn btn-default btn-sm" onClick={this.viewMap}>
|
||||
<i className="fa fa-globe"></i> View Map
|
||||
<i className="fa fa-globe"></i> {_("View Map")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -460,13 +524,13 @@ class ProjectListItem extends React.Component {
|
|||
<span>
|
||||
<i className='fa fa-tasks'>
|
||||
</i> <a href="javascript:void(0);" onClick={this.toggleTaskList}>
|
||||
{numTasks} Tasks <i className={'fa fa-caret-' + (this.state.showTaskList ? 'down' : 'right')}></i>
|
||||
{interpolate(_("%(count)s Tasks"), { count: numTasks})} <i className={'fa fa-caret-' + (this.state.showTaskList ? 'down' : 'right')}></i>
|
||||
</a>
|
||||
</span>
|
||||
: ""}
|
||||
|
||||
<i className='far fa-edit'>
|
||||
</i> <a href="javascript:void(0);" onClick={this.handleEditProject}> Edit
|
||||
</i> <a href="javascript:void(0);" onClick={this.handleEditProject}> {_("Edit")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -476,7 +540,7 @@ class ProjectListItem extends React.Component {
|
|||
|
||||
{this.state.upload.error !== "" ?
|
||||
<div className="alert alert-warning alert-dismissible">
|
||||
<button type="button" className="close" aria-label="Close" onClick={this.closeUploadError}><span aria-hidden="true">×</span></button>
|
||||
<button type="button" className="close" aria-label={_("Close")} onClick={this.closeUploadError}><span aria-hidden="true">×</span></button>
|
||||
{this.state.upload.error}
|
||||
</div>
|
||||
: ""}
|
||||
|
@ -485,6 +549,7 @@ class ProjectListItem extends React.Component {
|
|||
<NewTaskPanel
|
||||
onSave={this.handleTaskSaved}
|
||||
onCancel={this.handleTaskCanceled}
|
||||
suggestedTaskName={this.handleTaskTitleHint}
|
||||
filesCount={this.state.upload.totalCount}
|
||||
showResize={true}
|
||||
getFiles={() => this.state.upload.files }
|
||||
|
|
|
@ -19,4 +19,11 @@
|
|||
margin-top: 10px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.name-loading{
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
top: 15px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@
|
|||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.15.1",
|
||||
"epsg": "^0.5.0",
|
||||
"exifr": "^6.0.0",
|
||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||
"fbemitter": "^2.1.1",
|
||||
"file-loader": "^0.9.0",
|
||||
|
|
|
@ -3,7 +3,7 @@ import ErrorMessage from 'webodm/components/ErrorMessage';
|
|||
import PropTypes from 'prop-types';
|
||||
import './Dashboard.scss';
|
||||
import $ from 'jquery';
|
||||
import _ from 'gettext';
|
||||
import { _ } from 'webodm/classes/gettext';
|
||||
|
||||
export default class Dashboard extends React.Component {
|
||||
static defaultProps = {
|
||||
|
|
|
@ -12,7 +12,7 @@ if [[ "$1" == "extract" ]]; then
|
|||
|
||||
mkdir -p locale
|
||||
django-admin makemessages --keep-pot $locale_param --ignore=build --ignore=app/templates/app/admin/* --ignore=app/templates/app/registration/*
|
||||
django-admin makemessages --keep-pot $locale_param -d djangojs --extension jsx --ignore=build
|
||||
python manage.py makemessages_djangojs --keep-pot $locale_param -d djangojs --extension jsx --ignore=build --language Python
|
||||
fi
|
||||
|
||||
if [[ "$1" == "build" ]]; then
|
||||
|
|
|
@ -88,8 +88,7 @@ module.exports = {
|
|||
"SystemJS": "SystemJS",
|
||||
"THREE": "THREE",
|
||||
"React": "React",
|
||||
"ReactDOM": "ReactDOM",
|
||||
"gettext": "gettext"
|
||||
"ReactDOM": "ReactDOM"
|
||||
},
|
||||
|
||||
watchOptions: {
|
||||
|
|
Ładowanie…
Reference in New Issue