mirror of
https://github.com/balzack/databag.git
synced 2025-04-24 02:25:26 +00:00
implemented add new topic modal
This commit is contained in:
parent
ae9f737fa3
commit
43daaabcb1
@ -483,7 +483,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.30;
|
||||
MARKETING_VERSION = 1.31;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@ -515,7 +515,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.30;
|
||||
MARKETING_VERSION = 1.31;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
|
@ -17,8 +17,7 @@
|
||||
"@react-navigation/native": "^6.1.18",
|
||||
"@react-navigation/native-stack": "^6.11.0",
|
||||
"@react-navigation/stack": "^6.4.1",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"crypto-js": "^3.3.0",
|
||||
"databag-client-sdk": "^0.0.22",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"react": "18.2.0",
|
||||
|
@ -2,13 +2,12 @@ import {Crypto} from 'databag-client-sdk';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import {JSEncrypt} from 'jsencrypt';
|
||||
import {RSA} from 'react-native-rsa-native';
|
||||
import {generateSecureRandom} from 'react-native-securerandom';
|
||||
|
||||
export class NativeCrypto implements Crypto {
|
||||
// generate salt for pbk function
|
||||
public async pbkdfSalt(): Promise<{saltHex: string}> {
|
||||
const salt = await generateSecureRandom(16);
|
||||
const saltHex = this.uint8ToHexStr(salt);
|
||||
const salt = CryptoJS.lib.WordArray.random(128 / 8);
|
||||
const saltHex = salt.toString();
|
||||
return {saltHex};
|
||||
}
|
||||
|
||||
@ -26,15 +25,15 @@ export class NativeCrypto implements Crypto {
|
||||
|
||||
// generate random aes key
|
||||
public async aesKey(): Promise<{aesKeyHex: string}> {
|
||||
const aesKey = await generateSecureRandom(32);
|
||||
const aesKeyHex = this.uint8ToHexStr(aesKey);
|
||||
const aesKey = CryptoJS.lib.WordArray.random(256 / 8);
|
||||
const aesKeyHex = aesKey.toString();
|
||||
return {aesKeyHex};
|
||||
}
|
||||
|
||||
// generate iv to use to aes function
|
||||
public async aesIv(): Promise<{ivHex: string}> {
|
||||
const iv = await generateSecureRandom(16);
|
||||
const ivHex = this.uint8ToHexStr(iv);
|
||||
const iv = CryptoJS.lib.WordArray.random(128 / 8);
|
||||
const ivHex = iv.toString();
|
||||
return {ivHex};
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@ export const styles = StyleSheet.create({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
split: {
|
||||
display: 'flex',
|
||||
|
@ -29,6 +29,7 @@ export const styles = StyleSheet.create({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
paddingLeft: 8,
|
||||
paddingRight: 8,
|
||||
},
|
||||
|
@ -27,11 +27,13 @@ export function Card({
|
||||
<SafeAreaView style={styles.card}>
|
||||
<Image style={styles.image} resizeMode={'contain'} source={{uri: imageUrl}} />
|
||||
<View style={styles.details}>
|
||||
{name && <Text style={styles.nameSet}>{name}</Text>}
|
||||
{!name && <Text style={styles.nameUnset}>{placeholder}</Text>}
|
||||
<Text style={styles.handle}>{node ? `${handle}/${node}` : handle}</Text>
|
||||
{name && <Text numberOfLines={1} style={styles.nameSet}>{name}</Text>}
|
||||
{!name && <Text numberOfLines={1} style={styles.nameUnset}>{placeholder}</Text>}
|
||||
<Text numberOfLines={1} style={styles.handle}>{node ? `${handle}/${node}` : handle}</Text>
|
||||
</View>
|
||||
<View>
|
||||
{actions}
|
||||
</View>
|
||||
{actions}
|
||||
</SafeAreaView>
|
||||
</Pressable>
|
||||
);
|
||||
|
@ -5,6 +5,8 @@ export const en = {
|
||||
sealed: 'Sealed',
|
||||
notes: 'Notes',
|
||||
server: 'Server',
|
||||
token: 'Token',
|
||||
delayMessage: 'Key generation can take several minutes.',
|
||||
|
||||
code: 'en',
|
||||
settings: 'Settings',
|
||||
@ -256,6 +258,8 @@ export const fr = {
|
||||
sealed: 'Scellé',
|
||||
notes: 'Notes',
|
||||
server: 'Serveur',
|
||||
token: 'Code',
|
||||
delayMessage: 'La génération de clé peut prendre plusieurs minutes.',
|
||||
|
||||
code: 'fr',
|
||||
settings: 'Paramètres',
|
||||
@ -508,7 +512,9 @@ export const sp = {
|
||||
sealed: 'Sellado',
|
||||
notes: 'Notas',
|
||||
server: 'Server',
|
||||
|
||||
token: 'Código',
|
||||
delayMessage: 'La generación de claves puede tardar varios minutos.',
|
||||
|
||||
code: 'sp',
|
||||
settings: 'Configuración',
|
||||
contacts: 'Contactos',
|
||||
@ -759,7 +765,9 @@ export const pt = {
|
||||
sealed: 'Selado',
|
||||
notes: 'Notas',
|
||||
server: 'Servidor',
|
||||
|
||||
token: 'Code',
|
||||
delayMessage: 'A geração da chave pode levar vários minutos.',
|
||||
|
||||
code: 'pt',
|
||||
settings: 'Configurações',
|
||||
contacts: 'Contatos',
|
||||
@ -1010,6 +1018,8 @@ export const de = {
|
||||
sealed: 'Versiegelt',
|
||||
notes: 'Notizen',
|
||||
server: 'Servierer',
|
||||
token: 'Token',
|
||||
delayMessage: 'Die Schlüsselgenerierung kann mehrere Minuten dauern.',
|
||||
|
||||
code: 'de',
|
||||
settings: 'Einstellungen',
|
||||
@ -1261,6 +1271,8 @@ export const ru = {
|
||||
sealed: 'Запечатано',
|
||||
notes: 'Заметки',
|
||||
server: 'Сервер',
|
||||
token: 'Токен',
|
||||
delayMessage: 'Генерация ключа может занять несколько минут.',
|
||||
|
||||
code: 'ru',
|
||||
settings: 'Настройки',
|
||||
|
@ -7,6 +7,7 @@ import {Channel} from '../channel/Channel';
|
||||
import {Focus} from 'databag-client-sdk';
|
||||
import {BlurView} from '@react-native-community/blur';
|
||||
import {Card} from '../card/Card';
|
||||
import {Confirm} from '../confirm/Confirm';
|
||||
|
||||
export function Content({select}: {select: (focus: Focus) => void}) {
|
||||
const [add, setAdd] = useState(false);
|
||||
@ -14,10 +15,32 @@ export function Content({select}: {select: (focus: Focus) => void}) {
|
||||
const [sealed, setSealed] = useState(false);
|
||||
const {state, actions} = useContent();
|
||||
const theme = useTheme();
|
||||
const members = useRef(new Set<string>());
|
||||
const [subject, setSubject] = useState('');
|
||||
const [members, setMembers] = useState([]);
|
||||
const [alert, setAlert] = useState(false);
|
||||
const [alertParams] = useState({
|
||||
title: state.strings.operationFailed,
|
||||
prompt: state.strings.tryAgain,
|
||||
confirm: {
|
||||
label: state.strings.ok,
|
||||
action: () => setAlert(false),
|
||||
},
|
||||
});
|
||||
|
||||
const addTopic = async () => {
|
||||
console.log('add topic');
|
||||
setAdding(true);
|
||||
try {
|
||||
await actions.addTopic(sealed, subject, members);
|
||||
setAdd(false);
|
||||
setSubject('');
|
||||
setMembers([]);
|
||||
setSealed(false);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
setAdd(false);
|
||||
setAlert(true);
|
||||
}
|
||||
setAdding(false);
|
||||
};
|
||||
|
||||
const cards = sealed ? state.sealable : state.connected;
|
||||
@ -119,49 +142,45 @@ export function Content({select}: {select: (focus: Focus) => void}) {
|
||||
underlineStyle={styles.inputUnderline}
|
||||
placeholder={state.strings.subjectOptional}
|
||||
left={<TextInput.Icon style={styles.icon} icon="label-outline" />}
|
||||
value={state.topic}
|
||||
onChangeText={value => actions.setTopic(value)}
|
||||
value={subject}
|
||||
onChangeText={value => setSubject(value)}
|
||||
/>
|
||||
<Divider style={styles.modalDivider} />
|
||||
</Surface>
|
||||
<View style={styles.membersContainer}>
|
||||
<Divider style={styles.modalDivider} />
|
||||
<Surface elevation={0} mode="flat" style={styles.members}>
|
||||
|
||||
<FlatList
|
||||
style={styles.cards}
|
||||
data={cards}
|
||||
initialNumToRender={32}
|
||||
renderItem={({item}) => {
|
||||
|
||||
const enable = (<Switch style={styles.sealSwitch} value={members.current.has(item.guid)} onValueChange={flag => {
|
||||
if (flag) {
|
||||
members.current.add(item.guid);
|
||||
} else {
|
||||
members.current.delete(item.guid);
|
||||
}
|
||||
}} />)
|
||||
|
||||
return (
|
||||
<Card
|
||||
containerStyle={{
|
||||
...styles.card,
|
||||
borderColor: theme.colors.outlineVariant,
|
||||
}}
|
||||
imageUrl={item.imageUrl}
|
||||
name={item.name}
|
||||
handle={item.handle}
|
||||
node={item.node}
|
||||
placeholder={state.strings.name}
|
||||
actions={[enable]}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
keyExtractor={card => card.cardId}
|
||||
/>
|
||||
|
||||
</Surface>
|
||||
<Divider style={styles.modalDivider} />
|
||||
<Divider style={styles.modalDivider} />
|
||||
<Surface elevation={0} mode="flat" style={styles.members}>
|
||||
<FlatList
|
||||
style={styles.cards}
|
||||
data={cards}
|
||||
initialNumToRender={32}
|
||||
renderItem={({item}) => {
|
||||
const enable = (<Switch key="enable" style={styles.sealSwitch} value={Boolean(members.find(cardId => cardId === item.cardId))} onValueChange={flag => {
|
||||
if (flag) {
|
||||
setMembers([ item.cardId, ...members ]);
|
||||
} else {
|
||||
setMembers(members.filter(cardId => cardId != item.cardId));
|
||||
}
|
||||
}} />)
|
||||
return (
|
||||
<Card
|
||||
containerStyle={{
|
||||
...styles.card,
|
||||
borderColor: theme.colors.outlineVariant,
|
||||
}}
|
||||
imageUrl={item.imageUrl}
|
||||
name={item.name}
|
||||
handle={item.handle}
|
||||
node={item.node}
|
||||
placeholder={state.strings.name}
|
||||
actions={[enable]}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
keyExtractor={card => card.cardId}
|
||||
/>
|
||||
</Surface>
|
||||
<Divider style={styles.modalDivider} />
|
||||
</View>
|
||||
<View style={styles.addControls}>
|
||||
<View style={styles.sealable}>
|
||||
@ -183,6 +202,7 @@ export function Content({select}: {select: (focus: Focus) => void}) {
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
<Confirm show={alert} params={alertParams} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ export function useContent() {
|
||||
|
||||
useEffect(() => {
|
||||
const channels = state.sorted.map(channel => {
|
||||
const {cardId, channelId, unread, sealed, members, data, lastTopic} = channel;
|
||||
const {cardId, channelId, unread, sealed, members, dataType, data, lastTopic} = channel;
|
||||
const contacts = [] as (Card | undefined)[];
|
||||
if (cardId) {
|
||||
const card = state.cards.find(contact => contact.cardId === cardId);
|
||||
@ -110,7 +110,7 @@ export function useContent() {
|
||||
};
|
||||
|
||||
const getMessage = () => {
|
||||
if (!lastTopic) {
|
||||
if (!lastTopic || !lastTopic.status) {
|
||||
return '';
|
||||
}
|
||||
if (lastTopic.dataType === 'superbasictopic') {
|
||||
@ -219,9 +219,6 @@ export function useContent() {
|
||||
}, []);
|
||||
|
||||
const actions = {
|
||||
addChannel: () => {
|
||||
console.log('add channel');
|
||||
},
|
||||
setFilter: (filter: string) => {
|
||||
updateState({filter});
|
||||
},
|
||||
@ -231,6 +228,15 @@ export function useContent() {
|
||||
getFocus: (cardId: string | null, channelId: string) => {
|
||||
return app.state.session.setFocus(cardId, channelId);
|
||||
},
|
||||
addTopic: async (sealed: boolean, subject: string, contacts: string[]) => {
|
||||
const content = app.state.session.getContent();
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
if (sealed) {
|
||||
await content.addChannel(true, 'sealed', {subject}, contacts);
|
||||
} else {
|
||||
await content.addChannel(false, 'superbasic', {subject}, contacts);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return {state, actions};
|
||||
|
@ -747,7 +747,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
)}
|
||||
{!state.config.sealSet && (
|
||||
<>
|
||||
<Text style={styles.modalDescription}>{state.strings.sealUnset}</Text>
|
||||
<Text style={styles.modalDescription}>{state.strings.sealCreate}</Text>
|
||||
<Text style={styles.modalDescription}>{state.strings.delayMessage}</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
|
@ -2833,13 +2833,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/crypto-js@npm:^4.2.2":
|
||||
version: 4.2.2
|
||||
resolution: "@types/crypto-js@npm:4.2.2"
|
||||
checksum: 727daa0d2db35f0abefbab865c23213b6ee6a270e27e177939bbe4b70d1e84c2202d9fac4ea84859c4b4d49a4ee50f948f601327a39b69ec013288018ba07ca5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/graceful-fs@npm:^4.1.3":
|
||||
version: 4.1.9
|
||||
resolution: "@types/graceful-fs@npm:4.1.9"
|
||||
@ -3192,11 +3185,10 @@ __metadata:
|
||||
"@react-navigation/native": ^6.1.18
|
||||
"@react-navigation/native-stack": ^6.11.0
|
||||
"@react-navigation/stack": ^6.4.1
|
||||
"@types/crypto-js": ^4.2.2
|
||||
"@types/react": ^18.2.6
|
||||
"@types/react-test-renderer": ^18.0.0
|
||||
babel-jest: ^29.6.3
|
||||
crypto-js: ^4.2.0
|
||||
crypto-js: ^3.3.0
|
||||
databag-client-sdk: ^0.0.22
|
||||
eslint: ^8.19.0
|
||||
eslint-plugin-prettier: 5.0.0
|
||||
@ -4229,10 +4221,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"crypto-js@npm:^4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "crypto-js@npm:4.2.0"
|
||||
checksum: f051666dbc077c8324777f44fbd3aaea2986f198fe85092535130d17026c7c2ccf2d23ee5b29b36f7a4a07312db2fae23c9094b644cc35f7858b1b4fcaf27774
|
||||
"crypto-js@npm:^3.3.0":
|
||||
version: 3.3.0
|
||||
resolution: "crypto-js@npm:3.3.0"
|
||||
checksum: 193923143a4784b2f974366068d96fe8280168fd3fef2bfea9551a5c3e32096f5a8fa49ff4eeb5bd0b9716d325618d38cfbe6125e359a4ef488fbca93e600824
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -1,7 +1,12 @@
|
||||
import {NativeModules, Platform} from 'react-native';
|
||||
|
||||
export const en = {
|
||||
unknown: 'Unknown',
|
||||
sealed: 'Sealed',
|
||||
notes: 'Notes',
|
||||
server: 'Server',
|
||||
token: 'Token',
|
||||
delayMessage: 'Key generation can take several minutes.',
|
||||
|
||||
code: 'en',
|
||||
settings: 'Settings',
|
||||
@ -246,12 +251,16 @@ export const en = {
|
||||
disable: 'Disable',
|
||||
confirmDisable: 'Disabling Multi-Factor Authentication',
|
||||
disablePrompt: 'Are you sure you want to disable multi-factor authentication',
|
||||
}
|
||||
};
|
||||
|
||||
export const fr = {
|
||||
unknown: 'Inconnu',
|
||||
sealed: 'Scellé',
|
||||
notes: 'Notes',
|
||||
server: 'Serveur',
|
||||
token: 'Code',
|
||||
delayMessage: 'La génération de clé peut prendre plusieurs minutes.',
|
||||
|
||||
code: 'fr',
|
||||
settings: 'Paramètres',
|
||||
contacts: 'Contacts',
|
||||
@ -496,12 +505,15 @@ export const fr = {
|
||||
disable: 'Désactiver',
|
||||
confirmDisable: "Désactivation de l'authentification multi-facteurs",
|
||||
disablePrompt: "Êtes-vous sûr de vouloir désactiver l'authentification multi-facteurs",
|
||||
}
|
||||
};
|
||||
|
||||
export const sp = {
|
||||
unknown: 'Desconocido',
|
||||
sealed: 'Sellado',
|
||||
notes: 'Notas',
|
||||
server: 'Server',
|
||||
token: 'Código',
|
||||
delayMessage: 'La generación de claves puede tardar varios minutos.',
|
||||
|
||||
code: 'sp',
|
||||
settings: 'Configuración',
|
||||
@ -746,13 +758,16 @@ export const sp = {
|
||||
disable: 'Desactivar',
|
||||
confirmDisable: 'Desactivación de la autenticación de dos factores',
|
||||
disablePrompt: '¿Estás seguro de que quieres desactivar la autenticación de dos factores?',
|
||||
}
|
||||
};
|
||||
|
||||
export const pt = {
|
||||
unknown: 'Desconhecido',
|
||||
sealed: 'Selado',
|
||||
notes: 'Notas',
|
||||
|
||||
server: 'Servidor',
|
||||
token: 'Code',
|
||||
delayMessage: 'A geração da chave pode levar vários minutos.',
|
||||
|
||||
code: 'pt',
|
||||
settings: 'Configurações',
|
||||
contacts: 'Contatos',
|
||||
@ -996,12 +1011,15 @@ export const pt = {
|
||||
disable: 'Desativar',
|
||||
confirmDisable: 'Desativando Autenticação de Dois Fatores',
|
||||
disablePrompt: 'Tem certeza de que deseja desativar a autenticação de dois fatores?',
|
||||
}
|
||||
};
|
||||
|
||||
export const de = {
|
||||
unknown: 'Unbekannt',
|
||||
sealed: 'Versiegelt',
|
||||
notes: 'Notizen',
|
||||
server: 'Servierer',
|
||||
token: 'Token',
|
||||
delayMessage: 'Die Schlüsselgenerierung kann mehrere Minuten dauern.',
|
||||
|
||||
code: 'de',
|
||||
settings: 'Einstellungen',
|
||||
@ -1246,12 +1264,15 @@ export const de = {
|
||||
disable: 'Deaktivieren',
|
||||
confirmDisable: 'Deaktivierung der Zwei-Faktor-Authentifizierung',
|
||||
disablePrompt: 'Sind Sie sicher, dass Sie die Zwei-Faktor-Authentifizierung deaktivieren möchten?',
|
||||
}
|
||||
};
|
||||
|
||||
export const ru = {
|
||||
unknown: 'Неизвестно',
|
||||
sealed: 'Запечатано',
|
||||
notes: 'Заметки',
|
||||
server: 'Сервер',
|
||||
token: 'Токен',
|
||||
delayMessage: 'Генерация ключа может занять несколько минут.',
|
||||
|
||||
code: 'ru',
|
||||
settings: 'Настройки',
|
||||
@ -1496,4 +1517,5 @@ export const ru = {
|
||||
disable: 'Отключить',
|
||||
confirmDisable: 'Отключение двухфакторной аутентификации',
|
||||
disablePrompt: 'Вы уверены, что хотите отключить двухфакторную аутентификацию?',
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -247,10 +247,11 @@ export class StreamModule {
|
||||
throw new Error('seal not set');
|
||||
}
|
||||
const sealKey = await crypto.rsaEncrypt(aesKeyHex, seal.publicKey);
|
||||
const hostSeal = { publicKey: seal.publicKey, sealedKey: sealKey.encryptedDataB64 };
|
||||
const { ivHex } = await crypto.aesIv();
|
||||
const subjectData = JSON.stringify(subject);
|
||||
const { encryptedDataB64 } = await crypto.aesEncrypt(subjectData, ivHex, aesKeyHex);
|
||||
const sealedSubject = { subjectEncrypted: encryptedDataB64, subjectIv: ivHex, seals: [...seals, sealKey] };
|
||||
const sealedSubject = { subjectEncrypted: encryptedDataB64, subjectIv: ivHex, seals: [...seals, hostSeal] };
|
||||
return await addChannel(node, secure, token, type, sealedSubject, cardIds);
|
||||
}
|
||||
|
||||
@ -497,6 +498,7 @@ export class StreamModule {
|
||||
if (!item.channelKey) {
|
||||
item.channelKey = await this.getChannelKey(seals);
|
||||
}
|
||||
|
||||
if (item.channelKey) {
|
||||
const { data } = await this.crypto.aesDecrypt(subjectEncrypted, subjectIv, item.channelKey);
|
||||
item.unsealedDetail = data;
|
||||
|
Loading…
x
Reference in New Issue
Block a user