refactored app theme

changed texts to suit dark theme.
This commit is contained in:
suddenlyoranges committed 2026-04-09 21:39:42 +01:00
1 parent 07b12cdd0f
commit 12847cc46c
5 files changed
+204 -144

No files matched your search

@@ -1,10 +1,10 @@
import {useEffect} from "react";
import {StyleSheet, Text, View} from "react-native";
import { useEffect } from "react";
import { StyleSheet, Text, View } from "react-native";
import Screen from "../../layout/Screen";
import {Button, ButtonTray} from "../../UI/Button";
import { Button, ButtonTray } from "../../UI/Button";
import Card from "../../UI/Card";
import {getSession} from "../../../hooks/SessionStore";
import {GAME_MODE} from "../../../utils/gameConstants";
import { getSession } from "../../../hooks/SessionStore";
import { GAME_MODE } from "../../../utils/gameConstants";
const GlobalCacheViewScreen = ({ navigation, route }) => {
// Initialisations ---------------------
@@ -67,7 +67,8 @@ const GlobalCacheViewScreen = ({ navigation, route }) => {
{!initIsFound ? (
<Text style={styles.claimHint}>
Claim this cache from the Global Map while within 30m and facing it.
Claim this cache from the Global Map while within 30m and facing
it.
</Text>
) : null}
</Card>
@@ -91,7 +92,7 @@ const styles = StyleSheet.create({
title: {
fontSize: 22,
fontWeight: "700",
color: "#111827",
color: "#cdd6f4",
},
statusBadge: {
alignSelf: "flex-start",
@@ -100,15 +101,15 @@ const styles = StyleSheet.create({
borderRadius: 12,
},
badgeFound: {
backgroundColor: "#d1fae5",
backgroundColor: "#1e3a2f",
},
badgeOpen: {
backgroundColor: "#fef3c7",
backgroundColor: "#3d3117",
},
statusText: {
fontSize: 13,
fontWeight: "600",
color: "#374151",
color: "#cdd6f4",
},
detailRow: {
gap: 2,
@@ -116,17 +117,17 @@ const styles = StyleSheet.create({
label: {
fontSize: 12,
fontWeight: "600",
color: "#6b7280",
color: "#a6adc8",
textTransform: "uppercase",
letterSpacing: 0.5,
},
value: {
fontSize: 15,
color: "#111827",
color: "#cdd6f4",
},
claimHint: {
marginTop: 6,
color: "#2563eb",
color: "#89b4fa",
fontSize: 13,
fontWeight: "600",
},
@@ -1,21 +1,17 @@
import {useCallback, useEffect, useRef, useState} from "react";
import {ActivityIndicator, StyleSheet, Text, View} from "react-native";
import { useCallback, useEffect, useRef, useState } from "react";
import { ActivityIndicator, StyleSheet, Text, View } from "react-native";
import Screen from "../../layout/Screen";
import {Button, ButtonTray} from "../../UI/Button";
import { Button, ButtonTray } from "../../UI/Button";
import EventList from "../../../entity/event/EventList";
import useGlobalHook from "../../../hooks/useGlobalHook";
import {getSession, setGlobalSession} from "../../../hooks/SessionStore";
import {GAME_MODE} from "../../../utils/gameConstants";
import { getSession, setGlobalSession } from "../../../hooks/SessionStore";
import { GAME_MODE } from "../../../utils/gameConstants";
const GlobalEventsScreen = ({ navigation }) => {
// Initialisations ---------------------
const {
getPublicEvents,
getPlayersByEvent,
joinEvent,
isGlobalApiReady,
} = useGlobalHook();
const { getPublicEvents, getPlayersByEvent, joinEvent, isGlobalApiReady } =
useGlobalHook();
const session = getSession();
const globalApiRef = useRef({
getPublicEvents,
@@ -45,7 +41,9 @@ const GlobalEventsScreen = ({ navigation }) => {
if (!globalApiRef.current.isGlobalApiReady()) {
setEvents([]);
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;
}
@@ -68,7 +66,9 @@ const GlobalEventsScreen = ({ navigation }) => {
setJoining(event.EventID);
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.",
);
setJoining(null);
return;
}
@@ -82,7 +82,10 @@ const GlobalEventsScreen = ({ navigation }) => {
};
// Check if already a player
const players = await globalApiRef.current.getPlayersByEvent(event.EventID, {forceRefresh: true});
const players = await globalApiRef.current.getPlayersByEvent(
event.EventID,
{ forceRefresh: true },
);
const existing = (players || []).find(
(p) => String(p.PlayerUserID) === String(globalUserId),
);
@@ -110,7 +113,10 @@ const GlobalEventsScreen = ({ navigation }) => {
}
// If create fails due duplicate race/server behavior, recover by re-reading players.
const refreshedPlayers = await globalApiRef.current.getPlayersByEvent(event.EventID, {forceRefresh: true});
const refreshedPlayers = await globalApiRef.current.getPlayersByEvent(
event.EventID,
{ forceRefresh: true },
);
const recovered = (refreshedPlayers || []).find(
(p) => String(p.PlayerUserID) === String(globalUserId),
);
@@ -131,7 +137,10 @@ const GlobalEventsScreen = ({ navigation }) => {
<Screen>
<View style={styles.container}>
<ButtonTray>
<Button label="Refresh" onClick={() => loadEvents({forceRefresh: true})} />
<Button
label="Refresh"
onClick={() => loadEvents({ forceRefresh: true })}
/>
</ButtonTray>
{error ? <Text style={styles.error}>{error}</Text> : null}
@@ -174,21 +183,21 @@ const styles = StyleSheet.create({
gap: 10,
},
loadingText: {
color: "#6b7280",
color: "#a6adc8",
fontSize: 14,
},
empty: {
textAlign: "center",
color: "#9ca3af",
color: "#a6adc8",
marginTop: 30,
fontSize: 15,
},
hint: {
fontSize: 13,
color: "#6b7280",
color: "#a6adc8",
},
error: {
color: "#dc2626",
color: "#f38ba8",
fontSize: 14,
},
overlay: {
@@ -1,12 +1,18 @@
import {useCallback, useEffect, useRef, useState} from "react";
import {ActivityIndicator, ScrollView, StyleSheet, Text, View,} from "react-native";
import { useCallback, useEffect, useRef, useState } from "react";
import {
ActivityIndicator,
ScrollView,
StyleSheet,
Text,
View,
} from "react-native";
import Screen from "../../layout/Screen";
import {Button, ButtonTray} from "../../UI/Button";
import { Button, ButtonTray } from "../../UI/Button";
import Card from "../../UI/Card";
import FindList from "../../../entity/find/FindList";
import useGlobalHook from "../../../hooks/useGlobalHook";
import {getSession} from "../../../hooks/SessionStore";
import {GAME_MODE} from "../../../utils/gameConstants";
import { getSession } from "../../../hooks/SessionStore";
import { GAME_MODE } from "../../../utils/gameConstants";
const GlobalLeaderboardScreen = ({ navigation, route }) => {
// Initialisations ---------------------
@@ -35,75 +41,78 @@ const GlobalLeaderboardScreen = ({ navigation, route }) => {
// Handlers ----------------------------
const loadData = useCallback(async (options = {}) => {
if (!eventId) return;
const loadData = useCallback(
async (options = {}) => {
if (!eventId) return;
setIsLoading(true);
setIsLoading(true);
const [finds, caches, players] = await Promise.all([
globalApiRef.current.getFindsByEvent(eventId, options),
globalApiRef.current.getCachesByEvent(eventId, options),
globalApiRef.current.getPlayersByEvent(eventId, options),
]);
const [finds, caches, players] = await Promise.all([
globalApiRef.current.getFindsByEvent(eventId, options),
globalApiRef.current.getCachesByEvent(eventId, options),
globalApiRef.current.getPlayersByEvent(eventId, options),
]);
// Build cache points lookup
const cacheMap = {};
(caches || []).forEach((c) => {
cacheMap[String(c.CacheID)] = c;
});
// Build cache points lookup
const cacheMap = {};
(caches || []).forEach((c) => {
cacheMap[String(c.CacheID)] = c;
});
// Augment finds with cache info for FindList
const augmented = (finds || []).map((f) => ({
...f,
FindCache: f.FindCache || cacheMap[String(f.FindCacheID)] || null,
}));
// Augment finds with cache info for FindList
const augmented = (finds || []).map((f) => ({
...f,
FindCache: f.FindCache || cacheMap[String(f.FindCacheID)] || null,
}));
// Sort recent first
const sorted = [...augmented].sort(
(a, b) => new Date(b.FindDatetime) - new Date(a.FindDatetime),
);
setRecentFinds(sorted.slice(0, 50));
// Sort recent first
const sorted = [...augmented].sort(
(a, b) => new Date(b.FindDatetime) - new Date(a.FindDatetime),
);
setRecentFinds(sorted.slice(0, 50));
// Start ranking map with all players so zero-point players are visible.
const pointsMap = {};
(players || []).forEach((p) => {
const pid = p.PlayerID;
pointsMap[String(pid)] = {
playerId: pid,
points: Number(p.PlayerPoints || 0),
finds: 0,
name: p.PlayerUser?.UserUsername || `Player #${p.PlayerUserID}`,
};
});
// Aggregate points and finds from recorded discoveries.
augmented.forEach((f) => {
const pid = f.FindPlayerID;
const key = String(pid);
const pts = Number(f.FindCache?.CachePoints || 0);
if (!pointsMap[key]) {
pointsMap[key] = {
// Start ranking map with all players so zero-point players are visible.
const pointsMap = {};
(players || []).forEach((p) => {
const pid = p.PlayerID;
pointsMap[String(pid)] = {
playerId: pid,
points: 0,
points: Number(p.PlayerPoints || 0),
finds: 0,
name: `Player #${f.FindPlayerID}`,
name: p.PlayerUser?.UserUsername || `Player #${p.PlayerUserID}`,
};
}
});
pointsMap[key].points += pts;
pointsMap[key].finds += 1;
});
// Aggregate points and finds from recorded discoveries.
augmented.forEach((f) => {
const pid = f.FindPlayerID;
const key = String(pid);
const pts = Number(f.FindCache?.CachePoints || 0);
const ranked = Object.values(pointsMap).sort((a, b) => {
if (b.points !== a.points) return b.points - a.points;
if (b.finds !== a.finds) return b.finds - a.finds;
return String(a.name || "").localeCompare(String(b.name || ""));
});
if (!pointsMap[key]) {
pointsMap[key] = {
playerId: pid,
points: 0,
finds: 0,
name: `Player #${f.FindPlayerID}`,
};
}
setRankings(ranked);
setIsLoading(false);
}, [eventId]);
pointsMap[key].points += pts;
pointsMap[key].finds += 1;
});
const ranked = Object.values(pointsMap).sort((a, b) => {
if (b.points !== a.points) return b.points - a.points;
if (b.finds !== a.finds) return b.finds - a.finds;
return String(a.name || "").localeCompare(String(b.name || ""));
});
setRankings(ranked);
setIsLoading(false);
},
[eventId],
);
useEffect(() => {
if (session.isBusiness || session.currentGameMode !== GAME_MODE.GLOBAL) {
@@ -126,12 +135,21 @@ const GlobalLeaderboardScreen = ({ navigation, route }) => {
]);
useEffect(() => {
if (!eventId || session.currentGameMode !== GAME_MODE.GLOBAL || !session.currentGlobalPlayerId) {
if (
!eventId ||
session.currentGameMode !== GAME_MODE.GLOBAL ||
!session.currentGlobalPlayerId
) {
return;
}
loadData();
}, [eventId, loadData, session.currentGameMode, session.currentGlobalPlayerId]);
}, [
eventId,
loadData,
session.currentGameMode,
session.currentGlobalPlayerId,
]);
// View --------------------------------
@@ -199,7 +217,10 @@ const GlobalLeaderboardScreen = ({ navigation, route }) => {
)}
<ButtonTray>
<Button label="Refresh" onClick={() => loadData({forceRefresh: true})} />
<Button
label="Refresh"
onClick={() => loadData({ forceRefresh: true })}
/>
</ButtonTray>
</View>
</Screen>
@@ -222,13 +243,13 @@ const tabStyles = StyleSheet.create({
paddingVertical: 8,
fontSize: 14,
fontWeight: "600",
color: "#6b7280",
color: "#a6adc8",
borderBottomWidth: 2,
borderBottomColor: "transparent",
},
activeTab: {
color: "#111827",
borderBottomColor: "#111827",
color: "#cdd6f4",
borderBottomColor: "#89b4fa",
},
});
@@ -244,7 +265,7 @@ const styles = StyleSheet.create({
tabs: {
flexDirection: "row",
borderBottomWidth: 1,
borderBottomColor: "#e5e7eb",
borderBottomColor: "#45475a",
},
rankList: {
gap: 6,
@@ -254,7 +275,7 @@ const styles = StyleSheet.create({
marginBottom: 0,
},
myCard: {
borderColor: "#2563eb",
borderColor: "#89b4fa",
borderWidth: 2,
},
rankRow: {
@@ -265,14 +286,14 @@ const styles = StyleSheet.create({
rankPos: {
fontSize: 16,
fontWeight: "700",
color: "#6b7280",
color: "#a6adc8",
width: 32,
},
rankName: {
flex: 1,
fontSize: 15,
fontWeight: "600",
color: "#111827",
color: "#cdd6f4",
},
rankRight: {
alignItems: "flex-end",
@@ -280,15 +301,15 @@ const styles = StyleSheet.create({
rankPoints: {
fontSize: 16,
fontWeight: "700",
color: "#16a34a",
color: "#a6e3a1",
},
rankFinds: {
fontSize: 12,
color: "#9ca3af",
color: "#a6adc8",
},
empty: {
textAlign: "center",
color: "#9ca3af",
color: "#a6adc8",
marginTop: 30,
fontSize: 15,
},
@@ -1,16 +1,16 @@
import {useCallback, useEffect, useMemo, useRef, useState} from "react";
import {ActivityIndicator, StyleSheet, Text, View} from "react-native";
import MapView, {Circle, Marker, Polygon} from "react-native-maps";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { ActivityIndicator, StyleSheet, Text, View } from "react-native";
import MapView, { Circle, Marker, Polygon } from "react-native-maps";
import * as Location from "expo-location";
import Screen from "../../layout/Screen";
import {Button, ButtonTray} from "../../UI/Button";
import { Button, ButtonTray } from "../../UI/Button";
import ClaimTimerView from "../../gameplay/ClaimTimerView";
import CacheList from "../../../entity/cache/CacheList";
import useGlobalHook from "../../../hooks/useGlobalHook";
import usePlayerGame from "../../../hooks/usePlayerGame";
import {getSession} from "../../../hooks/SessionStore";
import {getFovCone} from "../../../utils/geoMath";
import {GAME_MODE} from "../../../utils/gameConstants";
import { getSession } from "../../../hooks/SessionStore";
import { getFovCone } from "../../../utils/geoMath";
import { GAME_MODE } from "../../../utils/gameConstants";
const DEFAULT_REGION = {
latitude: 51.5074,
@@ -77,23 +77,29 @@ const GlobalMapScreen = ({ navigation, route }) => {
// Handlers ----------------------------
const loadData = useCallback(async (options = {}) => {
if (!eventId) return;
const loadData = useCallback(
async (options = {}) => {
if (!eventId) return;
setIsLoading(true);
setError("");
setIsLoading(true);
setError("");
const [cacheData, findsData] = await Promise.all([
globalApiRef.current.getCachesByEvent(eventId, options),
session.currentGlobalPlayerId
? globalApiRef.current.getFindsByPlayer(session.currentGlobalPlayerId, options)
: Promise.resolve([]),
]);
const [cacheData, findsData] = await Promise.all([
globalApiRef.current.getCachesByEvent(eventId, options),
session.currentGlobalPlayerId
? globalApiRef.current.getFindsByPlayer(
session.currentGlobalPlayerId,
options,
)
: Promise.resolve([]),
]);
setCaches(cacheData || []);
setFoundCacheIds((findsData || []).map((f) => f.FindCacheID));
setIsLoading(false);
}, [eventId, session.currentGlobalPlayerId]);
setCaches(cacheData || []);
setFoundCacheIds((findsData || []).map((f) => f.FindCacheID));
setIsLoading(false);
},
[eventId, session.currentGlobalPlayerId],
);
const handleClaim = useCallback(
async (cacheId) => {
@@ -119,7 +125,7 @@ const GlobalMapScreen = ({ navigation, route }) => {
if (result) {
setClaimedPopupVisible(true);
setTimeout(() => setClaimedPopupVisible(false), 3000);
await loadData({forceRefresh: true});
await loadData({ forceRefresh: true });
}
},
[foundCacheIds, loadData, session.currentGlobalPlayerId, setIsClaiming],
@@ -131,7 +137,9 @@ const GlobalMapScreen = ({ navigation, route }) => {
};
const handleOpenSelectedCache = () => {
const selected = (caches || []).find((cache) => cache.CacheID === selectedCacheId);
const selected = (caches || []).find(
(cache) => cache.CacheID === selectedCacheId,
);
if (!selected) return;
navigation.navigate("GlobalCacheViewScreen", {
cache: selected,
@@ -166,12 +174,21 @@ const GlobalMapScreen = ({ navigation, route }) => {
]);
useEffect(() => {
if (!eventId || session.currentGameMode !== GAME_MODE.GLOBAL || !session.currentGlobalPlayerId) {
if (
!eventId ||
session.currentGameMode !== GAME_MODE.GLOBAL ||
!session.currentGlobalPlayerId
) {
return;
}
loadData();
}, [eventId, loadData, session.currentGameMode, session.currentGlobalPlayerId]);
}, [
eventId,
loadData,
session.currentGameMode,
session.currentGlobalPlayerId,
]);
useEffect(() => {
const routeSelected = route?.params?.selectedCacheId;
@@ -180,7 +197,10 @@ const GlobalMapScreen = ({ navigation, route }) => {
}, [route?.params?.selectedCacheId]);
useEffect(() => {
if (selectedCacheId && claimableCaches.some((cache) => cache.id === selectedCacheId)) {
if (
selectedCacheId &&
claimableCaches.some((cache) => cache.id === selectedCacheId)
) {
return;
}
setSelectedCacheId(claimableCaches[0]?.id || null);
@@ -291,7 +311,9 @@ const GlobalMapScreen = ({ navigation, route }) => {
}}
title={cache.CacheName}
description={`${cache.CachePoints ?? 0} pts${found ? " · Found" : ""}`}
pinColor={found ? "#22c55e" : selected ? "#f59e0b" : "#ef4444"}
pinColor={
found ? "#22c55e" : selected ? "#f59e0b" : "#ef4444"
}
onPress={() => setSelectedCacheId(cache.CacheID)}
onCalloutPress={() =>
navigation.navigate("GlobalCacheViewScreen", {
@@ -337,9 +359,16 @@ const GlobalMapScreen = ({ navigation, route }) => {
/>
<ButtonTray>
<Button label="Details" onClick={handleOpenSelectedCache} disabled={!selectedCacheId} />
<Button
label="Details"
onClick={handleOpenSelectedCache}
disabled={!selectedCacheId}
/>
<Button label="Leaderboard" onClick={handleGotoLeaderboard} />
<Button label="Refresh" onClick={() => loadData({forceRefresh: true})} />
<Button
label="Refresh"
onClick={() => loadData({ forceRefresh: true })}
/>
</ButtonTray>
</View>
</Screen>
@@ -362,13 +391,13 @@ const tabStyles = StyleSheet.create({
paddingVertical: 8,
fontSize: 14,
fontWeight: "600",
color: "#6b7280",
color: "#a6adc8",
borderBottomWidth: 2,
borderBottomColor: "transparent",
},
activeTab: {
color: "#111827",
borderBottomColor: "#111827",
color: "#cdd6f4",
borderBottomColor: "#89b4fa",
},
});
@@ -384,13 +413,13 @@ const styles = StyleSheet.create({
tabs: {
flexDirection: "row",
borderBottomWidth: 1,
borderBottomColor: "#e5e7eb",
borderBottomColor: "#45475a",
},
map: {
flex: 1,
},
error: {
color: "#dc2626",
color: "#f38ba8",
fontSize: 14,
},
});
@@ -242,7 +242,7 @@ const styles = StyleSheet.create({
justifyContent: "center",
},
error: {
color: "#dc2626",
color: "#f38ba8",
fontSize: 14,
},
avatarWrap: {
@@ -257,7 +257,7 @@ const styles = StyleSheet.create({
},
label: {
fontSize: 13,
color: "#9ca3af",
color: "#a6adc8",
fontWeight: "600",
},
input: {