merging rest of refactored profile into mobile app

This commit is contained in:
balzack 2023-02-22 22:03:55 -08:00
parent d7ea1806c1
commit 10cbd3eaab
8 changed files with 345 additions and 41 deletions

View File

@ -567,7 +567,7 @@
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++17"; CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
@ -639,7 +639,7 @@
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++17"; CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;

View File

@ -19,7 +19,8 @@
"@react-navigation/native-stack": "^6.9.10", "@react-navigation/native-stack": "^6.9.10",
"@react-navigation/stack": "^6.3.14", "@react-navigation/stack": "^6.3.14",
"axios": "^1.3.3", "axios": "^1.3.3",
"crypto-js": "^4.1.1", "crypto-js": "^3.3.0",
"jsencrypt": "^3.3.1",
"moment": "^2.29.4", "moment": "^2.29.4",
"react": "18.2.0", "react": "18.2.0",
"react-native": "0.71.3", "react-native": "0.71.3",

View File

@ -16,7 +16,7 @@ export function isUnsealed(seals, sealKey) {
return false; return false;
} }
export function getContentKey(seals, sealKey) { export async function getContentKey(seals, sealKey) {
for (let i = 0; i < seals?.length; i++) { for (let i = 0; i < seals?.length; i++) {
if (seals[i].publicKey === sealKey.public) { if (seals[i].publicKey === sealKey.public) {
const seal = seals[i]; const seal = seals[i];

View File

@ -88,7 +88,7 @@ export function ProfileBody() {
const saveSeal = async () => { const saveSeal = async () => {
try { try {
await actions.saveSeal(); await actions.saveSeal();
actions.hideSealEdit(); actions.hideEditSeal();
} }
catch (err) { catch (err) {
console.log(err); console.log(err);
@ -102,7 +102,7 @@ export function ProfileBody() {
const saveDetails = async () => { const saveDetails = async () => {
try { try {
await actions.saveDetails(); await actions.saveDetails();
actions.hideDetailEdit(); actions.hideEditDetails();
} }
catch (err) { catch (err) {
console.log(err); console.log(err);
@ -116,7 +116,7 @@ export function ProfileBody() {
const saveLogin = async () => { const saveLogin = async () => {
try { try {
await actions.saveLogin(); await actions.saveLogin();
actions.hideLoginEdit(); actions.hideEditLogin();
} }
catch (err) { catch (err) {
console.log(err); console.log(err);
@ -179,6 +179,7 @@ export function ProfileBody() {
</View> </View>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.group}>
<View style={styles.enable}> <View style={styles.enable}>
<TouchableOpacity onPress={() => setVisible(!state.searchable)} activeOpacity={1}> <TouchableOpacity onPress={() => setVisible(!state.searchable)} activeOpacity={1}>
<Text style={styles.enableText}>Visible in Registry</Text> <Text style={styles.enableText}>Visible in Registry</Text>
@ -191,13 +192,14 @@ export function ProfileBody() {
</TouchableOpacity> </TouchableOpacity>
<Switch style={styles.enableSwitch} value={state.pushEnabled} onValueChange={setNotifications} trackColor={styles.switch}/> <Switch style={styles.enableSwitch} value={state.pushEnabled} onValueChange={setNotifications} trackColor={styles.switch}/>
</View> </View>
</View>
<TouchableOpacity style={styles.logout} activeOpacity={1} onPress={logout}> <TouchableOpacity style={styles.logout} activeOpacity={1} onPress={logout}>
<Ionicons name="logout" size={14} color={Colors.primary} /> <Ionicons name="logout" size={14} color={Colors.primary} />
<Text style={styles.logoutText}>Logout</Text> <Text style={styles.logoutText}>Logout</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.logout} onPress={actions.showLoginEdit}> <TouchableOpacity style={styles.logout} onPress={actions.showEditLogin}>
<Ionicons name="lock" size={16} color={Colors.primary} /> <Ionicons name="lock" size={16} color={Colors.primary} />
<Text style={styles.logoutText}>Change Login</Text> <Text style={styles.logoutText}>Change Login</Text>
</TouchableOpacity> </TouchableOpacity>
@ -361,16 +363,16 @@ export function ProfileBody() {
transparent={true} transparent={true}
visible={state.editSeal} visible={state.editSeal}
supportedOrientations={['portrait', 'landscape']} supportedOrientations={['portrait', 'landscape']}
onRequestClose={actions.hideSealEdit} onRequestClose={actions.hideEditSeal}
> >
<KeyboardAvoidingView behavior="height" style={styles.modalWrapper}> <KeyboardAvoidingView behavior="height" style={styles.modalWrapper}>
<View style={styles.modalContainer}> <View style={styles.modalContainer}>
<Text style={styles.modalHeader}>Sealed Topics:</Text> <Text style={styles.modalHeader}>Sealed Topics:</Text>
<View style={styles.sealable}> <View style={styles.sealable}>
<TouchableOpacity onPress={() => actions.setSealable(!state.sealable)} activeOpacity={1}> <TouchableOpacity onPress={() => actions.setSealEnable(!state.sealEnabled)} activeOpacity={1}>
<Text style={styles.sealableText}>Enable Sealed Topics</Text> <Text style={styles.sealableText}>Enable Sealed Topics</Text>
</TouchableOpacity> </TouchableOpacity>
<Switch style={styles.enableSwitch} value={state.sealable} onValueChange={actions.setSealable} trackColor={styles.switch}/> <Switch style={styles.enableSwitch} value={state.sealEnabled} onValueChange={actions.setSealEnable} trackColor={styles.switch}/>
</View> </View>
{ state.sealMode === 'unlocking' && ( { state.sealMode === 'unlocking' && (
<> <>
@ -438,7 +440,7 @@ export function ProfileBody() {
</TouchableOpacity> </TouchableOpacity>
</View> </View>
)} )}
<Text style={styles.notice}>saving can take a minute</Text> <Text style={styles.notice}>saving can take a few minutes</Text>
</> </>
)} )}
{ state.sealMode === 'disabling' && ( { state.sealMode === 'disabling' && (
@ -457,23 +459,32 @@ export function ProfileBody() {
</View> </View>
)} )}
<View style={styles.modalControls}> <View style={styles.modalControls}>
<TouchableOpacity style={styles.cancel} onPress={actions.hideSealEdit}> <TouchableOpacity style={styles.cancel} onPress={actions.hideEditSeal}>
<Text>Cancel</Text> <Text>Cancel</Text>
</TouchableOpacity> </TouchableOpacity>
{ state.canSaveSeal && ( { state.canSaveSeal && (
<> <>
{ state.sealMode !== 'unlocking' && state.sealMode !== 'unlocked' && ( { state.sealMode !== 'unlocking' && state.sealMode !== 'unlocked' && (
<TouchableOpacity style={styles.save} onPress={saveSeal}> <TouchableOpacity style={styles.save} onPress={saveSeal}>
{ state.saving && (
<ActivityIndicator style={styles.activity} color={Colors.white} />
)}
<Text style={styles.saveText}>Save</Text> <Text style={styles.saveText}>Save</Text>
</TouchableOpacity> </TouchableOpacity>
)} )}
{ state.sealMode === 'unlocked' && ( { state.sealMode === 'unlocked' && (
<TouchableOpacity style={styles.save} onPress={saveSeal}> <TouchableOpacity style={styles.save} onPress={saveSeal}>
{ state.saving && (
<ActivityIndicator style={styles.activity} color={Colors.white} />
)}
<Text style={styles.saveText}>Forget</Text> <Text style={styles.saveText}>Forget</Text>
</TouchableOpacity> </TouchableOpacity>
)} )}
{ state.sealMode === 'unlocking' && ( { state.sealMode === 'unlocking' && (
<TouchableOpacity style={styles.save} onPress={saveSeal}> <TouchableOpacity style={styles.save} onPress={saveSeal}>
{ state.saving && (
<ActivityIndicator style={styles.activity} color={Colors.white} />
)}
<Text style={styles.saveText}>Unlock</Text> <Text style={styles.saveText}>Unlock</Text>
</TouchableOpacity> </TouchableOpacity>
)} )}
@ -483,11 +494,17 @@ export function ProfileBody() {
<> <>
{ state.sealMode !== 'unlocking' && ( { state.sealMode !== 'unlocking' && (
<View style={styles.disabled}> <View style={styles.disabled}>
{ state.saving && (
<ActivityIndicator style={styles.activity} color={Colors.white} />
)}
<Text style={styles.disabledText}>Save</Text> <Text style={styles.disabledText}>Save</Text>
</View> </View>
)} )}
{ state.sealMode === 'unlocking' && ( { state.sealMode === 'unlocking' && (
<View style={styles.disabled}> <View style={styles.disabled}>
{ state.saving && (
<ActivityIndicator style={styles.activity} color={Colors.white} />
)}
<Text style={styles.disabledText}>Unlock</Text> <Text style={styles.disabledText}>Unlock</Text>
</View> </View>
)} )}
@ -503,7 +520,7 @@ export function ProfileBody() {
transparent={true} transparent={true}
visible={state.editLogin} visible={state.editLogin}
supportedOrientations={['portrait', 'landscape']} supportedOrientations={['portrait', 'landscape']}
onRequestClose={actions.hideLoginEdit} onRequestClose={actions.hideEditLogin}
> >
<KeyboardAvoidingView behavior="height" style={styles.modalWrapper}> <KeyboardAvoidingView behavior="height" style={styles.modalWrapper}>
<View style={styles.modalContainer}> <View style={styles.modalContainer}>
@ -559,7 +576,7 @@ export function ProfileBody() {
</View> </View>
)} )}
<View style={styles.modalControls}> <View style={styles.modalControls}>
<TouchableOpacity style={styles.cancel} onPress={actions.hideLoginEdit}> <TouchableOpacity style={styles.cancel} onPress={actions.hideEditLogin}>
<Text>Cancel</Text> <Text>Cancel</Text>
</TouchableOpacity> </TouchableOpacity>
{ (state.checked && state.available && state.editConfirm === state.editPassword && state.editPassword) && ( { (state.checked && state.available && state.editConfirm === state.editPassword && state.editPassword) && (

View File

@ -10,6 +10,10 @@ export const styles = StyleSheet.create({
button: { button: {
paddingRight: 16, paddingRight: 16,
}, },
switch: {
false: Colors.grey,
true: Colors.background,
},
headerText: { headerText: {
fontSize: 18, fontSize: 18,
overflow: 'hidden', overflow: 'hidden',
@ -31,7 +35,7 @@ export const styles = StyleSheet.create({
color: Colors.alert, color: Colors.alert,
}, },
logout: { logout: {
marginTop: 8, marginTop: 16,
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
@ -41,9 +45,10 @@ export const styles = StyleSheet.create({
logoutText: { logoutText: {
marginLeft: 8, marginLeft: 8,
color: Colors.primary, color: Colors.primary,
fontSize: 16,
}, },
delete: { delete: {
marginTop: 8, marginTop: 16,
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
@ -53,6 +58,7 @@ export const styles = StyleSheet.create({
deleteText: { deleteText: {
marginLeft: 8, marginLeft: 8,
color: Colors.alert, color: Colors.alert,
fontSize: 16,
}, },
modalWrapper: { modalWrapper: {
display: 'flex', display: 'flex',
@ -89,7 +95,7 @@ export const styles = StyleSheet.create({
borderRadius: 4, borderRadius: 4,
padding: 8, padding: 8,
marginRight: 8, marginRight: 8,
width: 72, width: 88,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
}, },
@ -114,6 +120,10 @@ export const styles = StyleSheet.create({
removeText: { removeText: {
color: Colors.white, color: Colors.white,
}, },
input: {
fontSize: 14,
flexGrow: 1,
},
inputField: { inputField: {
width: '100%', width: '100%',
borderWidth: 1, borderWidth: 1,
@ -184,9 +194,11 @@ export const styles = StyleSheet.create({
padding: 8, padding: 8,
borderRadius: 4, borderRadius: 4,
backgroundColor: Colors.primary, backgroundColor: Colors.primary,
width: 72, width: 88,
display: 'flex', display: 'flex',
flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center',
}, },
saveText: { saveText: {
color: Colors.white, color: Colors.white,
@ -202,10 +214,14 @@ export const styles = StyleSheet.create({
flexDirection: 'row', flexDirection: 'row',
}, },
blockedLabel: { blockedLabel: {
marginTop: 24, marginTop: 32,
alignSelf: 'center', alignSelf: 'center',
color: Colors.grey, color: Colors.grey,
}, },
group: {
marginTop: 16,
marginBottom: 16,
},
enable: { enable: {
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
@ -213,10 +229,11 @@ export const styles = StyleSheet.create({
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
width: '100%', width: '100%',
marginTop: 8, marginBottom: 4,
}, },
enableText: { enableText: {
color: Colors.primary, color: Colors.primary,
fontSize: 16,
}, },
enableSwitch: { enableSwitch: {
transform: [{ scaleX: .6 }, { scaleY: .6 }], transform: [{ scaleX: .6 }, { scaleY: .6 }],
@ -238,5 +255,45 @@ export const styles = StyleSheet.create({
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
}, },
sealable: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
paddingBottom: 16,
},
disabled: {
borderWidth: 1,
borderColor: Colors.lightgrey,
padding: 8,
borderRadius: 4,
width: 88,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
activity: {
paddingRight: 4,
},
disabledText: {
color: Colors.disabled,
},
sealUpdate: {
position: 'absolute',
top: 0,
height: 36,
left: 8,
width: '100%',
},
notice: {
color: Colors.grey,
fontStyle: 'italic',
textAlign: 'center',
paddingBottom: 8,
},
warn: {
paddingTop: 2,
paddingRight: 8,
},
}); });

View File

@ -79,8 +79,8 @@ export function useBlockedMessages() {
}); });
}); });
for(let i = 0; i < channels.length; i++) {
const merged = []; const merged = [];
for(let i = 0; i < channels.length; i++) {
let topics; let topics;
const { cardId, channelId } = channels[i]; const { cardId, channelId } = channels[i];
if (cardId) { if (cardId) {

View File

@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { ProfileContext } from 'context/ProfileContext'; import { ProfileContext } from 'context/ProfileContext';
import { AccountContext } from 'context/AccountContext'; import { AccountContext } from 'context/AccountContext';
import { AppContext } from 'context/AppContext'; import { AppContext } from 'context/AppContext';
import { generateSeal, updateSeal, unlockSeal } from 'context/sealUtil';
export function useProfile() { export function useProfile() {
@ -25,6 +26,30 @@ export function useProfile() {
blockedMessages: false, blockedMessages: false,
logginOut: false, logginOut: false,
disconnected: false, disconnected: false,
pushEnabled: false,
searchable: false,
sealableFalse: false,
editPassword: null,
editConfirm: null,
showPassword: false,
showConfirm: false,
saving: false,
checked: true,
available: true,
seal: null,
sealKey: null,
sealEnabled: false,
sealUnlocked: false,
sealMode: null,
sealUnlock: null,
sealPassword: null,
sealConfirm: null,
sealDelete: null,
canSaveSeal: false,
showSealUnlock: false,
showSealConfirm: false,
showSealPassword: false,
}); });
const app = useContext(AppContext); const app = useContext(AppContext);
@ -32,10 +57,54 @@ export function useProfile() {
const profile = useContext(ProfileContext); const profile = useContext(ProfileContext);
const navigate = useNavigate(); const navigate = useNavigate();
const debounce = useRef();
const updateState = (value) => { const updateState = (value) => {
setState((s) => ({ ...s, ...value })); setState((s) => ({ ...s, ...value }));
} }
const unlock = async () => {
const sealKey = unlockSeal(state.seal, state.sealUnlock);
await account.actions.unlockAccountSeal(sealKey);
};
const forget = async () => {
await account.actions.unlockAccountSeal({});
}
const update = async () => {
const updated = updateSeal(state.seal, state.sealKey, state.sealPassword);
await account.actions.setAccountSeal(updated.seal, updated.sealKey);
}
const enable = async () => {
const created = await generateSeal(state.sealPassword);
await account.actions.setAccountSeal(created.seal, created.sealKey);
}
const disable = async () => {
await account.actions.setAccountSeal({}, {});
}
useEffect(() => {
if (state.sealMode === 'unlocked') {
return updateState({ canSaveSeal: true });
}
if (state.sealMode === 'unlocking' && state.sealUnlock != null && state.sealUnlock !== '') {
return updateState({ canSaveSeal: true });
}
if (state.sealMode === 'enabling' && state.sealPassword != null && state.sealPassword === state.sealConfirm) {
return updateState({ canSaveSeal: true });
}
if (state.sealMode === 'disabling' && state.sealDelete === 'delete') {
return updateState({ canSaveSeal: true });
}
if (state.sealMode === 'updating' && state.sealPassword != null && state.sealPassword === state.sealConfirm) {
return updateState({ canSaveSeal: true });
}
updateState({ canSaveSeal: false });
}, [state.sealMode, state.sealable, state.sealUnlock, state.sealPassword, state.sealConfirm, state.sealDelete]);
useEffect(() => { useEffect(() => {
const { name, handle, node, location, description, image } = profile.state.identity; const { name, handle, node, location, description, image } = profile.state.identity;
const imageSource = image ? profile.state.imageUrl : 'avatar'; const imageSource = image ? profile.state.imageUrl : 'avatar';
@ -44,8 +113,11 @@ export function useProfile() {
}, [profile.state]); }, [profile.state]);
useEffect(() => { useEffect(() => {
const { sealable } = account.state.status || {}; const { searchable, pushEnabled, seal, sealable } = account.state.status;
updateState({ sealable }); const sealKey = account.state.sealKey;
const sealEnabled = seal?.publicKey != null;
const sealUnlocked = seal?.publicKey === sealKey?.public && sealKey?.private && sealKey?.public;
updateState({ searchable, sealable, pushEnabled, seal, sealKey, sealEnabled, sealUnlocked });
}, [account.state]); }, [account.state]);
useEffect(() => { useEffect(() => {
@ -79,13 +151,24 @@ export function useProfile() {
updateState({ editDetails: false }); updateState({ editDetails: false });
}, },
showEditLogin: () => { showEditLogin: () => {
updateState({ editLogin: true }); updateState({ editLogin: true, editPassword: null, editConfirm: null });
}, },
hideEditLogin: () => { hideEditLogin: () => {
updateState({ editLogin: false }); updateState({ editLogin: false });
}, },
showEditSeal: () => { showEditSeal: () => {
updateState({ editSeal: true }); let sealMode = null;
const sealable = state.sealEnabled;
if (state.sealEnabled && !state.sealUnlocked) {
sealMode = 'unlocking';
}
else if (state.sealEnabled && state.sealUnlocked) {
sealMode = 'unlocked';
}
else {
sealMode = 'disabled';
}
updateState({ editSeal: true, sealMode, sealUnlock: null, sealPassword: null, sealConfirm: null, sealDelete: null });
}, },
hideEditSeal: () => { hideEditSeal: () => {
updateState({ editSeal: false }); updateState({ editSeal: false });
@ -117,6 +200,147 @@ export function useProfile() {
setEditDescription: (editDescription) => { setEditDescription: (editDescription) => {
updateState({ editDescription }); updateState({ editDescription });
}, },
setSealUnlock: (sealUnlock) => {
updateState({ sealUnlock });
},
setSealPassword: (sealPassword) => {
updateState({ sealPassword });
},
setSealConfirm: (sealConfirm) => {
updateState({ sealConfirm });
},
setSealDelete: (sealDelete) => {
updateState({ sealDelete });
},
showSealUnlock: () => {
updateState({ showSealUnlock: true });
},
hideSealUnlock: () => {
updateState({ showSealUnlock: false });
},
showSealPassword: () => {
updateState({ showSealPassword: true });
},
hideSealPassword: () => {
updateState({ showSealPassword: false });
},
showSealConfirm: () => {
updateState({ showSealConfirm: true });
},
hideSealConfirm: () => {
updateState({ showSealConfirm: false });
},
updateSeal: () => {
updateState({ sealMode: 'updating' });
},
setSealEnable: (sealEnabled) => {
let sealMode = null;
if (sealEnabled !== state.sealEnabled) {
if (sealEnabled) {
sealMode = 'enabling';
}
else {
sealMode = 'disabling';
}
}
else {
if (state.sealEnabled && !state.sealUnlocked) {
sealMode = 'unlocking';
}
else if (state.sealEnabled && state.sealUnlocked) {
sealMode = 'unlocked';
}
else {
sealMode = 'disabled';
}
}
updateState({ sealEnabled, sealMode });
},
saveSeal: async () => {
if (!state.saving) {
try {
updateState({ saving: true });
if (state.sealMode === 'enabling') {
await enable();
}
else if (state.sealMode === 'disabling') {
await disable();
}
else if (state.sealMode === 'unlocking') {
await unlock();
}
else if (state.sealMode === 'unlocked') {
await forget();
}
else if (state.sealMode === 'updating') {
await update();
}
else {
console.log(state.sealMode);
}
updateState({ saving: false });
}
catch(err) {
console(err);
updateState({ saving: false });
}
}
},
setVisible: async (searchable) => {
updateState({ searchable });
await account.actions.setSearchable(searchable);
},
setNotifications: async (pushEnabled) => {
updateState({ pushEnabled });
await account.actions.setNotifications(pushEnabled);
},
setEditPassword: (editPassword) => {
updateState({ editPassword });
},
setEditConfirm: (editConfirm) => {
updateState({ editConfirm });
},
showPassword: () => {
updateState({ showPassword: true });
},
hidePassword: () => {
updateState({ showPassword: false });
},
showConfirm: () => {
updateState({ showConfirm: true });
},
hideConfirm: () => {
updateState({ showConfirm: false });
},
setEditHandle: (editHandle) => {
updateState({ editHandle, checked: false });
if (debounce.current != null) {
clearTimeout(debounce.current);
}
debounce.current = setTimeout(async () => {
try {
if (editHandle === state.handle) {
updateState({ available: true, checked: true });
}
else {
const available = await profile.actions.getHandleStatus(editHandle);
updateState({ available, checked: true });
}
}
catch (err) {
console.log(err);
}
}, 1000);
},
saveDetails: async () => {
await profile.actions.setProfileData(state.editName, state.editLocation, state.editDescription);
},
saveLogin: async () => {
await account.actions.setLogin(state.editHandle, state.editPassword);
},
}; };
return { state, actions }; return { state, actions };

View File

@ -2938,10 +2938,10 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0" shebang-command "^2.0.0"
which "^2.0.1" which "^2.0.1"
crypto-js@^4.1.1: crypto-js@^3.3.0:
version "4.1.1" version "3.3.0"
resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b"
integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==
csstype@^3.0.2: csstype@^3.0.2:
version "3.1.1" version "3.1.1"
@ -4988,6 +4988,11 @@ jscodeshift@^0.13.1:
temp "^0.8.4" temp "^0.8.4"
write-file-atomic "^2.3.0" write-file-atomic "^2.3.0"
jsencrypt@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/jsencrypt/-/jsencrypt-3.3.1.tgz#5d47e6c1079bb50e714a36d009ba09ea81fbfa18"
integrity sha512-dVvV54GdFuJgmEKn+oBiaifDMen4p6o6j/lJh0OVMcouME8sST0bJ7bldIgKBQk4za0zyGn0/pm4vOznR25mLw==
jsesc@^2.5.1: jsesc@^2.5.1:
version "2.5.2" version "2.5.2"
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"