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 = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@ -639,7 +639,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;

View File

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

View File

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

View File

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

View File

@ -10,6 +10,10 @@ export const styles = StyleSheet.create({
button: {
paddingRight: 16,
},
switch: {
false: Colors.grey,
true: Colors.background,
},
headerText: {
fontSize: 18,
overflow: 'hidden',
@ -31,7 +35,7 @@ export const styles = StyleSheet.create({
color: Colors.alert,
},
logout: {
marginTop: 8,
marginTop: 16,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
@ -41,9 +45,10 @@ export const styles = StyleSheet.create({
logoutText: {
marginLeft: 8,
color: Colors.primary,
fontSize: 16,
},
delete: {
marginTop: 8,
marginTop: 16,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
@ -53,6 +58,7 @@ export const styles = StyleSheet.create({
deleteText: {
marginLeft: 8,
color: Colors.alert,
fontSize: 16,
},
modalWrapper: {
display: 'flex',
@ -89,7 +95,7 @@ export const styles = StyleSheet.create({
borderRadius: 4,
padding: 8,
marginRight: 8,
width: 72,
width: 88,
display: 'flex',
alignItems: 'center',
},
@ -114,6 +120,10 @@ export const styles = StyleSheet.create({
removeText: {
color: Colors.white,
},
input: {
fontSize: 14,
flexGrow: 1,
},
inputField: {
width: '100%',
borderWidth: 1,
@ -184,9 +194,11 @@ export const styles = StyleSheet.create({
padding: 8,
borderRadius: 4,
backgroundColor: Colors.primary,
width: 72,
width: 88,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
saveText: {
color: Colors.white,
@ -202,10 +214,14 @@ export const styles = StyleSheet.create({
flexDirection: 'row',
},
blockedLabel: {
marginTop: 24,
marginTop: 32,
alignSelf: 'center',
color: Colors.grey,
},
group: {
marginTop: 16,
marginBottom: 16,
},
enable: {
display: 'flex',
flexDirection: 'row',
@ -213,10 +229,11 @@ export const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
width: '100%',
marginTop: 8,
marginBottom: 4,
},
enableText: {
color: Colors.primary,
fontSize: 16,
},
enableSwitch: {
transform: [{ scaleX: .6 }, { scaleY: .6 }],
@ -238,5 +255,45 @@ export const styles = StyleSheet.create({
display: 'flex',
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() {
});
});
const merged = [];
for(let i = 0; i < channels.length; i++) {
const merged = [];
let topics;
const { cardId, channelId } = channels[i];
if (cardId) {

View File

@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { ProfileContext } from 'context/ProfileContext';
import { AccountContext } from 'context/AccountContext';
import { AppContext } from 'context/AppContext';
import { generateSeal, updateSeal, unlockSeal } from 'context/sealUtil';
export function useProfile() {
@ -25,6 +26,30 @@ export function useProfile() {
blockedMessages: false,
logginOut: 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);
@ -32,10 +57,54 @@ export function useProfile() {
const profile = useContext(ProfileContext);
const navigate = useNavigate();
const debounce = useRef();
const updateState = (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(() => {
const { name, handle, node, location, description, image } = profile.state.identity;
const imageSource = image ? profile.state.imageUrl : 'avatar';
@ -44,8 +113,11 @@ export function useProfile() {
}, [profile.state]);
useEffect(() => {
const { sealable } = account.state.status || {};
updateState({ sealable });
const { searchable, pushEnabled, seal, sealable } = account.state.status;
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]);
useEffect(() => {
@ -79,13 +151,24 @@ export function useProfile() {
updateState({ editDetails: false });
},
showEditLogin: () => {
updateState({ editLogin: true });
updateState({ editLogin: true, editPassword: null, editConfirm: null });
},
hideEditLogin: () => {
updateState({ editLogin: false });
},
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: () => {
updateState({ editSeal: false });
@ -117,6 +200,147 @@ export function useProfile() {
setEditDescription: (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 };

View File

@ -2938,10 +2938,10 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
crypto-js@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz"
integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==
crypto-js@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b"
integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==
csstype@^3.0.2:
version "3.1.1"
@ -4988,6 +4988,11 @@ jscodeshift@^0.13.1:
temp "^0.8.4"
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:
version "2.5.2"
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"