refactor username

Changed username to be the first name "_" user id
This commit is contained in:
suddenlyoranges committed 2026-04-09 21:33:57 +01:00
1 parent 53fddc8220
commit 07b12cdd0f
3 files changed
+151 -57

No files matched your search

-3
View File
@@ -1,8 +1,5 @@
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
# Environment variables (contains secrets)
.env
.env.local
# dependencies
node_modules/
@@ -1,5 +1,13 @@
import { useCallback, useEffect, useRef, useState } from "react";
import {ActivityIndicator, Image, ScrollView, StyleSheet, Text, TextInput, View} from "react-native";
import {
ActivityIndicator,
Image,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from "react-native";
import Screen from "../../layout/Screen";
import { Button, ButtonTray } from "../../UI/Button";
import useGlobalHook from "../../../hooks/useGlobalHook";
@@ -44,7 +52,8 @@ const GlobalProfileScreen = ({navigation}) => {
// Handlers ----------------------------
const hydrateProfile = useCallback(async (options = {}) => {
const hydrateProfile = useCallback(
async (options = {}) => {
setIsLoading(true);
setError("");
@@ -54,7 +63,9 @@ const GlobalProfileScreen = ({navigation}) => {
}
if (!globalApiRef.current.isGlobalApiReady()) {
setError("Global API URL is not configured yet. Add it in src/components/API/api.json.");
setError(
"Global API URL is not configured yet. Add it in src/components/API/api.json.",
);
setIsLoading(false);
return;
}
@@ -62,7 +73,9 @@ const GlobalProfileScreen = ({navigation}) => {
const globalUserId = session.currentGlobalUserId ?? session.currentUid;
const latest = await globalApiRef.current.getUser(globalUserId, options);
if (!latest) {
setError("Global profile not found. Tap Join Global Game first to initialise it.");
setError(
"Global profile not found. Tap Join Global Game first to initialise it.",
);
setIsLoading(false);
return;
}
@@ -75,14 +88,16 @@ const GlobalProfileScreen = ({navigation}) => {
UserImageURL: latest.UserImageURL || defaultGlobalProfileImageUrl,
});
setIsLoading(false);
}, [
},
[
defaultGlobalProfileImageUrl,
navigation,
session.currentGameMode,
session.currentGlobalUserId,
session.currentUid,
session.isBusiness,
]);
],
);
useEffect(() => {
hydrateProfile();
@@ -98,19 +113,24 @@ const GlobalProfileScreen = ({navigation}) => {
UserFirstname: profile.UserFirstname.trim() || "Global",
UserLastname: profile.UserLastname.trim() || "User",
UserPhone: profile.UserPhone.trim() || "00000",
UserUsername: profile.UserUsername.trim() || `global_${globalUserId}`,
UserUsername: `${profile.UserFirstname.trim() || "Global"}_${globalUserId}`,
UserImageURL: profile.UserImageURL.trim() || defaultGlobalProfileImageUrl,
UserTimestamp: Date.now(),
};
const updated = await globalApiRef.current.updateUser(globalUserId, payload);
const updated = await globalApiRef.current.updateUser(
globalUserId,
payload,
);
if (!updated) {
setError("Failed to save profile. Please try again.");
setIsSaving(false);
return;
}
const latest = await globalApiRef.current.getUser(globalUserId, {forceRefresh: true});
const latest = await globalApiRef.current.getUser(globalUserId, {
forceRefresh: true,
});
setProfile({
UserFirstname: latest?.UserFirstname || payload.UserFirstname,
UserLastname: latest?.UserLastname || payload.UserLastname,
@@ -140,7 +160,9 @@ const GlobalProfileScreen = ({navigation}) => {
<View style={styles.avatarWrap}>
<Image
source={{uri: profile.UserImageURL || defaultGlobalProfileImageUrl}}
source={{
uri: profile.UserImageURL || defaultGlobalProfileImageUrl,
}}
style={styles.avatar}
/>
</View>
@@ -254,4 +276,3 @@ const styles = StyleSheet.create({
});
export default GlobalProfileScreen;
+119 -43
View File
@@ -9,10 +9,14 @@ import {
} from "../utils/globalDataCache";
const GLOBAL_BASE = String(
process.env.EXPO_PUBLIC_GLOBAL_API_BASE || globalApiConfig.GLOBAL_API_BASE_URL || "",
process.env.EXPO_PUBLIC_GLOBAL_API_BASE ||
globalApiConfig.GLOBAL_API_BASE_URL ||
"",
).trim();
const GLOBAL_KEY = String(
process.env.EXPO_PUBLIC_GLOBAL_API_KEY || globalApiConfig.GLOBAL_API_KEY || "",
process.env.EXPO_PUBLIC_GLOBAL_API_KEY ||
globalApiConfig.GLOBAL_API_KEY ||
"",
).trim();
const DEFAULT_GLOBAL_PROFILE_IMAGE_URL =
String(globalApiConfig.DEFAULT_GLOBAL_PROFILE_IMAGE_URL || "").trim() ||
@@ -104,11 +108,16 @@ const splitUsername = (username) => {
const buildGlobalUserPayload = (userId, privateUser = null) => {
const privateUsername = firstNonEmpty(privateUser?.username);
const nameParts = splitUsername(privateUsername);
const syncedUsername = `mad_uid_${userId}`;
const firstName = firstNonEmpty(
privateUser?.firstName,
nameParts.first,
"Global",
);
const syncedUsername = `${firstName}_${userId}`;
return {
UserID: userId,
UserFirstname: firstNonEmpty(privateUser?.firstName, nameParts.first, "Global"),
UserFirstname: firstName,
UserLastname: firstNonEmpty(privateUser?.lastName, nameParts.last, "User"),
// The live global API validates phone length >= 12.
UserPhone: withMinLength(privateUser?.phone, 12, "000000000000"),
@@ -146,7 +155,8 @@ const useGlobalHook = () => {
// Handlers ----------------------------
// Events
const getPublicEvents = useCallback(async (options = {}) => {
const getPublicEvents = useCallback(
async (options = {}) => {
if (!isGlobalApiReady()) return [];
return getOrFetchGlobalData(
CACHE_KEY.publicEvents,
@@ -158,36 +168,49 @@ const useGlobalHook = () => {
},
{ ttlMs: CACHE_TTL.events, forceRefresh: options.forceRefresh },
);
}, [endpoints.events]);
},
[endpoints.events],
);
const getEvent = useCallback(async (eventId, options = {}) => {
const getEvent = useCallback(
async (eventId, options = {}) => {
if (!isGlobalApiReady()) return null;
return getOrFetchGlobalData(
CACHE_KEY.event(eventId),
async () => {
const response = await API.get(withKey(`${endpoints.events}/${eventId}`));
const response = await API.get(
withKey(`${endpoints.events}/${eventId}`),
);
if (!response.isSuccess) return null;
return unwrapSingle(response.result);
},
{ ttlMs: CACHE_TTL.events, forceRefresh: options.forceRefresh },
);
}, [endpoints.events]);
},
[endpoints.events],
);
// Players
const getPlayersByEvent = useCallback(async (eventId, options = {}) => {
const getPlayersByEvent = useCallback(
async (eventId, options = {}) => {
if (!isGlobalApiReady()) return [];
return getOrFetchGlobalData(
CACHE_KEY.playersByEvent(eventId),
async () => {
const response = await API.get(withKey(`${endpoints.players}/events/${eventId}`));
const response = await API.get(
withKey(`${endpoints.players}/events/${eventId}`),
);
if (!response.isSuccess) return [];
return unwrapList(response.result);
},
{ ttlMs: CACHE_TTL.players, forceRefresh: options.forceRefresh },
);
}, [endpoints.players]);
},
[endpoints.players],
);
const joinEvent = useCallback(async (data) => {
const joinEvent = useCallback(
async (data) => {
if (!isGlobalApiReady()) return null;
const response = await API.post(withKey(endpoints.players), data);
if (!response.isSuccess) return null;
@@ -197,63 +220,86 @@ const useGlobalHook = () => {
clearGlobalDataCache(CACHE_KEY.playersByEvent(eventId));
}
return joined;
}, [endpoints.players]);
},
[endpoints.players],
);
// Caches
const getCachesByEvent = useCallback(async (eventId, options = {}) => {
const getCachesByEvent = useCallback(
async (eventId, options = {}) => {
if (!isGlobalApiReady()) return [];
return getOrFetchGlobalData(
CACHE_KEY.cachesByEvent(eventId),
async () => {
const response = await API.get(withKey(`${endpoints.caches}/events/${eventId}`));
const response = await API.get(
withKey(`${endpoints.caches}/events/${eventId}`),
);
if (!response.isSuccess) return [];
return unwrapList(response.result);
},
{ ttlMs: CACHE_TTL.caches, forceRefresh: options.forceRefresh },
);
}, [endpoints.caches]);
},
[endpoints.caches],
);
const getCache = useCallback(async (cacheId, options = {}) => {
const getCache = useCallback(
async (cacheId, options = {}) => {
if (!isGlobalApiReady()) return null;
return getOrFetchGlobalData(
CACHE_KEY.cacheById(cacheId),
async () => {
const response = await API.get(withKey(`${endpoints.caches}/${cacheId}`));
const response = await API.get(
withKey(`${endpoints.caches}/${cacheId}`),
);
if (!response.isSuccess) return null;
return unwrapSingle(response.result);
},
{ ttlMs: CACHE_TTL.caches, forceRefresh: options.forceRefresh },
);
}, [endpoints.caches]);
},
[endpoints.caches],
);
// Finds
const getFindsByEvent = useCallback(async (eventId, options = {}) => {
const getFindsByEvent = useCallback(
async (eventId, options = {}) => {
if (!isGlobalApiReady()) return [];
return getOrFetchGlobalData(
CACHE_KEY.findsByEvent(eventId),
async () => {
const response = await API.get(withKey(`${endpoints.finds}/events/${eventId}`));
const response = await API.get(
withKey(`${endpoints.finds}/events/${eventId}`),
);
if (!response.isSuccess) return [];
return unwrapList(response.result);
},
{ ttlMs: CACHE_TTL.finds, forceRefresh: options.forceRefresh },
);
}, [endpoints.finds]);
},
[endpoints.finds],
);
const getFindsByPlayer = useCallback(async (playerId, options = {}) => {
const getFindsByPlayer = useCallback(
async (playerId, options = {}) => {
if (!isGlobalApiReady()) return [];
return getOrFetchGlobalData(
CACHE_KEY.findsByPlayer(playerId),
async () => {
const response = await API.get(withKey(`${endpoints.finds}/players/${playerId}`));
const response = await API.get(
withKey(`${endpoints.finds}/players/${playerId}`),
);
if (!response.isSuccess) return [];
return unwrapList(response.result);
},
{ ttlMs: CACHE_TTL.finds, forceRefresh: options.forceRefresh },
);
}, [endpoints.finds]);
},
[endpoints.finds],
);
const logFind = useCallback(async (data) => {
const logFind = useCallback(
async (data) => {
if (!isGlobalApiReady()) return null;
const response = await API.post(withKey(endpoints.finds), data);
if (!response.isSuccess) return null;
@@ -264,10 +310,13 @@ const useGlobalHook = () => {
// Event ID is not always present in payload, so clear all event-level find caches.
clearGlobalDataCacheByPrefix("global:finds:event:");
return find;
}, [endpoints.finds]);
},
[endpoints.finds],
);
// Users
const getUsers = useCallback(async (options = {}) => {
const getUsers = useCallback(
async (options = {}) => {
if (!isGlobalApiReady()) return [];
return getOrFetchGlobalData(
CACHE_KEY.users,
@@ -278,9 +327,12 @@ const useGlobalHook = () => {
},
{ ttlMs: CACHE_TTL.users, forceRefresh: options.forceRefresh },
);
}, [endpoints.users]);
},
[endpoints.users],
);
const getUser = useCallback(async (userId, options = {}) => {
const getUser = useCallback(
async (userId, options = {}) => {
if (!isGlobalApiReady()) return null;
const key = CACHE_KEY.userById(userId);
@@ -288,20 +340,27 @@ const useGlobalHook = () => {
key,
async () => {
// Some global API deployments expose list-only user routes.
const byIdResponse = await API.get(withKey(`${endpoints.users}/${userId}`));
const byIdResponse = await API.get(
withKey(`${endpoints.users}/${userId}`),
);
if (byIdResponse.isSuccess) {
const single = unwrapSingle(byIdResponse.result);
if (single) return single;
}
const users = await getUsers({ forceRefresh: options.forceRefresh });
return users.find((user) => String(user.UserID) === String(userId)) || null;
return (
users.find((user) => String(user.UserID) === String(userId)) || null
);
},
{ ttlMs: CACHE_TTL.users, forceRefresh: options.forceRefresh },
);
}, [endpoints.users, getUsers]);
},
[endpoints.users, getUsers],
);
const createUser = useCallback(async (payload) => {
const createUser = useCallback(
async (payload) => {
if (!isGlobalApiReady()) return null;
const response = await API.post(withKey(endpoints.users), payload);
if (!response.isSuccess) return null;
@@ -316,20 +375,33 @@ const useGlobalHook = () => {
}
clearGlobalDataCache(CACHE_KEY.users);
return created;
}, [endpoints.users]);
},
[endpoints.users],
);
const updateUser = useCallback(async (userId, payload) => {
const updateUser = useCallback(
async (userId, payload) => {
if (!isGlobalApiReady()) return null;
const response = await API.put(withKey(`${endpoints.users}/${userId}`), payload);
const response = await API.put(
withKey(`${endpoints.users}/${userId}`),
payload,
);
if (!response.isSuccess) return null;
const updated = unwrapSingle(response.result) || payload;
writeGlobalDataCache(CACHE_KEY.userById(userId), updated, CACHE_TTL.users);
writeGlobalDataCache(
CACHE_KEY.userById(userId),
updated,
CACHE_TTL.users,
);
clearGlobalDataCache(CACHE_KEY.users);
return updated;
}, [endpoints.users]);
},
[endpoints.users],
);
// Only called by Join Global Game flow to mirror a private user into global API schema.
const ensureGlobalUser = useCallback(async (userId, privateUser = null) => {
const ensureGlobalUser = useCallback(
async (userId, privateUser = null) => {
if (!userId || !isGlobalApiReady()) return null;
const seed = buildGlobalUserPayload(userId, privateUser);
@@ -350,11 +422,15 @@ const useGlobalHook = () => {
// Final fallback: check list endpoint again after attempted creation.
const refreshed = await getUsers({ forceRefresh: true });
return (
refreshed.find((user) => String(user.UserUsername) === String(seed.UserUsername)) ||
refreshed.find(
(user) => String(user.UserUsername) === String(seed.UserUsername),
) ||
refreshed.find((user) => String(user.UserID) === String(userId)) ||
null
);
}, [createUser, getUser, getUsers]);
},
[createUser, getUser, getUsers],
);
// Return --------------------------------