Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
import {
createSlice,
addListener,
isAnyOf,
} from "moz-src:///comm/third_party/redux/redux-toolkit/redux-toolkit.mjs";
import { store } from "moz-src:///comm/mail/base/content/state/store.mjs";
const { XPCOMUtils } = ChromeUtils.importESModule(
);
/**
* Create a slice for a preference. Populated with a set and reset action/reducer
* pair and a selectValue selector.
*
* The slice name is only based on the preference.
*
* @param {string} preference - The string path of the preference.
* @param {any} fallbackValue - The value to use as fallback.
* @param {Function} [transform = value => value] - A transformer for the
* preference value, applied before it is stored in the redux store.
* @param {object} [extraReducers = {}] - Additional reducers (and actions) to
* add to the slice.
* @returns {object} Redux slice for the preference.
*/
export const createPreferenceSlice = (
preference,
fallbackValue,
transform = value => value,
extraReducers = {}
) => {
const sliceScope = {};
const preferenceSlice = createSlice({
name: `prefs/${preference}`,
initialState: () => transform(sliceScope.pref),
reducers: {
...extraReducers,
set: (state, action) => {
return transform(action.payload);
},
// Reducer/action pair for syncing the pref from a pref change.
sync(state, action) {
return transform(action.payload);
},
reset: () => {
return transform(sliceScope.pref);
},
},
selectors: {
selectValue: state => state,
},
});
XPCOMUtils.defineLazyPreferenceGetter(
sliceScope,
"pref",
preference,
fallbackValue,
(pref, previousValue, newValue) => {
store.dispatch(preferenceSlice.actions.sync(newValue));
}
);
store.dispatch(
addListener({
matcher: isAnyOf(
preferenceSlice.actions.set,
preferenceSlice.actions.reset
),
effect: action => {
if (preferenceSlice.actions.set.match(action)) {
switch (Services.prefs.getPrefType(preference)) {
case Ci.nsIPrefBranch.PREF_STRING:
Services.prefs.setStringPref(preference, action.payload);
break;
case Ci.nsIPrefBranch.PREF_INT:
Services.prefs.setIntPref(preference, action.payload);
break;
case Ci.nsIPrefBranch.PREF_BOOL:
Services.prefs.setBoolPref(preference, action.payload);
break;
case Ci.nsIPrefBranch.PREF_INVALID:
default:
throw new Error("Invalid preference type");
}
} else if (preferenceSlice.actions.reset.match(action)) {
Services.prefs.clearUserPref(preference);
}
},
})
);
return preferenceSlice;
};
/**
* Create a slice for a boolean preference, populated with common actions and
* reducers (toggle, setTrue, setFalse, set, reset) and a selectValue selector.
*
* The slice name is only based on the preference.
*
* @param {string} preference - The string path of the preference.
* @param {boolean} fallbackValue - The boolean value to use as fallback.
* @returns {object} A Redux slice.
*/
export const createBoolPreferenceSlice = (preference, fallbackValue) => {
const preferenceSlice = createPreferenceSlice(
preference,
fallbackValue,
value => Boolean(value),
{
toggle: state => {
return !state;
},
setTrue: () => {
return true;
},
setFalse: () => {
return false;
},
}
);
store.dispatch(
addListener({
matcher: isAnyOf(
preferenceSlice.actions.toggle,
preferenceSlice.actions.setTrue,
preferenceSlice.actions.setFalse
),
effect: (action, context) => {
if (preferenceSlice.actions.toggle.match(action)) {
const state = preferenceSlice.selectors.selectValue(
context.getOriginalState()
);
Services.prefs.setBoolPref(preference, !state);
} else if (preferenceSlice.actions.setTrue.match(action)) {
Services.prefs.setBoolPref(preference, true);
} else if (preferenceSlice.actions.setFalse.match(action)) {
Services.prefs.setBoolPref(preference, false);
}
},
})
);
return preferenceSlice;
};