@fluid-app/portal-sdk 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AccountScreen-B08uxzt6.mjs +2 -0
- package/dist/AccountScreen-CQCwpKEC.mjs +741 -0
- package/dist/AccountScreen-CQCwpKEC.mjs.map +1 -0
- package/dist/AccountScreen-CbhrIAa4.cjs +780 -0
- package/dist/AccountScreen-CbhrIAa4.cjs.map +1 -0
- package/dist/{ContactsScreen-CB6l0Lf1.mjs → ContactsScreen-CHLFZX_N.mjs} +2 -2
- package/dist/{ContactsScreen-CB6l0Lf1.mjs.map → ContactsScreen-CHLFZX_N.mjs.map} +1 -1
- package/dist/{ContactsScreen-Dwn5onLu.cjs → ContactsScreen-CjnCemeI.cjs} +4 -4
- package/dist/{ContactsScreen-Dwn5onLu.cjs.map → ContactsScreen-CjnCemeI.cjs.map} +1 -1
- package/dist/{CoreScreenPlaceholder-Bw8YOPwv.cjs → CoreScreenPlaceholder-C9lBkcyc.cjs} +2 -2
- package/dist/{CoreScreenPlaceholder-Bw8YOPwv.cjs.map → CoreScreenPlaceholder-C9lBkcyc.cjs.map} +1 -1
- package/dist/{CoreScreenPlaceholder-D93ZYKt2.mjs → CoreScreenPlaceholder-DCJ1hFvJ.mjs} +1 -1
- package/dist/{CoreScreenPlaceholder-D93ZYKt2.mjs.map → CoreScreenPlaceholder-DCJ1hFvJ.mjs.map} +1 -1
- package/dist/{CustomersScreen-gQb6cWL5.cjs → CustomersScreen-CqwRJogV.cjs} +4 -4
- package/dist/{CustomersScreen-gQb6cWL5.cjs.map → CustomersScreen-CqwRJogV.cjs.map} +1 -1
- package/dist/{CustomersScreen-BEar6Leg.mjs → CustomersScreen-DBicCB3o.mjs} +2 -2
- package/dist/{CustomersScreen-BEar6Leg.mjs.map → CustomersScreen-DBicCB3o.mjs.map} +1 -1
- package/dist/{MessagingScreen-YyzXdr95.mjs → FluidProvider-BafZw8PJ.mjs} +6 -503
- package/dist/FluidProvider-BafZw8PJ.mjs.map +1 -0
- package/dist/{MessagingScreen-Ca22FObh.cjs → FluidProvider-C6SCZDjX.cjs} +3 -542
- package/dist/FluidProvider-C6SCZDjX.cjs.map +1 -0
- package/dist/MessagingScreen-Bd-1_J9q.cjs +369 -0
- package/dist/MessagingScreen-Bd-1_J9q.cjs.map +1 -0
- package/dist/MessagingScreen-D8W8V2TW.mjs +2 -0
- package/dist/MessagingScreen-DCS0mtbd.mjs +324 -0
- package/dist/MessagingScreen-DCS0mtbd.mjs.map +1 -0
- package/dist/{OrdersScreen-DB1v051q.mjs → OrdersScreen-B6JCMBY5.mjs} +2 -2
- package/dist/{OrdersScreen-DB1v051q.mjs.map → OrdersScreen-B6JCMBY5.mjs.map} +1 -1
- package/dist/{OrdersScreen-DbON-kBA.cjs → OrdersScreen-DBxpXgZ9.cjs} +4 -4
- package/dist/{OrdersScreen-DbON-kBA.cjs.map → OrdersScreen-DBxpXgZ9.cjs.map} +1 -1
- package/dist/{ProductsScreen-CMDGGvE2.cjs → ProductsScreen-BK8cz_MN.cjs} +4 -4
- package/dist/{ProductsScreen-CMDGGvE2.cjs.map → ProductsScreen-BK8cz_MN.cjs.map} +1 -1
- package/dist/{ProductsScreen-nVDsY6kf.mjs → ProductsScreen-DafsauTY.mjs} +2 -2
- package/dist/{ProductsScreen-nVDsY6kf.mjs.map → ProductsScreen-DafsauTY.mjs.map} +1 -1
- package/dist/index.cjs +73 -138
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -22
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +29 -22
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +49 -116
- package/dist/index.mjs.map +1 -1
- package/package.json +19 -13
- package/dist/MessagingScreen-Ca22FObh.cjs.map +0 -1
- package/dist/MessagingScreen-YyzXdr95.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessagingScreen-Bd-1_J9q.cjs","names":["useFluidContext","useFluidAuthContext","useFluidContext","useFluidAuthContext","useFluidContext","MessagingApp"],"sources":["../src/hooks/use-fluid-api.ts","../src/messaging/use-messaging-auth.ts","../src/messaging/use-messaging-config.ts","../src/messaging/fluid-file-uploader.ts","../src/screens/MessagingScreen.tsx"],"sourcesContent":["import { useFluidContext } from \"../providers/FluidProvider\";\nimport type { FluidClient } from \"../client/fluid-client\";\n\n/**\n * Hook to access the Fluid API client\n *\n * @example\n * ```tsx\n * function ProductList() {\n * const api = useFluidApi();\n *\n * const { data: products } = useQuery({\n * queryKey: [\"products\"],\n * queryFn: () => api.products.list(),\n * });\n *\n * return <ul>{products?.map(p => <li key={p.id}>{p.name}</li>)}</ul>;\n * }\n * ```\n */\nexport function useFluidApi(): FluidClient {\n const { client } = useFluidContext();\n return client;\n}\n","/**\n * Bridge hook: maps portal SDK auth context to MessagingAuthContext.\n *\n * The messaging system identifies users by `recipient_id`, which is NOT in the\n * JWT payload or the /reps/me endpoint. It IS returned by GET /api/me.\n * This hook fetches that data and maps it into the shape MessagingApp expects.\n */\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport type {\n MessagingAuthContext,\n MessagingCurrentUser,\n} from \"@fluid-app/messaging-core\";\nimport { useFluidAuthContext } from \"../providers/FluidAuthProvider\";\nimport { useFluidApi } from \"../hooks/use-fluid-api\";\n\nconst USERS_ME_QUERY_KEY = [\"fluid\", \"users\", \"me\"] as const;\n\nexport function useMessagingAuth(): MessagingAuthContext {\n const auth = useFluidAuthContext();\n const api = useFluidApi();\n\n const {\n data: userMe,\n isLoading: isUserMeLoading,\n isError,\n } = useQuery({\n queryKey: USERS_ME_QUERY_KEY,\n queryFn: () => api.users.me(),\n enabled: auth.isAuthenticated,\n staleTime: Infinity,\n retry: 1,\n });\n\n if (auth.isLoading || (auth.isAuthenticated && isUserMeLoading)) {\n return {\n recipientId: null,\n companyId: null,\n currentUser: null,\n isLoading: true,\n };\n }\n\n if (!auth.isAuthenticated || isError || !userMe) {\n return {\n recipientId: null,\n companyId: null,\n currentUser: null,\n isLoading: false,\n };\n }\n\n const currentUser: MessagingCurrentUser | null = userMe.recipient_id\n ? {\n id: userMe.id,\n recipientId: userMe.recipient_id,\n firstName: userMe.first_name ?? \"\",\n lastName: userMe.last_name ?? \"\",\n email: userMe.email,\n ...(userMe.image_url != null && { imageUrl: userMe.image_url }),\n ...(userMe.affiliate_id != null && {\n affiliateId: userMe.affiliate_id,\n }),\n }\n : null;\n\n return {\n recipientId: userMe.recipient_id,\n companyId: userMe.company_id ?? null,\n currentUser,\n isLoading: false,\n };\n}\n","/**\n * Hook that derives MessagingApiConfig from the portal SDK's FluidProvider context.\n *\n * Maps FluidSDKConfig fields to the shape expected by MessagingApp:\n * - baseUrl -> from config.baseUrl\n * - getHeaders -> builds Authorization header from config.getAuthToken()\n * - onAuthError -> from config.onAuthError\n * - websocketUrl -> config.websocketUrl or derived from baseUrl\n * - token -> from auth context\n */\n\nimport { useCallback, useMemo } from \"react\";\nimport type { MessagingApiConfig } from \"@fluid-app/messaging-api-client\";\nimport { useFluidContext } from \"../providers/FluidProvider\";\nimport { useFluidAuthContext } from \"../providers/FluidAuthProvider\";\n\nexport interface MessagingConfig {\n readonly apiConfig: MessagingApiConfig;\n readonly websocketUrl: string;\n readonly token: string | null;\n}\n\nfunction deriveWebsocketUrl(baseUrl: string): string {\n // Strip trailing slashes and /api suffix, then append /cable\n // Handles both \"https://api.fluid.app\" and \"https://api.fluid.app/api\"\n const base = baseUrl.replace(/\\/+$/, \"\").replace(/\\/api$/, \"\");\n return `${base}/cable`;\n}\n\nexport function useMessagingConfig(): MessagingConfig {\n const { config } = useFluidContext();\n const auth = useFluidAuthContext();\n\n const getHeaders = useCallback(async (): Promise<Record<string, string>> => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...config.defaultHeaders,\n };\n\n if (config.getAuthToken) {\n const token = await config.getAuthToken();\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n }\n\n return headers;\n }, [config]);\n\n const apiBaseUrl = useMemo(() => {\n const base = config.baseUrl.replace(/\\/+$/, \"\");\n return base.endsWith(\"/api\") ? base : `${base}/api`;\n }, [config.baseUrl]);\n\n const apiConfig = useMemo(\n (): MessagingApiConfig => ({\n baseUrl: apiBaseUrl,\n getHeaders,\n ...(config.onAuthError != null && { onAuthError: config.onAuthError }),\n }),\n [apiBaseUrl, config.onAuthError, getHeaders],\n );\n\n const websocketUrl = useMemo(\n () => config.websocketUrl ?? deriveWebsocketUrl(config.baseUrl),\n [config.websocketUrl, config.baseUrl],\n );\n\n return {\n apiConfig,\n websocketUrl,\n token: auth.token,\n };\n}\n","/**\n * Lightweight Filestack file uploader for the portal SDK.\n *\n * Uploads files to Filestack using the REST API (no filestack-js dependency).\n * Flow: upload to `https://www.filestackapi.com/api/store/S3?key=<apiKey>`\n * Uses XMLHttpRequest for upload progress tracking.\n */\n\nimport type {\n FileUploader,\n UploadCallbacks,\n UploadResult,\n} from \"@fluid-app/messaging-core\";\nimport { getFileTypeFromMimetype } from \"@fluid-app/messaging-core\";\n\nconst FILESTACK_UPLOAD_URL = \"https://www.filestackapi.com/api/store/S3\";\n\nfunction getImageDimensions(\n file: File,\n): Promise<{ width: number; height: number } | null> {\n if (!file.type.startsWith(\"image/\")) return Promise.resolve(null);\n\n return new Promise((resolve) => {\n const img = new Image();\n const url = URL.createObjectURL(file);\n\n img.onload = () => {\n URL.revokeObjectURL(url);\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n };\n img.onerror = () => {\n URL.revokeObjectURL(url);\n resolve(null);\n };\n\n img.src = url;\n });\n}\n\nfunction uploadToFilestack(\n file: File,\n apiKey: string,\n callbacks: UploadCallbacks,\n): { abort: () => void } {\n const xhr = new XMLHttpRequest();\n let aborted = false;\n\n // Extract dimensions first (for images), then upload\n getImageDimensions(file)\n .then((metadata) => {\n if (aborted) return;\n\n const url = `${FILESTACK_UPLOAD_URL}?key=${encodeURIComponent(apiKey)}`;\n\n xhr.open(\"POST\", url);\n xhr.setRequestHeader(\"Content-Type\", file.type);\n\n xhr.upload.onprogress = (event) => {\n if (event.lengthComputable) {\n const progress = Math.round((event.loaded / event.total) * 100);\n callbacks.onProgress(progress);\n }\n };\n\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n const response = JSON.parse(xhr.responseText) as {\n url: string;\n size: number;\n type: string;\n };\n\n const result: UploadResult = {\n url: response.url,\n size: file.size,\n mimetype: file.type,\n kind: getFileTypeFromMimetype(file.type),\n metadata,\n };\n callbacks.onSuccess(result);\n } catch {\n callbacks.onError(new Error(\"Failed to parse upload response\"));\n }\n } else {\n callbacks.onError(\n new Error(`Upload failed with status ${xhr.status}`),\n );\n }\n };\n\n xhr.onerror = () => {\n if (!aborted) {\n callbacks.onError(new Error(\"Upload failed due to a network error\"));\n }\n };\n\n xhr.onabort = () => {\n // Silently handle abort — caller initiated it\n };\n\n xhr.send(file);\n })\n .catch((error) => {\n if (!aborted) {\n callbacks.onError(\n error instanceof Error\n ? error\n : new Error(\"Upload preparation failed\"),\n );\n }\n });\n\n return {\n abort: () => {\n aborted = true;\n xhr.abort();\n },\n };\n}\n\n/**\n * Creates a FileUploader that uploads to Filestack using the REST API.\n *\n * @param apiKey - Filestack API key. If falsy, returns a noop uploader\n * that rejects uploads with a helpful error message.\n */\nexport function createFluidFileUploader(\n apiKey: string | undefined,\n): FileUploader {\n if (!apiKey) {\n return {\n uploadFile: (_file: File, callbacks: UploadCallbacks) => {\n callbacks.onError(\n new Error(\n \"File uploads are not configured. Set filestackApiKey in your SDK config to enable attachments.\",\n ),\n );\n return { abort: () => {} };\n },\n };\n }\n\n return {\n uploadFile: (file: File, callbacks: UploadCallbacks) =>\n uploadToFilestack(file, apiKey, callbacks),\n };\n}\n","import {\n useCallback,\n useMemo,\n type ComponentProps,\n type ReactNode,\n} from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { MessagingApp } from \"@fluid-app/messaging-ui/app\";\nimport type { TagOption } from \"@fluid-app/messaging-ui\";\nimport {\n extractEmoji,\n formatMessageChannelName,\n} from \"@fluid-app/messaging-ui\";\nimport {\n listConnectedRecipients,\n searchConversations,\n} from \"@fluid-app/messaging-api-client\";\nimport type {\n Recipient,\n NominalConversation,\n} from \"@fluid-app/messaging-api-client\";\nimport { useMessagingAuth } from \"../messaging/use-messaging-auth\";\nimport { useMessagingConfig } from \"../messaging/use-messaging-config\";\nimport { createFluidFileUploader } from \"../messaging/fluid-file-uploader\";\nimport { useFluidContext } from \"../providers/FluidProvider\";\n\ntype MessagingScreenProps = ComponentProps<\"div\"> & {\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n\n // Messaging overrides\n onToast?: (message: string, type: \"success\" | \"error\") => void;\n filestackApiKey?: string;\n websocketUrl?: string;\n};\n\nfunction renderImage(props: {\n src: string;\n alt: string;\n width: number;\n height: number;\n className?: string;\n}): ReactNode {\n return (\n <img\n src={props.src}\n alt={props.alt}\n width={props.width}\n height={props.height}\n className={props.className}\n />\n );\n}\n\nfunction defaultToast(message: string, type: \"success\" | \"error\") {\n if (type === \"error\") {\n console.warn(\"[Messaging]\", message);\n }\n}\n\nexport function MessagingScreen({\n onToast,\n filestackApiKey,\n websocketUrl: websocketUrlOverride,\n /* eslint-disable @typescript-eslint/no-unused-vars -- destructured to exclude from divProps spread */\n background,\n textColor,\n accentColor,\n padding,\n borderRadius,\n /* eslint-enable @typescript-eslint/no-unused-vars */\n ...divProps\n}: MessagingScreenProps): React.JSX.Element {\n const { config } = useFluidContext();\n const { apiConfig, websocketUrl, token } = useMessagingConfig();\n const messagingAuth = useMessagingAuth();\n\n const effectiveApiKey = filestackApiKey ?? config.filestackApiKey;\n const uploader = useMemo(\n () => createFluidFileUploader(effectiveApiKey),\n [effectiveApiKey],\n );\n\n const effectiveWsUrl = websocketUrlOverride ?? websocketUrl;\n const effectiveToast = onToast ?? defaultToast;\n\n const searchUsers = useCallback(\n async (query: string): Promise<TagOption[]> => {\n const result = await listConnectedRecipients(apiConfig, {\n filterrific: { search_query: query },\n per_page: 10,\n page: 1,\n });\n // Without `kind`, the API returns Recipient[] (not WithContactRecipient[])\n const recipients = (result?.[1]?.items ?? []) as Recipient[];\n return recipients.map((user) => {\n const hasName =\n [user.first_name, user.last_name].filter(Boolean).length > 0;\n const name = hasName\n ? [user.first_name, user.last_name].filter(Boolean).join(\" \")\n : `User ${user.id}`;\n return {\n id: String(user.id),\n label: name,\n imageType: \"user\" as const,\n userData: {\n first_name: user.first_name,\n last_name: user.last_name,\n image_url: user.avatar_url,\n phone: user.phone || undefined,\n email: user.email || undefined,\n },\n conversationName: name,\n };\n });\n },\n [apiConfig],\n );\n\n const searchChannels = useCallback(\n async (query: string): Promise<TagOption[]> => {\n const channels = ((await searchConversations(apiConfig, {\n filterrific: { search_query: query },\n })) ?? []) as NominalConversation[];\n return channels.map((channel) => {\n const { text: nameWithoutEmoji } = extractEmoji(channel.name);\n return {\n id: `channel-${channel.id}`,\n label: formatMessageChannelName(nameWithoutEmoji),\n imageType: \"channel\" as const,\n userData: {\n first_name: channel.name,\n last_name: null,\n image_url: channel.avatar_url,\n },\n conversationName: channel.name,\n };\n });\n },\n [apiConfig],\n );\n\n if (messagingAuth.isLoading) {\n return (\n <div\n {...divProps}\n className={`flex h-full items-center justify-center ${divProps.className ?? \"\"}`}\n >\n <div className=\"text-muted-foreground\">Loading messaging...</div>\n </div>\n );\n }\n\n if (!messagingAuth.recipientId) {\n return (\n <div\n {...divProps}\n className={`flex h-full items-center justify-center ${divProps.className ?? \"\"}`}\n >\n <div className=\"border-border max-w-sm rounded-lg border border-dashed p-8 text-center\">\n <h2 className=\"text-foreground text-xl font-semibold\">Messaging</h2>\n <p className=\"text-muted-foreground mt-2\">\n Messaging is not available for your account\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div {...divProps} className={`h-full ${divProps.className ?? \"\"}`}>\n <MessagingApp\n config={apiConfig}\n auth={messagingAuth}\n websocketUrl={effectiveWsUrl}\n token={token}\n renderImage={renderImage}\n showAdminFeatures={false}\n onToast={effectiveToast}\n messagesViewProps={{\n uploader,\n saveDrafts: true,\n onToast: effectiveToast,\n }}\n newMessageViewProps={{\n searchUsers,\n searchChannels,\n }}\n />\n </div>\n );\n}\n\nexport const messagingScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"MessagingScreen\",\n displayName: \"Messaging Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { a as useFluidAuthContext, n as useFluidContext } from "./FluidProvider-BafZw8PJ.mjs";
|
|
2
|
+
import { useCallback, useMemo } from "react";
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { getFileTypeFromMimetype } from "@fluid-app/messaging-core";
|
|
6
|
+
import { MessagingApp } from "@fluid-app/messaging-ui/app";
|
|
7
|
+
import { extractEmoji, formatMessageChannelName } from "@fluid-app/messaging-ui";
|
|
8
|
+
import { listConnectedRecipients, searchConversations } from "@fluid-app/messaging-api-client";
|
|
9
|
+
//#region src/hooks/use-fluid-api.ts
|
|
10
|
+
/**
|
|
11
|
+
* Hook to access the Fluid API client
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* function ProductList() {
|
|
16
|
+
* const api = useFluidApi();
|
|
17
|
+
*
|
|
18
|
+
* const { data: products } = useQuery({
|
|
19
|
+
* queryKey: ["products"],
|
|
20
|
+
* queryFn: () => api.products.list(),
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* return <ul>{products?.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function useFluidApi() {
|
|
28
|
+
const { client } = useFluidContext();
|
|
29
|
+
return client;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/messaging/use-messaging-auth.ts
|
|
33
|
+
/**
|
|
34
|
+
* Bridge hook: maps portal SDK auth context to MessagingAuthContext.
|
|
35
|
+
*
|
|
36
|
+
* The messaging system identifies users by `recipient_id`, which is NOT in the
|
|
37
|
+
* JWT payload or the /reps/me endpoint. It IS returned by GET /api/me.
|
|
38
|
+
* This hook fetches that data and maps it into the shape MessagingApp expects.
|
|
39
|
+
*/
|
|
40
|
+
const USERS_ME_QUERY_KEY = [
|
|
41
|
+
"fluid",
|
|
42
|
+
"users",
|
|
43
|
+
"me"
|
|
44
|
+
];
|
|
45
|
+
function useMessagingAuth() {
|
|
46
|
+
const auth = useFluidAuthContext();
|
|
47
|
+
const api = useFluidApi();
|
|
48
|
+
const { data: userMe, isLoading: isUserMeLoading, isError } = useQuery({
|
|
49
|
+
queryKey: USERS_ME_QUERY_KEY,
|
|
50
|
+
queryFn: () => api.users.me(),
|
|
51
|
+
enabled: auth.isAuthenticated,
|
|
52
|
+
staleTime: Infinity,
|
|
53
|
+
retry: 1
|
|
54
|
+
});
|
|
55
|
+
if (auth.isLoading || auth.isAuthenticated && isUserMeLoading) return {
|
|
56
|
+
recipientId: null,
|
|
57
|
+
companyId: null,
|
|
58
|
+
currentUser: null,
|
|
59
|
+
isLoading: true
|
|
60
|
+
};
|
|
61
|
+
if (!auth.isAuthenticated || isError || !userMe) return {
|
|
62
|
+
recipientId: null,
|
|
63
|
+
companyId: null,
|
|
64
|
+
currentUser: null,
|
|
65
|
+
isLoading: false
|
|
66
|
+
};
|
|
67
|
+
const currentUser = userMe.recipient_id ? {
|
|
68
|
+
id: userMe.id,
|
|
69
|
+
recipientId: userMe.recipient_id,
|
|
70
|
+
firstName: userMe.first_name ?? "",
|
|
71
|
+
lastName: userMe.last_name ?? "",
|
|
72
|
+
email: userMe.email,
|
|
73
|
+
...userMe.image_url != null && { imageUrl: userMe.image_url },
|
|
74
|
+
...userMe.affiliate_id != null && { affiliateId: userMe.affiliate_id }
|
|
75
|
+
} : null;
|
|
76
|
+
return {
|
|
77
|
+
recipientId: userMe.recipient_id,
|
|
78
|
+
companyId: userMe.company_id ?? null,
|
|
79
|
+
currentUser,
|
|
80
|
+
isLoading: false
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/messaging/use-messaging-config.ts
|
|
85
|
+
/**
|
|
86
|
+
* Hook that derives MessagingApiConfig from the portal SDK's FluidProvider context.
|
|
87
|
+
*
|
|
88
|
+
* Maps FluidSDKConfig fields to the shape expected by MessagingApp:
|
|
89
|
+
* - baseUrl -> from config.baseUrl
|
|
90
|
+
* - getHeaders -> builds Authorization header from config.getAuthToken()
|
|
91
|
+
* - onAuthError -> from config.onAuthError
|
|
92
|
+
* - websocketUrl -> config.websocketUrl or derived from baseUrl
|
|
93
|
+
* - token -> from auth context
|
|
94
|
+
*/
|
|
95
|
+
function deriveWebsocketUrl(baseUrl) {
|
|
96
|
+
return `${baseUrl.replace(/\/+$/, "").replace(/\/api$/, "")}/cable`;
|
|
97
|
+
}
|
|
98
|
+
function useMessagingConfig() {
|
|
99
|
+
const { config } = useFluidContext();
|
|
100
|
+
const auth = useFluidAuthContext();
|
|
101
|
+
const getHeaders = useCallback(async () => {
|
|
102
|
+
const headers = {
|
|
103
|
+
"Content-Type": "application/json",
|
|
104
|
+
...config.defaultHeaders
|
|
105
|
+
};
|
|
106
|
+
if (config.getAuthToken) {
|
|
107
|
+
const token = await config.getAuthToken();
|
|
108
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
109
|
+
}
|
|
110
|
+
return headers;
|
|
111
|
+
}, [config]);
|
|
112
|
+
const apiBaseUrl = useMemo(() => {
|
|
113
|
+
const base = config.baseUrl.replace(/\/+$/, "");
|
|
114
|
+
return base.endsWith("/api") ? base : `${base}/api`;
|
|
115
|
+
}, [config.baseUrl]);
|
|
116
|
+
return {
|
|
117
|
+
apiConfig: useMemo(() => ({
|
|
118
|
+
baseUrl: apiBaseUrl,
|
|
119
|
+
getHeaders,
|
|
120
|
+
...config.onAuthError != null && { onAuthError: config.onAuthError }
|
|
121
|
+
}), [
|
|
122
|
+
apiBaseUrl,
|
|
123
|
+
config.onAuthError,
|
|
124
|
+
getHeaders
|
|
125
|
+
]),
|
|
126
|
+
websocketUrl: useMemo(() => config.websocketUrl ?? deriveWebsocketUrl(config.baseUrl), [config.websocketUrl, config.baseUrl]),
|
|
127
|
+
token: auth.token
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/messaging/fluid-file-uploader.ts
|
|
132
|
+
const FILESTACK_UPLOAD_URL = "https://www.filestackapi.com/api/store/S3";
|
|
133
|
+
function getImageDimensions(file) {
|
|
134
|
+
if (!file.type.startsWith("image/")) return Promise.resolve(null);
|
|
135
|
+
return new Promise((resolve) => {
|
|
136
|
+
const img = new Image();
|
|
137
|
+
const url = URL.createObjectURL(file);
|
|
138
|
+
img.onload = () => {
|
|
139
|
+
URL.revokeObjectURL(url);
|
|
140
|
+
resolve({
|
|
141
|
+
width: img.naturalWidth,
|
|
142
|
+
height: img.naturalHeight
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
img.onerror = () => {
|
|
146
|
+
URL.revokeObjectURL(url);
|
|
147
|
+
resolve(null);
|
|
148
|
+
};
|
|
149
|
+
img.src = url;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function uploadToFilestack(file, apiKey, callbacks) {
|
|
153
|
+
const xhr = new XMLHttpRequest();
|
|
154
|
+
let aborted = false;
|
|
155
|
+
getImageDimensions(file).then((metadata) => {
|
|
156
|
+
if (aborted) return;
|
|
157
|
+
const url = `${FILESTACK_UPLOAD_URL}?key=${encodeURIComponent(apiKey)}`;
|
|
158
|
+
xhr.open("POST", url);
|
|
159
|
+
xhr.setRequestHeader("Content-Type", file.type);
|
|
160
|
+
xhr.upload.onprogress = (event) => {
|
|
161
|
+
if (event.lengthComputable) {
|
|
162
|
+
const progress = Math.round(event.loaded / event.total * 100);
|
|
163
|
+
callbacks.onProgress(progress);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
xhr.onload = () => {
|
|
167
|
+
if (xhr.status >= 200 && xhr.status < 300) try {
|
|
168
|
+
const result = {
|
|
169
|
+
url: JSON.parse(xhr.responseText).url,
|
|
170
|
+
size: file.size,
|
|
171
|
+
mimetype: file.type,
|
|
172
|
+
kind: getFileTypeFromMimetype(file.type),
|
|
173
|
+
metadata
|
|
174
|
+
};
|
|
175
|
+
callbacks.onSuccess(result);
|
|
176
|
+
} catch {
|
|
177
|
+
callbacks.onError(/* @__PURE__ */ new Error("Failed to parse upload response"));
|
|
178
|
+
}
|
|
179
|
+
else callbacks.onError(/* @__PURE__ */ new Error(`Upload failed with status ${xhr.status}`));
|
|
180
|
+
};
|
|
181
|
+
xhr.onerror = () => {
|
|
182
|
+
if (!aborted) callbacks.onError(/* @__PURE__ */ new Error("Upload failed due to a network error"));
|
|
183
|
+
};
|
|
184
|
+
xhr.onabort = () => {};
|
|
185
|
+
xhr.send(file);
|
|
186
|
+
}).catch((error) => {
|
|
187
|
+
if (!aborted) callbacks.onError(error instanceof Error ? error : /* @__PURE__ */ new Error("Upload preparation failed"));
|
|
188
|
+
});
|
|
189
|
+
return { abort: () => {
|
|
190
|
+
aborted = true;
|
|
191
|
+
xhr.abort();
|
|
192
|
+
} };
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Creates a FileUploader that uploads to Filestack using the REST API.
|
|
196
|
+
*
|
|
197
|
+
* @param apiKey - Filestack API key. If falsy, returns a noop uploader
|
|
198
|
+
* that rejects uploads with a helpful error message.
|
|
199
|
+
*/
|
|
200
|
+
function createFluidFileUploader(apiKey) {
|
|
201
|
+
if (!apiKey) return { uploadFile: (_file, callbacks) => {
|
|
202
|
+
callbacks.onError(/* @__PURE__ */ new Error("File uploads are not configured. Set filestackApiKey in your SDK config to enable attachments."));
|
|
203
|
+
return { abort: () => {} };
|
|
204
|
+
} };
|
|
205
|
+
return { uploadFile: (file, callbacks) => uploadToFilestack(file, apiKey, callbacks) };
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region src/screens/MessagingScreen.tsx
|
|
209
|
+
function renderImage(props) {
|
|
210
|
+
return /* @__PURE__ */ jsx("img", {
|
|
211
|
+
src: props.src,
|
|
212
|
+
alt: props.alt,
|
|
213
|
+
width: props.width,
|
|
214
|
+
height: props.height,
|
|
215
|
+
className: props.className
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
function defaultToast(message, type) {
|
|
219
|
+
if (type === "error") console.warn("[Messaging]", message);
|
|
220
|
+
}
|
|
221
|
+
function MessagingScreen({ onToast, filestackApiKey, websocketUrl: websocketUrlOverride, background, textColor, accentColor, padding, borderRadius, ...divProps }) {
|
|
222
|
+
const { config } = useFluidContext();
|
|
223
|
+
const { apiConfig, websocketUrl, token } = useMessagingConfig();
|
|
224
|
+
const messagingAuth = useMessagingAuth();
|
|
225
|
+
const effectiveApiKey = filestackApiKey ?? config.filestackApiKey;
|
|
226
|
+
const uploader = useMemo(() => createFluidFileUploader(effectiveApiKey), [effectiveApiKey]);
|
|
227
|
+
const effectiveWsUrl = websocketUrlOverride ?? websocketUrl;
|
|
228
|
+
const effectiveToast = onToast ?? defaultToast;
|
|
229
|
+
const searchUsers = useCallback(async (query) => {
|
|
230
|
+
return ((await listConnectedRecipients(apiConfig, {
|
|
231
|
+
filterrific: { search_query: query },
|
|
232
|
+
per_page: 10,
|
|
233
|
+
page: 1
|
|
234
|
+
}))?.[1]?.items ?? []).map((user) => {
|
|
235
|
+
const name = [user.first_name, user.last_name].filter(Boolean).length > 0 ? [user.first_name, user.last_name].filter(Boolean).join(" ") : `User ${user.id}`;
|
|
236
|
+
return {
|
|
237
|
+
id: String(user.id),
|
|
238
|
+
label: name,
|
|
239
|
+
imageType: "user",
|
|
240
|
+
userData: {
|
|
241
|
+
first_name: user.first_name,
|
|
242
|
+
last_name: user.last_name,
|
|
243
|
+
image_url: user.avatar_url,
|
|
244
|
+
phone: user.phone || void 0,
|
|
245
|
+
email: user.email || void 0
|
|
246
|
+
},
|
|
247
|
+
conversationName: name
|
|
248
|
+
};
|
|
249
|
+
});
|
|
250
|
+
}, [apiConfig]);
|
|
251
|
+
const searchChannels = useCallback(async (query) => {
|
|
252
|
+
return (await searchConversations(apiConfig, { filterrific: { search_query: query } }) ?? []).map((channel) => {
|
|
253
|
+
const { text: nameWithoutEmoji } = extractEmoji(channel.name);
|
|
254
|
+
return {
|
|
255
|
+
id: `channel-${channel.id}`,
|
|
256
|
+
label: formatMessageChannelName(nameWithoutEmoji),
|
|
257
|
+
imageType: "channel",
|
|
258
|
+
userData: {
|
|
259
|
+
first_name: channel.name,
|
|
260
|
+
last_name: null,
|
|
261
|
+
image_url: channel.avatar_url
|
|
262
|
+
},
|
|
263
|
+
conversationName: channel.name
|
|
264
|
+
};
|
|
265
|
+
});
|
|
266
|
+
}, [apiConfig]);
|
|
267
|
+
if (messagingAuth.isLoading) return /* @__PURE__ */ jsx("div", {
|
|
268
|
+
...divProps,
|
|
269
|
+
className: `flex h-full items-center justify-center ${divProps.className ?? ""}`,
|
|
270
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
271
|
+
className: "text-muted-foreground",
|
|
272
|
+
children: "Loading messaging..."
|
|
273
|
+
})
|
|
274
|
+
});
|
|
275
|
+
if (!messagingAuth.recipientId) return /* @__PURE__ */ jsx("div", {
|
|
276
|
+
...divProps,
|
|
277
|
+
className: `flex h-full items-center justify-center ${divProps.className ?? ""}`,
|
|
278
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
279
|
+
className: "border-border max-w-sm rounded-lg border border-dashed p-8 text-center",
|
|
280
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
281
|
+
className: "text-foreground text-xl font-semibold",
|
|
282
|
+
children: "Messaging"
|
|
283
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
284
|
+
className: "text-muted-foreground mt-2",
|
|
285
|
+
children: "Messaging is not available for your account"
|
|
286
|
+
})]
|
|
287
|
+
})
|
|
288
|
+
});
|
|
289
|
+
return /* @__PURE__ */ jsx("div", {
|
|
290
|
+
...divProps,
|
|
291
|
+
className: `h-full ${divProps.className ?? ""}`,
|
|
292
|
+
children: /* @__PURE__ */ jsx(MessagingApp, {
|
|
293
|
+
config: apiConfig,
|
|
294
|
+
auth: messagingAuth,
|
|
295
|
+
websocketUrl: effectiveWsUrl,
|
|
296
|
+
token,
|
|
297
|
+
renderImage,
|
|
298
|
+
showAdminFeatures: false,
|
|
299
|
+
onToast: effectiveToast,
|
|
300
|
+
messagesViewProps: {
|
|
301
|
+
uploader,
|
|
302
|
+
saveDrafts: true,
|
|
303
|
+
onToast: effectiveToast
|
|
304
|
+
},
|
|
305
|
+
newMessageViewProps: {
|
|
306
|
+
searchUsers,
|
|
307
|
+
searchChannels
|
|
308
|
+
}
|
|
309
|
+
})
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
const messagingScreenPropertySchema = {
|
|
313
|
+
widgetType: "MessagingScreen",
|
|
314
|
+
displayName: "Messaging Screen",
|
|
315
|
+
tabsConfig: [{
|
|
316
|
+
id: "styling",
|
|
317
|
+
label: "Styling"
|
|
318
|
+
}],
|
|
319
|
+
fields: []
|
|
320
|
+
};
|
|
321
|
+
//#endregion
|
|
322
|
+
export { useMessagingAuth as a, useMessagingConfig as i, messagingScreenPropertySchema as n, useFluidApi as o, createFluidFileUploader as r, MessagingScreen as t };
|
|
323
|
+
|
|
324
|
+
//# sourceMappingURL=MessagingScreen-DCS0mtbd.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessagingScreen-DCS0mtbd.mjs","names":[],"sources":["../src/hooks/use-fluid-api.ts","../src/messaging/use-messaging-auth.ts","../src/messaging/use-messaging-config.ts","../src/messaging/fluid-file-uploader.ts","../src/screens/MessagingScreen.tsx"],"sourcesContent":["import { useFluidContext } from \"../providers/FluidProvider\";\nimport type { FluidClient } from \"../client/fluid-client\";\n\n/**\n * Hook to access the Fluid API client\n *\n * @example\n * ```tsx\n * function ProductList() {\n * const api = useFluidApi();\n *\n * const { data: products } = useQuery({\n * queryKey: [\"products\"],\n * queryFn: () => api.products.list(),\n * });\n *\n * return <ul>{products?.map(p => <li key={p.id}>{p.name}</li>)}</ul>;\n * }\n * ```\n */\nexport function useFluidApi(): FluidClient {\n const { client } = useFluidContext();\n return client;\n}\n","/**\n * Bridge hook: maps portal SDK auth context to MessagingAuthContext.\n *\n * The messaging system identifies users by `recipient_id`, which is NOT in the\n * JWT payload or the /reps/me endpoint. It IS returned by GET /api/me.\n * This hook fetches that data and maps it into the shape MessagingApp expects.\n */\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport type {\n MessagingAuthContext,\n MessagingCurrentUser,\n} from \"@fluid-app/messaging-core\";\nimport { useFluidAuthContext } from \"../providers/FluidAuthProvider\";\nimport { useFluidApi } from \"../hooks/use-fluid-api\";\n\nconst USERS_ME_QUERY_KEY = [\"fluid\", \"users\", \"me\"] as const;\n\nexport function useMessagingAuth(): MessagingAuthContext {\n const auth = useFluidAuthContext();\n const api = useFluidApi();\n\n const {\n data: userMe,\n isLoading: isUserMeLoading,\n isError,\n } = useQuery({\n queryKey: USERS_ME_QUERY_KEY,\n queryFn: () => api.users.me(),\n enabled: auth.isAuthenticated,\n staleTime: Infinity,\n retry: 1,\n });\n\n if (auth.isLoading || (auth.isAuthenticated && isUserMeLoading)) {\n return {\n recipientId: null,\n companyId: null,\n currentUser: null,\n isLoading: true,\n };\n }\n\n if (!auth.isAuthenticated || isError || !userMe) {\n return {\n recipientId: null,\n companyId: null,\n currentUser: null,\n isLoading: false,\n };\n }\n\n const currentUser: MessagingCurrentUser | null = userMe.recipient_id\n ? {\n id: userMe.id,\n recipientId: userMe.recipient_id,\n firstName: userMe.first_name ?? \"\",\n lastName: userMe.last_name ?? \"\",\n email: userMe.email,\n ...(userMe.image_url != null && { imageUrl: userMe.image_url }),\n ...(userMe.affiliate_id != null && {\n affiliateId: userMe.affiliate_id,\n }),\n }\n : null;\n\n return {\n recipientId: userMe.recipient_id,\n companyId: userMe.company_id ?? null,\n currentUser,\n isLoading: false,\n };\n}\n","/**\n * Hook that derives MessagingApiConfig from the portal SDK's FluidProvider context.\n *\n * Maps FluidSDKConfig fields to the shape expected by MessagingApp:\n * - baseUrl -> from config.baseUrl\n * - getHeaders -> builds Authorization header from config.getAuthToken()\n * - onAuthError -> from config.onAuthError\n * - websocketUrl -> config.websocketUrl or derived from baseUrl\n * - token -> from auth context\n */\n\nimport { useCallback, useMemo } from \"react\";\nimport type { MessagingApiConfig } from \"@fluid-app/messaging-api-client\";\nimport { useFluidContext } from \"../providers/FluidProvider\";\nimport { useFluidAuthContext } from \"../providers/FluidAuthProvider\";\n\nexport interface MessagingConfig {\n readonly apiConfig: MessagingApiConfig;\n readonly websocketUrl: string;\n readonly token: string | null;\n}\n\nfunction deriveWebsocketUrl(baseUrl: string): string {\n // Strip trailing slashes and /api suffix, then append /cable\n // Handles both \"https://api.fluid.app\" and \"https://api.fluid.app/api\"\n const base = baseUrl.replace(/\\/+$/, \"\").replace(/\\/api$/, \"\");\n return `${base}/cable`;\n}\n\nexport function useMessagingConfig(): MessagingConfig {\n const { config } = useFluidContext();\n const auth = useFluidAuthContext();\n\n const getHeaders = useCallback(async (): Promise<Record<string, string>> => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...config.defaultHeaders,\n };\n\n if (config.getAuthToken) {\n const token = await config.getAuthToken();\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n }\n\n return headers;\n }, [config]);\n\n const apiBaseUrl = useMemo(() => {\n const base = config.baseUrl.replace(/\\/+$/, \"\");\n return base.endsWith(\"/api\") ? base : `${base}/api`;\n }, [config.baseUrl]);\n\n const apiConfig = useMemo(\n (): MessagingApiConfig => ({\n baseUrl: apiBaseUrl,\n getHeaders,\n ...(config.onAuthError != null && { onAuthError: config.onAuthError }),\n }),\n [apiBaseUrl, config.onAuthError, getHeaders],\n );\n\n const websocketUrl = useMemo(\n () => config.websocketUrl ?? deriveWebsocketUrl(config.baseUrl),\n [config.websocketUrl, config.baseUrl],\n );\n\n return {\n apiConfig,\n websocketUrl,\n token: auth.token,\n };\n}\n","/**\n * Lightweight Filestack file uploader for the portal SDK.\n *\n * Uploads files to Filestack using the REST API (no filestack-js dependency).\n * Flow: upload to `https://www.filestackapi.com/api/store/S3?key=<apiKey>`\n * Uses XMLHttpRequest for upload progress tracking.\n */\n\nimport type {\n FileUploader,\n UploadCallbacks,\n UploadResult,\n} from \"@fluid-app/messaging-core\";\nimport { getFileTypeFromMimetype } from \"@fluid-app/messaging-core\";\n\nconst FILESTACK_UPLOAD_URL = \"https://www.filestackapi.com/api/store/S3\";\n\nfunction getImageDimensions(\n file: File,\n): Promise<{ width: number; height: number } | null> {\n if (!file.type.startsWith(\"image/\")) return Promise.resolve(null);\n\n return new Promise((resolve) => {\n const img = new Image();\n const url = URL.createObjectURL(file);\n\n img.onload = () => {\n URL.revokeObjectURL(url);\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n };\n img.onerror = () => {\n URL.revokeObjectURL(url);\n resolve(null);\n };\n\n img.src = url;\n });\n}\n\nfunction uploadToFilestack(\n file: File,\n apiKey: string,\n callbacks: UploadCallbacks,\n): { abort: () => void } {\n const xhr = new XMLHttpRequest();\n let aborted = false;\n\n // Extract dimensions first (for images), then upload\n getImageDimensions(file)\n .then((metadata) => {\n if (aborted) return;\n\n const url = `${FILESTACK_UPLOAD_URL}?key=${encodeURIComponent(apiKey)}`;\n\n xhr.open(\"POST\", url);\n xhr.setRequestHeader(\"Content-Type\", file.type);\n\n xhr.upload.onprogress = (event) => {\n if (event.lengthComputable) {\n const progress = Math.round((event.loaded / event.total) * 100);\n callbacks.onProgress(progress);\n }\n };\n\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n const response = JSON.parse(xhr.responseText) as {\n url: string;\n size: number;\n type: string;\n };\n\n const result: UploadResult = {\n url: response.url,\n size: file.size,\n mimetype: file.type,\n kind: getFileTypeFromMimetype(file.type),\n metadata,\n };\n callbacks.onSuccess(result);\n } catch {\n callbacks.onError(new Error(\"Failed to parse upload response\"));\n }\n } else {\n callbacks.onError(\n new Error(`Upload failed with status ${xhr.status}`),\n );\n }\n };\n\n xhr.onerror = () => {\n if (!aborted) {\n callbacks.onError(new Error(\"Upload failed due to a network error\"));\n }\n };\n\n xhr.onabort = () => {\n // Silently handle abort — caller initiated it\n };\n\n xhr.send(file);\n })\n .catch((error) => {\n if (!aborted) {\n callbacks.onError(\n error instanceof Error\n ? error\n : new Error(\"Upload preparation failed\"),\n );\n }\n });\n\n return {\n abort: () => {\n aborted = true;\n xhr.abort();\n },\n };\n}\n\n/**\n * Creates a FileUploader that uploads to Filestack using the REST API.\n *\n * @param apiKey - Filestack API key. If falsy, returns a noop uploader\n * that rejects uploads with a helpful error message.\n */\nexport function createFluidFileUploader(\n apiKey: string | undefined,\n): FileUploader {\n if (!apiKey) {\n return {\n uploadFile: (_file: File, callbacks: UploadCallbacks) => {\n callbacks.onError(\n new Error(\n \"File uploads are not configured. Set filestackApiKey in your SDK config to enable attachments.\",\n ),\n );\n return { abort: () => {} };\n },\n };\n }\n\n return {\n uploadFile: (file: File, callbacks: UploadCallbacks) =>\n uploadToFilestack(file, apiKey, callbacks),\n };\n}\n","import {\n useCallback,\n useMemo,\n type ComponentProps,\n type ReactNode,\n} from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { MessagingApp } from \"@fluid-app/messaging-ui/app\";\nimport type { TagOption } from \"@fluid-app/messaging-ui\";\nimport {\n extractEmoji,\n formatMessageChannelName,\n} from \"@fluid-app/messaging-ui\";\nimport {\n listConnectedRecipients,\n searchConversations,\n} from \"@fluid-app/messaging-api-client\";\nimport type {\n Recipient,\n NominalConversation,\n} from \"@fluid-app/messaging-api-client\";\nimport { useMessagingAuth } from \"../messaging/use-messaging-auth\";\nimport { useMessagingConfig } from \"../messaging/use-messaging-config\";\nimport { createFluidFileUploader } from \"../messaging/fluid-file-uploader\";\nimport { useFluidContext } from \"../providers/FluidProvider\";\n\ntype MessagingScreenProps = ComponentProps<\"div\"> & {\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n\n // Messaging overrides\n onToast?: (message: string, type: \"success\" | \"error\") => void;\n filestackApiKey?: string;\n websocketUrl?: string;\n};\n\nfunction renderImage(props: {\n src: string;\n alt: string;\n width: number;\n height: number;\n className?: string;\n}): ReactNode {\n return (\n <img\n src={props.src}\n alt={props.alt}\n width={props.width}\n height={props.height}\n className={props.className}\n />\n );\n}\n\nfunction defaultToast(message: string, type: \"success\" | \"error\") {\n if (type === \"error\") {\n console.warn(\"[Messaging]\", message);\n }\n}\n\nexport function MessagingScreen({\n onToast,\n filestackApiKey,\n websocketUrl: websocketUrlOverride,\n /* eslint-disable @typescript-eslint/no-unused-vars -- destructured to exclude from divProps spread */\n background,\n textColor,\n accentColor,\n padding,\n borderRadius,\n /* eslint-enable @typescript-eslint/no-unused-vars */\n ...divProps\n}: MessagingScreenProps): React.JSX.Element {\n const { config } = useFluidContext();\n const { apiConfig, websocketUrl, token } = useMessagingConfig();\n const messagingAuth = useMessagingAuth();\n\n const effectiveApiKey = filestackApiKey ?? config.filestackApiKey;\n const uploader = useMemo(\n () => createFluidFileUploader(effectiveApiKey),\n [effectiveApiKey],\n );\n\n const effectiveWsUrl = websocketUrlOverride ?? websocketUrl;\n const effectiveToast = onToast ?? defaultToast;\n\n const searchUsers = useCallback(\n async (query: string): Promise<TagOption[]> => {\n const result = await listConnectedRecipients(apiConfig, {\n filterrific: { search_query: query },\n per_page: 10,\n page: 1,\n });\n // Without `kind`, the API returns Recipient[] (not WithContactRecipient[])\n const recipients = (result?.[1]?.items ?? []) as Recipient[];\n return recipients.map((user) => {\n const hasName =\n [user.first_name, user.last_name].filter(Boolean).length > 0;\n const name = hasName\n ? [user.first_name, user.last_name].filter(Boolean).join(\" \")\n : `User ${user.id}`;\n return {\n id: String(user.id),\n label: name,\n imageType: \"user\" as const,\n userData: {\n first_name: user.first_name,\n last_name: user.last_name,\n image_url: user.avatar_url,\n phone: user.phone || undefined,\n email: user.email || undefined,\n },\n conversationName: name,\n };\n });\n },\n [apiConfig],\n );\n\n const searchChannels = useCallback(\n async (query: string): Promise<TagOption[]> => {\n const channels = ((await searchConversations(apiConfig, {\n filterrific: { search_query: query },\n })) ?? []) as NominalConversation[];\n return channels.map((channel) => {\n const { text: nameWithoutEmoji } = extractEmoji(channel.name);\n return {\n id: `channel-${channel.id}`,\n label: formatMessageChannelName(nameWithoutEmoji),\n imageType: \"channel\" as const,\n userData: {\n first_name: channel.name,\n last_name: null,\n image_url: channel.avatar_url,\n },\n conversationName: channel.name,\n };\n });\n },\n [apiConfig],\n );\n\n if (messagingAuth.isLoading) {\n return (\n <div\n {...divProps}\n className={`flex h-full items-center justify-center ${divProps.className ?? \"\"}`}\n >\n <div className=\"text-muted-foreground\">Loading messaging...</div>\n </div>\n );\n }\n\n if (!messagingAuth.recipientId) {\n return (\n <div\n {...divProps}\n className={`flex h-full items-center justify-center ${divProps.className ?? \"\"}`}\n >\n <div className=\"border-border max-w-sm rounded-lg border border-dashed p-8 text-center\">\n <h2 className=\"text-foreground text-xl font-semibold\">Messaging</h2>\n <p className=\"text-muted-foreground mt-2\">\n Messaging is not available for your account\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div {...divProps} className={`h-full ${divProps.className ?? \"\"}`}>\n <MessagingApp\n config={apiConfig}\n auth={messagingAuth}\n websocketUrl={effectiveWsUrl}\n token={token}\n renderImage={renderImage}\n showAdminFeatures={false}\n onToast={effectiveToast}\n messagesViewProps={{\n uploader,\n saveDrafts: true,\n onToast: effectiveToast,\n }}\n newMessageViewProps={{\n searchUsers,\n searchChannels,\n }}\n />\n </div>\n );\n}\n\nexport const messagingScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"MessagingScreen\",\n displayName: \"Messaging Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-D1SwGrFN.mjs";
|
|
2
|
-
import { t as CoreScreenPlaceholder } from "./CoreScreenPlaceholder-
|
|
2
|
+
import { t as CoreScreenPlaceholder } from "./CoreScreenPlaceholder-DCJ1hFvJ.mjs";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
//#region src/screens/OrdersScreen.tsx
|
|
5
5
|
var OrdersScreen_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -21,4 +21,4 @@ const ordersScreenPropertySchema = {
|
|
|
21
21
|
//#endregion
|
|
22
22
|
export { OrdersScreen_exports as n, ordersScreenPropertySchema as r, OrdersScreen as t };
|
|
23
23
|
|
|
24
|
-
//# sourceMappingURL=OrdersScreen-
|
|
24
|
+
//# sourceMappingURL=OrdersScreen-B6JCMBY5.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrdersScreen-
|
|
1
|
+
{"version":3,"file":"OrdersScreen-B6JCMBY5.mjs","names":[],"sources":["../src/screens/OrdersScreen.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { CoreScreenPlaceholder } from \"./CoreScreenPlaceholder\";\n\ntype OrdersScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function OrdersScreen(_props: OrdersScreenProps): React.JSX.Element {\n return <CoreScreenPlaceholder name=\"Orders\" />;\n}\n\nexport const ordersScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"OrdersScreen\",\n displayName: \"Orders Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":";;;;;;;;AAmBA,SAAgB,aAAa,QAA8C;AACzE,QAAO,oBAAC,uBAAD,EAAuB,MAAK,UAAW,CAAA;;AAGhD,MAAa,6BAAmD;CAC9D,YAAY;CACZ,aAAa;CACb,YAAY,CAAC;EAAE,IAAI;EAAW,OAAO;EAAW,CAAC;CACjD,QAAQ,EAAE;CACX"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const
|
|
2
|
-
const require_CoreScreenPlaceholder = require("./CoreScreenPlaceholder-
|
|
1
|
+
const require_FluidProvider = require("./FluidProvider-C6SCZDjX.cjs");
|
|
2
|
+
const require_CoreScreenPlaceholder = require("./CoreScreenPlaceholder-C9lBkcyc.cjs");
|
|
3
3
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
4
4
|
//#region src/screens/OrdersScreen.tsx
|
|
5
|
-
var OrdersScreen_exports = /* @__PURE__ */
|
|
5
|
+
var OrdersScreen_exports = /* @__PURE__ */ require_FluidProvider.__exportAll({
|
|
6
6
|
OrdersScreen: () => OrdersScreen,
|
|
7
7
|
ordersScreenPropertySchema: () => ordersScreenPropertySchema
|
|
8
8
|
});
|
|
@@ -38,4 +38,4 @@ Object.defineProperty(exports, "ordersScreenPropertySchema", {
|
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
//# sourceMappingURL=OrdersScreen-
|
|
41
|
+
//# sourceMappingURL=OrdersScreen-DBxpXgZ9.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrdersScreen-
|
|
1
|
+
{"version":3,"file":"OrdersScreen-DBxpXgZ9.cjs","names":["CoreScreenPlaceholder"],"sources":["../src/screens/OrdersScreen.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { CoreScreenPlaceholder } from \"./CoreScreenPlaceholder\";\n\ntype OrdersScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function OrdersScreen(_props: OrdersScreenProps): React.JSX.Element {\n return <CoreScreenPlaceholder name=\"Orders\" />;\n}\n\nexport const ordersScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"OrdersScreen\",\n displayName: \"Orders Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":";;;;;;;;AAmBA,SAAgB,aAAa,QAA8C;AACzE,QAAO,iBAAA,GAAA,kBAAA,KAACA,8BAAAA,uBAAD,EAAuB,MAAK,UAAW,CAAA;;AAGhD,MAAa,6BAAmD;CAC9D,YAAY;CACZ,aAAa;CACb,YAAY,CAAC;EAAE,IAAI;EAAW,OAAO;EAAW,CAAC;CACjD,QAAQ,EAAE;CACX"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const
|
|
2
|
-
const require_CoreScreenPlaceholder = require("./CoreScreenPlaceholder-
|
|
1
|
+
const require_FluidProvider = require("./FluidProvider-C6SCZDjX.cjs");
|
|
2
|
+
const require_CoreScreenPlaceholder = require("./CoreScreenPlaceholder-C9lBkcyc.cjs");
|
|
3
3
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
4
4
|
//#region src/screens/ProductsScreen.tsx
|
|
5
|
-
var ProductsScreen_exports = /* @__PURE__ */
|
|
5
|
+
var ProductsScreen_exports = /* @__PURE__ */ require_FluidProvider.__exportAll({
|
|
6
6
|
ProductsScreen: () => ProductsScreen,
|
|
7
7
|
productsScreenPropertySchema: () => productsScreenPropertySchema
|
|
8
8
|
});
|
|
@@ -38,4 +38,4 @@ Object.defineProperty(exports, "productsScreenPropertySchema", {
|
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
//# sourceMappingURL=ProductsScreen-
|
|
41
|
+
//# sourceMappingURL=ProductsScreen-BK8cz_MN.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsScreen-
|
|
1
|
+
{"version":3,"file":"ProductsScreen-BK8cz_MN.cjs","names":["CoreScreenPlaceholder"],"sources":["../src/screens/ProductsScreen.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { CoreScreenPlaceholder } from \"./CoreScreenPlaceholder\";\n\ntype ProductsScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function ProductsScreen(_props: ProductsScreenProps): React.JSX.Element {\n return <CoreScreenPlaceholder name=\"Products\" />;\n}\n\nexport const productsScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"ProductsScreen\",\n displayName: \"Products Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":";;;;;;;;AAmBA,SAAgB,eAAe,QAAgD;AAC7E,QAAO,iBAAA,GAAA,kBAAA,KAACA,8BAAAA,uBAAD,EAAuB,MAAK,YAAa,CAAA;;AAGlD,MAAa,+BAAqD;CAChE,YAAY;CACZ,aAAa;CACb,YAAY,CAAC;EAAE,IAAI;EAAW,OAAO;EAAW,CAAC;CACjD,QAAQ,EAAE;CACX"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-D1SwGrFN.mjs";
|
|
2
|
-
import { t as CoreScreenPlaceholder } from "./CoreScreenPlaceholder-
|
|
2
|
+
import { t as CoreScreenPlaceholder } from "./CoreScreenPlaceholder-DCJ1hFvJ.mjs";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
//#region src/screens/ProductsScreen.tsx
|
|
5
5
|
var ProductsScreen_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -21,4 +21,4 @@ const productsScreenPropertySchema = {
|
|
|
21
21
|
//#endregion
|
|
22
22
|
export { ProductsScreen_exports as n, productsScreenPropertySchema as r, ProductsScreen as t };
|
|
23
23
|
|
|
24
|
-
//# sourceMappingURL=ProductsScreen-
|
|
24
|
+
//# sourceMappingURL=ProductsScreen-DafsauTY.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsScreen-
|
|
1
|
+
{"version":3,"file":"ProductsScreen-DafsauTY.mjs","names":[],"sources":["../src/screens/ProductsScreen.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { CoreScreenPlaceholder } from \"./CoreScreenPlaceholder\";\n\ntype ProductsScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function ProductsScreen(_props: ProductsScreenProps): React.JSX.Element {\n return <CoreScreenPlaceholder name=\"Products\" />;\n}\n\nexport const productsScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"ProductsScreen\",\n displayName: \"Products Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":";;;;;;;;AAmBA,SAAgB,eAAe,QAAgD;AAC7E,QAAO,oBAAC,uBAAD,EAAuB,MAAK,YAAa,CAAA;;AAGlD,MAAa,+BAAqD;CAChE,YAAY;CACZ,aAAa;CACb,YAAY,CAAC;EAAE,IAAI;EAAW,OAAO;EAAW,CAAC;CACjD,QAAQ,EAAE;CACX"}
|