#114 / #117 Save history

This commit is contained in:
Keith Paterson 2021-01-14 22:27:42 +01:00
parent fbf0e5cbab
commit bcc5bbb335
8 changed files with 373 additions and 193 deletions

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "flightgear-airports",
"version": "0.0.27",
"version": "0.0.28",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -92,7 +92,7 @@
"coordinate-parser": "^1.0.3",
"dijkstrajs": "^1.0.1",
"electron-debug": "^3.0.1",
"element-ui": "^2.13.2",
"element-ui": "^2.14.1",
"file-url": "^3.0.0",
"fs": "0.0.1-security",
"fs-extra": "^9.0.1",

View File

@ -11,32 +11,84 @@ You should have received a copy of the GNU General Public License along with FG
-->
<template>
<div id="EditBar">
<ZoomButton icon="fas fa-th" v-on:click="zoomin" :show="true" tooltip="Zoomin"></ZoomButton>
<ZoomButton icon="fas fa-th-large" v-on:click="zoomout" :show="!editing" tooltip="Zoomout"></ZoomButton>
<!--<ZoomButton icon="far fa-eye-slash" v-on:click="hideAPT" :show='true' tooltip="Hide APT"></ZoomButton>-->
<!--<EditButton icon="fas fa-upload" v-on:click="upload" :show="!editing" tooltip="Upload"></EditButton>-->
<!--<EditButton icon="fas fa-edit" v-on:click="edit" :show="!editing" tooltip="Edit"></EditButton>-->
<EditButton
icon="fas fa-undo"
v-on:click="centerDialogVisible = true"
:show="editing"
tooltip="Undo"
></EditButton>
<el-dialog title="Reload" :visible.sync="centerDialogVisible" width="30%" center>
<span style="center">Reload from last save? You will lose the current edits.</span>
<el-dialog
title="Checking"
width="30%"
center
:visible.sync="checkDialogVisible"
>
<el-container direction="vertical">
<el-progress
:percentage="Number(((progress / max) * 100).toPrecision(3))"
v-if="max > 0"
></el-progress>
</el-container>
</el-dialog>
<el-dialog
title="Revert"
:visible.sync="centerDialogVisible"
width="550px"
center
>
<span>
Please select the Version to revert to.
<el-row v-for="item in saves" :key="item.file">
<el-button @click="revert(item.file)">{{item.mtime}}</el-button>
</el-row>
</span>
<span slot="footer" class="dialog-footer">
<el-button @click="undoFirst">Base version (GIT)</el-button>
<el-button type="primary" @click="undoLast">Last save</el-button>
<el-button type="primary" @click="cancel">Cancel</el-button>
</span>
</el-dialog>
<el-dialog title="Saving" :visible.sync="saveDialogVisible" width="30%" center>
<el-dialog
title="Saving"
:visible.sync="saveDialogVisible"
width="30%"
center
>
<span style="center">Saving..</span>
</el-dialog>
<EditButton icon="fas fa-save" v-on:click="save" :show="editing" tooltip="Save"></EditButton>
<EditButton icon="far fa-check-square" v-on:click="showCheck" :show="editing" tooltip="Check"></EditButton>
<ZoomButton
icon="fas fa-th"
v-on:click="zoomin"
:show="true"
tooltip="Zoomin"
></ZoomButton>
<ZoomButton
icon="fas fa-th-large"
v-on:click="zoomout"
:show="!editing"
tooltip="Zoomout"
></ZoomButton>
<EditButton
icon="fa fa-window-close"
v-on:click="close"
:show="editing"
tooltip="Close/Save Editing"
></EditButton>
<EditButton
icon="fas fa-undo"
v-on:click="openReload"
:show="editing"
tooltip="Revert to Savepoint"
></EditButton>
<EditButton
icon="fas fa-save"
v-on:click="save"
:show="editing"
tooltip="Save"
></EditButton>
<EditButton
icon="far fa-check-square"
v-on:click="showCheck"
:show="editing"
tooltip="Check"
></EditButton>
<EditButton
icon="fas fa-draw-polygon"
v-on:click="drawPolyline"
@ -61,12 +113,12 @@ You should have received a copy of the GNU General Public License along with FG
:show="editing"
tooltip="Draw Parking"
></EditButton>
<EditButton icon="fas fa-trash-alt" v-on:click="deleteFeature" :show="editing" tooltip="Remove"></EditButton>
<el-dialog title="Checking" width="30%" center :visible.sync="checkDialogVisible">
<el-container direction="vertical">
<el-progress :percentage="Number(((progress / max)*100).toPrecision(3))" v-if="max>0"></el-progress>
</el-container>
</el-dialog>
<EditButton
icon="fas fa-trash-alt"
v-on:click="deleteFeature"
:show="editing"
tooltip="Remove"
></EditButton>
</div>
</template>
@ -76,6 +128,8 @@ You should have received a copy of the GNU General Public License along with FG
const fs = require('fs');
const mapper = require('../check/mapper');
import {listSaves} from '../loaders/groundnet_loader'
import EditButton from './EditButton'
import ZoomButton from './ZoomButton';
import Vue from 'vue'
@ -85,11 +139,14 @@ You should have received a copy of the GNU General Public License along with FG
export default {
components: { EditButton, ZoomButton },
data () {
return {isEditing: false, uploadVisible: false, centerDialogVisible: false, saveDialogVisible: false, checkDialogVisible: false, checking: false, progress: 0, max: 0, pavementLayerVisible: true}
return {isEditing: false, uploadVisible: false, centerDialogVisible: false, saveDialogVisible: false, checkDialogVisible: false, checking: false, progress: 0, max: 0, pavementLayerVisible: true, saves: [] }
},
created () {
},
methods: {
cancel () {
this.centerDialogVisible = false
},
zoomout() {
this.$parent.$parent.$refs.editLayer.stopDrawing()
this.$parent.$parent.zoomUpdated(9)
@ -109,7 +166,7 @@ You should have received a copy of the GNU General Public License along with FG
setEditing (editing) {
this.isEditing = editing
},
undoFirst () {
revert (file) {
this.isEditing = false
this.$emit('edit', false)
this.centerDialogVisible = false
@ -117,25 +174,32 @@ You should have received a copy of the GNU General Public License along with FG
this.$parent.$parent.$refs.editLayer.disableEdit()
this.$parent.$parent.$refs.towerLayer.disableEdit()
this.$parent.$parent.$refs.thresholdLayer.disableEdit()
this.$parent.$parent.$refs.editLayer.reload(true)
this.$parent.$parent.$refs.editLayer.reload(file)
},
undoLast () {
this.isEditing = false
this.$emit('edit', false)
this.centerDialogVisible = false
this.$parent.$parent.$refs.map.mapObject.options.minZoom = 1;
this.$parent.$parent.$refs.editLayer.disableEdit()
this.$parent.$parent.$refs.towerLayer.disableEdit()
this.$parent.$parent.$refs.thresholdLayer.disableEdit()
this.$parent.$parent.$refs.editLayer.reload(false)
},
save () {
close () {
this.$parent.$parent.$refs.editLayer.stopDrawing()
this.isEditing = false
this.$emit('edit', false)
this.$parent.$parent.$refs.map.mapObject.options.minZoom = 1;
Vue.set(this, 'saveDialogVisible', true)
this.$emit('edit', false)
Vue.nextTick( function () {
setTimeout( this.closeDefered.bind(this), 100);
}, this)
},
closeDefered () {
this.$parent.$parent.$refs.editLayer.save()
this.$parent.$parent.$refs.towerLayer.save()
this.$parent.$parent.$refs.thresholdLayer.save()
this.$parent.$parent.$refs.editLayer.disableEdit()
this.$parent.$parent.$refs.towerLayer.disableEdit()
this.$parent.$parent.$refs.thresholdLayer.disableEdit()
this.rescanCurrentGroundnet()
Vue.set(this, 'saveDialogVisible', false)
},
save () {
Vue.set(this, 'saveDialogVisible', true)
this.$parent.$parent.$refs.editLayer.stopDrawing()
Vue.nextTick( function () {
setTimeout( this.saveDefered.bind(this), 100);
}, this)
@ -144,13 +208,10 @@ You should have received a copy of the GNU General Public License along with FG
this.$parent.$parent.$refs.editLayer.save()
this.$parent.$parent.$refs.towerLayer.save()
this.$parent.$parent.$refs.thresholdLayer.save()
this.$parent.$parent.$refs.editLayer.disableEdit()
this.$parent.$parent.$refs.towerLayer.disableEdit()
this.$parent.$parent.$refs.thresholdLayer.disableEdit()
this.scanGroundnets()
this.rescanCurrentGroundnet()
Vue.set(this, 'saveDialogVisible', false)
},
scanGroundnets () {
rescanCurrentGroundnet () {
try {
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/src/renderer/utils/worker.js`
@ -297,6 +358,13 @@ You should have received a copy of the GNU General Public License along with FG
this.$parent.$parent.$refs.editLayer.stopDrawing()
Vue.set(this, 'checkDialogVisible', true)
this.check()
},
openReload: function() {
this.centerDialogVisible = true
var icao = this.$parent.$parent.$refs.editLayer.icao
if (icao !== undefined && icao !== '') {
this.saves = listSaves(this.$store.state.Settings.settings.airportsDirectory, icao).sort((a, b) => a.mtimeMs - b.mtimeMs)
}
}
},
computed: {

View File

@ -109,7 +109,7 @@ You should have received a copy of the GNU General Public License along with FG
},
data () {
return {
maxId: 1, icao: String, checking: false, editing: false
maxId: 1, icao: '', checking: false, editing: false
}
},
methods: {
@ -128,13 +128,18 @@ You should have received a copy of the GNU General Public License along with FG
this.selection = parkings;
return parkings;
},
load (icao, force) {
load (icao, filename) {
if (this.groundnetLayerGroup !== undefined) {
this.groundnetLayerGroup.removeFrom(this.$parent.mapObject)
}
this.$parent.$parent.setIcao(icao)
this.icao = icao
this.groundnetLayerGroup = readGroundnetXML(this.$store.state.Settings.settings.airportsDirectory, icao, force)
var f = path.join(this.$store.state.Settings.settings.airportsDirectory, icao[0], icao[1], icao[2], icao + '.groundnet.new.xml');
if (!fs.existsSync(f)) {
f = path.join(this.$store.state.Settings.settings.airportsDirectory, icao[0], icao[1], icao[2], icao + '.groundnet.xml');
}
this.groundnetLayerGroup = readGroundnetXML(this.$store.state.Settings.settings.airportsDirectory, icao, f)
if (this.groundnetLayerGroup === undefined) {
console.warn('Groundnet for ICAO not loaded ' + icao)
return
@ -912,8 +917,8 @@ You should have received a copy of the GNU General Public License along with FG
this.$parent.mapObject.off('click', this.addParking)
this.$parent.mapObject._container.style.cursor = ''
},
reload (force) {
this.load(this.icao, force)
reload (filename) {
this.load(this.icao, filename)
},
link (index) {
var layers = []
@ -1008,7 +1013,6 @@ You should have received a copy of the GNU General Public License along with FG
xml.push(l)
})
writeGroundnetXML(this.$store.state.Settings.settings.airportsDirectory, this.icao, xml)
this.load(this.icao, false)
},
//Copy to test directory
test() {

View File

@ -6,12 +6,40 @@
<i class="fa fa-caret-left"></i>
</div>
</h1>
<div id="panel" width="100%">
<el-collapse v-model="activeName" accordion>
<el-collapse-item title="General" name="1">
<el-row>
<el-col :span="12" class="label">Number of saves : </el-col>
<el-col :span="12">
<el-popover
placement="top-start"
title="Saves"
width="200"
trigger="hover"
content="How many previous versions should be kept."
>
<el-input
placeholder="Number of versions"
slot="reference"
v-model="numberOfSaves"
></el-input>
</el-popover>
</el-col>
</el-row>
</el-collapse-item>
<el-collapse-item title="Directories" name="2">
<el-row>
<el-col :span="22" class="label">Airports Directory</el-col>
</el-row>
<el-row>
<el-col :span="22" v-bind:class="{ invalid: !airports_directory_ok, file_label: airports_directory_ok}">{{ airports_directory }}</el-col>
<el-col
:span="22"
v-bind:class="{
invalid: !airports_directory_ok,
file_label: airports_directory_ok,
}"
>{{ airports_directory }}</el-col
>
<el-col :span="2">
<el-popover
placement="top-start"
@ -20,7 +48,10 @@
trigger="hover"
content="The work directory. Best is a copy from groundweb"
>
<directory-select @input="airportsDirectorySelect" slot="reference"></directory-select>
<directory-select
@input="airportsDirectorySelect"
slot="reference"
></directory-select>
</el-popover>
</el-col>
</el-row>
@ -28,7 +59,14 @@
<el-col :span="22" class="label">Flightgear Data Directory</el-col>
</el-row>
<el-row>
<el-col :span="22" v-bind:class="{ invalid: !flightgear_directory_ok, file_label: flightgear_directory_ok}">{{ flightgear_directory }}</el-col>
<el-col
:span="22"
v-bind:class="{
invalid: !flightgear_directory_ok,
file_label: flightgear_directory_ok,
}"
>{{ flightgear_directory }}</el-col
>
<el-col :span="2">
<el-popover
placement="top-start"
@ -37,7 +75,10 @@
trigger="hover"
content="The FGDATA directory."
>
<directory-select @input="flightgearDirectorySelect" slot="reference"></directory-select>
<directory-select
@input="flightgearDirectorySelect"
slot="reference"
></directory-select>
</el-popover>
</el-col>
</el-row>
@ -45,35 +86,42 @@
<el-col :span="22" class="label">Traffic Directory</el-col>
</el-row>
<el-row>
<el-col :span="22" v-bind:class="{ invalid: !Traffic_directory_ok, file_label: Traffic_directory_ok}">{{ Traffic_directory }}</el-col>
<el-col :span="2">
</el-col>
<el-col
:span="22"
v-bind:class="{
invalid: !Traffic_directory_ok,
file_label: Traffic_directory_ok,
}"
>{{ Traffic_directory }}</el-col
>
<el-col :span="2"> </el-col>
</el-row>
<el-row>
<el-col :span="22" class="label">APT File</el-col>
</el-row>
<el-row>
<el-col :span="22" v-bind:class="{invalid: !apt_file_ok}" >{{ apt_file }}</el-col>
<el-col :span="2">
</el-col>
<el-col :span="22" v-bind:class="{ invalid: !apt_file_ok }">{{
apt_file
}}</el-col>
<el-col :span="2"> </el-col>
</el-row>
<el-row>
<el-col :span="7" class="label">Export Directory</el-col>
<el-col :span="15" v-bind:class="{invalid: !test_directory_ok, file_label: test_directory_ok}">{{ test_directory }}</el-col>
<el-col :span="7" class="label">Export Directory : </el-col>
<el-col
:span="15"
v-bind:class="{
invalid: !test_directory_ok,
file_label: test_directory_ok,
}"
>{{ test_directory }}</el-col
>
<el-col :span="2">
<directory-select @input="testDirectorySelect"></directory-select>
</el-col>
</el-row>
</el-collapse-item>
<el-collapse-item title="User" name="3">
<el-row>
<el-col :span="22" class="label">Phi Host Url</el-col>
</el-row>
<el-row>
<el-col :span="24" class="label">
<el-input placeholder="Please input a valid Phi URL" v-model="phi_url"></el-input>
</el-col>
</el-row>
<el-row>
<el-col :span="7" class="label">Author E-Mail : </el-col>
<el-col :span="17">
<el-popover
@ -83,12 +131,15 @@
trigger="hover"
content="Only used as a committer/author for Github. This e-mail is only visible via GIT."
>
<el-input placeholder="Please input your email" slot="reference" v-model="email"></el-input>
<el-input
placeholder="Please input your email"
slot="reference"
v-model="email"
></el-input>
</el-popover>
</el-col>
</el-row>
<el-row>
<el-col :span="7" class="label">Author Name : </el-col>
<el-col :span="17">
<el-popover
@ -98,10 +149,16 @@
trigger="hover"
content="This is saved to the file and is therefore distributed via Terrasync."
>
<el-input placeholder="Please input your Name" slot="reference" v-model="name"></el-input>
<el-input
placeholder="Please input your Name"
slot="reference"
v-model="name"
></el-input>
</el-popover>
</el-col>
</el-row>
</el-collapse-item>
<el-collapse-item title="Troubleshooting" name="4">
<el-row>
<el-col :span="7">
<span class="label">Scan logging :</span>
@ -134,7 +191,21 @@
</el-popover>
</el-col>
</el-row>
</div>
</el-collapse-item>
<el-collapse-item title="Flightgear" name="5">
<el-row>
<el-col :span="22" class="label">Phi Host Url</el-col>
</el-row>
<el-row>
<el-col :span="24" class="label">
<el-input
placeholder="Please input a valid Phi URL"
v-model="phi_url"
></el-input>
</el-col>
</el-row>
</el-collapse-item>
</el-collapse>
</div>
</template>
@ -152,8 +223,7 @@
},
data () {
return { ok: true
}
return { ok: true, activeName: '0' }
},
methods: {
flightgearDirectorySelect: function (flightgearDirectory) {
@ -173,6 +243,16 @@
}
},
computed: {
numberOfSaves: {
// getter
get: function () {
return this.$store.state.Settings.settings.numberOfSaves
},
// setter
set: function (newValue) {
this.$store.commit('SET_NUMBER_OF_SAVES', newValue)
}
},
email: {
// getter
get: function () {
@ -293,5 +373,4 @@
padding: 5px;
background-color: red;
}
</style>

View File

@ -29,22 +29,37 @@ function addFrequencies (type, value) {
}
exports.addFeature = function (feature) {
featureLookup[feature.id] = new Array();
featureLookup[feature.id] = [];
}
exports.readGroundnetXML = function (fDir, icao, force) {
exports.listSaves = function (fDir, icao) {
var directory = path.join(fDir, icao[0], icao[1], icao[2]);
var files = fs.readdirSync(directory);
var ret = files
.filter(f => f.includes(icao) )
.filter(f => f.includes('groundnet') )
.map(f => {
try {
var fileDate = fs.lstatSync(path.join(directory, f));
return {file: f, mtime: `${fileDate.mtime}`, mtimeMs: `${fileDate.mtimeMs}`};
} catch (error) {
console.error(error);
}
});
ret.forEach( f => {
console.debug(f);
});
return ret;
}
exports.readGroundnetXML = function (fDir, icao, f) {
try {
store.default.dispatch('setGroundnetLoaded', false);
var layerGroup = L.layerGroup();
layerGroup.maxId = 0;
var f = path.join(fDir, icao[0], icao[1], icao[2], icao + '.groundnet.xml');
var fNew = path.join(fDir, icao[0], icao[1], icao[2], icao + '.groundnet.new.xml');
if (f == null || (!fs.existsSync(f) && force)|| (!fs.existsSync(f) && !fs.existsSync(fNew) ))
if (f == null || (!fs.existsSync(f) ))
return layerGroup;
if (fNew != null && fs.existsSync(fNew) && !force) {
f = fNew;
}
var features = new Array();

View File

@ -65,11 +65,22 @@ exports.writeGroundnetXML = function (fDir, icao, featureList) {
try { fs.mkdirSync(path.join(fDir, icao[0], icao[1]), { recursive: true })} catch (err) { }
try { fs.mkdirSync(path.join(fDir, icao[0], icao[1], icao[2]), { recursive: true })} catch (err) { }
var fileNames = [];
for (let index = 1; index <= store.default.state.Settings.settings.numberOfSaves; index++) {
fileNames.push(path.join(fDir, icao[0], icao[1], icao[2], icao + `.groundnet.bak.${index}.xml`));
}
var f = path.join(fDir, icao[0], icao[1], icao[2], icao + '.groundnet.new.xml');
var fBak = path.join(fDir, icao[0], icao[1], icao[2], icao + '.groundnet.bak.xml');
if( fs.existsSync(f) ) {
fs.copyFileSync(f, fBak);
var previous = '';
fileNames.reverse().forEach(fBak => {
if (fs.existsSync(fBak) && previous !== '') {
console.debug( `Copy ${fBak} to ${previous}`);
fs.copyFileSync(fBak, previous);
}
previous = fBak;
});
fs.copyFileSync(f, previous);
}
if (f == null)
return;

View File

@ -3,7 +3,7 @@ const path = require('path');
const fs = require('fs');
const state = {
settings: { flightgearDirectory: '.', testDirectory: '.', email: 'flightgearairports@example.org', name: 'unknown', phi_url: 'http://localhost:8080' },
settings: { numberOfSaves: 1, flightgearDirectory: '.', testDirectory: '.', email: 'flightgearairports@example.org', name: 'unknown', phi_url: 'http://localhost:8080' },
zoom: 14,
center: [47.413220, -1.219482],
bounds: undefined,
@ -50,6 +50,9 @@ const mutations = {
'SET_NAME'(state, name) {
state.settings.name = name
},
'SET_NUMBER_OF_SAVES'(state, numberOfSaves) {
state.settings.numberOfSaves = numberOfSaves
},
'SET_PHI_URL'(state, phi_url) {
state.settings.phi_url = phi_url
},