From 6b9639e927ec8e9e2c50b19d20338aaedafd7270 Mon Sep 17 00:00:00 2001 From: knoxfighter Date: Tue, 14 Nov 2017 13:13:55 +0100 Subject: [PATCH] user-interface to show everything correctly without race-conditions --- src/lockfile/lockfile.go | 8 +- src/mod_modSimple.go | 10 +- src/mods_handler.go | 30 ++-- ui/App/components/Mods/Mod.jsx | 6 +- .../components/Mods/packs/ModPackOverview.jsx | 153 +++++++++++++++--- ui/App/components/ModsContent.jsx | 108 +++++++++++-- ui/package.json | 1 + 7 files changed, 253 insertions(+), 63 deletions(-) diff --git a/src/lockfile/lockfile.go b/src/lockfile/lockfile.go index 93cab0d..4ae9f91 100644 --- a/src/lockfile/lockfile.go +++ b/src/lockfile/lockfile.go @@ -56,7 +56,6 @@ func (fl *FileLock) Lock(filePath string) error { } else { return ErrorAlreadyLocked } - log.Println("write locked") return nil } @@ -73,7 +72,6 @@ func (fl *FileLock) Unlock(filePath string) error { } else { return ErrorAlreadyLocked } - log.Println("write unlocked") return nil } @@ -90,7 +88,6 @@ func (fl *FileLock) RLock(filePath string) error { } else { return ErrorAlreadyLocked } - log.Println("read locked") return nil } @@ -107,15 +104,12 @@ func (fl *FileLock) RUnlock(filePath string) error { } else { return ErrorAlreadyLocked } - log.Println("read unlocked") return nil } func (fl *FileLock) LockW(filePath string) { - log.Println("LockW called") for { err := fl.Lock(filePath) - log.Println(err) if err == ErrorAlreadyLocked { time.Sleep(time.Second * 2) log.Println("file locked wait two seconds to access write-lock") @@ -134,7 +128,7 @@ func (fl *FileLock) RLockW(filePath string) { if err == ErrorAlreadyLocked { 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 { diff --git a/src/mod_modSimple.go b/src/mod_modSimple.go index c77e024..3cf2b69 100644 --- a/src/mod_modSimple.go +++ b/src/mod_modSimple.go @@ -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 newEnabled bool for index, mod := range mod_simple_list.Mods { 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 } } @@ -152,10 +154,10 @@ func (mod_simple_list *ModSimpleList) toggleMod(mod_name string) error { err = mod_simple_list.saveModInfoJson() if err != nil { log.Printf("error on savin new ModSimpleList: %s", err) - return err + return err, newEnabled } //i changed it already don't need to reload it - return nil + return nil, newEnabled } diff --git a/src/mods_handler.go b/src/mods_handler.go index e66f195..4c4b2c9 100644 --- a/src/mods_handler.go +++ b/src/mods_handler.go @@ -239,7 +239,7 @@ func ToggleModHandler(w http.ResponseWriter, r *http.Request) { mods, err := newMods(config.FactorioModsDir) if err == nil { - err = mods.ModSimpleList.toggleMod(mod_name) + err, resp.Data = mods.ModSimpleList.toggleMod(mod_name) } if err != nil { @@ -251,7 +251,6 @@ func ToggleModHandler(w http.ResponseWriter, r *http.Request) { return } - resp.Data = mods.listInstalledMods().ModsResult resp.Success = true if err := json.NewEncoder(w).Encode(resp); err != nil { @@ -284,7 +283,7 @@ func DeleteModHandler(w http.ResponseWriter, r *http.Request) { return } - resp.Data = mods.listInstalledMods().ModsResult + resp.Data = mod_name resp.Success = true if err := json.NewEncoder(w).Encode(resp); err != nil { @@ -341,7 +340,7 @@ func UpdateModHandler(w http.ResponseWriter, r *http.Request) { } if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) resp.Data = fmt.Sprintf("Error in deleteMod: %s", err) if err := json.NewEncoder(w).Encode(resp); err != nil { log.Printf("Error in DeleteModHandler: %s", err) @@ -349,7 +348,13 @@ func UpdateModHandler(w http.ResponseWriter, r *http.Request) { 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 if err := json.NewEncoder(w).Encode(resp); err != nil { @@ -537,7 +542,7 @@ func DeleteModPackHandler(w http.ResponseWriter, r *http.Request) { return } - resp.Data = mod_pack_map.listInstalledModPacks() + resp.Data = name resp.Success = true 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() 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 { w.WriteHeader(http.StatusInternalServerError) @@ -601,7 +606,6 @@ func ModPackToggleModHandler(w http.ResponseWriter, r *http.Request) { return } - resp.Data = mod_pack_map.listInstalledModPacks() resp.Success = true if err := json.NewEncoder(w).Encode(resp); err != nil { @@ -637,7 +641,7 @@ func ModPackDeleteModHandler(w http.ResponseWriter, r *http.Request) { return } - resp.Data = mod_pack_map.listInstalledModPacks() + resp.Data = true resp.Success = true if err := json.NewEncoder(w).Encode(resp); err != nil { @@ -679,7 +683,13 @@ func ModPackUpdateModHandler(w http.ResponseWriter, r *http.Request) { 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 if err := json.NewEncoder(w).Encode(resp); err != nil { diff --git a/ui/App/components/Mods/Mod.jsx b/ui/App/components/Mods/Mod.jsx index 883be78..2874167 100644 --- a/ui/App/components/Mods/Mod.jsx +++ b/ui/App/components/Mods/Mod.jsx @@ -125,7 +125,8 @@ class Mod extends React.Component { ref='modName' type='submit' value='Toggle' - onClick={this.props.toggleMod} + onClick={(event) => this.props.toggleMod(event, this.state.updateInProgress)} + disabled={this.state.updateInProgress} /> this.props.deleteMod(event, this.state.updateInProgress)} + disabled={this.state.updateInProgress} /> diff --git a/ui/App/components/Mods/packs/ModPackOverview.jsx b/ui/App/components/Mods/packs/ModPackOverview.jsx index eea2f3f..1ed22f9 100644 --- a/ui/App/components/Mods/packs/ModPackOverview.jsx +++ b/ui/App/components/Mods/packs/ModPackOverview.jsx @@ -2,6 +2,7 @@ import React from 'react'; import ModManager from "../ModManager.jsx"; import NativeListener from 'react-native-listener'; import {instanceOfModsContent} from "../ModsPropTypes.js"; +import locks from "locks"; class ModPackOverview extends React.Component { constructor(props) { @@ -17,6 +18,8 @@ class ModPackOverview extends React.Component { this.state = { listPacks: [] } + + this.mutex = locks.createMutex(); } componentDidMount() { @@ -65,13 +68,26 @@ class ModPackOverview extends React.Component { data: {name: inputValue}, dataType: "JSON", success: (data) => { - this_class.setState({ - listPacks: data.data.mod_packs - }); + this_class.mutex.lock(() => { + let packList = this_class.state.listPacks; - swal({ - title: "modpack created successfully", - type: "success" + data.data.mod_packs.forEach((v, k) => { + if(v.name == inputValue) { + packList.push(data.data.mod_packs[k]); + return false; + } + }); + + this_class.setState({ + listPacks: packList + }); + + swal({ + title: "modpack created successfully", + type: "success" + }); + + this_class.mutex.unlock(); }); }, error: (jqXHR, status, err) => { @@ -109,14 +125,28 @@ class ModPackOverview extends React.Component { data: {name: name}, dataType: "JSON", success: (data) => { - this_class.setState({ - listPacks: data.data.mod_packs - }); + if(data.success) { + this_class.mutex.lock(() => { + let mod_packs = this_class.state.listPacks; - swal({ - title: "Modpack deleted successfully", - type: "success" - }); + mod_packs.forEach((v, k) => { + if(v.name == name) { + delete mod_packs[k]; + } + }); + + this_class.setState({ + listPacks: mod_packs + }); + + swal({ + title: "Modpack deleted successfully", + type: "success" + }); + + this_class.mutex.unlock(); + }); + } }, error: (jqXHR, status, err) => { console.log('api/mods/packs/delete', status, err.toString()); @@ -184,12 +214,20 @@ class ModPackOverview extends React.Component { e.stopPropagation(); } - modPackToggleModHandler(e) { + modPackToggleModHandler(e, updatesInProgress) { 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 $row = $button.parents("tr"); let mod_name = $row.data("mod-name"); let mod_pack = $row.parents(".single-modpack").find("h3").html(); + let this_class = this; $.ajax({ url: "/api/mods/packs/mod/toggle", @@ -200,9 +238,28 @@ class ModPackOverview extends React.Component { }, dataType: "JSON", success: (data) => { - this.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 == 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) => { 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(); + + 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 $row = $button.parents("tr"); let mod_name = $row.data("mod-name"); @@ -243,10 +306,30 @@ class ModPackOverview extends React.Component { }, dataType: "JSON", success: (data) => { - swal("Delete of mod " + mod_name + " inside modPack " + mod_pack + " successful", "", "success"); - class_this.setState({ - listPacks: data.data.mod_packs - }); + if(data.success) { + class_this.mutex.lock(() => { + swal("Delete of mod " + mod_name + " inside modPack " + mod_pack + " successful", "", "success"); + + 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) => { console.log('api/mods/packs/mod/delete', status, err.toString()); @@ -299,9 +382,29 @@ class ModPackOverview extends React.Component { success: (data) => { toggleUpdateStatus(); 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) => { console.log('api/mods/packs/mod/update', status, err.toString()); diff --git a/ui/App/components/ModsContent.jsx b/ui/App/components/ModsContent.jsx index 91ad232..bef6d02 100644 --- a/ui/App/components/ModsContent.jsx +++ b/ui/App/components/ModsContent.jsx @@ -2,6 +2,7 @@ import React from 'react'; import ReactDOMServer from 'react-dom/server'; import {IndexLink} from 'react-router'; import ModOverview from './Mods/ModOverview.jsx'; +import locks from "locks"; class ModsContent extends React.Component { constructor(props) { @@ -27,7 +28,9 @@ class ModsContent extends React.Component { logged_in: false, installedMods: null, updates_available: 0, - } + }; + + this.mutex = locks.createMutex(); } componentDidMount() { @@ -141,7 +144,7 @@ class ModsContent extends React.Component { success: (data) => { this_class.setState({ installedMods: data.data.mods - }) + }); swal({ type: "success", @@ -251,11 +254,18 @@ class ModsContent extends React.Component { }) } - toggleModHandler(e) { + toggleModHandler(e, updatesInProgress) { 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 $row = $button.parents("tr"); let mod_name = $row.data("mod-name"); + let this_class = this; $.ajax({ url: "/api/mods/toggle", @@ -265,9 +275,23 @@ class ModsContent extends React.Component { }, dataType: "JSON", success: (data) => { - this.setState({ - installedMods: data.data - }); + if(data.success) { + 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) => { console.log('api/mods/toggle', status, err.toString()); @@ -280,8 +304,14 @@ class ModsContent extends React.Component { }); } - deleteModHandler(e) { + deleteModHandler(e, updatesInProgress) { 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 $row = $button.parents("tr"); let mod_name = $row.data("mod-name"); @@ -306,10 +336,24 @@ class ModsContent extends React.Component { }, dataType: "JSON", success: (data) => { - swal("Delete of mod " + mod_name + " successful", "", "success"); - class_this.setState({ - installedMods: data.data - }); + if(data.success) { + class_this.mutex.lock(() => { + swal("Delete of mod " + mod_name + " successful", "", "success"); + let installedMods = class_this.state.installedMods; + + 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) => { console.log('api/mods/delete', status, err.toString()); @@ -401,12 +445,26 @@ class ModsContent extends React.Component { toggleUpdateStatus(); removeVersionAvailableStatus(); - this_class.updatesAvailable(); this_class.updateCountSubtract(); - this_class.setState({ - installedMods: data.data.mods - }); + if(data.success) { + 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) => { console.log('api/mods/delete', status, err.toString()); @@ -451,6 +509,26 @@ class ModsContent extends React.Component { } 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({ installedMods: data.response.data.mods }); diff --git a/ui/package.json b/ui/package.json index 522bfdf..0c444ff 100644 --- a/ui/package.json +++ b/ui/package.json @@ -15,6 +15,7 @@ "babel-loader": "^6.2.1", "babel-preset-es2015": "^6.3.13", "babel-preset-react": "^6.3.13", + "locks": "^0.2.2", "react": "^15.0.1", "react-console-component": "^0.6.1", "react-dom": "^15.0.1",