user-interface to show everything correctly without race-conditions

This commit is contained in:
knoxfighter 2017-11-14 13:13:55 +01:00
parent 8179e2c917
commit 6b9639e927
7 changed files with 253 additions and 63 deletions

View File

@ -56,7 +56,6 @@ func (fl *FileLock) Lock(filePath string) error {
} else { } else {
return ErrorAlreadyLocked return ErrorAlreadyLocked
} }
log.Println("write locked")
return nil return nil
} }
@ -73,7 +72,6 @@ func (fl *FileLock) Unlock(filePath string) error {
} else { } else {
return ErrorAlreadyLocked return ErrorAlreadyLocked
} }
log.Println("write unlocked")
return nil return nil
} }
@ -90,7 +88,6 @@ func (fl *FileLock) RLock(filePath string) error {
} else { } else {
return ErrorAlreadyLocked return ErrorAlreadyLocked
} }
log.Println("read locked")
return nil return nil
} }
@ -107,15 +104,12 @@ func (fl *FileLock) RUnlock(filePath string) error {
} else { } else {
return ErrorAlreadyLocked return ErrorAlreadyLocked
} }
log.Println("read unlocked")
return nil return nil
} }
func (fl *FileLock) LockW(filePath string) { func (fl *FileLock) LockW(filePath string) {
log.Println("LockW called")
for { for {
err := fl.Lock(filePath) err := fl.Lock(filePath)
log.Println(err)
if err == ErrorAlreadyLocked { if err == ErrorAlreadyLocked {
time.Sleep(time.Second * 2) time.Sleep(time.Second * 2)
log.Println("file locked wait two seconds to access write-lock") log.Println("file locked wait two seconds to access write-lock")
@ -134,7 +128,7 @@ func (fl *FileLock) RLockW(filePath string) {
if err == ErrorAlreadyLocked { if err == ErrorAlreadyLocked {
time.Sleep(time.Second * 2) time.Sleep(time.Second * 2)
log.Println("file locked wait two seconds to access read-lock") log.Println("file locked ... wait two seconds to try to access read-lock")
} }
if err == nil { if err == nil {

View File

@ -139,12 +139,14 @@ func (mod_simple_list *ModSimpleList) createMod(mod_name string) (error) {
} }
func (mod_simple_list *ModSimpleList) toggleMod(mod_name string) error { func (mod_simple_list *ModSimpleList) toggleMod(mod_name string) (error, bool) {
var err error var err error
var newEnabled bool
for index, mod := range mod_simple_list.Mods { for index, mod := range mod_simple_list.Mods {
if mod.Name == mod_name { if mod.Name == mod_name {
mod_simple_list.Mods[index].Enabled = !mod_simple_list.Mods[index].Enabled newEnabled = !mod_simple_list.Mods[index].Enabled
mod_simple_list.Mods[index].Enabled = newEnabled
break break
} }
} }
@ -152,10 +154,10 @@ func (mod_simple_list *ModSimpleList) toggleMod(mod_name string) error {
err = mod_simple_list.saveModInfoJson() err = mod_simple_list.saveModInfoJson()
if err != nil { if err != nil {
log.Printf("error on savin new ModSimpleList: %s", err) log.Printf("error on savin new ModSimpleList: %s", err)
return err return err, newEnabled
} }
//i changed it already don't need to reload it //i changed it already don't need to reload it
return nil return nil, newEnabled
} }

View File

@ -239,7 +239,7 @@ func ToggleModHandler(w http.ResponseWriter, r *http.Request) {
mods, err := newMods(config.FactorioModsDir) mods, err := newMods(config.FactorioModsDir)
if err == nil { if err == nil {
err = mods.ModSimpleList.toggleMod(mod_name) err, resp.Data = mods.ModSimpleList.toggleMod(mod_name)
} }
if err != nil { if err != nil {
@ -251,7 +251,6 @@ func ToggleModHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp.Data = mods.listInstalledMods().ModsResult
resp.Success = true resp.Success = true
if err := json.NewEncoder(w).Encode(resp); err != nil { if err := json.NewEncoder(w).Encode(resp); err != nil {
@ -284,7 +283,7 @@ func DeleteModHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp.Data = mods.listInstalledMods().ModsResult resp.Data = mod_name
resp.Success = true resp.Success = true
if err := json.NewEncoder(w).Encode(resp); err != nil { if err := json.NewEncoder(w).Encode(resp); err != nil {
@ -341,7 +340,7 @@ func UpdateModHandler(w http.ResponseWriter, r *http.Request) {
} }
if err != nil { if err != nil {
w.WriteHeader(500) w.WriteHeader(http.StatusInternalServerError)
resp.Data = fmt.Sprintf("Error in deleteMod: %s", err) resp.Data = fmt.Sprintf("Error in deleteMod: %s", err)
if err := json.NewEncoder(w).Encode(resp); err != nil { if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Error in DeleteModHandler: %s", err) log.Printf("Error in DeleteModHandler: %s", err)
@ -349,7 +348,13 @@ func UpdateModHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp.Data = mods.listInstalledMods() installedMods := mods.listInstalledMods().ModsResult
for _, mod := range installedMods {
if mod.Name == mod_name {
resp.Data = mod
break
}
}
resp.Success = true resp.Success = true
if err := json.NewEncoder(w).Encode(resp); err != nil { if err := json.NewEncoder(w).Encode(resp); err != nil {
@ -537,7 +542,7 @@ func DeleteModPackHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp.Data = mod_pack_map.listInstalledModPacks() resp.Data = name
resp.Success = true resp.Success = true
if err := json.NewEncoder(w).Encode(resp); err != nil { if err := json.NewEncoder(w).Encode(resp); err != nil {
@ -590,7 +595,7 @@ func ModPackToggleModHandler(w http.ResponseWriter, r *http.Request) {
mod_pack_map, err := newModPackMap() mod_pack_map, err := newModPackMap()
if err == nil { if err == nil {
err = mod_pack_map[mod_pack_name].Mods.ModSimpleList.toggleMod(mod_name) err, resp.Data = mod_pack_map[mod_pack_name].Mods.ModSimpleList.toggleMod(mod_name)
} }
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
@ -601,7 +606,6 @@ func ModPackToggleModHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp.Data = mod_pack_map.listInstalledModPacks()
resp.Success = true resp.Success = true
if err := json.NewEncoder(w).Encode(resp); err != nil { if err := json.NewEncoder(w).Encode(resp); err != nil {
@ -637,7 +641,7 @@ func ModPackDeleteModHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp.Data = mod_pack_map.listInstalledModPacks() resp.Data = true
resp.Success = true resp.Success = true
if err := json.NewEncoder(w).Encode(resp); err != nil { if err := json.NewEncoder(w).Encode(resp); err != nil {
@ -679,7 +683,13 @@ func ModPackUpdateModHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp.Data = mod_pack_map.listInstalledModPacks() installedMods := mod_pack_map[mod_pack_name].Mods.listInstalledMods().ModsResult
for _, mod := range installedMods {
if mod.Name == mod_name {
resp.Data = mod
break
}
}
resp.Success = true resp.Success = true
if err := json.NewEncoder(w).Encode(resp); err != nil { if err := json.NewEncoder(w).Encode(resp); err != nil {

View File

@ -125,7 +125,8 @@ class Mod extends React.Component {
ref='modName' ref='modName'
type='submit' type='submit'
value='Toggle' value='Toggle'
onClick={this.props.toggleMod} onClick={(event) => this.props.toggleMod(event, this.state.updateInProgress)}
disabled={this.state.updateInProgress}
/> />
<input className="btn btn-danger btn-sm" <input className="btn btn-danger btn-sm"
@ -133,7 +134,8 @@ class Mod extends React.Component {
ref="modName" ref="modName"
type="submit" type="submit"
value="Delete" value="Delete"
onClick={this.props.deleteMod} onClick={(event) => this.props.deleteMod(event, this.state.updateInProgress)}
disabled={this.state.updateInProgress}
/> />
</td> </td>
</tr> </tr>

View File

@ -2,6 +2,7 @@ import React from 'react';
import ModManager from "../ModManager.jsx"; import ModManager from "../ModManager.jsx";
import NativeListener from 'react-native-listener'; import NativeListener from 'react-native-listener';
import {instanceOfModsContent} from "../ModsPropTypes.js"; import {instanceOfModsContent} from "../ModsPropTypes.js";
import locks from "locks";
class ModPackOverview extends React.Component { class ModPackOverview extends React.Component {
constructor(props) { constructor(props) {
@ -17,6 +18,8 @@ class ModPackOverview extends React.Component {
this.state = { this.state = {
listPacks: [] listPacks: []
} }
this.mutex = locks.createMutex();
} }
componentDidMount() { componentDidMount() {
@ -65,14 +68,27 @@ class ModPackOverview extends React.Component {
data: {name: inputValue}, data: {name: inputValue},
dataType: "JSON", dataType: "JSON",
success: (data) => { success: (data) => {
this_class.mutex.lock(() => {
let packList = this_class.state.listPacks;
data.data.mod_packs.forEach((v, k) => {
if(v.name == inputValue) {
packList.push(data.data.mod_packs[k]);
return false;
}
});
this_class.setState({ this_class.setState({
listPacks: data.data.mod_packs listPacks: packList
}); });
swal({ swal({
title: "modpack created successfully", title: "modpack created successfully",
type: "success" type: "success"
}); });
this_class.mutex.unlock();
});
}, },
error: (jqXHR, status, err) => { error: (jqXHR, status, err) => {
console.log('api/mods/packs/create', status, err.toString()); console.log('api/mods/packs/create', status, err.toString());
@ -109,14 +125,28 @@ class ModPackOverview extends React.Component {
data: {name: name}, data: {name: name},
dataType: "JSON", dataType: "JSON",
success: (data) => { success: (data) => {
if(data.success) {
this_class.mutex.lock(() => {
let mod_packs = this_class.state.listPacks;
mod_packs.forEach((v, k) => {
if(v.name == name) {
delete mod_packs[k];
}
});
this_class.setState({ this_class.setState({
listPacks: data.data.mod_packs listPacks: mod_packs
}); });
swal({ swal({
title: "Modpack deleted successfully", title: "Modpack deleted successfully",
type: "success" type: "success"
}); });
this_class.mutex.unlock();
});
}
}, },
error: (jqXHR, status, err) => { error: (jqXHR, status, err) => {
console.log('api/mods/packs/delete', status, err.toString()); console.log('api/mods/packs/delete', status, err.toString());
@ -184,12 +214,20 @@ class ModPackOverview extends React.Component {
e.stopPropagation(); e.stopPropagation();
} }
modPackToggleModHandler(e) { modPackToggleModHandler(e, updatesInProgress) {
e.preventDefault(); e.preventDefault();
if(updatesInProgress) {
swal("Toggle mod failed", "Can't toggle the mod, when an update is still in progress", "error");
return false;
}
let $button = $(e.target); let $button = $(e.target);
let $row = $button.parents("tr"); let $row = $button.parents("tr");
let mod_name = $row.data("mod-name"); let mod_name = $row.data("mod-name");
let mod_pack = $row.parents(".single-modpack").find("h3").html(); let mod_pack = $row.parents(".single-modpack").find("h3").html();
let this_class = this;
$.ajax({ $.ajax({
url: "/api/mods/packs/mod/toggle", url: "/api/mods/packs/mod/toggle",
@ -200,9 +238,28 @@ class ModPackOverview extends React.Component {
}, },
dataType: "JSON", dataType: "JSON",
success: (data) => { success: (data) => {
this.setState({ if(data.success) {
listPacks: data.data.mod_packs this_class.mutex.lock(() => {
let packList = this_class.state.listPacks;
packList.forEach((modPack, modPackKey) => {
if(modPack.name == mod_pack) {
packList[modPackKey].mods.mods.forEach((mod, modKey) => {
if(mod.name == mod_name) {
packList[modPackKey].mods.mods[modKey].enabled = data.data;
return false;
}
}); });
}
});
this_class.setState({
listPacks: packList
});
this_class.mutex.unlock();
});
}
}, },
error: (jqXHR, status, err) => { error: (jqXHR, status, err) => {
console.log('api/mods/packs/mod/toggle', status, err.toString()); console.log('api/mods/packs/mod/toggle', status, err.toString());
@ -215,8 +272,14 @@ class ModPackOverview extends React.Component {
}); });
} }
modPackDeleteModHandler(e) { modPackDeleteModHandler(e, updatesInProgress) {
e.preventDefault(); e.preventDefault();
if(updatesInProgress) {
swal("Delete failed", "Can't delete the mod, when an update is still in progress", "error");
return false;
}
let $button = $(e.target); let $button = $(e.target);
let $row = $button.parents("tr"); let $row = $button.parents("tr");
let mod_name = $row.data("mod-name"); let mod_name = $row.data("mod-name");
@ -243,10 +306,30 @@ class ModPackOverview extends React.Component {
}, },
dataType: "JSON", dataType: "JSON",
success: (data) => { success: (data) => {
if(data.success) {
class_this.mutex.lock(() => {
swal("Delete of mod " + mod_name + " inside modPack " + mod_pack + " successful", "", "success"); swal("Delete of mod " + mod_name + " inside modPack " + mod_pack + " successful", "", "success");
class_this.setState({
listPacks: data.data.mod_packs let packList = class_this.state.listPacks;
packList.forEach((modPack, modPackKey) => {
if(modPack.name == mod_pack) {
packList[modPackKey].mods.mods.forEach((mod, modKey) => {
if(mod.name == mod_name) {
delete packList[modPackKey].mods.mods[modKey];
return false;
}
}); });
}
});
class_this.setState({
listPacks: packList
});
class_this.mutex.unlock();
});
}
}, },
error: (jqXHR, status, err) => { error: (jqXHR, status, err) => {
console.log('api/mods/packs/mod/delete', status, err.toString()); console.log('api/mods/packs/mod/delete', status, err.toString());
@ -299,9 +382,29 @@ class ModPackOverview extends React.Component {
success: (data) => { success: (data) => {
toggleUpdateStatus(); toggleUpdateStatus();
removeVersionAvailableStatus(); removeVersionAvailableStatus();
this_class.setState({
listPacks: data.data.mod_packs if(data.success) {
this_class.mutex.lock(() => {
let packList = this_class.state.listPacks;
packList.forEach((modPack, modPackKey) => {
if(modPack.name == mod_pack) {
packList[modPackKey].mods.mods.forEach((mod, modKey) => {
if(mod.name == modname) {
packList[modPackKey].mods.mods[modKey] = data.data;
return false;
}
}); });
}
});
this_class.setState({
listPacks: packList
});
this_class.mutex.unlock();
});
}
}, },
error: (jqXHR, status, err) => { error: (jqXHR, status, err) => {
console.log('api/mods/packs/mod/update', status, err.toString()); console.log('api/mods/packs/mod/update', status, err.toString());

View File

@ -2,6 +2,7 @@ import React from 'react';
import ReactDOMServer from 'react-dom/server'; import ReactDOMServer from 'react-dom/server';
import {IndexLink} from 'react-router'; import {IndexLink} from 'react-router';
import ModOverview from './Mods/ModOverview.jsx'; import ModOverview from './Mods/ModOverview.jsx';
import locks from "locks";
class ModsContent extends React.Component { class ModsContent extends React.Component {
constructor(props) { constructor(props) {
@ -27,7 +28,9 @@ class ModsContent extends React.Component {
logged_in: false, logged_in: false,
installedMods: null, installedMods: null,
updates_available: 0, updates_available: 0,
} };
this.mutex = locks.createMutex();
} }
componentDidMount() { componentDidMount() {
@ -141,7 +144,7 @@ class ModsContent extends React.Component {
success: (data) => { success: (data) => {
this_class.setState({ this_class.setState({
installedMods: data.data.mods installedMods: data.data.mods
}) });
swal({ swal({
type: "success", type: "success",
@ -251,11 +254,18 @@ class ModsContent extends React.Component {
}) })
} }
toggleModHandler(e) { toggleModHandler(e, updatesInProgress) {
e.preventDefault(); e.preventDefault();
if(updatesInProgress) {
swal("Toggle mod failed", "Can't toggle the mod, when an update is still in progress", "error");
return false;
}
let $button = $(e.target); let $button = $(e.target);
let $row = $button.parents("tr"); let $row = $button.parents("tr");
let mod_name = $row.data("mod-name"); let mod_name = $row.data("mod-name");
let this_class = this;
$.ajax({ $.ajax({
url: "/api/mods/toggle", url: "/api/mods/toggle",
@ -265,9 +275,23 @@ class ModsContent extends React.Component {
}, },
dataType: "JSON", dataType: "JSON",
success: (data) => { success: (data) => {
this.setState({ if(data.success) {
installedMods: data.data this_class.mutex.lock(() => {
let installedMods = this_class.state.installedMods;
$.each(installedMods, (k, v) => {
if(v.name == mod_name) {
installedMods[k].enabled = data.data;
}
}); });
this_class.setState({
installedMods: installedMods
});
this_class.mutex.unlock();
});
}
}, },
error: (jqXHR, status, err) => { error: (jqXHR, status, err) => {
console.log('api/mods/toggle', status, err.toString()); console.log('api/mods/toggle', status, err.toString());
@ -280,8 +304,14 @@ class ModsContent extends React.Component {
}); });
} }
deleteModHandler(e) { deleteModHandler(e, updatesInProgress) {
e.preventDefault(); e.preventDefault();
if(updatesInProgress) {
swal("Delete failed", "Can't delete the mod, when an update is still in progress", "error");
return false;
}
let $button = $(e.target); let $button = $(e.target);
let $row = $button.parents("tr"); let $row = $button.parents("tr");
let mod_name = $row.data("mod-name"); let mod_name = $row.data("mod-name");
@ -306,10 +336,24 @@ class ModsContent extends React.Component {
}, },
dataType: "JSON", dataType: "JSON",
success: (data) => { success: (data) => {
if(data.success) {
class_this.mutex.lock(() => {
swal("Delete of mod " + mod_name + " successful", "", "success"); swal("Delete of mod " + mod_name + " successful", "", "success");
class_this.setState({ let installedMods = class_this.state.installedMods;
installedMods: data.data
installedMods.forEach((v, k) => {
if(v.name == mod_name) {
delete installedMods[k];
}
}); });
class_this.setState({
installedMods: installedMods
});
class_this.mutex.unlock();
});
}
}, },
error: (jqXHR, status, err) => { error: (jqXHR, status, err) => {
console.log('api/mods/delete', status, err.toString()); console.log('api/mods/delete', status, err.toString());
@ -401,12 +445,26 @@ class ModsContent extends React.Component {
toggleUpdateStatus(); toggleUpdateStatus();
removeVersionAvailableStatus(); removeVersionAvailableStatus();
this_class.updatesAvailable();
this_class.updateCountSubtract(); this_class.updateCountSubtract();
this_class.setState({ if(data.success) {
installedMods: data.data.mods this_class.mutex.lock(() => {
swal("Delete of mod " + modname + " successful", "", "success");
let installedMods = this_class.state.installedMods;
installedMods.forEach((v, k) => {
if(v.name == modname) {
installedMods[k] = data.data;
}
}); });
this_class.setState({
installedMods: installedMods
});
this_class.mutex.unlock();
});
}
}, },
error: (jqXHR, status, err) => { error: (jqXHR, status, err) => {
console.log('api/mods/delete', status, err.toString()); console.log('api/mods/delete', status, err.toString());
@ -451,6 +509,26 @@ class ModsContent extends React.Component {
} }
uploadModSuccessHandler(event, data) { uploadModSuccessHandler(event, data) {
// let this_class = this;
// console.log(data.response);
//
// if(data.response.success) {
// this.mutex.lock(() => {
// let installedMods = this_class.state.installedMods;
// console.log(installedMods);
// data.response.data.mods.forEach((key, mod) => {
// if(!installedMods.find((sMod) => mod.name == sMod.name)) {
// installedMods.push(mod);
// }
// });
//
// this_class.setState({
// installedMods: installedMods
// });
//
// this_class.mutex.unlock();
// });
// }
this.setState({ this.setState({
installedMods: data.response.data.mods installedMods: data.response.data.mods
}); });

View File

@ -15,6 +15,7 @@
"babel-loader": "^6.2.1", "babel-loader": "^6.2.1",
"babel-preset-es2015": "^6.3.13", "babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13", "babel-preset-react": "^6.3.13",
"locks": "^0.2.2",
"react": "^15.0.1", "react": "^15.0.1",
"react-console-component": "^0.6.1", "react-console-component": "^0.6.1",
"react-dom": "^15.0.1", "react-dom": "^15.0.1",