diff --git a/app/mobile/ios/Databag.xcodeproj/project.pbxproj b/app/mobile/ios/Databag.xcodeproj/project.pbxproj
index e18215b7..d6343f17 100644
--- a/app/mobile/ios/Databag.xcodeproj/project.pbxproj
+++ b/app/mobile/ios/Databag.xcodeproj/project.pbxproj
@@ -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;
diff --git a/app/mobile/package.json b/app/mobile/package.json
index ae430929..fdf29b17 100644
--- a/app/mobile/package.json
+++ b/app/mobile/package.json
@@ -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",
diff --git a/app/mobile/src/context/sealUtil.js b/app/mobile/src/context/sealUtil.js
index d40310ec..eb54af50 100644
--- a/app/mobile/src/context/sealUtil.js
+++ b/app/mobile/src/context/sealUtil.js
@@ -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];
diff --git a/app/mobile/src/session/profile/Profile.jsx b/app/mobile/src/session/profile/Profile.jsx
index fad470ca..acf46d83 100644
--- a/app/mobile/src/session/profile/Profile.jsx
+++ b/app/mobile/src/session/profile/Profile.jsx
@@ -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() {
-
- setVisible(!state.searchable)} activeOpacity={1}>
- Visible in Registry
-
-
-
-
- setNotifications(!state.pushEnabled)} activeOpacity={1}>
- Enable Notifications
-
-
+
+
+ setVisible(!state.searchable)} activeOpacity={1}>
+ Visible in Registry
+
+
+
+
+ setNotifications(!state.pushEnabled)} activeOpacity={1}>
+ Enable Notifications
+
+
+
@@ -197,7 +199,7 @@ export function ProfileBody() {
Logout
-
+
Change Login
@@ -361,16 +363,16 @@ export function ProfileBody() {
transparent={true}
visible={state.editSeal}
supportedOrientations={['portrait', 'landscape']}
- onRequestClose={actions.hideSealEdit}
+ onRequestClose={actions.hideEditSeal}
>
Sealed Topics:
- actions.setSealable(!state.sealable)} activeOpacity={1}>
+ actions.setSealEnable(!state.sealEnabled)} activeOpacity={1}>
Enable Sealed Topics
-
+
{ state.sealMode === 'unlocking' && (
<>
@@ -438,7 +440,7 @@ export function ProfileBody() {
)}
- saving can take a minute
+ saving can take a few minutes
>
)}
{ state.sealMode === 'disabling' && (
@@ -457,23 +459,32 @@ export function ProfileBody() {
)}
-
+
Cancel
{ state.canSaveSeal && (
<>
{ state.sealMode !== 'unlocking' && state.sealMode !== 'unlocked' && (
+ { state.saving && (
+
+ )}
Save
)}
{ state.sealMode === 'unlocked' && (
+ { state.saving && (
+
+ )}
Forget
)}
{ state.sealMode === 'unlocking' && (
+ { state.saving && (
+
+ )}
Unlock
)}
@@ -483,11 +494,17 @@ export function ProfileBody() {
<>
{ state.sealMode !== 'unlocking' && (
+ { state.saving && (
+
+ )}
Save
)}
{ state.sealMode === 'unlocking' && (
+ { state.saving && (
+
+ )}
Unlock
)}
@@ -503,7 +520,7 @@ export function ProfileBody() {
transparent={true}
visible={state.editLogin}
supportedOrientations={['portrait', 'landscape']}
- onRequestClose={actions.hideLoginEdit}
+ onRequestClose={actions.hideEditLogin}
>
@@ -559,7 +576,7 @@ export function ProfileBody() {
)}
-
+
Cancel
{ (state.checked && state.available && state.editConfirm === state.editPassword && state.editPassword) && (
diff --git a/app/mobile/src/session/profile/Profile.styled.js b/app/mobile/src/session/profile/Profile.styled.js
index 54950226..a21125f6 100644
--- a/app/mobile/src/session/profile/Profile.styled.js
+++ b/app/mobile/src/session/profile/Profile.styled.js
@@ -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,
+ },
});
diff --git a/app/mobile/src/session/profile/blockedMessages/useBlockedMessages.hook.js b/app/mobile/src/session/profile/blockedMessages/useBlockedMessages.hook.js
index 69f3053d..dec98703 100644
--- a/app/mobile/src/session/profile/blockedMessages/useBlockedMessages.hook.js
+++ b/app/mobile/src/session/profile/blockedMessages/useBlockedMessages.hook.js
@@ -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) {
diff --git a/app/mobile/src/session/profile/useProfile.hook.js b/app/mobile/src/session/profile/useProfile.hook.js
index 3afcd3d1..b338ae77 100644
--- a/app/mobile/src/session/profile/useProfile.hook.js
+++ b/app/mobile/src/session/profile/useProfile.hook.js
@@ -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 };
diff --git a/app/mobile/yarn.lock b/app/mobile/yarn.lock
index 9f56ffac..409f0f1b 100644
--- a/app/mobile/yarn.lock
+++ b/app/mobile/yarn.lock
@@ -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"