mirror of
https://github.com/balzack/databag.git
synced 2025-02-11 19:19:16 +00:00
merging back refactored profile screen
This commit is contained in:
parent
d91a120ec8
commit
afc8c45721
@ -567,7 +567,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
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++14";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
|
@ -428,6 +428,15 @@ PODS:
|
||||
- RNFBApp
|
||||
- RNGestureHandler (2.9.0):
|
||||
- React-Core
|
||||
- RNImageCropPicker (0.39.0):
|
||||
- React-Core
|
||||
- React-RCTImage
|
||||
- RNImageCropPicker/QBImagePickerController (= 0.39.0)
|
||||
- TOCropViewController
|
||||
- RNImageCropPicker/QBImagePickerController (0.39.0):
|
||||
- React-Core
|
||||
- React-RCTImage
|
||||
- TOCropViewController
|
||||
- RNReanimated (2.14.4):
|
||||
- DoubleConversion
|
||||
- FBLazyVector
|
||||
@ -460,6 +469,7 @@ PODS:
|
||||
- React-RCTImage
|
||||
- RNVectorIcons (9.2.0):
|
||||
- React-Core
|
||||
- TOCropViewController (2.6.1)
|
||||
- Yoga (1.14.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
@ -506,6 +516,7 @@ DEPENDENCIES:
|
||||
- "RNFBApp (from `../node_modules/@react-native-firebase/app`)"
|
||||
- "RNFBMessaging (from `../node_modules/@react-native-firebase/messaging`)"
|
||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
|
||||
- RNReanimated (from `../node_modules/react-native-reanimated`)
|
||||
- RNScreens (from `../node_modules/react-native-screens`)
|
||||
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
|
||||
@ -525,6 +536,7 @@ SPEC REPOS:
|
||||
- libevent
|
||||
- nanopb
|
||||
- PromisesObjC
|
||||
- TOCropViewController
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
boost:
|
||||
@ -609,6 +621,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/@react-native-firebase/messaging"
|
||||
RNGestureHandler:
|
||||
:path: "../node_modules/react-native-gesture-handler"
|
||||
RNImageCropPicker:
|
||||
:path: "../node_modules/react-native-image-crop-picker"
|
||||
RNReanimated:
|
||||
:path: "../node_modules/react-native-reanimated"
|
||||
RNScreens:
|
||||
@ -672,9 +686,11 @@ SPEC CHECKSUMS:
|
||||
RNFBApp: 4f8ea53443d52c7db793234d2398a357fc6cfbf1
|
||||
RNFBMessaging: c686471358d20d54f716a8b7b7f10f8944c966ec
|
||||
RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
|
||||
RNImageCropPicker: 14fe1c29298fb4018f3186f455c475ab107da332
|
||||
RNReanimated: cc5e3aa479cb9170bcccf8204291a6950a3be128
|
||||
RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
|
||||
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
|
||||
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
|
||||
Yoga: 5ed1699acbba8863755998a4245daa200ff3817b
|
||||
|
||||
PODFILE CHECKSUM: 8a4cbf61c865c6e404d389f26395fa04926b8f43
|
||||
|
@ -20,11 +20,13 @@
|
||||
"@react-navigation/stack": "^6.3.14",
|
||||
"axios": "^1.3.3",
|
||||
"crypto-js": "^4.1.1",
|
||||
"moment": "^2.29.4",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.71.3",
|
||||
"react-native-base64": "^0.2.1",
|
||||
"react-native-device-info": "^10.4.0",
|
||||
"react-native-gesture-handler": "^2.9.0",
|
||||
"react-native-image-crop-picker": "^0.39.0",
|
||||
"react-native-reanimated": "^2.14.4",
|
||||
"react-native-rsa-native": "^2.0.5",
|
||||
"react-native-safe-area-context": "^4.5.0",
|
||||
|
@ -16,7 +16,7 @@ import messaging from '@react-native-firebase/messaging';
|
||||
export function useAppContext() {
|
||||
const [state, setState] = useState({
|
||||
session: null,
|
||||
status: 'disconnected',
|
||||
status: null,
|
||||
loggingOut: false,
|
||||
adminToken: null,
|
||||
version: getVersion(),
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { View, TouchableOpacity, StatusBar, Text, Image } from 'react-native';
|
||||
import { View, ScrollView, TouchableOpacity, StatusBar, Text, Image } from 'react-native';
|
||||
import { useState, useEffect, useContext } from 'react';
|
||||
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
@ -9,7 +9,7 @@ import Ionicons from 'react-native-vector-icons/AntDesign';
|
||||
import { useSession } from './useSession.hook';
|
||||
import { styles } from './Session.styled';
|
||||
import Colors from 'constants/Colors';
|
||||
import { Profile } from './profile/Profile';
|
||||
import { Profile, ProfileHeader, ProfileBody } from './profile/Profile';
|
||||
import { CardsTitle, CardsBody, Cards } from './cards/Cards';
|
||||
import { RegistryTitle, RegistryBody, Registry } from './registry/Registry';
|
||||
import { Contact, ContactTitle } from './contact/Contact';
|
||||
@ -83,7 +83,9 @@ export function Session() {
|
||||
const ProfileStackScreen = () => {
|
||||
return (
|
||||
<ProfileStack.Navigator screenOptions={({ route }) => (screenParams)}>
|
||||
<ProfileStack.Screen name="profile" component={Profile} options={{ headerStyle: { backgroundColor: Colors.titleBackground }}} />
|
||||
<ProfileStack.Screen name="profile" options={{ ...stackParams, headerTitle: () => <ProfileHeader /> }}>
|
||||
{(props) => <ScrollView><ProfileBody /></ScrollView>}
|
||||
</ProfileStack.Screen>
|
||||
</ProfileStack.Navigator>
|
||||
);
|
||||
}
|
||||
@ -268,8 +270,9 @@ export function Session() {
|
||||
{ state.firstRun == false && (
|
||||
<View style={styles.container}>
|
||||
{ state.tabbed === false && (
|
||||
<ProfileDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '45%' } }}
|
||||
drawerContent={(props) => <Profile />}>
|
||||
<ProfileDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '45%' } }} drawerContent={(props) => (
|
||||
<ScrollView><SafeAreaView style={styles.drawer} edges={['top', 'bottom', 'right']}><Profile /></SafeAreaView></ScrollView>
|
||||
)}>
|
||||
<ProfileDrawer.Screen name="detail">
|
||||
{(props) => <DetailDrawerScreen navParams={{ profileNav: props.navigation }} />}
|
||||
</ProfileDrawer.Screen>
|
||||
|
@ -1,6 +1,590 @@
|
||||
import { Text } from 'react-native';
|
||||
import { ActivityIndicator, KeyboardAvoidingView, Modal, View, Switch, Text, TextInput, TouchableOpacity, Alert } from 'react-native';
|
||||
import Ionicons from 'react-native-vector-icons/AntDesign';
|
||||
import ImagePicker from 'react-native-image-crop-picker'
|
||||
import { Colors } from 'constants/Colors';
|
||||
import { useProfile } from './useProfile.hook';
|
||||
import { Logo } from 'utils/Logo';
|
||||
import { styles } from './Profile.styled';
|
||||
import { BlockedTopics } from './blockedTopics/BlockedTopics';
|
||||
import { BlockedContacts } from './blockedContacts/BlockedContacts';
|
||||
import { BlockedMessages } from './blockedMessages/BlockedMessages';
|
||||
|
||||
export function Profile({ navigation }) {
|
||||
return <Text>Profile</Text>;
|
||||
export function ProfileHeader() {
|
||||
const { state, actions } = useProfile();
|
||||
|
||||
return (
|
||||
<Text style={styles.headerText}>{ `${state.handle}@${state.node}` }</Text>
|
||||
)
|
||||
}
|
||||
|
||||
export function ProfileBody() {
|
||||
const { state, actions } = useProfile();
|
||||
|
||||
const logout = async () => {
|
||||
Alert.alert(
|
||||
"Logging Out",
|
||||
"Confirm?",
|
||||
[
|
||||
{ text: "Cancel",
|
||||
onPress: () => {},
|
||||
},
|
||||
{ text: "Logout", onPress: () => {
|
||||
actions.logout();
|
||||
}}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
const remove = async () => {
|
||||
try {
|
||||
await actions.remove();
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
Alert.alert(
|
||||
'Failed to Delete Account',
|
||||
'Please try again.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const onGallery = async () => {
|
||||
try {
|
||||
const full = await ImagePicker.openPicker({ mediaType: 'photo', width: 256, height: 256 });
|
||||
const crop = await ImagePicker.openCropper({ path: full.path, width: 256, height: 256, cropperCircleOverlay: true, includeBase64: true });
|
||||
await actions.setProfileImage(crop.data);
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
const setNotifications = async (notify) => {
|
||||
try {
|
||||
await actions.setNotifications(notify);
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
Alert.alert(
|
||||
'Account Update Failed',
|
||||
'Please try again.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const setVisible = async (visible) => {
|
||||
try {
|
||||
await actions.setVisible(visible);
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
Alert.alert(
|
||||
'Account Update Failed',
|
||||
'Please try again.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const saveSeal = async () => {
|
||||
try {
|
||||
await actions.saveSeal();
|
||||
actions.hideSealEdit();
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
Alert.alert(
|
||||
'Failed to Update Topic Sealing',
|
||||
'Please try again.',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const saveDetails = async () => {
|
||||
try {
|
||||
await actions.saveDetails();
|
||||
actions.hideDetailEdit();
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
Alert.alert(
|
||||
'Failed to Save Details',
|
||||
'Please try again.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const saveLogin = async () => {
|
||||
try {
|
||||
await actions.saveLogin();
|
||||
actions.hideLoginEdit();
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
Alert.alert(
|
||||
'Failed to Change Login',
|
||||
'Please try again.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.body}>
|
||||
|
||||
<View style={styles.logo}>
|
||||
<View>
|
||||
<Logo src={state.imageSource} width={128} height={128} radius={8} />
|
||||
<TouchableOpacity style={styles.gallery} onPress={onGallery}>
|
||||
<Ionicons name="picture" size={14} color={Colors.white} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.alert}>
|
||||
{ state.disconnected && (
|
||||
<Text style={styles.alertText}>Disconnected</Text>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<TouchableOpacity style={styles.detail} onPress={actions.showEditDetails}>
|
||||
<View style={styles.attribute}>
|
||||
{ state.name && (
|
||||
<Text style={styles.nametext}>{ state.name }</Text>
|
||||
)}
|
||||
{ !state.name && (
|
||||
<Text style={styles.nonametext}>Name</Text>
|
||||
)}
|
||||
<Ionicons name="edit" size={16} color={Colors.text} />
|
||||
</View>
|
||||
<View style={styles.attribute}>
|
||||
<View style={styles.icon}>
|
||||
<Ionicons name="enviromento" size={14} color={Colors.text} />
|
||||
</View>
|
||||
{ state.location && (
|
||||
<Text style={styles.locationtext}>{ state.location }</Text>
|
||||
)}
|
||||
{ !state.location && (
|
||||
<Text style={styles.nolocationtext}>Location</Text>
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.attribute}>
|
||||
<View style={styles.icon}>
|
||||
<Ionicons name="book" size={14} color={Colors.text} />
|
||||
</View>
|
||||
{ state.description && (
|
||||
<Text style={styles.descriptiontext}>{ state.description }</Text>
|
||||
)}
|
||||
{ !state.description && (
|
||||
<Text style={styles.nodescriptiontext}>Description</Text>
|
||||
)}
|
||||
</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>
|
||||
{ state.sealable && (
|
||||
<TouchableOpacity style={styles.link} onPress={actions.showSealEdit}>
|
||||
<Ionicons name="setting" size={14} color={Colors.primary} />
|
||||
<Text style={styles.linkText}>Sealed Topics</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
||||
<TouchableOpacity style={styles.logout} onPress={actions.showLoginEdit}>
|
||||
<Ionicons name="lock" size={16} color={Colors.primary} />
|
||||
<Text style={styles.logoutText}>Change Login</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity style={styles.logout} activeOpacity={1} onPress={logout}>
|
||||
<Ionicons name="logout" size={16} color={Colors.primary} />
|
||||
<Text style={styles.logoutText}>Logout</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity style={styles.delete} activeOpacity={1} onPress={actions.showDelete}>
|
||||
<Ionicons name="delete" size={16} color={Colors.alert} />
|
||||
<Text style={styles.deleteText}>Delete Account</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<Text style={styles.blockedLabel}>Manage Blocked:</Text>
|
||||
<View style={styles.blocked}>
|
||||
<TouchableOpacity style={styles.link} onPress={actions.showBlockedCards}>
|
||||
<Text style={styles.linkText}>Contacts</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.link} onPress={actions.showBlockedChannels}>
|
||||
<Text style={styles.linkText}>Topics</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.link} onPress={actions.showBlockedMessages}>
|
||||
<Text style={styles.linkText}>Messages</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={state.showDelete}
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={actions.hideDelete}
|
||||
>
|
||||
<KeyboardAvoidingView behavior="height" style={styles.modalWrapper}>
|
||||
<View style={styles.modalContainer}>
|
||||
<Text style={styles.modalHeader}>Deleting Your Account</Text>
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.confirmDelete} onChangeText={actions.setConfirmDelete}
|
||||
autoCapitalize="none" placeholder="Type 'delete' to Confirm" placeholderTextColor={Colors.grey} />
|
||||
</View>
|
||||
<View style={styles.modalControls}>
|
||||
<TouchableOpacity style={styles.cancel} onPress={actions.hideDelete}>
|
||||
<Text>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
{ state.confirmDelete === 'delete' && (
|
||||
<TouchableOpacity style={styles.remove} onPress={remove}>
|
||||
<Text style={styles.removeText}>Delete</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{ state.confirmDelete !== 'delete' && (
|
||||
<TouchableOpacity style={styles.unconfirmed}>
|
||||
<Text style={styles.removeText}>Delete</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={state.blockedCards}
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={actions.hideBlockedCards}
|
||||
>
|
||||
<KeyboardAvoidingView behavior="height" style={styles.modalWrapper}>
|
||||
<View style={styles.modalContainer}>
|
||||
<Text style={styles.modalHeader}>Blocked Contacts:</Text>
|
||||
<View style={styles.modalList}>
|
||||
<BlockedContacts />
|
||||
</View>
|
||||
<View style={styles.modalControls}>
|
||||
<TouchableOpacity style={styles.close} onPress={actions.hideBlockedCards}>
|
||||
<Text>Close</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={state.blockedChannels}
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={actions.hideBlockedChannels}
|
||||
>
|
||||
<KeyboardAvoidingView behavior="height" style={styles.modalWrapper}>
|
||||
<View style={styles.modalContainer}>
|
||||
<Text style={styles.modalHeader}>Blocked Topics:</Text>
|
||||
<View style={styles.modalList}>
|
||||
<BlockedTopics />
|
||||
</View>
|
||||
<View style={styles.modalControls}>
|
||||
<TouchableOpacity style={styles.close} onPress={actions.hideBlockedChannels}>
|
||||
<Text>Close</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={state.blockedMessages}
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={actions.hideBlockedMessages}
|
||||
>
|
||||
<KeyboardAvoidingView behavior="height" style={styles.modalWrapper}>
|
||||
<View style={styles.modalContainer}>
|
||||
<Text style={styles.modalHeader}>Blocked Messages:</Text>
|
||||
<View style={styles.modalList}>
|
||||
<BlockedMessages />
|
||||
</View>
|
||||
<View style={styles.modalControls}>
|
||||
<TouchableOpacity style={styles.close} onPress={actions.hideBlockedMessages}>
|
||||
<Text>Close</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={state.editDetails}
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={actions.hideEditDetails}
|
||||
>
|
||||
<KeyboardAvoidingView behavior="height" style={styles.modalWrapper}>
|
||||
<View style={styles.modalContainer}>
|
||||
<Text style={styles.modalHeader}>Edit Details:</Text>
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.editName} onChangeText={actions.setEditName}
|
||||
autoCapitalize="words" placeholder="Name" placeholderTextColor={Colors.grey} />
|
||||
</View>
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.editLocation} onChangeText={actions.setEditLocation}
|
||||
autoCapitalize="words" placeholder="Location" placeholderTextColor={Colors.grey} />
|
||||
</View>
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.editDescription} onChangeText={actions.setEditDescription}
|
||||
autoCapitalize="sentences" placeholder="Description" multiline={true}
|
||||
placeholderTextColor={Colors.grey} />
|
||||
</View>
|
||||
<View style={styles.modalControls}>
|
||||
<TouchableOpacity style={styles.cancel} onPress={actions.hideEditDetails}>
|
||||
<Text>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.save} onPress={saveDetails}>
|
||||
<Text style={styles.saveText}>Save</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={state.editSeal}
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={actions.hideSealEdit}
|
||||
>
|
||||
<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}>
|
||||
<Text style={styles.sealableText}>Enable Sealed Topics</Text>
|
||||
</TouchableOpacity>
|
||||
<Switch style={styles.sealableSwitch} value={state.sealable} onValueChange={actions.setSealable} trackColor={styles.switch}/>
|
||||
</View>
|
||||
{ state.sealMode === 'unlocking' && (
|
||||
<>
|
||||
{ !state.showSealUnlock && (
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.sealUnlock} onChangeText={actions.setSealUnlock}
|
||||
autoCapitalize={'none'} secureTextEntry={true} placeholder="Password for Seal"
|
||||
placeholderTextColor={Colors.grey} />
|
||||
<TouchableOpacity onPress={actions.showSealUnlock}>
|
||||
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
{ state.showSealUnlock && (
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.sealUnlock} onChangeText={actions.setSealUnlock}
|
||||
autoCapitalize={'none'} secureTextEntry={false} placeholder="Password for Seal"
|
||||
placeholderTextColor={Colors.grey} />
|
||||
<TouchableOpacity onPress={actions.hideSealUnlock}>
|
||||
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{ (state.sealMode === 'updating' || state.sealMode === 'enabling') && (
|
||||
<>
|
||||
{ !state.showSealPassword && (
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.sealPassword} onChangeText={actions.setSealPassword}
|
||||
autoCapitalize={'none'} secureTextEntry={true} placeholder="Password for Seal"
|
||||
placeholderTextColor={Colors.grey} />
|
||||
<TouchableOpacity onPress={actions.showSealPassword}>
|
||||
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
{ state.showSealPassword && (
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.sealPassword} onChangeText={actions.setSealPassword}
|
||||
autoCapitalize={'none'} secureTextEntry={false} placeholder="Password for Seal"
|
||||
placeholderTextColor={Colors.grey} />
|
||||
<TouchableOpacity onPress={actions.hideSealPassword}>
|
||||
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
{ !state.showSealConfirm && (
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.sealConfirm} onChangeText={actions.setSealConfirm}
|
||||
autoCapitalize={'none'} secureTextEntry={true} placeholder="Confirm Password"
|
||||
placeholderTextColor={Colors.grey} />
|
||||
<TouchableOpacity onPress={actions.showSealConfirm}>
|
||||
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
{ state.showSealConfirm && (
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.sealConfirm} onChangeText={actions.setSealConfirm}
|
||||
autoCapitalize={'none'} secureTextEntry={false} placeholder="Confirm Password"
|
||||
placeholderTextColor={Colors.grey} />
|
||||
<TouchableOpacity onPress={actions.hideSealConfirm}>
|
||||
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
<Text style={styles.notice}>saving can take a minute</Text>
|
||||
</>
|
||||
)}
|
||||
{ state.sealMode === 'disabling' && (
|
||||
<View style={styles.inputField}>
|
||||
<Ionicons style={styles.warn} name="exclamationcircleo" size={18} color="#888888" />
|
||||
<TextInput style={styles.input} value={state.sealDelete} onChangeText={actions.setSealDelete}
|
||||
autoCapitalize={'none'} placeholder="Type 'delete' to remove sealing key"
|
||||
placeholderTextColor={Colors.grey} />
|
||||
</View>
|
||||
)}
|
||||
{ state.sealMode === 'unlocked' && (
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={'xxxxxxxx'} editable={false} secureTextEntry={true} />
|
||||
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
|
||||
<TouchableOpacity style={styles.sealUpdate} onPress={actions.updateSeal} />
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.modalControls}>
|
||||
<TouchableOpacity style={styles.cancel} onPress={actions.hideSealEdit}>
|
||||
<Text>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
{ state.canSaveSeal && (
|
||||
<>
|
||||
{ state.sealMode !== 'unlocking' && state.sealMode !== 'unlocked' && (
|
||||
<TouchableOpacity style={styles.save} onPress={saveSeal}>
|
||||
<Text style={styles.saveText}>Save</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{ state.sealMode === 'unlocked' && (
|
||||
<TouchableOpacity style={styles.save} onPress={saveSeal}>
|
||||
<Text style={styles.saveText}>Forget</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{ state.sealMode === 'unlocking' && (
|
||||
<TouchableOpacity style={styles.save} onPress={saveSeal}>
|
||||
<Text style={styles.saveText}>Unlock</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{ !state.canSaveSeal && (
|
||||
<>
|
||||
{ state.sealMode !== 'unlocking' && (
|
||||
<View style={styles.disabled}>
|
||||
<Text style={styles.disabledText}>Save</Text>
|
||||
</View>
|
||||
)}
|
||||
{ state.sealMode === 'unlocking' && (
|
||||
<View style={styles.disabled}>
|
||||
<Text style={styles.disabledText}>Unlock</Text>
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={state.editLogin}
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={actions.hideLoginEdit}
|
||||
>
|
||||
<KeyboardAvoidingView behavior="height" style={styles.modalWrapper}>
|
||||
<View style={styles.modalContainer}>
|
||||
<Text style={styles.modalHeader}>Change Login:</Text>
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.editHandle} onChangeText={actions.setEditHandle}
|
||||
autoCapitalize={'none'} placeholder="Username" placeholderTextColor={Colors.grey} />
|
||||
{ state.checked && state.available && (
|
||||
<Ionicons style={styles.icon} name="checkcircleo" size={18} color={Colors.background} />
|
||||
)}
|
||||
{ state.checked && !state.available && (
|
||||
<Ionicons style={styles.icon} name="exclamationcircleo" size={18} color={Colors.alert} />
|
||||
)}
|
||||
</View>
|
||||
{ !state.showPassword && (
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.editPassword} onChangeText={actions.setEditPassword}
|
||||
autoCapitalize={'none'} secureTextEntry={true} placeholder="Password"
|
||||
placeholderTextColor={Colors.grey} />
|
||||
<TouchableOpacity onPress={actions.showPassword}>
|
||||
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
{ state.showPassword && (
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.editPassword} onChangeText={actions.setEditPassword}
|
||||
autoCapitalize={'none'} secureTextEntry={false} placeholder="Password"
|
||||
placeholderTextColor={Colors.grey} />
|
||||
<TouchableOpacity onPress={actions.hidePassword}>
|
||||
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
{ !state.showConfirm && (
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.editConfirm} onChangeText={actions.setEditConfirm}
|
||||
autoCapitalize={'none'} secureTextEntry={true} placeholder="Confirm"
|
||||
placeholderTextColor={Colors.grey} />
|
||||
<TouchableOpacity onPress={actions.showConfirm}>
|
||||
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
{ state.showConfirm && (
|
||||
<View style={styles.inputField}>
|
||||
<TextInput style={styles.input} value={state.editConfirm} onChangeText={actions.setEditConfirm}
|
||||
autoCapitalize={'none'} secureTextEntry={false} placeholder="Confirm"
|
||||
placeholderTextColor={Colors.grey} />
|
||||
<TouchableOpacity onPress={actions.hideConfirm}>
|
||||
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.modalControls}>
|
||||
<TouchableOpacity style={styles.cancel} onPress={actions.hideLoginEdit}>
|
||||
<Text>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
{ (state.checked && state.available && state.editConfirm === state.editPassword && state.editPassword) && (
|
||||
<TouchableOpacity style={styles.save} onPress={saveLogin}>
|
||||
<Text style={styles.saveText}>Save</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{ !(state.checked && state.available && state.editConfirm === state.editPassword && state.editPassword) && (
|
||||
<View style={styles.disabled}>
|
||||
<Text style={styles.disabledText}>Save</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export function Profile() {
|
||||
return (
|
||||
<View>
|
||||
<ProfileHeader />
|
||||
<ProfileBody />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
|
241
app/mobile/src/session/profile/Profile.styled.js
Normal file
241
app/mobile/src/session/profile/Profile.styled.js
Normal file
@ -0,0 +1,241 @@
|
||||
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { Colors } from 'constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
body: {
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
},
|
||||
button: {
|
||||
paddingRight: 16,
|
||||
},
|
||||
headerText: {
|
||||
fontSize: 18,
|
||||
overflow: 'hidden',
|
||||
textAlign: 'center',
|
||||
},
|
||||
logo: {
|
||||
marginTop: 16,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
},
|
||||
alert: {
|
||||
height: 16,
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
},
|
||||
alertText: {
|
||||
color: Colors.alert,
|
||||
},
|
||||
logout: {
|
||||
marginTop: 16,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
logoutText: {
|
||||
marginLeft: 8,
|
||||
color: Colors.primary,
|
||||
},
|
||||
delete: {
|
||||
marginTop: 16,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
deleteText: {
|
||||
marginLeft: 8,
|
||||
color: Colors.alert,
|
||||
},
|
||||
modalWrapper: {
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'rgba(52, 52, 52, 0.8)'
|
||||
},
|
||||
modalContainer: {
|
||||
backgroundColor: Colors.formBackground,
|
||||
padding: 16,
|
||||
width: '80%',
|
||||
maxWidth: 400,
|
||||
},
|
||||
modalHeader: {
|
||||
fontSize: 18,
|
||||
paddingBottom: 16,
|
||||
},
|
||||
modalList: {
|
||||
width: '100%',
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.lightgrey,
|
||||
borderRadius: 2,
|
||||
},
|
||||
modalControls: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
cancel: {
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.lightgrey,
|
||||
borderRadius: 4,
|
||||
padding: 8,
|
||||
marginRight: 8,
|
||||
width: 72,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
unconfirmed: {
|
||||
backgroundColor: Colors.lightgrey,
|
||||
borderRadius: 4,
|
||||
padding: 8,
|
||||
width: 72,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
remove: {
|
||||
backgroundColor: Colors.error,
|
||||
borderRadius: 4,
|
||||
padding: 8,
|
||||
width: 72,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
removeText: {
|
||||
color: Colors.white,
|
||||
},
|
||||
inputField: {
|
||||
width: '100%',
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.lightgrey,
|
||||
borderRadius: 4,
|
||||
padding: 8,
|
||||
marginBottom: 8,
|
||||
maxHeight: 92,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
gallery: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
padding: 8,
|
||||
backgroundColor: Colors.lightgrey,
|
||||
borderBottomRightRadius: 8,
|
||||
borderTopLeftRadius: 8,
|
||||
},
|
||||
detail: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
color: Colors.text,
|
||||
paddingLeft: 32,
|
||||
paddingRight: 32,
|
||||
marginTop: 16,
|
||||
marginBottom: 16,
|
||||
},
|
||||
attribute: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
paddingBottom: 8,
|
||||
},
|
||||
nonametext: {
|
||||
fontSize: 18,
|
||||
paddingRight: 8,
|
||||
fontWeight: 'bold',
|
||||
color: Colors.grey,
|
||||
},
|
||||
nametext: {
|
||||
fontSize: 18,
|
||||
paddingRight: 8,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
locationtext: {
|
||||
fontSize: 16,
|
||||
paddingLeft: 8,
|
||||
color: Colors.text,
|
||||
},
|
||||
nolocationtext: {
|
||||
fontSize: 16,
|
||||
paddingLeft: 8,
|
||||
color: Colors.grey,
|
||||
},
|
||||
descriptiontext: {
|
||||
fontSize: 16,
|
||||
paddingLeft: 8,
|
||||
color: Colors.text,
|
||||
},
|
||||
nodescriptiontext: {
|
||||
fontSize: 16,
|
||||
paddingLeft: 8,
|
||||
color: Colors.grey,
|
||||
},
|
||||
save: {
|
||||
padding: 8,
|
||||
borderRadius: 4,
|
||||
backgroundColor: Colors.primary,
|
||||
width: 72,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
saveText: {
|
||||
color: Colors.white,
|
||||
},
|
||||
blocked: {
|
||||
alignSelf: 'center',
|
||||
borderColor: Colors.lightgrey,
|
||||
borderWidth: 1,
|
||||
borderRadius: 4,
|
||||
padding: 8,
|
||||
marginBottom: 8,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
blockedLabel: {
|
||||
marginTop: 24,
|
||||
alignSelf: 'center',
|
||||
color: Colors.grey,
|
||||
},
|
||||
enable: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
enableText: {
|
||||
color: Colors.primary,
|
||||
},
|
||||
enableSwitch: {
|
||||
transform: [{ scaleX: .6 }, { scaleY: .6 }],
|
||||
},
|
||||
link: {
|
||||
marginLeft: 8,
|
||||
marginRight: 8,
|
||||
},
|
||||
linkText: {
|
||||
color: Colors.primary,
|
||||
},
|
||||
close: {
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.lightgrey,
|
||||
borderRadius: 4,
|
||||
padding: 8,
|
||||
marginTop: 8,
|
||||
width: 72,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
||||
|
@ -0,0 +1,48 @@
|
||||
import { FlatList, View, Alert, TouchableOpacity, Text } from 'react-native';
|
||||
import { styles } from './BlockedContacts.styled';
|
||||
import { useBlockedContacts } from './useBlockedContacts.hook';
|
||||
import { Logo } from 'utils/Logo';
|
||||
|
||||
export function BlockedContacts() {
|
||||
|
||||
const { state, actions } = useBlockedContacts();
|
||||
|
||||
const unblock = (cardId) => {
|
||||
Alert.alert(
|
||||
'Unblocking Contact',
|
||||
'Confirm?',
|
||||
[
|
||||
{ text: "Cancel", onPress: () => {}, },
|
||||
{ text: "Unblock", onPress: () => actions.unblock(cardId) },
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
const BlockedItem = ({ item }) => {
|
||||
return (
|
||||
<TouchableOpacity style={styles.item} onPress={() => unblock(item.cardId)}>
|
||||
<Logo src={item.logo} width={32} height={32} radius={6} />
|
||||
<View style={styles.detail}>
|
||||
<Text style={styles.name} numberOfLines={1} ellipsizeMode={'tail'}>{ item.name }</Text>
|
||||
<Text style={styles.handle} numberOfLines={1} ellipsizeMode={'tail'}>{ item.handle }</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{ state.cards.length === 0 && (
|
||||
<Text style={styles.default}>No Blocked Contacts</Text>
|
||||
)}
|
||||
{ state.cards.length !== 0 && (
|
||||
<FlatList
|
||||
data={state.cards}
|
||||
renderItem={({item}) => <BlockedItem item={item} />}
|
||||
keyExtractor={item => item.cardId}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { Colors } from 'constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: Colors.white,
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
fontSize: 14,
|
||||
height: 200,
|
||||
},
|
||||
default: {
|
||||
textAlign: 'center',
|
||||
color: Colors.grey,
|
||||
},
|
||||
item: {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
height: 48,
|
||||
paddingLeft: 16,
|
||||
alignItems: 'center',
|
||||
borderBottomWidth: 1,
|
||||
borderColor: Colors.itemDivider,
|
||||
},
|
||||
detail: {
|
||||
paddingLeft: 12,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
},
|
||||
name: {
|
||||
color: Colors.text,
|
||||
fontSize: 14,
|
||||
},
|
||||
handle: {
|
||||
color: Colors.text,
|
||||
fontSize: 12,
|
||||
},
|
||||
});
|
@ -0,0 +1,53 @@
|
||||
import { useState, useEffect, useContext } from 'react';
|
||||
import { CardContext } from 'context/CardContext';
|
||||
|
||||
export function useBlockedContacts() {
|
||||
|
||||
const [state, setState] = useState({
|
||||
cards: [],
|
||||
});
|
||||
|
||||
const card = useContext(CardContext);
|
||||
|
||||
const updateState = (value) => {
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
}
|
||||
|
||||
const setCardItem = (item) => {
|
||||
const { profile } = item;
|
||||
return {
|
||||
cardId: item.cardId,
|
||||
name: profile?.name,
|
||||
handle: `${profile?.handle}@${profile?.node}`,
|
||||
blocked: item.blocked,
|
||||
logo: profile?.imageSet ? card.actions.getCardImageUrl(item.cardId) : 'avatar',
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const cards = Array.from(card.state.cards.values());
|
||||
const items = cards.map(setCardItem);
|
||||
const filtered = items.filter(item => {
|
||||
return item.blocked;
|
||||
});
|
||||
filtered.sort((a, b) => {
|
||||
if (a.name === b.name) {
|
||||
return 0;
|
||||
}
|
||||
if (!a.name || (a.name < b.name)) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
updateState({ cards: filtered });
|
||||
}, [card]);
|
||||
|
||||
const actions = {
|
||||
unblock: async (cardId) => {
|
||||
await card.actions.clearCardBlocked(cardId);
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
import { FlatList, View, Alert, TouchableOpacity, Text } from 'react-native';
|
||||
import { styles } from './BlockedMessages.styled';
|
||||
import { useBlockedMessages } from './useBlockedMessages.hook';
|
||||
import { Logo } from 'utils/Logo';
|
||||
|
||||
export function BlockedMessages() {
|
||||
|
||||
const { state, actions } = useBlockedMessages();
|
||||
|
||||
const unblock = (cardId, channelId, topicId) => {
|
||||
Alert.alert(
|
||||
'Unblocking Message',
|
||||
'Confirm?',
|
||||
[
|
||||
{ text: "Cancel", onPress: () => {}, },
|
||||
{ text: "Unblock", onPress: () => actions.unblock(cardId, channelId, topicId) },
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
const BlockedItem = ({ item }) => {
|
||||
return (
|
||||
<TouchableOpacity style={styles.item} onPress={() => unblock(item.cardId, item.channelId, item.topicId)}>
|
||||
<View style={styles.detail}>
|
||||
<Text style={styles.name} numberOfLines={1} ellipsizeMode={'tail'}>{ item.name }</Text>
|
||||
<Text style={styles.created} numberOfLines={1} ellipsizeMode={'tail'}>{ item.timestamp }</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{ state.messages.length === 0 && (
|
||||
<Text style={styles.default}>No Blocked Messages</Text>
|
||||
)}
|
||||
{ state.messages.length !== 0 && (
|
||||
<FlatList
|
||||
data={state.messages}
|
||||
renderItem={({item}) => <BlockedItem item={item} />}
|
||||
keyExtractor={item => item.id}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { Colors } from 'constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: Colors.white,
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
fontSize: 14,
|
||||
height: 200,
|
||||
},
|
||||
default: {
|
||||
textAlign: 'center',
|
||||
color: Colors.grey,
|
||||
},
|
||||
item: {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
height: 32,
|
||||
paddingLeft: 16,
|
||||
alignItems: 'center',
|
||||
borderBottomWidth: 1,
|
||||
borderColor: Colors.itemDivider,
|
||||
},
|
||||
detail: {
|
||||
paddingLeft: 12,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
name: {
|
||||
color: Colors.text,
|
||||
fontSize: 14,
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
minWidth: 0,
|
||||
},
|
||||
created: {
|
||||
color: Colors.text,
|
||||
fontSize: 12,
|
||||
paddingRight: 16,
|
||||
},
|
||||
});
|
@ -0,0 +1,96 @@
|
||||
import { useState, useEffect, useContext } from 'react';
|
||||
import { StoreContext } from 'context/StoreContext';
|
||||
import { ChannelContext } from 'context/ChannelContext';
|
||||
import { CardContext } from 'context/CardContext';
|
||||
import { ProfileContext } from 'context/ProfileContext';
|
||||
import { ConversationContext } from 'context/ConversationContext';
|
||||
import moment from 'moment';
|
||||
|
||||
export function useBlockedMessages() {
|
||||
|
||||
const [state, setState] = useState({
|
||||
messages: []
|
||||
});
|
||||
|
||||
const store = useContext(StoreContext);
|
||||
const card = useContext(CardContext);
|
||||
const channel = useContext(ChannelContext);
|
||||
const profile = useContext(ProfileContext);
|
||||
const conversation = useContext(ConversationContext);
|
||||
|
||||
const updateState = (value) => {
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
}
|
||||
|
||||
const setItem = (item) => {
|
||||
let name, nameSet
|
||||
if (item.detail.guid === profile.state.identity.guid) {
|
||||
const identity = profile.state.identity;
|
||||
if (identity.name) {
|
||||
name = identity.name;
|
||||
}
|
||||
else {
|
||||
name = `${identity.handle}@${identity.node}`;
|
||||
}
|
||||
nameSet = true;
|
||||
}
|
||||
else {
|
||||
const contact = card.actions.getByGuid(item.detail.guid);
|
||||
if (contact) {
|
||||
if (contact?.profile?.name) {
|
||||
name = contact.profile.name;
|
||||
}
|
||||
else {
|
||||
name = `${contact.profile.handle}@${contact.profile.node}`;
|
||||
}
|
||||
nameSet = true;
|
||||
}
|
||||
else {
|
||||
name = 'unknown';
|
||||
nameSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
let timestamp;
|
||||
const date = new Date(item.detail.created * 1000);
|
||||
const now = new Date();
|
||||
const offset = now.getTime() - date.getTime();
|
||||
if(offset < 86400000) {
|
||||
timestamp = moment(date).format('h:mma');
|
||||
}
|
||||
else if (offset < 31449600000) {
|
||||
timestamp = moment(date).format('M/DD');
|
||||
}
|
||||
else {
|
||||
timestamp = moment(date).format('M/DD/YYYY');
|
||||
}
|
||||
|
||||
const { cardId, channelId, topicId } = item;
|
||||
return { name, nameSet, timestamp, cardId, channelId, topicId, id: `${cardId}:${channelId}:${topicId}` };
|
||||
};
|
||||
|
||||
const loadBlocked = async () => {
|
||||
//TODO
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadBlocked();
|
||||
}, []);
|
||||
|
||||
const actions = {
|
||||
unblock: async (cardId, channelId, topicId) => {
|
||||
const id = `${cardId}:${channelId}:${topicId}`;
|
||||
if (cardId) {
|
||||
card.actions.clearChannelTopicBlocked(cardId, channelId, topicId);
|
||||
}
|
||||
else {
|
||||
channel.actions.clearTopicBlocked(channelId, topicId);
|
||||
}
|
||||
conversation.actions.unblockTopic(cardId, channelId, topicId);
|
||||
updateState({ messages: state.messages.filter(item => item.id !== id) });
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions };
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
import { FlatList, View, Alert, TouchableOpacity, Text } from 'react-native';
|
||||
import { styles } from './BlockedTopics.styled';
|
||||
import { useBlockedTopics } from './useBlockedTopics.hook';
|
||||
import { Logo } from 'utils/Logo';
|
||||
|
||||
export function BlockedTopics() {
|
||||
|
||||
const { state, actions } = useBlockedTopics();
|
||||
|
||||
const unblock = (cardId, channelId) => {
|
||||
Alert.alert(
|
||||
'Unblocking Contact',
|
||||
'Confirm?',
|
||||
[
|
||||
{ text: "Cancel", onPress: () => {}, },
|
||||
{ text: "Unblock", onPress: () => actions.unblock(cardId, channelId) },
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
const BlockedItem = ({ item }) => {
|
||||
return (
|
||||
<TouchableOpacity style={styles.item} onPress={() => unblock(item.cardId, item.channelId)}>
|
||||
<View style={styles.detail}>
|
||||
<Text style={styles.name} numberOfLines={1} ellipsizeMode={'tail'}>{ item.name }</Text>
|
||||
<Text style={styles.created} numberOfLines={1} ellipsizeMode={'tail'}>{ item.created }</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{ state.channels.length === 0 && (
|
||||
<Text style={styles.default}>No Blocked Topics</Text>
|
||||
)}
|
||||
{ state.channels.length !== 0 && (
|
||||
<FlatList
|
||||
data={state.channels}
|
||||
renderItem={({item}) => <BlockedItem item={item} />}
|
||||
keyExtractor={item => item.id}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { Colors } from 'constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: Colors.white,
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
fontSize: 14,
|
||||
height: 200,
|
||||
},
|
||||
default: {
|
||||
textAlign: 'center',
|
||||
color: Colors.grey,
|
||||
},
|
||||
item: {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
height: 32,
|
||||
paddingLeft: 16,
|
||||
alignItems: 'center',
|
||||
borderBottomWidth: 1,
|
||||
borderColor: Colors.itemDivider,
|
||||
},
|
||||
detail: {
|
||||
paddingLeft: 12,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
name: {
|
||||
color: Colors.text,
|
||||
fontSize: 14,
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
minWidth: 0,
|
||||
},
|
||||
created: {
|
||||
color: Colors.text,
|
||||
fontSize: 12,
|
||||
paddingRight: 16,
|
||||
},
|
||||
});
|
@ -0,0 +1,122 @@
|
||||
import { useState, useEffect, useContext } from 'react';
|
||||
import { CardContext } from 'context/CardContext';
|
||||
import { ChannelContext } from 'context/ChannelContext';
|
||||
import { ProfileContext } from 'context/ProfileContext';
|
||||
import moment from 'moment';
|
||||
|
||||
export function useBlockedTopics() {
|
||||
|
||||
const [state, setState] = useState({
|
||||
channels: []
|
||||
});
|
||||
|
||||
const profile = useContext(ProfileContext);
|
||||
const card = useContext(CardContext);
|
||||
const channel = useContext(ChannelContext);
|
||||
|
||||
const updateState = (value) => {
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
}
|
||||
|
||||
const getCard = (guid) => {
|
||||
let contact = null
|
||||
card.state.cards.forEach((card, cardId, map) => {
|
||||
if (card?.profile?.guid === guid) {
|
||||
contact = card;
|
||||
}
|
||||
});
|
||||
return contact;
|
||||
}
|
||||
|
||||
const setChannelItem = (item) => {
|
||||
let timestamp;
|
||||
const date = new Date(item.detail.created * 1000);
|
||||
const now = new Date();
|
||||
const offset = now.getTime() - date.getTime();
|
||||
if(offset < 86400000) {
|
||||
timestamp = moment(date).format('h:mma');
|
||||
}
|
||||
else if (offset < 31449600000) {
|
||||
timestamp = moment(date).format('M/DD');
|
||||
}
|
||||
else {
|
||||
timestamp = moment(date).format('M/DD/YYYY');
|
||||
}
|
||||
|
||||
let contacts = [];
|
||||
if (item.cardId) {
|
||||
contacts.push(card.state.cards.get(item.cardId));
|
||||
}
|
||||
if (item?.detail?.members) {
|
||||
const profileGuid = profile.state.identity.guid;
|
||||
item.detail.members.forEach(guid => {
|
||||
if (profileGuid !== guid) {
|
||||
const contact = getCard(guid);
|
||||
contacts.push(contact);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let subject;
|
||||
if (item?.detail?.data) {
|
||||
try {
|
||||
topic = JSON.parse(item?.detail?.data).subject;
|
||||
subject = topic;
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
if (!subject) {
|
||||
if (contacts.length) {
|
||||
let names = [];
|
||||
for (let contact of contacts) {
|
||||
if (contact?.profile?.name) {
|
||||
names.push(contact.profile.name);
|
||||
}
|
||||
else if (contact?.profile?.handle) {
|
||||
names.push(contact?.profile?.handle);
|
||||
}
|
||||
}
|
||||
subject = names.join(', ');
|
||||
}
|
||||
else {
|
||||
subject = "Notes";
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: `${item.cardId}:${item.channelId}`,
|
||||
cardId: item.cardId,
|
||||
channelId: item.channelId,
|
||||
name: subject,
|
||||
blocked: item.blocked,
|
||||
created: timestamp,
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let merged = [];
|
||||
card.state.cards.forEach((card, cardId, map) => {
|
||||
merged.push(...Array.from(card.channels.values()));
|
||||
});
|
||||
merged.push(...Array.from(channel.state.channels.values()));
|
||||
const items = merged.map(setChannelItem);
|
||||
const filtered = items.filter(item => item.blocked);
|
||||
updateState({ channels: filtered });
|
||||
}, [card, channel]);
|
||||
|
||||
const actions = {
|
||||
unblock: async (cardId, channelId) => {
|
||||
if (cardId) {
|
||||
await card.actions.clearChannelBlocked(cardId, channelId);
|
||||
}
|
||||
else {
|
||||
await channel.actions.clearBlocked(channelId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return { state, actions };
|
||||
}
|
||||
|
118
app/mobile/src/session/profile/useProfile.hook.js
Normal file
118
app/mobile/src/session/profile/useProfile.hook.js
Normal file
@ -0,0 +1,118 @@
|
||||
import { useState, useEffect, useRef, useContext } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ProfileContext } from 'context/ProfileContext';
|
||||
import { AppContext } from 'context/AppContext';
|
||||
|
||||
export function useProfile() {
|
||||
|
||||
const [state, setState] = useState({
|
||||
name: null,
|
||||
handle: null,
|
||||
editHandle: null,
|
||||
location: null,
|
||||
editLocation: null,
|
||||
description: null,
|
||||
editDescritpion: null,
|
||||
node: null,
|
||||
showDelete: false,
|
||||
editDetails: false,
|
||||
editLogin: false,
|
||||
editSeal: false,
|
||||
confirmDelete: null,
|
||||
blockedChannels: false,
|
||||
blockedCards: false,
|
||||
blockedMessages: false,
|
||||
logginOut: false,
|
||||
disconnected: false,
|
||||
});
|
||||
|
||||
const app = useContext(AppContext);
|
||||
const profile = useContext(ProfileContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const updateState = (value) => {
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const { name, handle, node, location, description, image } = profile.state.identity;
|
||||
const imageSource = image ? profile.state.imageUrl : 'avatar';
|
||||
updateState({ name, handle, node, location, description, imageSource, editHandle: handle,
|
||||
editName: name, editLocation: location, editDescription: description });
|
||||
}, [profile]);
|
||||
|
||||
useEffect(() => {
|
||||
const { loggingOut, status } = app.state;
|
||||
updateState({ loggingOut, disconnected: status === 'disconnected' });
|
||||
}, [app.state]);
|
||||
|
||||
const actions = {
|
||||
logout: async () => {
|
||||
await app.actions.logout();
|
||||
navigate('/');
|
||||
},
|
||||
remove: async () => {
|
||||
await app.actions.remove();
|
||||
updateState({ showDelete: false });
|
||||
navigate('/');
|
||||
},
|
||||
showDelete: () => {
|
||||
updateState({ showDelete: true, confirmDelete: null });
|
||||
},
|
||||
hideDelete: () => {
|
||||
updateState({ showDelete: false });
|
||||
},
|
||||
setConfirmDelete: (confirmDelete) => {
|
||||
updateState({ confirmDelete });
|
||||
},
|
||||
showEditDetails: () => {
|
||||
updateState({ editDetails: true });
|
||||
},
|
||||
hideEditDetails: () => {
|
||||
updateState({ editDetails: false });
|
||||
},
|
||||
showEditLogin: () => {
|
||||
updateState({ editLogin: true });
|
||||
},
|
||||
hideEditLogin: () => {
|
||||
updateState({ editLogin: false });
|
||||
},
|
||||
showEditSeal: () => {
|
||||
updateState({ editSeal: true });
|
||||
},
|
||||
hideEditSeal: () => {
|
||||
updateState({ editSeal: false });
|
||||
},
|
||||
showBlockedChannels: () => {
|
||||
updateState({ blockedChannels: true });
|
||||
},
|
||||
hideBlockedChannels: () => {
|
||||
updateState({ blockedChannels: false });
|
||||
},
|
||||
showBlockedCards: () => {
|
||||
updateState({ blockedCards: true });
|
||||
},
|
||||
hideBlockedCards: () => {
|
||||
updateState({ blockedCards: false });
|
||||
},
|
||||
showBlockedMessages: () => {
|
||||
updateState({ blockedMessages: true });
|
||||
},
|
||||
hideBlockedMessages: () => {
|
||||
updateState({ blockedMessages: false });
|
||||
},
|
||||
setEditName: (editName) => {
|
||||
updateState({ editName });
|
||||
},
|
||||
setEditLocation: (editLocation) => {
|
||||
updateState({ editLocation });
|
||||
},
|
||||
setEditDescription: (editDescription) => {
|
||||
updateState({ editDescription });
|
||||
},
|
||||
};
|
||||
|
||||
return { state, actions };
|
||||
}
|
||||
|
||||
|
@ -5597,6 +5597,11 @@ mkdirp@^0.5.1:
|
||||
dependencies:
|
||||
minimist "^1.2.6"
|
||||
|
||||
moment@^2.29.4:
|
||||
version "2.29.4"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
|
||||
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
||||
@ -6228,6 +6233,11 @@ react-native-gradle-plugin@^0.71.15:
|
||||
resolved "https://registry.npmjs.org/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.15.tgz"
|
||||
integrity sha512-7S3pAuPaQJlhax6EZ4JMsDNpj05TfuzX9gPgWLrFfAIWIFLuJ6aDQYAZy2TEI9QJALPoWrj8LWaqP/DGYh14pw==
|
||||
|
||||
react-native-image-crop-picker@^0.39.0:
|
||||
version "0.39.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-image-crop-picker/-/react-native-image-crop-picker-0.39.0.tgz#9cb8e8ffb0e8ab06f7b3227cadf077169e225eba"
|
||||
integrity sha512-4aANbQMrmU6zN/4b0rVBA7SbaZ3aa5JESm3Xk751sINybZMt1yz/9h95LkO7U0pbslHDo3ofXjG75PmQRP6a/w==
|
||||
|
||||
react-native-reanimated@^2.14.4:
|
||||
version "2.14.4"
|
||||
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.14.4.tgz#3fa3da4e7b99f5dfb28f86bcf24d9d1024d38836"
|
||||
|
Loading…
Reference in New Issue
Block a user