From 07a772e73a1c2ec9a677a704f77638110452fdb9 Mon Sep 17 00:00:00 2001 From: Ari Spokony Date: Thu, 16 Apr 2026 21:06:43 -0400 Subject: [PATCH 1/5] mobile pfp --- clients/mobile/app.json | 7 + clients/mobile/app/(tabs)/profile.tsx | 40 ++++- clients/mobile/app/index.tsx | 1 + .../mobile/components/profile/ProfileHero.tsx | 42 ++++- clients/mobile/context/startup.tsx | 6 + clients/mobile/hooks/use-profile-picture.ts | 166 ++++++++++++++++++ clients/mobile/package-lock.json | 22 +++ clients/mobile/package.json | 1 + 8 files changed, 277 insertions(+), 8 deletions(-) create mode 100644 clients/mobile/hooks/use-profile-picture.ts diff --git a/clients/mobile/app.json b/clients/mobile/app.json index 435dd5872..e90ba75e5 100644 --- a/clients/mobile/app.json +++ b/clients/mobile/app.json @@ -45,6 +45,13 @@ "color": "#ffffff", "sounds": [] } + ], + [ + "expo-image-picker", + { + "photosPermission": "Allow SelfServe to access your photos to set your profile picture.", + "cameraPermission": "Allow SelfServe to use the camera for your profile picture." + } ] ], "experiments": { diff --git a/clients/mobile/app/(tabs)/profile.tsx b/clients/mobile/app/(tabs)/profile.tsx index febf818f9..be365a95c 100644 --- a/clients/mobile/app/(tabs)/profile.tsx +++ b/clients/mobile/app/(tabs)/profile.tsx @@ -1,4 +1,4 @@ -import { View, Text, ScrollView, ActivityIndicator } from "react-native"; +import { View, Text, ScrollView, ActivityIndicator, Pressable } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { useAuth } from "@clerk/clerk-expo"; import { router } from "expo-router"; @@ -6,10 +6,19 @@ import { useGetUser } from "@shared"; import LogoutButton from "@/components/Logout"; import { ProfileHero } from "@/components/profile/ProfileHero"; import { ProfileInfoCard } from "@/components/profile/ProfileInfoCard"; +import { useProfilePicture } from "@/hooks/use-profile-picture"; export default function Profile() { const { userId } = useAuth(); const { data: user, isLoading } = useGetUser(userId ?? undefined); + const { + profilePicUrl, + status, + isLoading: isPicLoading, + isInitialLoading: isPicInitialLoading, + pickAndUpload, + handleRemove, + } = useProfilePicture(userId ?? undefined); const onSignOut = () => { router.replace("/sign-in"); @@ -37,8 +46,35 @@ export default function Profile() { void pickAndUpload()} + isAvatarBusy={isPicLoading} /> + + void handleRemove()} + disabled={ + isPicLoading || + (!profilePicUrl && !user?.profile_picture) + } + className="py-2 px-4 rounded-lg border border-stroke-default opacity-100 disabled:opacity-40" + > + Remove photo + + + {status ? ( + + {status} + + ) : null} { + console.log("status", status); if (status === StartupStatus.Loading) return; if (status === StartupStatus.Unauthenticated) { router.replace("/sign-in"); diff --git a/clients/mobile/components/profile/ProfileHero.tsx b/clients/mobile/components/profile/ProfileHero.tsx index 691adc1bc..50fa56b4d 100644 --- a/clients/mobile/components/profile/ProfileHero.tsx +++ b/clients/mobile/components/profile/ProfileHero.tsx @@ -1,27 +1,57 @@ -import { View, Text, Image } from "react-native"; +import { View, Text, Image, Pressable, ActivityIndicator } from "react-native"; +import { Pencil } from "lucide-react-native"; type ProfileHeroProps = { firstName: string; lastName: string; avatarUrl: string | undefined; + /** When set, tapping the avatar opens the picker / upload flow. */ + onAvatarPress?: () => void; + isAvatarBusy?: boolean; }; export function ProfileHero({ firstName, lastName, avatarUrl, + onAvatarPress, + isAvatarBusy = false, }: ProfileHeroProps) { const displayName = [firstName, lastName].filter(Boolean).join(" ") || "User"; const initial = displayName.charAt(0).toUpperCase(); + const avatarInner = avatarUrl ? ( + + ) : ( + + {initial} + + ); + return ( - {avatarUrl ? ( - + {onAvatarPress ? ( + + {avatarInner} + {isAvatarBusy ? ( + + + + ) : null} + {!isAvatarBusy ? ( + + + + ) : null} + ) : ( - - {initial} - + avatarInner )} diff --git a/clients/mobile/context/startup.tsx b/clients/mobile/context/startup.tsx index 44843e5ae..bf2e9d0e4 100644 --- a/clients/mobile/context/startup.tsx +++ b/clients/mobile/context/startup.tsx @@ -43,6 +43,12 @@ export function StartupProvider({ children }: { children: React.ReactNode }) { }); } + console.log("data", data); + console.log("status", status); + console.log("isLoaded", isLoaded); + console.log("isSignedIn", isSignedIn); + console.log("userId", userId); + const startupStatus = useMemo(() => { if (!isLoaded) return StartupStatus.Loading; if (!isSignedIn) return StartupStatus.Unauthenticated; diff --git a/clients/mobile/hooks/use-profile-picture.ts b/clients/mobile/hooks/use-profile-picture.ts new file mode 100644 index 000000000..45ed7bb46 --- /dev/null +++ b/clients/mobile/hooks/use-profile-picture.ts @@ -0,0 +1,166 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import * as ImagePicker from "expo-image-picker"; +import { useQueryClient } from "@tanstack/react-query"; +import { useAPIClient } from "@shared/api/client"; +import { + deleteProfilePicture as deleteProfilePictureApi, + getExtFromMime, + getProfilePicture, + getUploadUrl, + getUserQueryKey, + saveProfilePictureKey, + uploadToS3PresignedPut, +} from "@shared"; + +import { StartupStatus, useStartup } from "@/context/startup"; + +async function readPickedImage( + uri: string, + mimeHint: string | null | undefined, +): Promise<{ body: ArrayBuffer; contentType: string }> { + const res = await fetch(uri); + if (!res.ok) { + throw new Error("Could not read the selected image"); + } + const body = await res.arrayBuffer(); + const fromHeader = res.headers.get("content-type")?.split(";")[0]?.trim(); + const contentType = + (mimeHint && mimeHint.length > 0 ? mimeHint : undefined) || + (fromHeader && fromHeader.length > 0 ? fromHeader : undefined) || + "image/jpeg"; + return { body, contentType }; +} + +export function useProfilePicture(userId: string | undefined): { + profilePicUrl: string | null; + status: string; + isLoading: boolean; + isInitialLoading: boolean; + pickAndUpload: () => Promise; + handleRemove: () => Promise; +} { + const startupStatus = useStartup(); + const api = useAPIClient(); + const apiRef = useRef(api); + apiRef.current = api; + const queryClient = useQueryClient(); + + const [profilePicUrl, setProfilePicUrl] = useState(null); + const [status, setStatus] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const [isInitialLoading, setIsInitialLoading] = useState(!!userId); + + useEffect(() => { + if (!userId) { + setProfilePicUrl(null); + setIsInitialLoading(false); + return; + } + if (startupStatus !== StartupStatus.Ready) { + if (startupStatus !== StartupStatus.Loading) { + setIsInitialLoading(false); + } + return; + } + + const ac = new AbortController(); + setIsInitialLoading(true); + void (async () => { + try { + const data = await getProfilePicture(apiRef.current, userId); + if (!ac.signal.aborted) { + setProfilePicUrl(data?.presigned_url ?? null); + } + } finally { + if (!ac.signal.aborted) { + setIsInitialLoading(false); + } + } + })(); + return () => ac.abort(); + }, [userId, startupStatus]); + + const pickAndUpload = useCallback(async () => { + if (!userId || startupStatus !== StartupStatus.Ready) { + return; + } + + const perm = await ImagePicker.requestMediaLibraryPermissionsAsync(); + if (!perm.granted) { + setStatus("Error: Photo library permission is required."); + return; + } + + const picked = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ["images"], + allowsEditing: true, + aspect: [1, 1], + quality: 0.85, + }); + + if (picked.canceled || !picked.assets[0]) { + return; + } + + const asset = picked.assets[0]; + const uri = asset.uri; + const mimeType = asset.mimeType ?? undefined; + + setIsLoading(true); + setStatus("Reading image..."); + try { + const { body, contentType } = await readPickedImage(uri, mimeType); + const ext = getExtFromMime(contentType); + + setStatus("Getting upload URL..."); + const { presigned_url, key } = await getUploadUrl(api, userId, ext); + + setStatus("Uploading..."); + await uploadToS3PresignedPut(presigned_url, body, contentType); + + setStatus("Saving..."); + await saveProfilePictureKey(api, userId, key); + + setStatus("Refreshing..."); + const refreshed = await getProfilePicture(api, userId); + setProfilePicUrl(refreshed?.presigned_url ?? null); + await queryClient.invalidateQueries({ queryKey: getUserQueryKey(userId) }); + setStatus("Upload complete!"); + } catch (err) { + setStatus( + `Error: ${err instanceof Error ? err.message : "Unknown error"}`, + ); + } finally { + setIsLoading(false); + } + }, [api, userId, startupStatus, queryClient]); + + const handleRemove = useCallback(async () => { + if (!userId || startupStatus !== StartupStatus.Ready) { + return; + } + setIsLoading(true); + setStatus("Removing profile picture..."); + try { + await deleteProfilePictureApi(api, userId); + setProfilePicUrl(null); + await queryClient.invalidateQueries({ queryKey: getUserQueryKey(userId) }); + setStatus("Profile picture removed!"); + } catch (err) { + setStatus( + `Error: ${err instanceof Error ? err.message : "Unknown error"}`, + ); + } finally { + setIsLoading(false); + } + }, [api, userId, startupStatus, queryClient]); + + return { + profilePicUrl, + status, + isLoading, + isInitialLoading, + pickAndUpload, + handleRemove, + }; +} diff --git a/clients/mobile/package-lock.json b/clients/mobile/package-lock.json index a2cc9e487..aebbb428e 100644 --- a/clients/mobile/package-lock.json +++ b/clients/mobile/package-lock.json @@ -23,6 +23,7 @@ "expo-font": "~14.0.10", "expo-haptics": "~15.0.8", "expo-image": "~3.0.11", + "expo-image-picker": "~17.0.10", "expo-linking": "~8.0.11", "expo-notifications": "^0.32.16", "expo-router": "~6.0.21", @@ -7667,6 +7668,27 @@ } } }, + "node_modules/expo-image-loader": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-6.0.0.tgz", + "integrity": "sha512-nKs/xnOGw6ACb4g26xceBD57FKLFkSwEUTDXEDF3Gtcu3MqF3ZIYd3YM+sSb1/z9AKV1dYT7rMSGVNgsveXLIQ==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-image-picker": { + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-17.0.10.tgz", + "integrity": "sha512-a2xrowp2trmvXyUWgX3O6Q2rZaa2C59AqivKI7+bm+wLvMfTEbZgldLX4rEJJhM8xtmEDTNU+lzjtObwzBRGaw==", + "license": "MIT", + "dependencies": { + "expo-image-loader": "~6.0.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-keep-awake": { "version": "15.0.8", "license": "MIT", diff --git a/clients/mobile/package.json b/clients/mobile/package.json index a0bb953a5..abd2331d7 100644 --- a/clients/mobile/package.json +++ b/clients/mobile/package.json @@ -30,6 +30,7 @@ "expo-font": "~14.0.10", "expo-haptics": "~15.0.8", "expo-image": "~3.0.11", + "expo-image-picker": "~17.0.10", "expo-linking": "~8.0.11", "expo-notifications": "^0.32.16", "expo-router": "~6.0.21", From 78ad947d7721a7198d05b7b1c5af4d2d0cd8a2de Mon Sep 17 00:00:00 2001 From: Ari Spokony Date: Thu, 16 Apr 2026 23:06:22 -0400 Subject: [PATCH 2/5] fixing the format of the pfpto be circle and be expandable --- clients/mobile/app/(tabs)/profile.tsx | 21 +--- clients/mobile/app/index.tsx | 1 - .../mobile/components/profile/ProfileHero.tsx | 60 ++++++++-- clients/mobile/context/startup.tsx | 9 ++ clients/shared/src/api/profile-picture.ts | 17 +++ clients/shared/src/index.ts | 1 + go.work | 3 + go.work.sum | 103 ++++++++++++++++++ 8 files changed, 189 insertions(+), 26 deletions(-) create mode 100644 go.work create mode 100644 go.work.sum diff --git a/clients/mobile/app/(tabs)/profile.tsx b/clients/mobile/app/(tabs)/profile.tsx index be365a95c..ea19b00ee 100644 --- a/clients/mobile/app/(tabs)/profile.tsx +++ b/clients/mobile/app/(tabs)/profile.tsx @@ -1,4 +1,4 @@ -import { View, Text, ScrollView, ActivityIndicator, Pressable } from "react-native"; +import { View, Text, ScrollView, ActivityIndicator } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { useAuth } from "@clerk/clerk-expo"; import { router } from "expo-router"; @@ -17,7 +17,6 @@ export default function Profile() { isLoading: isPicLoading, isInitialLoading: isPicInitialLoading, pickAndUpload, - handleRemove, } = useProfilePicture(userId ?? undefined); const onSignOut = () => { @@ -54,23 +53,9 @@ export default function Profile() { onAvatarPress={() => void pickAndUpload()} isAvatarBusy={isPicLoading} /> - - void handleRemove()} - disabled={ - isPicLoading || - (!profilePicUrl && !user?.profile_picture) - } - className="py-2 px-4 rounded-lg border border-stroke-default opacity-100 disabled:opacity-40" - > - Remove photo - - - {status ? ( + {status.startsWith("Error") ? ( {status} diff --git a/clients/mobile/app/index.tsx b/clients/mobile/app/index.tsx index 6f739aee2..2ac21ffd1 100644 --- a/clients/mobile/app/index.tsx +++ b/clients/mobile/app/index.tsx @@ -7,7 +7,6 @@ export default function Index() { const router = useRouter(); useEffect(() => { - console.log("status", status); if (status === StartupStatus.Loading) return; if (status === StartupStatus.Unauthenticated) { router.replace("/sign-in"); diff --git a/clients/mobile/components/profile/ProfileHero.tsx b/clients/mobile/components/profile/ProfileHero.tsx index 50fa56b4d..6af2bec42 100644 --- a/clients/mobile/components/profile/ProfileHero.tsx +++ b/clients/mobile/components/profile/ProfileHero.tsx @@ -1,4 +1,12 @@ -import { View, Text, Image, Pressable, ActivityIndicator } from "react-native"; +import { useRef, useState } from "react"; +import { + View, + Text, + Image, + Pressable, + ActivityIndicator, + Modal, +} from "react-native"; import { Pencil } from "lucide-react-native"; type ProfileHeroProps = { @@ -17,14 +25,20 @@ export function ProfileHero({ onAvatarPress, isAvatarBusy = false, }: ProfileHeroProps) { + const [isPreviewVisible, setIsPreviewVisible] = useState(false); + const shouldSkipNextPressRef = useRef(false); const displayName = [firstName, lastName].filter(Boolean).join(" ") || "User"; const initial = displayName.charAt(0).toUpperCase(); - const avatarInner = avatarUrl ? ( - - ) : ( - - {initial} + const avatarInner = ( + + {avatarUrl ? ( + + ) : ( + + {initial} + + )} ); @@ -32,8 +46,20 @@ export function ProfileHero({ {onAvatarPress ? ( { + if (shouldSkipNextPressRef.current) { + shouldSkipNextPressRef.current = false; + return; + } + onAvatarPress(); + }} + onLongPress={() => { + if (!avatarUrl || isAvatarBusy) return; + shouldSkipNextPressRef.current = true; + setIsPreviewVisible(true); + }} disabled={isAvatarBusy} + delayLongPress={220} className="relative" accessibilityRole="button" accessibilityLabel="Change profile photo" @@ -53,6 +79,26 @@ export function ProfileHero({ ) : ( avatarInner )} + {avatarUrl ? ( + setIsPreviewVisible(false)} + > + setIsPreviewVisible(false)} + > + {}}> + + + + + + + ) : null} {displayName} diff --git a/clients/mobile/context/startup.tsx b/clients/mobile/context/startup.tsx index bf2e9d0e4..d0a2710d5 100644 --- a/clients/mobile/context/startup.tsx +++ b/clients/mobile/context/startup.tsx @@ -23,10 +23,13 @@ export function StartupProvider({ children }: { children: React.ReactNode }) { queryKey: ["startup-user", userId], queryFn: async () => { const token = await getToken(); + const url = `${process.env.EXPO_PUBLIC_API_BASE_URL}/users/${userId}`; + console.log("url", url); const res = await fetch( `${process.env.EXPO_PUBLIC_API_BASE_URL}/users/${userId}`, { headers: { Authorization: `Bearer ${token}` } }, ); + console.log("res", res); if (!res.ok) throw new Error(`${res.status}`); return res.json(); }, @@ -49,6 +52,12 @@ export function StartupProvider({ children }: { children: React.ReactNode }) { console.log("isSignedIn", isSignedIn); console.log("userId", userId); + console.log("data", data); + console.log("status", status); + console.log("isLoaded", isLoaded); + console.log("isSignedIn", isSignedIn); + console.log("userId", userId); + const startupStatus = useMemo(() => { if (!isLoaded) return StartupStatus.Loading; if (!isSignedIn) return StartupStatus.Unauthenticated; diff --git a/clients/shared/src/api/profile-picture.ts b/clients/shared/src/api/profile-picture.ts index f893476b8..08fd8398a 100644 --- a/clients/shared/src/api/profile-picture.ts +++ b/clients/shared/src/api/profile-picture.ts @@ -64,4 +64,21 @@ export async function uploadFileToS3( if (!res.ok) { throw new Error("Failed to upload to S3"); } +} + +export async function uploadToS3PresignedPut( + presignedUrl: string, + body: ArrayBuffer, + contentType: string, +): Promise { + const res = await fetch(presignedUrl, { + method: "PUT", + body, + headers: { + "Content-Type": contentType, + }, + }); + if (!res.ok) { + throw new Error("Failed to upload to S3"); + } } \ No newline at end of file diff --git a/clients/shared/src/index.ts b/clients/shared/src/index.ts index 87c0fabd6..604234fd1 100644 --- a/clients/shared/src/index.ts +++ b/clients/shared/src/index.ts @@ -13,6 +13,7 @@ export { saveProfilePictureKey, deleteProfilePicture, uploadFileToS3, + uploadToS3PresignedPut, } from "./api/profile-picture"; export type { UploadUrlResponse, diff --git a/go.work b/go.work new file mode 100644 index 000000000..49e522aa5 --- /dev/null +++ b/go.work @@ -0,0 +1,3 @@ +go 1.25.5 + +use ./backend diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 000000000..c5bd611cc --- /dev/null +++ b/go.work.sum @@ -0,0 +1,103 @@ +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U= +buf.build/go/protovalidate v0.12.0/go.mod h1:q3PFfbzI05LeqxSwq+begW2syjy2Z6hLxZSkP1OH/D0= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cloud.google.com/go/alloydb v1.16.1/go.mod h1:zeZuGJ5mEaQE70FMXEvZIp5hQLR9yrGnHo1YUOncWRY= +cloud.google.com/go/alloydbconn v1.15.3/go.mod h1:9yrNzUeMr3wR/D4gTJrh5ph2VDW/19tAMV7TlNuyRfM= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/bigquery v1.67.0/go.mod h1:HQeP1AHFuAz0Y55heDSb0cjZIhnEkuwFRBGo6EEKHug= +cloud.google.com/go/cloudsqlconn v1.17.2/go.mod h1:l7NymuoD+hycOo+92SJEyETPtE05oRG4oXjcH3swftw= +cloud.google.com/go/firestore v1.18.0/go.mod h1:5ye0v48PhseZBdcl0qbl3uttu7FIEwEYVaWm0UIEOEU= +cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= +cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= +cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= +cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= +cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY= +cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= +cloud.google.com/go/translate v1.10.3/go.mod h1:GW0vC1qvPtd3pgtypCv4k4U8B7EdgK9/QEF2aJEUovs= +firebase.google.com/go/v4 v4.15.2/go.mod h1:qkD/HtSumrPMTLs0ahQrje5gTw2WKFKrzVFoqy4SbKA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.52.0/go.mod h1:ayYHuYU7iNcNtEs1K9k6D/Bju7u1VEHMQm5qQ1n3GtM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.27.0/go.mod h1:E05RN++yLx9W4fXPtX978OLo9P0+fBacauUdET1BckA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.52.0/go.mod h1:gdIm9TxRk5soClCwuB0FtdXsbqtw0aqPwBEurK9tPkw= +github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/anthropics/anthropic-sdk-go v1.19.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/apache/arrow/go/v15 v15.0.2/go.mod h1:DGXsR3ajT524njufqf95822i+KTh+yea1jass9YXgjA= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/blues/jsonata-go v1.5.4/go.mod h1:uns2jymDrnI7y+UFYCqsRTEiAH22GyHnNXrkupAVFWI= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/eliben/go-sentencepiece v0.6.0/go.mod h1:nNYk4aMzgBoI6QFp4LUG8Eu1uO9fHD9L5ZEre93o9+c= +github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= +github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= +github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= +github.com/go-openapi/runtime v0.24.2/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk= +github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= +github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI= +github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= +github.com/jba/slog v0.2.0/go.mod h1:0Dh7Vyz3Td68Z1OwzadfincHwr7v+PpzadrS2Jua338= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mark3labs/mcp-go v0.29.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/openai/openai-go v1.8.2/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pgvector/pgvector-go v0.3.0/go.mod h1:duFy+PXWfW7QQd5ibqutBO4GxLsUZ9RVXhFZGIBsWSA= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/weaviate/weaviate v1.30.0/go.mod h1:2bp9vRsQVA1bzJIGlxyQMq4VwDBUmIETbMYLAYTouxk= +github.com/weaviate/weaviate-go-client/v5 v5.1.0/go.mod h1:gg5qyiHk53+HMZW2ynkrgm+cMQDD2Ewyma84rBeChz4= +github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/appengine/v2 v2.0.6/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7Z1JKf3J3wLI= +google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= +google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= From 25580abcffb97e9ff5ac237f4068cc2d464d76ea Mon Sep 17 00:00:00 2001 From: Ari Spokony Date: Thu, 16 Apr 2026 23:15:45 -0400 Subject: [PATCH 3/5] cleaning up --- backend/docs/swagger.yaml | 211 ++++++++++++++++++ .../mobile/components/profile/ProfileHero.tsx | 8 +- clients/mobile/context/startup.tsx | 15 -- clients/mobile/hooks/use-profile-picture.ts | 30 +-- 4 files changed, 218 insertions(+), 46 deletions(-) diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index bcece6343..042c326e4 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -80,6 +80,25 @@ definitions: example: America/New_York type: string type: object + CreateViewInput: + properties: + display_name: + type: string + filters: + items: + type: integer + type: array + slug: + allOf: + - $ref: '#/definitions/github_com_generate_selfserve_internal_models.ViewSlug' + enum: + - requests_web + - rooms_web + - guests_web + required: + - filters + - slug + type: object Department: properties: created_at: @@ -554,6 +573,8 @@ definitions: - in progress - completed type: string + unassign: + type: boolean user_id: type: string type: object @@ -691,6 +712,25 @@ definitions: $ref: '#/definitions/User' type: array type: object + View: + properties: + created_at: + type: string + display_name: + type: string + filters: + items: + type: integer + type: array + id: + type: string + slug: + $ref: '#/definitions/github_com_generate_selfserve_internal_models.ViewSlug' + updated_at: + type: string + user_id: + type: string + type: object github_com_generate_selfserve_internal_errs.HTTPError: properties: code: @@ -751,6 +791,16 @@ definitions: name: type: string type: object + github_com_generate_selfserve_internal_models.ViewSlug: + enum: + - requests_web + - rooms_web + - guests_web + type: string + x-enum-varnames: + - ViewSlugRequestsWeb + - ViewSlugRoomsWeb + - ViewSlugGuestsWeb github_com_generate_selfserve_internal_utils.CursorPage-GuestRequest: properties: has_more: @@ -1612,6 +1662,78 @@ paths: summary: generates a request tags: - requests + /request/generate/async: + post: + consumes: + - application/json + description: Starts async request generation via Temporal workflow + parameters: + - description: Request data with raw text + in: body + name: request + required: true + schema: + $ref: '#/definitions/GenerateRequestInput' + produces: + - application/json + responses: + "202": + description: Accepted + schema: + additionalProperties: + type: string + type: object + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "503": + description: Service Unavailable + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: starts request generation workflow + tags: + - requests + /request/generate/async/{workflowId}: + get: + description: Gets async request generation workflow status/result + parameters: + - description: Workflow ID + in: path + name: workflowId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "404": + description: Not Found + schema: + additionalProperties: + type: string + type: object + "503": + description: Service Unavailable + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: gets request generation workflow status + tags: + - requests /request/guest/{id}: get: description: Retrieves all requests for a given guest @@ -2336,6 +2458,95 @@ paths: summary: Search users by hotel tags: - users + /views: + get: + description: Returns all saved filter views for the authenticated user scoped + to a page slug + parameters: + - description: Page slug (e.g. requests_web) + in: query + name: slug + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/View' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/github_com_generate_selfserve_internal_errs.HTTPError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/github_com_generate_selfserve_internal_errs.HTTPError' + security: + - BearerAuth: [] + summary: List views + tags: + - views + post: + consumes: + - application/json + description: Saves the current filter state as a named view for the authenticated + user + parameters: + - description: View payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/CreateViewInput' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/View' + "400": + description: Bad Request + schema: + $ref: '#/definitions/github_com_generate_selfserve_internal_errs.HTTPError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/github_com_generate_selfserve_internal_errs.HTTPError' + security: + - BearerAuth: [] + summary: Create view + tags: + - views + /views/{id}: + delete: + description: Deletes a saved filter view owned by the authenticated user + parameters: + - description: View ID + in: path + name: id + required: true + type: string + responses: + "204": + description: No Content + "404": + description: Not Found + schema: + $ref: '#/definitions/github_com_generate_selfserve_internal_errs.HTTPError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/github_com_generate_selfserve_internal_errs.HTTPError' + security: + - BearerAuth: [] + summary: Delete view + tags: + - views schemes: - http - https diff --git a/clients/mobile/components/profile/ProfileHero.tsx b/clients/mobile/components/profile/ProfileHero.tsx index 6af2bec42..52448a7d3 100644 --- a/clients/mobile/components/profile/ProfileHero.tsx +++ b/clients/mobile/components/profile/ProfileHero.tsx @@ -27,6 +27,10 @@ export function ProfileHero({ }: ProfileHeroProps) { const [isPreviewVisible, setIsPreviewVisible] = useState(false); const shouldSkipNextPressRef = useRef(false); + const closePreview = () => { + shouldSkipNextPressRef.current = false; + setIsPreviewVisible(false); + }; const displayName = [firstName, lastName].filter(Boolean).join(" ") || "User"; const initial = displayName.charAt(0).toUpperCase(); @@ -85,11 +89,11 @@ export function ProfileHero({ transparent animationType="fade" statusBarTranslucent - onRequestClose={() => setIsPreviewVisible(false)} + onRequestClose={closePreview} > setIsPreviewVisible(false)} + onPress={closePreview} > {}}> diff --git a/clients/mobile/context/startup.tsx b/clients/mobile/context/startup.tsx index d0a2710d5..44843e5ae 100644 --- a/clients/mobile/context/startup.tsx +++ b/clients/mobile/context/startup.tsx @@ -23,13 +23,10 @@ export function StartupProvider({ children }: { children: React.ReactNode }) { queryKey: ["startup-user", userId], queryFn: async () => { const token = await getToken(); - const url = `${process.env.EXPO_PUBLIC_API_BASE_URL}/users/${userId}`; - console.log("url", url); const res = await fetch( `${process.env.EXPO_PUBLIC_API_BASE_URL}/users/${userId}`, { headers: { Authorization: `Bearer ${token}` } }, ); - console.log("res", res); if (!res.ok) throw new Error(`${res.status}`); return res.json(); }, @@ -46,18 +43,6 @@ export function StartupProvider({ children }: { children: React.ReactNode }) { }); } - console.log("data", data); - console.log("status", status); - console.log("isLoaded", isLoaded); - console.log("isSignedIn", isSignedIn); - console.log("userId", userId); - - console.log("data", data); - console.log("status", status); - console.log("isLoaded", isLoaded); - console.log("isSignedIn", isSignedIn); - console.log("userId", userId); - const startupStatus = useMemo(() => { if (!isLoaded) return StartupStatus.Loading; if (!isSignedIn) return StartupStatus.Unauthenticated; diff --git a/clients/mobile/hooks/use-profile-picture.ts b/clients/mobile/hooks/use-profile-picture.ts index 45ed7bb46..5598d84d2 100644 --- a/clients/mobile/hooks/use-profile-picture.ts +++ b/clients/mobile/hooks/use-profile-picture.ts @@ -3,7 +3,6 @@ import * as ImagePicker from "expo-image-picker"; import { useQueryClient } from "@tanstack/react-query"; import { useAPIClient } from "@shared/api/client"; import { - deleteProfilePicture as deleteProfilePictureApi, getExtFromMime, getProfilePicture, getUploadUrl, @@ -37,7 +36,6 @@ export function useProfilePicture(userId: string | undefined): { isLoading: boolean; isInitialLoading: boolean; pickAndUpload: () => Promise; - handleRemove: () => Promise; } { const startupStatus = useStartup(); const api = useAPIClient(); @@ -107,45 +105,20 @@ export function useProfilePicture(userId: string | undefined): { const mimeType = asset.mimeType ?? undefined; setIsLoading(true); - setStatus("Reading image..."); + setStatus(""); try { const { body, contentType } = await readPickedImage(uri, mimeType); const ext = getExtFromMime(contentType); - setStatus("Getting upload URL..."); const { presigned_url, key } = await getUploadUrl(api, userId, ext); - setStatus("Uploading..."); await uploadToS3PresignedPut(presigned_url, body, contentType); - setStatus("Saving..."); await saveProfilePictureKey(api, userId, key); - setStatus("Refreshing..."); const refreshed = await getProfilePicture(api, userId); setProfilePicUrl(refreshed?.presigned_url ?? null); await queryClient.invalidateQueries({ queryKey: getUserQueryKey(userId) }); - setStatus("Upload complete!"); - } catch (err) { - setStatus( - `Error: ${err instanceof Error ? err.message : "Unknown error"}`, - ); - } finally { - setIsLoading(false); - } - }, [api, userId, startupStatus, queryClient]); - - const handleRemove = useCallback(async () => { - if (!userId || startupStatus !== StartupStatus.Ready) { - return; - } - setIsLoading(true); - setStatus("Removing profile picture..."); - try { - await deleteProfilePictureApi(api, userId); - setProfilePicUrl(null); - await queryClient.invalidateQueries({ queryKey: getUserQueryKey(userId) }); - setStatus("Profile picture removed!"); } catch (err) { setStatus( `Error: ${err instanceof Error ? err.message : "Unknown error"}`, @@ -161,6 +134,5 @@ export function useProfilePicture(userId: string | undefined): { isLoading, isInitialLoading, pickAndUpload, - handleRemove, }; } From 15659bd289f4828016a263dc775e1872bd59fa19 Mon Sep 17 00:00:00 2001 From: Ari Spokony Date: Thu, 16 Apr 2026 23:23:16 -0400 Subject: [PATCH 4/5] removing wrong files --- go.work | 3 -- go.work.sum | 103 ---------------------------------------------------- 2 files changed, 106 deletions(-) delete mode 100644 go.work delete mode 100644 go.work.sum diff --git a/go.work b/go.work deleted file mode 100644 index 49e522aa5..000000000 --- a/go.work +++ /dev/null @@ -1,3 +0,0 @@ -go 1.25.5 - -use ./backend diff --git a/go.work.sum b/go.work.sum deleted file mode 100644 index c5bd611cc..000000000 --- a/go.work.sum +++ /dev/null @@ -1,103 +0,0 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U= -buf.build/go/protovalidate v0.12.0/go.mod h1:q3PFfbzI05LeqxSwq+begW2syjy2Z6hLxZSkP1OH/D0= -cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= -cloud.google.com/go/alloydb v1.16.1/go.mod h1:zeZuGJ5mEaQE70FMXEvZIp5hQLR9yrGnHo1YUOncWRY= -cloud.google.com/go/alloydbconn v1.15.3/go.mod h1:9yrNzUeMr3wR/D4gTJrh5ph2VDW/19tAMV7TlNuyRfM= -cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/bigquery v1.67.0/go.mod h1:HQeP1AHFuAz0Y55heDSb0cjZIhnEkuwFRBGo6EEKHug= -cloud.google.com/go/cloudsqlconn v1.17.2/go.mod h1:l7NymuoD+hycOo+92SJEyETPtE05oRG4oXjcH3swftw= -cloud.google.com/go/firestore v1.18.0/go.mod h1:5ye0v48PhseZBdcl0qbl3uttu7FIEwEYVaWm0UIEOEU= -cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= -cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= -cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= -cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= -cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY= -cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= -cloud.google.com/go/translate v1.10.3/go.mod h1:GW0vC1qvPtd3pgtypCv4k4U8B7EdgK9/QEF2aJEUovs= -firebase.google.com/go/v4 v4.15.2/go.mod h1:qkD/HtSumrPMTLs0ahQrje5gTw2WKFKrzVFoqy4SbKA= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.52.0/go.mod h1:ayYHuYU7iNcNtEs1K9k6D/Bju7u1VEHMQm5qQ1n3GtM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.27.0/go.mod h1:E05RN++yLx9W4fXPtX978OLo9P0+fBacauUdET1BckA= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.52.0/go.mod h1:gdIm9TxRk5soClCwuB0FtdXsbqtw0aqPwBEurK9tPkw= -github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/anthropics/anthropic-sdk-go v1.19.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= -github.com/apache/arrow/go/v15 v15.0.2/go.mod h1:DGXsR3ajT524njufqf95822i+KTh+yea1jass9YXgjA= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/blues/jsonata-go v1.5.4/go.mod h1:uns2jymDrnI7y+UFYCqsRTEiAH22GyHnNXrkupAVFWI= -github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/eliben/go-sentencepiece v0.6.0/go.mod h1:nNYk4aMzgBoI6QFp4LUG8Eu1uO9fHD9L5ZEre93o9+c= -github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= -github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= -github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= -github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= -github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= -github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= -github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= -github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= -github.com/go-openapi/runtime v0.24.2/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk= -github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= -github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= -github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI= -github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= -github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= -github.com/jba/slog v0.2.0/go.mod h1:0Dh7Vyz3Td68Z1OwzadfincHwr7v+PpzadrS2Jua338= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mark3labs/mcp-go v0.29.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/openai/openai-go v1.8.2/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pgvector/pgvector-go v0.3.0/go.mod h1:duFy+PXWfW7QQd5ibqutBO4GxLsUZ9RVXhFZGIBsWSA= -github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= -github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= -github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= -github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= -github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/weaviate/weaviate v1.30.0/go.mod h1:2bp9vRsQVA1bzJIGlxyQMq4VwDBUmIETbMYLAYTouxk= -github.com/weaviate/weaviate-go-client/v5 v5.1.0/go.mod h1:gg5qyiHk53+HMZW2ynkrgm+cMQDD2Ewyma84rBeChz4= -github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= -golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/appengine/v2 v2.0.6/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7Z1JKf3J3wLI= -google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= -google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= From d76118701ae6e6406aa2ba54029f242cddcc1122 Mon Sep 17 00:00:00 2001 From: Ari Spokony Date: Thu, 16 Apr 2026 23:38:23 -0400 Subject: [PATCH 5/5] format --- clients/mobile/app/(tabs)/profile.tsx | 4 +--- clients/mobile/hooks/use-profile-picture.ts | 4 +++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clients/mobile/app/(tabs)/profile.tsx b/clients/mobile/app/(tabs)/profile.tsx index ea19b00ee..9ab86a2d8 100644 --- a/clients/mobile/app/(tabs)/profile.tsx +++ b/clients/mobile/app/(tabs)/profile.tsx @@ -54,9 +54,7 @@ export default function Profile() { isAvatarBusy={isPicLoading} /> {status.startsWith("Error") ? ( - + {status} ) : null} diff --git a/clients/mobile/hooks/use-profile-picture.ts b/clients/mobile/hooks/use-profile-picture.ts index 5598d84d2..f28ef2a79 100644 --- a/clients/mobile/hooks/use-profile-picture.ts +++ b/clients/mobile/hooks/use-profile-picture.ts @@ -118,7 +118,9 @@ export function useProfilePicture(userId: string | undefined): { const refreshed = await getProfilePicture(api, userId); setProfilePicUrl(refreshed?.presigned_url ?? null); - await queryClient.invalidateQueries({ queryKey: getUserQueryKey(userId) }); + await queryClient.invalidateQueries({ + queryKey: getUserQueryKey(userId), + }); } catch (err) { setStatus( `Error: ${err instanceof Error ? err.message : "Unknown error"}`,