Skip to main content

Overview

The profile screen provides riders with access to their personal information, account settings, and logout functionality. It displays user details retrieved from secure storage and offers quick access to account management features.

Profile Screen

Main Component

The profile screen combines user information display with navigation to various settings.
src/app/(parcel)/(tabs)/profile.tsx
import { IconSymbol } from "@/components/ui/icon-symbol";
import { ProfileMenuItem } from "@/modules/dashboard/profile/profile-menu-item";
import type { User } from "@/types/auth.types";
import { getInitials } from "@/utils/common";
import { Storage, StorageKeys } from "@/utils/storage";
import { useRouter } from "expo-router";
import { useMemo } from "react";
import { Alert, Pressable, ScrollView, Text, View } from "react-native";

export default function Profile() {
  const router = useRouter();

  const user = useMemo(() => {
    return Storage.getObject(StorageKeys.USER) as User | null;
  }, []);

  const handleLogout = () => {
    Alert.alert(
      "Logout",
      "Are you sure you want to logout?",
      [
        { text: "Cancel", style: "cancel" },
        {
          text: "Logout",
          style: "destructive",
          onPress: async () => {
            try {
              await Storage.deleteToken(StorageKeys.AUTH_TOKEN);
              await Storage.deleteToken(StorageKeys.REFRESH_TOKEN);
              Storage.removeItem(StorageKeys.USER);
              router.replace("/(public)/(auth)/sign-in");
            } catch (error) {
              console.error("Logout error:", error);
              Alert.alert("Error", "Failed to logout. Please try again.");
            }
          },
        },
      ],
      { cancelable: true },
    );
  };

  return (
    <View className="flex-1 bg-background pt-safe">
      <ScrollView showsVerticalScrollIndicator={false}>
        {/* Header Section */}
        <View className="items-center pt-8 pb-6 px-5">
          <View className="relative mb-4">
            <View
              className="w-[100px] h-[100px] rounded-full items-center justify-center"
              style={{ backgroundColor: accentColor }}
            >
              <Text className="text-4xl font-bold text-white">
                {getInitials(user?.fullName || "U")}
              </Text>
            </View>
            {user?.isVerified && (
              <View className="absolute bottom-0 right-0 bg-white rounded-xl p-1">
                <IconSymbol name="checkmark.seal.fill" size={16} color="#10B981" />
              </View>
            )}
          </View>

          <Text className="text-[32px] font-bold text-center text-secondary">
            {user?.fullName || "User"}
          </Text>

          {user?.phone && (
            <View className="flex-row items-center gap-2 mb-2">
              <IconSymbol name="phone.fill" size={16} />
              <Text className="text-sm opacity-70 text-secondary">{user.phone}</Text>
            </View>
          )}

          {user?.email && (
            <View className="flex-row items-center gap-2 mb-2">
              <IconSymbol name="envelope.fill" size={16} />
              <Text className="text-sm opacity-70 text-secondary">{user.email}</Text>
            </View>
          )}
        </View>

        {/* Account Section */}
        <View className="mt-2 px-5">
          <Text className="text-xl font-bold mb-3 mt-2 opacity-80 text-secondary">
            Account
          </Text>
          <View className="bg-transparent rounded-xl overflow-hidden mb-2">
            <ProfileMenuItem
              icon="pencil"
              title="Edit Profile"
              subtitle="Update your personal information"
              disabled={true}
            />
            <ProfileMenuItem
              icon="lock.fill"
              title="Change Password"
              subtitle="Update your password"
              disabled={true}
            />
          </View>
        </View>

        {/* Settings Section */}
        <View className="mt-2 px-5">
          <Text className="text-xl font-bold mb-3 mt-2 opacity-80 text-secondary">
            Settings
          </Text>
          <View className="bg-transparent rounded-xl overflow-hidden mb-2">
            <ProfileMenuItem
              icon="gearshape.fill"
              title="App Settings"
              subtitle="Notifications, preferences, and more"
              disabled={true}
            />
            <ProfileMenuItem
              icon="phone.fill"
              title="Contact Support"
              subtitle="Get help from our support team"
              disabled={true}
            />
          </View>
        </View>

        {/* Logout Button */}
        <View className="px-5 mt-6">
          <Pressable
            onPress={handleLogout}
            className="flex-row items-center justify-center gap-2 py-3.5 rounded-xl bg-red-500"
          >
            <IconSymbol
              name="rectangle.portrait.and.arrow.right"
              size={20}
              color="#FFFFFF"
            />
            <Text className="text-white text-base font-semibold">Logout</Text>
          </Pressable>
        </View>

        {/* Version Info */}
        <View className="items-center mt-8 px-5">
          <Text className="text-xs opacity-50 text-secondary">Version 1.0.0</Text>
        </View>
      </ScrollView>
    </View>
  );
}

User Information Display

The profile screen displays key user information retrieved from secure storage.

Avatar

Shows user initials with a colored background and verification badge if applicable.

Full Name

Displays the rider’s complete name prominently.

Contact Details

Shows phone number and email address with appropriate icons.

User Data Retrieval

const user = useMemo(() => {
  return Storage.getObject(StorageKeys.USER) as User | null;
}, []);

User Type Definition

src/types/auth.types.ts
export interface User {
  id: string;
  fullName: string;
  phone: string;
  email?: string;
  address?: string;
  profilePicture?: string;
  isVerified: boolean;
  role: string;
  createdAt: string;
  updatedAt: string;
}

Avatar Display

The profile avatar shows user initials with a verification badge for verified accounts.
<View className="relative mb-4">
  <View
    className="w-[100px] h-[100px] rounded-full items-center justify-center"
    style={{ backgroundColor: accentColor }}
  >
    <Text className="text-4xl font-bold text-white">
      {getInitials(user?.fullName || "U")}
    </Text>
  </View>
  {user?.isVerified && (
    <View className="absolute bottom-0 right-0 bg-white rounded-xl p-1">
      <IconSymbol name="checkmark.seal.fill" size={16} color="#10B981" />
    </View>
  )}
</View>

Get Initials Helper

src/utils/common.ts
export function getInitials(name: string): string {
  return name
    .split(" ")
    .map((n) => n[0])
    .join("")
    .toUpperCase()
    .slice(0, 2);
}

Profile Menu Items

The profile screen includes organized menu sections for account and settings.

Profile Menu Item Component

src/modules/dashboard/profile/profile-menu-item.tsx
import { IconSymbol } from "@/components/ui/icon-symbol";
import { Pressable, Text, View } from "react-native";

interface ProfileMenuItemProps {
  icon: string;
  title: string;
  subtitle: string;
  onPress?: () => void;
  disabled?: boolean;
}

export function ProfileMenuItem({
  icon,
  title,
  subtitle,
  onPress,
  disabled = false,
}: ProfileMenuItemProps) {
  return (
    <Pressable
      onPress={onPress}
      disabled={disabled}
      className="flex-row items-center py-4 px-4 border-b border-gray-100"
    >
      <View className="bg-gray-100 rounded-full p-3 mr-4">
        <IconSymbol name={icon} size={20} />
      </View>
      <View className="flex-1">
        <Text className="text-base font-semibold text-secondary">{title}</Text>
        <Text className="text-sm text-gray-500 mt-0.5">{subtitle}</Text>
      </View>
      {!disabled && <IconSymbol name="chevron.right" size={20} color="#9ca3af" />}
    </Pressable>
  );
}
  • Edit Profile: Update personal information (coming soon)
  • Change Password: Modify account password (coming soon)
Some menu items are currently disabled as their functionality is under development. They’re displayed to provide a preview of upcoming features.

Logout Functionality

The logout feature securely clears all user data and tokens.

Logout Handler

const handleLogout = () => {
  Alert.alert(
    "Logout",
    "Are you sure you want to logout?",
    [
      {
        text: "Cancel",
        style: "cancel",
      },
      {
        text: "Logout",
        style: "destructive",
        onPress: async () => {
          try {
            await Storage.deleteToken(StorageKeys.AUTH_TOKEN);
            await Storage.deleteToken(StorageKeys.REFRESH_TOKEN);
            Storage.removeItem(StorageKeys.USER);
            router.replace("/(public)/(auth)/sign-in");
          } catch (error) {
            console.error("Logout error:", error);
            Alert.alert("Error", "Failed to logout. Please try again.");
          }
        },
      },
    ],
    { cancelable: true },
  );
};

Logout Process

1

Show Confirmation

Display a native alert asking the user to confirm logout action.
2

Delete Tokens

Remove authentication and refresh tokens from secure storage.
3

Clear User Data

Remove cached user object from MMKV storage.
4

Navigate to Login

Redirect to the sign-in screen using router.replace.
5

Error Handling

Show error alert if logout process fails.

Storage Cleanup

// Delete secure tokens
await Storage.deleteToken(StorageKeys.AUTH_TOKEN);
await Storage.deleteToken(StorageKeys.REFRESH_TOKEN);

// Remove user object
Storage.removeItem(StorageKeys.USER);
The logout process uses router.replace() instead of router.push() to prevent users from navigating back to authenticated screens using the back button.

Confirmation Dialog

The app uses the native Alert API for logout confirmation.
Alert.alert(
  "Logout",                          // Title
  "Are you sure you want to logout?", // Message
  [
    {
      text: "Cancel",
      style: "cancel",
    },
    {
      text: "Logout",
      style: "destructive",           // Red color on iOS
      onPress: async () => { /* logout logic */ },
    },
  ],
  { cancelable: true }                // Allow tap outside to dismiss
);

Verification Badge

Verified users receive a green checkmark badge on their avatar.
{user?.isVerified && (
  <View className="absolute bottom-0 right-0 bg-white rounded-xl p-1 border-2 border-white">
    <IconSymbol
      name="checkmark.seal.fill"
      size={16}
      color="#10B981"
    />
  </View>
)}
Verification Badge

Version Information

The app version is displayed at the bottom of the profile screen.
<View className="items-center mt-8 px-5">
  <Text className="text-xs opacity-50 text-secondary">
    Version 1.0.0
  </Text>
</View>

User Data Structure

The user object contains all relevant rider information.
  • id: Unique user identifier
  • fullName: Complete name of the rider
  • phone: Primary contact number
  • email: Optional email address
  • address: Residential address
  • profilePicture: URL to avatar image (optional)
  • isVerified: Verification status flag
  • role: User role in the system
  • createdAt: Account creation timestamp
  • updatedAt: Last update timestamp

Security Considerations

Secure Storage

User data is stored in encrypted MMKV storage on native platforms.

Token Cleanup

All authentication tokens are properly removed on logout.

Confirmation Required

Users must confirm before logging out to prevent accidental logouts.

Error Handling

Logout errors are caught and reported to the user.

Authentication

Learn about the login process and token management.

Dashboard

Return to the main dashboard.

Transactions

View transaction history and earnings.

Build docs developers (and LLMs) love