Gracefully handle backend failures in Expo app

This commit is contained in:
Adolfo Reyna
2026-02-20 19:25:38 -05:00
parent fe9fc8e3e4
commit 009f1ec792
14 changed files with 205 additions and 97 deletions

View File

@@ -142,7 +142,7 @@ const Courses = () => {
horizontal={true}
data={watching}
renderItem={watchingCourse}
keyExtractor={item => item.profile._id}
keyExtractor={(item, index) => item?.profile?._id || item?._id || `watching-${index}`}
initialNumToRender={2}
/>
</View> : <></>

View File

@@ -49,6 +49,7 @@ const handleURL = (url, navigation) => {
}
async function onFetchUpdateAsync() {
if (__DEV__) return;
try {
const update = await Updates.checkForUpdateAsync();
@@ -90,11 +91,11 @@ let Feed = ({ navigation, route }) => {
}
if (!route.params?.reRender) {
API.getMe().then((me) => {
if (subscribed) {
if (subscribed && me && me._id) {
GlobalState.me = me;
posthog.identify(me.userid,
{
name: me.profile.firstName,
name: me.profile?.firstName,
profileid: me._id,
is_superuser: me.superuser,
}
@@ -108,11 +109,12 @@ let Feed = ({ navigation, route }) => {
console.log("Feed from server")
}
await onFetchUpdateAsync();
flatListRef.current.scrollToOffset({ animated: true, offset: 0 })
flatListRef.current?.scrollToOffset({ animated: true, offset: 0 })
let posts = await API.getPosts();
if (subscribed) {
setPosts(posts);
storeFeed(posts);
const safePosts = Array.isArray(posts) ? posts : [];
setPosts(safePosts);
storeFeed(safePosts);
}
console.log("Feed, end useEffect")
}
@@ -141,7 +143,7 @@ let Feed = ({ navigation, route }) => {
//ListHeaderComponent={<NewPost newPostCB={(newPost) => setPosts([newPost, ...Posts])} />}
refreshing={Posts.length === 0}
onRefresh={() => {
API.getPosts().then(setPosts);
API.getPosts().then((data) => setPosts(Array.isArray(data) ? data : []));
}}
initialNumToRender={3}
maxToRenderPerBatch={3}

View File

@@ -24,7 +24,7 @@ let MenuView = ({ navigation }) => {
let getData = async () => {
const r = await API.getMyProfiles();
if (!subscribed) return;
setMyProfiles(r.profiles);
setMyProfiles(Array.isArray(r?.profiles) ? r.profiles : []);
}
getData();
return () => {
@@ -40,16 +40,17 @@ let MenuView = ({ navigation }) => {
//reloadAppAsync();
}
const profileLists = myProfiles.map((profile) => {
const profileInfo = profile?.profile || {};
const DefaultPhoto = "https://social.emmint.com/uploads/e6f9be6d665dc43417701bf16a90122c.png";
let photoUrl = profile.profile?.photo ? 'https://social.emmint.com/' + profile.profile.photo : DefaultPhoto;
let photoUrl = profileInfo.photo ? 'https://social.emmint.com/' + profileInfo.photo : DefaultPhoto;
let icon = profile._id ? (!profile.isGroup ? "person-outline" : "group") : '';
icon = icon === "person-outline" && profile.subscription && profile.subscription > (new Date() - 0) ? "assignment-ind" : icon;
icon = icon === "group" && profile.isCourse ? "subscriptions" : icon;
icon = icon === "group" && profile.isPrivate ? "screen-lock-portrait" : icon;
return <>
<List.Item
title={profile.profile.firstName + " " + profile.profile.lastName}
description={profile.profile.description}
title={(profileInfo.firstName || "") + " " + (profileInfo.lastName || "")}
description={profileInfo.description || ""}
onPress={async () => {
await API.changeProfile(profile._id);
GlobalState.me = await API.getMe();
@@ -103,4 +104,4 @@ let MenuView = ({ navigation }) => {
)
}
export default MenuView;
export default MenuView;

View File

@@ -44,16 +44,17 @@ let Profile = ({ navigation, route }) => {
console.log('Loading Cache Profile:' + route.params.profileid);
await API.getUserProfile(route.params.profileid).then((profileObj) => {
if(!subscribed) return 0;
let profile = profileObj.profile
setProfile(profileObj);
navigation.setOptions({ title: profile.firstName + " " + profile.lastName });
const nextProfile = profileObj && profileObj._id ? profileObj : {};
const profileData = nextProfile.profile || {};
setProfile(nextProfile);
navigation.setOptions({ title: (profileData.firstName || "") + " " + (profileData.lastName || "") });
});
await getProfilePosts(route.params.profileid).then(setPosts);
console.log('Loaded Cache Profile:' + route.params.profileid);
API.getPosts(route.params.profileid).then((data) => {
setLoading(false);
if(!subscribed) return 0;
setPosts(data);
setPosts(Array.isArray(data) ? data : []);
storeProfilePosts(route.params.profileid, data);
console.log('Store Cache Profile:' + route.params.profileid);
});
@@ -62,7 +63,7 @@ let Profile = ({ navigation, route }) => {
console.log('Getting posts with tag', tag)
API.getPostsWithTag(route.params.profileid, tag).then((data) => {
if(!subscribed) return 0;
setPosts(data.posts);
setPosts(Array.isArray(data?.posts) ? data.posts : []);
});
} else {
// if no profile information is pressent should load feed
@@ -80,7 +81,7 @@ let Profile = ({ navigation, route }) => {
API.getPostsWithTag(tag).then((data) => {
//if(!subscribed) return 0;
console.log(data.posts);
setPosts(data.posts);
setPosts(Array.isArray(data?.posts) ? data.posts : []);
});
}
@@ -148,7 +149,7 @@ let Profile = ({ navigation, route }) => {
maxToRenderPerBatch={3}
removeClippedSubviews={true}
onRefresh={() => {
API.getPosts(route.params.profileid).then(setPosts);
API.getPosts(route.params.profileid).then((data) => setPosts(Array.isArray(data) ? data : []));
}}
/> :
<></> //TODO: Add empty profile card here

View File

@@ -16,13 +16,14 @@ import * as ImagePicker from 'expo-image-picker';
let ProfileSettings = () => {
const gState = useSnapshot(GlobalState);
const viewer = gState.me;
const viewerProfile = viewer?.profile || {};
const [photo, setPhoto] = React.useState(null);
const [name, setName] = React.useState(viewer.profile.firstName);
const [lastName, setLastName] = React.useState(viewer.profile.lastName);
const [photoUrl, setphotoUrl] = React.useState(viewer.profile.photo);
const [language, setLanguage] = React.useState(viewer.profile.language);
const [name, setName] = React.useState(viewerProfile.firstName || "");
const [lastName, setLastName] = React.useState(viewerProfile.lastName || "");
const [photoUrl, setphotoUrl] = React.useState(viewerProfile.photo || "");
const [language, setLanguage] = React.useState(viewerProfile.language || "en");
const [updateKey, setUpdateKey] = React.useState(0);
const [description, setDescription] = React.useState(viewer.profile.description);
const [description, setDescription] = React.useState(viewerProfile.description || "");
const [uploading, setUploading] = React.useState(false);
const pickImage = async () => {
@@ -41,6 +42,7 @@ let ProfileSettings = () => {
let newPhotoURL = await handleUploadPhoto(result.assets[0]);
if (newPhotoURL !== "") {
setphotoUrl(newPhotoURL);
if (!GlobalState.me.profile) GlobalState.me.profile = {};
GlobalState.me.profile.photo = newPhotoURL;
updateProfile()
setUpdateKey(updateKey + 1);
@@ -90,9 +92,9 @@ let ProfileSettings = () => {
};
let updateProfile = async () => {
let currentProfile = await API.getUserProfile(viewer._id)
currentData = currentProfile.data;
currentProfile = currentProfile.profile;
let currentProfile = await API.getUserProfile(viewer?._id);
const currentData = currentProfile?.data || {};
currentProfile = currentProfile?.profile || {};
try {
//let currentProfile = JSON.parse(JSON.stringify(viewer.profile));
currentProfile.firstName = name;
@@ -204,4 +206,4 @@ let ProfileSettings = () => {
)
}
export default ProfileSettings;
export default ProfileSettings;