eletrotupi / tcc/ commit / bee79a3

frontend: guard nullable user reads

Pedro Lucas Porcellis porcellis@eletrotupi.com · 12 days ago bee79a3ab499c11539d5799b6c9cb1fc0d8ba1e0 Browse files
Parents 9897f03
Changed files
7
Additions
+21
Deletions
-35
frontend/app/(tabs)/actions.tsx
1 1
import { View, StyleSheet } from "react-native";
2 2
import { Text } from "@/components/ui/Text";
3 3
import { ThemedView } from "@/components/misc/themed-view";
4 4
import { ScreenLayout } from "@/components/ui/ScreenLayout";
5 5
import { useAuth } from "@/context/AuthContext";
28 28
    router.push("/triggers/new");
29 29
  };
30 30

31 31
  return (
32 32
    <ScreenLayout
33 -
      userName={user.firstName}
34 -
      userAvatar={user.avatarURL}
33 +
      userName={user?.firstName}
34 +
      userAvatar={user?.avatarURL}
35 35
      showNotificationBadge={true}
36 36
    >
37 37
      <Section>
38 38
        <SectionHeader
39 39
          title="Ações de Cuidado"
172 172
    fontSize: 12,
173 173
    lineHeight: 16,
174 174
    alignItems: "center",
175 175
  },
176 176
});
frontend/app/(tabs)/history.tsx
1 1
import { useState } from "react";
2 2
import { ScrollView, View } from "react-native";
3 3
import { ScreenLayout } from "@/components/ui/ScreenLayout";
4 4
import { Section, SectionHeader } from "@/components/ui/Sections";
5 5
import { FilterPills } from "@/components/ui/FilterPills";
43 43
  );
44 44
  const grouped = useHistoryFeed(activeFilter);
45 45

46 46
  return (
47 47
    <ScreenLayout
48 -
      userName={user.firstName}
49 -
      userAvatar={user.avatarURL}
48 +
      userName={user?.firstName}
49 +
      userAvatar={user?.avatarURL}
50 50
      showNotificationBadge={true}
51 51
    >
52 52
      <ScrollView testID="history-list">
53 53
        <FilterPills active={activeFilter} onChange={setActiveFilter} />
54 54

72 72
        )}
73 73
      </ScrollView>
74 74
    </ScreenLayout>
75 75
  );
76 76
}
frontend/app/(tabs)/insights.tsx
1 1
import { View, ScrollView, StyleSheet } from "react-native";
2 2

3 3
import { ThemedView } from "@/components/misc/themed-view";
4 4
import { ScreenLayout } from "@/components/ui/ScreenLayout";
5 5
import { useAuth } from "@/context/AuthContext";
16 16
export default function Insights() {
17 17
  const { user } = useAuth();
18 18

19 19
  return (
20 20
    <ScreenLayout
21 -
      userName={user.firstName}
22 -
      userAvatar={user.avatarURL}
21 +
      userName={user?.firstName}
22 +
      userAvatar={user?.avatarURL}
23 23
      showNotificationBadge={true}
24 24
    >
25 25
      <WeeklyResume
26 26
        avgMood="Neutro"
27 27
        stats={[
130 130
  insightMetric: {
131 131
    ...Typography.headlineLg,
132 132
    fontSize: 22,
133 133
  },
134 134
});
frontend/app/(tabs)/new/index.tsx
1 1
import { useEffect, useState } from "react";
2 2
import { StyleSheet, View, ActivityIndicator } from "react-native";
3 3

4 4
import { Text } from "@/components/ui/Text";
5 5
import { ThemedView } from "@/components/misc/themed-view";
40 40
    router.navigate(`/new/entry?initialMood=${mood}`);
41 41
  };
42 42

43 43
  return (
44 44
    <ScreenLayout
45 -
      userName={user.firstName}
46 -
      userAvatar={user.avatarURL}
45 +
      userName={user?.firstName}
46 +
      userAvatar={user?.avatarURL}
47 47
      showNotificationBadge={true}
48 48
    >
49 49
      <Section>
50 50
        <SectionHeader
51 51
          title="Como você está hoje?"
161 161
    fontSize: 11,
162 162
    fontWeight: 600,
163 163
    lineHeight: 16.5,
164 164
  },
165 165
});
frontend/app/(tabs)/settings.tsx
1 1
import {
2 2
  TouchableOpacity,
3 3
  StyleSheet,
4 4
  SectionList,
5 5
  View,
83 83
    }
84 84
  };
85 85

86 86
  return (
87 87
    <ScreenLayout
88 -
      userName={user.firstName}
89 -
      userAvatar={user.avatarURL}
88 +
      userName={user?.firstName}
89 +
      userAvatar={user?.avatarURL}
90 90
      showNotificationBadge={true}
91 91
    >
92 92
      <Section>
93 93
        <SectionHeader title="Perfil" />
94 94

371 371
    height: StyleSheet.hairlineWidth,
372 372
    backgroundColor: "#D1D5DB",
373 373
    marginVertical: 16,
374 374
  },
375 375
});
frontend/app/index.tsx
1 1
import { Redirect } from "expo-router";
2 2
import { useAuth } from "@/context/AuthContext";
3 3
import { ActivityIndicator, View } from "react-native";
4 4

5 5
export default function Index() {
13 13
      </View>
14 14
    );
15 15
  }
16 16

17 17
  if (isAuthenticated) {
18 -
    if (user.active) {
18 +
    if (user?.active) {
19 19
      url = "/(tabs)/new";
20 20
    } else {
21 21
      url = "/auth/activate";
22 22
    }
23 23
  }
24 24

25 25
  return <Redirect href={url} />;
26 26
}
frontend/app/profile.tsx modified+10 -24
frontend/app/profile.tsx
1 1
import { useState, useEffect } from 'react';
2 2
import {
3 3
  View,
4 4
  StyleSheet,
5 5
  ScrollView,
22 22
import { Typography, Spacing, Colors } from '@/constants/theme';
23 23

24 24
export default function Profile() {
25 25
  const { user, updateAuthUser } = useAuth();
26 26
  const { showToast } = useToast();
27 -
  const [firstName, setFirstName] = useState(user.firstName);
28 -
  const [lastName, setLastName] = useState(user.lastName);
29 -
  const [email, setEmail] = useState(user.email);
30 -
  const [password, setPassword] = useState();
31 -
  const [confirmPassword, setConfirmPassword] = useState();
32 -
  const [loading, setLoading] = useState(false);
27 +
  const [firstName, setFirstName] = useState(user?.firstName ?? '');
28 +
  const [lastName, setLastName] = useState(user?.lastName ?? '');
29 +
  const [email, setEmail] = useState(user?.email ?? '');
30 +
  const [password, setPassword] = useState('');
31 +
  const [confirmPassword, setConfirmPassword] = useState('');
32 +
  const [loading] = useState(false);
33 33
  const [errors, setErrors] = useState<{
34 34
    firstName?: string;
35 +
    lastName?: string;
36 +
    email?: string;
35 37
    password?: string;
36 38
    confirmPassword?: string;
37 39
  }>({});
38 40

39 -
  useEffect(() => {
40 -
    setFirstName(user.firstName)
41 -
    setLastName(user.lastName)
42 -
    setEmail(user.email)
43 -
  }, [user])
44 -

45 41
  const backgroundColor = useThemeColor({}, 'background');
46 -
  const textColor = useThemeColor({}, 'text');
47 42

48 43
  const validateForm = () => {
49 44
    const newErrors: {
50 45
      firstName?: string;
51 46
      email?: string;
75 70

76 71
    return Object.keys(newErrors).length === 0;
77 72
  }
78 73

79 74
  const handleUpdate = async () => {
75 +
    if (!user) return;
80 76
    if (!validateForm()) return;
81 77

82 -
    const data = {};
83 -

84 -
    data.firstName = firstName
85 -
    data.lastName = lastName
86 -
    data.email = email
87 -

88 -
    if (password) {
89 -
      data.password = password
90 -
    }
91 -

92 78
    try {
93 79
      const id = user.id;
94 80

95 81
      const response = await apiClient.updateUser({
96 82
        id,
127 113
        <ScrollView
128 114
          contentContainerStyle={styles.scrollContainer}
129 115
          showsVerticalScrollIndicator={false}
130 116
        >
131 117
          <AvatarUpload
132 -
            currentAvatar={user.avatarURL}
118 +
            currentAvatar={user?.avatarURL}
133 119
            onUploadSuccess={onChangeAvatar}
134 120
            onRemoveSuccess={onChangeAvatar} />
135 121

136 122
          <View style={styles.form}>
137 123
            <Card style={styles.cardBlock}>
263 249
  cardBlockHint: {
264 250
    ...Typography.cardHint,
265 251
    color: Colors.light.textSecondary
266 252
  }
267 253
});