From 75d83006350415d7d06fded373774bd304ea8e71 Mon Sep 17 00:00:00 2001
From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com>
Date: Wed, 24 Dec 2025 00:54:26 +0530
Subject: [PATCH] fix/companion-consider-all-profile-links (#26158)
---
companion/components/Header.tsx | 5 ++--
companion/utils/getAvatarUrl.ts | 49 +++++++++++++++++++++++++++++++++
companion/utils/index.ts | 3 ++
3 files changed, 55 insertions(+), 2 deletions(-)
create mode 100644 companion/utils/getAvatarUrl.ts
diff --git a/companion/components/Header.tsx b/companion/components/Header.tsx
index b7a4f5af45..18be182093 100644
--- a/companion/components/Header.tsx
+++ b/companion/components/Header.tsx
@@ -1,5 +1,6 @@
import { CalComAPIService, UserProfile } from "../services/calcom";
import { openInAppBrowser } from "../utils/browser";
+import { getAvatarUrl } from "../utils/getAvatarUrl";
import { CalComLogo } from "./CalComLogo";
import { FullScreenModal } from "./FullScreenModal";
import { Ionicons } from "@expo/vector-icons";
@@ -164,7 +165,7 @@ export function Header() {
) : userProfile?.avatarUrl ? (
@@ -203,7 +204,7 @@ export function Header() {
{userProfile?.avatarUrl ? (
diff --git a/companion/utils/getAvatarUrl.ts b/companion/utils/getAvatarUrl.ts
new file mode 100644
index 0000000000..d3fb78aaf1
--- /dev/null
+++ b/companion/utils/getAvatarUrl.ts
@@ -0,0 +1,49 @@
+import { z } from "zod";
+
+const CAL_URL = "https://cal.com";
+const AVATAR_FALLBACK = "/avatar.png";
+
+/**
+ * Base64 image data URL regex pattern
+ * Matches: data:image/[type];base64,[base64data]
+ */
+const BASE64_IMAGE_REGEX =
+ /^data:image\/[^;]+;base64,(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
+
+/**
+ * Normalizes avatar URL to a format that can be used by React Native Image component.
+ * Handles three formats:
+ * 1. Absolute HTTPS URLs - returns as-is
+ * 2. Base64 data URLs (data:image/...) - returns as-is
+ * 3. Relative URLs (e.g., /api/avatar/[uuid]) - prefixes with CAL_URL
+ *
+ * @param avatarUrl - The avatar URL from the API (can be absolute URL, base64, or relative path)
+ * @returns A normalized URL that can be used by React Native Image component, or fallback URL
+ *
+ * @example
+ * getAvatarUrl("https://example.com/avatar.jpg") // "https://example.com/avatar.jpg"
+ * getAvatarUrl("data:image/png;base64,iVBORw0KG...") // "data:image/png;base64,iVBORw0KG..."
+ * getAvatarUrl("/api/avatar/123") // "https://cal.com/api/avatar/123"
+ * getAvatarUrl(undefined) // "https://cal.com/avatar.png"
+ */
+export const getAvatarUrl = (avatarUrl: string | null | undefined): string => {
+ if (!avatarUrl) {
+ return CAL_URL + AVATAR_FALLBACK;
+ }
+
+ // Check if it's a base64 data URL
+ if (BASE64_IMAGE_REGEX.test(avatarUrl)) {
+ return avatarUrl;
+ }
+
+ // Check if it's already an absolute URL (HTTPS or HTTP)
+ const isAbsoluteUrl = z.string().url().safeParse(avatarUrl).success;
+ if (isAbsoluteUrl) {
+ return avatarUrl;
+ }
+
+ // Treat as relative URL and prefix with CAL_URL
+ // Ensure the relative URL starts with /
+ const relativePath = avatarUrl.startsWith("/") ? avatarUrl : `/${avatarUrl}`;
+ return CAL_URL + relativePath;
+};
diff --git a/companion/utils/index.ts b/companion/utils/index.ts
index 224349c17d..8ab29c45ce 100644
--- a/companion/utils/index.ts
+++ b/companion/utils/index.ts
@@ -39,6 +39,9 @@ export { slugify } from "./slugify";
// App icon utilities
export { getAppIconUrl } from "./getAppIconUrl";
+// Avatar utilities
+export { getAvatarUrl } from "./getAvatarUrl";
+
// Default location utilities
export {
defaultLocations,