fix: Handle storage correctly on software upgrade.

On 2.0.0 the persistent data was inconsistently stored with electron-settings.
It is now saved separately for each version and an upgrade path is defined for each upgrade.
This commit is contained in:
Quentin Decaunes 2020-03-17 15:48:42 +01:00
parent 8dd94fd126
commit e178e86062
10 changed files with 157 additions and 33 deletions

View File

@ -4,6 +4,7 @@ const electron = require("electron");
const path = require("path");
const isDev = require("electron-is-dev");
const electronSettings = require("electron-settings");
const upgrader = require('./upgrader.js');
const app = electron.app;
const Tray = electron.Tray;
@ -13,10 +14,12 @@ const app_version_as_string = app.getVersion().replace(/\./g, "_") + (isDev ? "-
let mainWindow;
let tray;
upgrader();
const currentSettings = () => {
const localStorage = electronSettings.get(app_version_as_string);
if (localStorage) {
return localStorage.settings;
return localStorage.appContext.settings;
}
return false;
};

101
public/upgrader.js Normal file
View File

@ -0,0 +1,101 @@
const electronSettings = require("electron-settings");
const electron = require("electron");
const isDev = require("electron-is-dev");
const app = electron.app;
const app_version_as_string = app.getVersion().replace(/\./g, "_") + (isDev ? "-dev" : "");
function storageHaveCurrentVersion() {
return typeof electronSettings.get(app_version_as_string) === "object";
}
function getAllAvailableVersion() {
var keys = Object.keys(electronSettings.getAll());
var versionAsRegex = isDev
? /^\d*_\d*_\d*(-dev)$/
: /^\d*_\d*_\d*$/;
var validKeys = keys.filter((val, index) => {
return versionAsRegex.test(val);
});
validKeys.push(app_version_as_string);
validKeys.sort((a, b) => {
return a.localeCompare(b);
});
return validKeys;
}
function storageHasPreviousVersion() {
var validKeys = getAllAvailableVersion();
return validKeys.indexOf(app_version_as_string) > 0;
}
function getPreviousVersion() {
var validKeys = getAllAvailableVersion();
const index = validKeys.indexOf(app_version_as_string);
return validKeys[index-1];
}
function from_2_0_0_MoveAppContext(previousVersion) {
electronSettings.set(
`${app_version_as_string}.appContext`,
electronSettings.get(previousVersion)
);
}
function from_2_0_0_MoveLightMode() {
electronSettings.set(
`${app_version_as_string}.lightMode`,
electronSettings.get("lightMode")
);
}
function from_2_0_0_MoveLocale() {
electronSettings.set(
`${app_version_as_string}.locale`,
electronSettings.get("locale")
);
}
function from_2_0_0_MoveReApplyPeriodically() {
electronSettings.set(
`${app_version_as_string}.reApplyPeriodically`,
electronSettings.get("reApplyPeriodically")
);
}
function from_2_0_0_MoveVotedPresets() {
electronSettings.set(
`${app_version_as_string}.votedPresets`,
electronSettings.get("votedPresets")
);
}
function upgradeFromPreviousVersion() {
isDev && console.log('Upgrading storage...');
const previousVersion = getPreviousVersion();
switch (previousVersion) {
case "2_0_0":
case "2_0_0-dev":
from_2_0_0_MoveAppContext(previousVersion);
from_2_0_0_MoveLightMode(previousVersion);
from_2_0_0_MoveLocale(previousVersion);
from_2_0_0_MoveReApplyPeriodically(previousVersion);
from_2_0_0_MoveVotedPresets(previousVersion);
isDev && console.log('... from 2.0.0');
break;
default:
break;
}
}
const upgrader = () => {
if (storageHaveCurrentVersion()) {
// No need to upgrade.
return;
}
if (!storageHasPreviousVersion()) {
// No need to upgrade.
return;
}
upgradeFromPreviousVersion();
};
module.exports = upgrader;

View File

@ -8,11 +8,13 @@ import SysInfoContext, {
SysInfoState,
createPermissiveMachineSignature,
} from "./contexts/SysInfoContext";
import LightModeContext from "./contexts/LightModeContext";
import LightModeContext, { lightModeSettingsKey } from "./contexts/LightModeContext";
import { checkNewVersion } from "./contexts/RyzenControllerAppContext";
import LocaleContext, { getTranslation } from "./contexts/LocaleContext";
import LocaleContext, { getTranslation, localeSettingsKey } from "./contexts/LocaleContext";
import LocaleSelectorModal from "./components/LocaleSelectorModal";
const si = window.require("systeminformation");
const electronSettings = window.require("electron-settings");
type AppState = {
sysinfo: SysInfoState;
@ -55,9 +57,9 @@ class App extends React.Component<{}, AppState> {
};
switchLightMode(): void {
let lightMode = window.require("electron-settings").get("lightMode") || "light";
let lightMode = electronSettings.get(lightModeSettingsKey) || "light";
lightMode = lightMode === "light" ? "dark" : "light";
window.require("electron-settings").set("lightMode", lightMode);
electronSettings.set(lightModeSettingsKey, lightMode);
this.setState({
lightMode: {
mode: lightMode,
@ -67,16 +69,16 @@ class App extends React.Component<{}, AppState> {
}
changeLocale(to: AvailableLanguages): void {
window.require("electron-settings").set("locale", to);
electronSettings.set(localeSettingsKey, to);
window.location.reload();
}
getLatestLightMode(): "light" | "dark" {
return window.require("electron-settings").get("lightMode") || "light";
return electronSettings.get(lightModeSettingsKey) || "light";
}
getLatestLocale(): AvailableLanguages {
return window.require("electron-settings").get("locale") || "en";
return electronSettings.get(localeSettingsKey) || "en";
}
componentDidMount() {

View File

@ -3,6 +3,7 @@ import logo from "../assets/icon.png";
import Badge from "./Badge";
import LightModeContext from "../contexts/LightModeContext";
import { getTranslation } from "../contexts/LocaleContext";
import AppVersion from "../contexts/AppVersion";
function TopBar() {
return (
@ -18,7 +19,7 @@ function TopBar() {
</div>
<Badge
className="uk-margin-left"
value={process.env.REACT_APP_VERSION || "dev"}
value={AppVersion.semver}
onClick={openExternal("https://gitlab.com/ryzen-controller-team/ryzen-controller/releases")}
background="#EE0000"
/>

View File

@ -0,0 +1,6 @@
export default {
string: process.env?.REACT_APP_VERSION?.replace(/\./g, "_") || "dev",
semver: process.env?.REACT_APP_VERSION || "dev",
isDev: process.env?.REACT_APP_VERSION?.indexOf("dev") !== -1,
};

View File

@ -1,4 +1,7 @@
import { createContext } from "react";
import AppVersion from "./AppVersion";
const lightModeSettingsKey = `${AppVersion.string}.lightMode`;
const LightModeContext = createContext({
mode: "light",
@ -6,4 +9,5 @@ const LightModeContext = createContext({
});
LightModeContext.displayName = "LightModeContext";
export { lightModeSettingsKey };
export default LightModeContext;

View File

@ -1,8 +1,10 @@
import { createContext } from "react";
import LocaleTranslations from "../locales/LocaleTranslations";
import AppVersion from "./AppVersion";
const fs = window.require("fs");
const electronSettings = window.require("electron-settings");
const localeSettingsKey = `${AppVersion.string}.locale`;
const LocaleContext = createContext({
is: "en",
@ -26,12 +28,12 @@ function addKeyToLocale(_id: string, _currentLocale: string, _fallback: string |
let localeFile = `src/locales/${_currentLocale}.json`;
let inter = setInterval(() => {
let lock = window.require("electron-settings").get("lock");
let lock = electronSettings.get("lock");
if (lock) {
return;
}
clearInterval(inter);
window.require("electron-settings").set("lock", true);
electronSettings.set("lock", true);
console.log(`Writting key ${id} to locale ${currentLocale}...`);
fs.readFile(localeFile, (err: string | null, data: string) => {
if (err) {
@ -44,7 +46,7 @@ function addKeyToLocale(_id: string, _currentLocale: string, _fallback: string |
localeTranslation[id] = fallback;
}
fs.writeFile(localeFile, JSON.stringify(localeTranslation, null, 4), function(err: string | null) {
window.require("electron-settings").delete("lock");
electronSettings.delete("lock");
if (err) {
console.log("error", err);
return;
@ -73,13 +75,13 @@ function addKeyToLocale(_id: string, _currentLocale: string, _fallback: string |
* @param variables Variables to replace in the sentence
*/
function getTranslation(id: string, fallback?: string, variables?: Record<string, string>): string {
const currentLocale = electronSettings.get("locale") ? (electronSettings.get("locale") as AvailableLanguages) : "en";
const currentLocale = electronSettings.get(localeSettingsKey) ? (electronSettings.get(localeSettingsKey) as AvailableLanguages) : "en";
var sentence: string | undefined = LocaleTranslations[currentLocale][id];
if (!sentence && sentence !== "") {
console.warn(`Missing translation for ${id} in locale ${currentLocale}.`);
if (process.env.REACT_APP_VERSION?.indexOf("-dev") !== -1) {
if (AppVersion.isDev) {
addKeyToLocale(id, currentLocale, fallback);
}
}
@ -100,5 +102,5 @@ function getTranslation(id: string, fallback?: string, variables?: Record<string
return sentence;
}
export { getTranslation };
export { localeSettingsKey, getTranslation };
export default LocaleContext;

View File

@ -4,10 +4,13 @@ import { isNumber } from "util";
import compareVersions from "compare-versions";
import NotificationContext from "./NotificationContext";
import { getTranslation } from "./LocaleContext";
import AppVersion from './AppVersion';
const isDev = window.require("electron-is-dev");
const electronSettings = window.require("electron-settings");
const reApplyPeriodicallySettingsKey = `${AppVersion.string}.reApplyPeriodically`;
const appContextSettingsKey = `${AppVersion.string}.appContext`;
const fileSystem = window.require("fs");
const app_version_as_string = process.env?.REACT_APP_VERSION?.replace(/\./g, "_") || "dev";
const defaultPreset = {
"--slow-time=": { enabled: false, value: getOptionDefinition("--slow-time=").default },
@ -26,7 +29,7 @@ const defaultPreset = {
const getRyzenAdjExecutablePath = function(): string {
const cwd = window.require("electron").remote.app.getAppPath();
let path: string | undefined = electronSettings.get(app_version_as_string)?.settings?.ryzenAdjPath;
let path: string | undefined = electronSettings.get(appContextSettingsKey)?.settings?.ryzenAdjPath;
if (path) {
return path;
@ -162,10 +165,10 @@ const RyzenControllerSettingsDefinitions: RyzenControllerSettingDefinitionList =
}
return new Promise((resolve, reject) => {
try {
let intervalId = electronSettings.get("reApplyPeriodically");
let intervalId = electronSettings.get(reApplyPeriodicallySettingsKey);
clearInterval(intervalId);
intervalId = false;
electronSettings.set("reApplyPeriodically", false);
electronSettings.set(reApplyPeriodicallySettingsKey, false);
if (parsedSeconds <= 0) {
resolve(false);
@ -173,9 +176,9 @@ const RyzenControllerSettingsDefinitions: RyzenControllerSettingDefinitionList =
}
electronSettings.set(
"reApplyPeriodically",
reApplyPeriodicallySettingsKey,
setInterval(() => {
let preset = electronSettings.get(app_version_as_string).currentSettings;
let preset = electronSettings.get(appContextSettingsKey).currentSettings;
if (!isPresetValid(preset)) {
return;
}
@ -336,7 +339,7 @@ const isPresetValid = function(preset: PartialRyzenAdjOptionListType): boolean {
return true;
};
let loadedContext = window.require("electron-settings").get(app_version_as_string);
let loadedContext = electronSettings.get(appContextSettingsKey);
let context = defaultRyzenControllerAppContext;
if (loadedContext) {
context = {
@ -353,11 +356,11 @@ const persistentSave = function(context: RyzenControllerAppContextType) {
...context,
currentSettings: context.latestSettings,
};
window.require("electron-settings").set(app_version_as_string, savedContext);
electronSettings.set(appContextSettingsKey, savedContext);
};
const executeRyzenAdjUsingPreset = function(presetName: string): boolean {
const presets = electronSettings.get(app_version_as_string)?.presets;
const presets = electronSettings.get(appContextSettingsKey)?.presets;
if (!presets.hasOwnProperty(presetName)) {
return false;
}
@ -407,7 +410,6 @@ export {
RyzenControllerSettingsDefinitions,
getSettingDefinition,
getRyzenAdjExecutablePath,
app_version_as_string,
executeRyzenAdjUsingPreset,
checkIfNewerReleaseExist as checkNewVersion,
isPresetValid,

View File

@ -8,7 +8,10 @@ import PresetOnline from "../components/PresetOnline";
import NotificationContext from "../contexts/NotificationContext";
import PresetsOnlineContext from "../contexts/PresetsOnline";
import { getTranslation } from "../contexts/LocaleContext";
import AppVersion from "../contexts/AppVersion";
const uikit = window.require("uikit");
const electronSettings = window.require("electron-settings");
const votedPresetsSettingsKey = `${AppVersion.string}.votedPresets`;
class PresetsScene extends React.Component<{}, PresetsOnlineContextType> {
_isMounted = false;
@ -165,7 +168,7 @@ class PresetsScene extends React.Component<{}, PresetsOnlineContextType> {
}
isUserAlreadyVotedForThisPreset(presetId: number): boolean {
const votedPresets: Array<number> = window.require("electron-settings").get("votedPresets");
const votedPresets: Array<number> = electronSettings.get(votedPresetsSettingsKey);
if (!votedPresets) {
return false;
}
@ -173,12 +176,12 @@ class PresetsScene extends React.Component<{}, PresetsOnlineContextType> {
}
retainVotedPreset(presetId: number): void {
let votedPresets = window.require("electron-settings").get("votedPresets");
let votedPresets = electronSettings.get(votedPresetsSettingsKey);
if (!votedPresets) {
votedPresets = [];
}
votedPresets.push(presetId);
window.require("electron-settings").set("votedPresets", votedPresets);
electronSettings.set(votedPresetsSettingsKey, votedPresets);
}
componentDidMount() {

View File

@ -5,7 +5,6 @@ import RyzenControllerAppContext, {
defaultPreset,
persistentSave,
getSettingDefinition,
app_version_as_string,
executeRyzenAdjUsingPreset,
} from "../contexts/RyzenControllerAppContext";
import RyzenAdjScene from "../scenes/RyzenAdjScene";
@ -13,6 +12,7 @@ import PresetsScene from "../scenes/PresetsScene";
import SettingsScene from "../scenes/SettingsScene";
import NotificationContext from "../contexts/NotificationContext";
import { getTranslation } from "../contexts/LocaleContext";
import AppVersion from "../contexts/AppVersion";
const electronSettings = window.require("electron-settings");
const powerMonitor = window.require("electron").remote.powerMonitor;
@ -27,7 +27,7 @@ class Scene extends React.Component<{}, RyzenControllerAppContextType> {
};
componentDidMount() {
let settings = electronSettings.get(app_version_as_string);
let settings = electronSettings.get(AppVersion.string);
if (settings) {
settings = settings.settings;
} else {
@ -58,7 +58,7 @@ class Scene extends React.Component<{}, RyzenControllerAppContextType> {
}
powerMonitor.on("unlock-screen", () => {
const presetName = electronSettings.get(app_version_as_string)?.settings?.onSessionResume;
const presetName = electronSettings.get(AppVersion.string)?.settings?.onSessionResume;
if (presetName) {
executeRyzenAdjUsingPreset(presetName);
}
@ -75,14 +75,14 @@ class Scene extends React.Component<{}, RyzenControllerAppContextType> {
handleBatteryStatusChange() {
powerMonitor.on("on-ac", () => {
const presetName = electronSettings.get(app_version_as_string)?.settings?.onLaptopPluggedIn;
const presetName = electronSettings.get(AppVersion.string)?.settings?.onLaptopPluggedIn;
if (presetName !== false && presetName !== "") {
executeRyzenAdjUsingPreset(presetName);
}
});
powerMonitor.on("on-battery", () => {
const presetName = electronSettings.get(app_version_as_string)?.settings?.onLaptopPluggedOut;
const presetName = electronSettings.get(AppVersion.string)?.settings?.onLaptopPluggedOut;
if (presetName !== false && presetName !== "") {
executeRyzenAdjUsingPreset(presetName);
}