Skip to content
13 changes: 8 additions & 5 deletions shatter-mobile/app/auth/callback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ActivityIndicator, Text, View } from "react-native";

export default function AuthCallback() {
const { code } = useLocalSearchParams<{ code: string }>();
const { authenticate, authStorage } = useAuth();
const { authenticate } = useAuth();
const [error, setError] = useState("");

useEffect(() => {
Expand All @@ -28,21 +28,24 @@ export default function AuthCallback() {
// Fetch full user profile using returned userId + token
const userData = await userFetch(response.userId, response.token);

const existingSocialLinks = userData.user.socialLinks ?? {};
const linkedinUrl = existingSocialLinks.linkedin
?? `https://www.linkedin.com/in/${userData.user.name.toLowerCase().replace(/\s+/g, "-")}/`;

const user: User = {
_id: response.userId,
name: userData.user.name,
email: userData.user.email,
socialLinks: userData.user.socialLinks ?? {},
socialLinks: { ...existingSocialLinks, linkedin: linkedinUrl },
profilePhoto: userData.user.profilePhoto,
isGuest: false,
};

// Store user + JWT in auth context
await authenticate(user, response.token, false);

// Update stored user with LinkedIn data
const token = authStorage.accessToken;
userUpdate(response.userId, user, token);
// Persist LinkedIn URL to backend using the token from the exchange response
userUpdate(response.userId, user, response.token);
router.replace("/JoinEventPage");
} catch (err) {
setError((err as Error).message || "Authentication failed.");
Expand Down
60 changes: 59 additions & 1 deletion shatter-mobile/src/components/login-signup/SignupForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//called by Profile.tsx for signing up
import { User } from "@/src/interfaces/User";
import { SocialLinks } from "@/src/interfaces/User";
import { userSignup, userUpdate } from "@/src/services/user.service";
import { FontAwesome, Feather } from "@expo/vector-icons";
import { router, Stack } from "expo-router";
import * as WebBrowser from "expo-web-browser";
import { useState } from "react";
Expand All @@ -18,6 +20,7 @@ import {
import { SafeAreaView } from "react-native-safe-area-context";
import { SignUpFormStyling as styles } from "../../styling/SignUpFormStyling.styles";
import { useAuth } from "../context/AuthContext";
import { colors } from "@/src/styling/constants";

export default function SignUpForm() {
const { authenticate } = useAuth();
Expand All @@ -26,6 +29,10 @@ export default function SignUpForm() {
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const [err, setError] = useState("");
const [selectedType, setSelectedType] = useState<"linkedin" | "github" | "other" | null>(null);
const [linkedin, setLinkedin] = useState("");
const [github, setGithub] = useState("");
const [other, setOther] = useState("");

const handleLinkedIn = async () => {
await WebBrowser.openBrowserAsync(
Expand Down Expand Up @@ -76,11 +83,16 @@ export default function SignUpForm() {
setError("User info could not be created. Please try again later.");
}

const socialLinks: SocialLinks = {};
if (linkedin.trim()) socialLinks.linkedin = linkedin.trim();
if (github.trim()) socialLinks.github = github.trim();
if (other.trim()) socialLinks.other = [{ label: "Contact Link", url: other.trim() }];

const user: User = {
_id: userResponse.userId,
name,
email,
socialLinks: {},
socialLinks,
profilePhoto: profilePhoto,
isGuest: false,
};
Expand Down Expand Up @@ -147,6 +159,52 @@ export default function SignUpForm() {
placeholderTextColor="#888"
/>

<Text style={styles.input && { color: colors.darkNavy, fontWeight: "600", fontSize: 14, marginBottom: 8 }}>Contact Link</Text>
<View style={{ flexDirection: "row", justifyContent: "space-between", marginBottom: 12 }}>
<TouchableOpacity
onPress={() => setSelectedType("linkedin")}
style={{ padding: 12, borderRadius: 12, backgroundColor: selectedType === "linkedin" ? "#0A66C2" : colors.lightGrey2, flex: 1, marginRight: 8, alignItems: "center" }}
>
<FontAwesome name="linkedin" size={22} color="white" />
</TouchableOpacity>
<TouchableOpacity
onPress={() => setSelectedType("github")}
style={{ padding: 12, borderRadius: 12, backgroundColor: selectedType === "github" ? "#24292e" : colors.lightGrey2, flex: 1, marginRight: 8, alignItems: "center" }}
>
<FontAwesome name="github" size={22} color="white" />
</TouchableOpacity>
<TouchableOpacity
onPress={() => setSelectedType("other")}
style={{ padding: 12, borderRadius: 12, backgroundColor: selectedType === "other" ? "#6c63ff" : colors.lightGrey2, flex: 1, alignItems: "center" }}
>
<Feather name="link" size={22} color="white" />
</TouchableOpacity>
</View>

{selectedType && (
<TextInput
style={styles.input}
placeholder={
selectedType === "linkedin" ? "LinkedIn URL"
: selectedType === "github" ? "GitHub URL"
: "Portfolio / Other URL"
}
placeholderTextColor="#888"
value={selectedType === "linkedin" ? linkedin : selectedType === "github" ? github : other}
onChangeText={(text) => {
if (selectedType === "linkedin") setLinkedin(text);
else if (selectedType === "github") setGithub(text);
else setOther(text);
}}
autoCapitalize="none"
keyboardType="url"
/>
)}

<Text style={{ color: "#888", fontSize: 13, fontStyle: "italic", marginBottom: 12 }}>
Select a platform above, then enter your profile link.
</Text>

<TouchableOpacity
style={[
styles.button,
Expand Down
6 changes: 6 additions & 0 deletions shatter-mobile/src/services/linkedin_auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as Linking from "expo-linking";
import * as WebBrowser from "expo-web-browser";
import { Platform } from "react-native";
import { User } from "../interfaces/User";
import { exchangeLinkedInCode, userFetch } from "./user.service";

Expand All @@ -8,6 +9,11 @@ const REDIRECT_SCHEME = "shattermobile://auth";
export async function loginWithLinkedIn(): Promise<
{ user: User; token: string } | null
> {
if (Platform.OS === "web") {
window.location.href = `${process.env.EXPO_PUBLIC_API_BASE}/api/auth/linkedin`;
return null;
}

const result = await WebBrowser.openAuthSessionAsync(
`${process.env.EXPO_PUBLIC_API_BASE}/api/auth/linkedin?platform=mobile`,
REDIRECT_SCHEME,
Expand Down
2 changes: 1 addition & 1 deletion shatter-mobile/src/styling/SignUpFormStyling.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const SignUpFormStyling = StyleSheet.create({
background: {
flex: 1,
width: "100%",
height: "50%",
height: "100%",
},
safe: {
flex: 1,
Expand Down
Loading