@adminide-stack/yantra-mobile 12.0.109 → 12.0.110-alpha.0
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/lib/features/webbuilder/WebBuilderExperience.js +8 -7
- package/lib/features/webbuilder/WebBuilderExperience.js.map +1 -1
- package/lib/features/webbuilder/buildFragments.js +55 -15
- package/lib/features/webbuilder/buildFragments.js.map +1 -1
- package/lib/features/webbuilder/components/BuildingStage.js +87 -0
- package/lib/features/webbuilder/components/BuildingStage.js.map +1 -0
- package/lib/features/webbuilder/components/PreviewScreen.js +6 -3
- package/lib/features/webbuilder/components/PreviewScreen.js.map +1 -1
- package/lib/features/webbuilder/components/ProjectChat.js +44 -6
- package/lib/features/webbuilder/components/ProjectChat.js.map +1 -1
- package/lib/features/webbuilder/components/ProjectsScreen.js +150 -32
- package/lib/features/webbuilder/components/ProjectsScreen.js.map +1 -1
- package/lib/features/webbuilder/components/TemplateCard.js +2 -2
- package/lib/features/webbuilder/components/TemplateCard.js.map +1 -1
- package/lib/features/webbuilder/webbuilderPrompts.js +26 -18
- package/lib/features/webbuilder/webbuilderPrompts.js.map +1 -1
- package/lib/screens/WebBuilderNative/useRestoredSandboxes.js +63 -0
- package/lib/screens/WebBuilderNative/useRestoredSandboxes.js.map +1 -0
- package/lib/screens/WebBuilderNative/useWebBuilderGallery.js +192 -80
- package/lib/screens/WebBuilderNative/useWebBuilderGallery.js.map +1 -1
- package/lib/screens/WebBuilderNative/useWebBuilderHost.js +36 -10
- package/lib/screens/WebBuilderNative/useWebBuilderHost.js.map +1 -1
- package/lib/screens/WebBuilderNative/webbuilderTransport.js +83 -20
- package/lib/screens/WebBuilderNative/webbuilderTransport.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {jsx
|
|
1
|
+
import {jsxs,jsx}from'react/jsx-runtime';import {useState}from'react';import {StyleSheet,View,Text}from'react-native';import {Image}from'expo-image';import {MaterialCommunityIcons}from'@expo/vector-icons';import {Tap,DIM}from'../../inbox/components/Tap.js';function TemplateCover({
|
|
2
2
|
uri,
|
|
3
3
|
theme,
|
|
4
4
|
style
|
|
@@ -8,7 +8,7 @@ import {jsx,jsxs}from'react/jsx-runtime';import {useState}from'react';import {St
|
|
|
8
8
|
backgroundColor: theme.chip
|
|
9
9
|
}, style], children: uri && !failed ? /* @__PURE__ */ jsx(Image, { source: {
|
|
10
10
|
uri
|
|
11
|
-
}, style: StyleSheet.absoluteFill,
|
|
11
|
+
}, style: StyleSheet.absoluteFill, contentFit: "cover", contentPosition: "top", cachePolicy: "memory-disk", transition: 150, onError: () => setFailed(true), accessibilityIgnoresInvertColors: true }) : /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "page-layout-body", size: 26, color: theme.faint }) });
|
|
12
12
|
}
|
|
13
13
|
function TemplateCard({
|
|
14
14
|
template,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TemplateCard.js","sources":["../../../../src/features/webbuilder/components/TemplateCard.tsx"],"sourcesContent":["/**\n * One starter template: its cover, name and category - the web StudioSidebar\n * card. Tapping it hands the template to the composer (the web \"Use template\").\n */\nimport React, { useState } from 'react';\nimport {
|
|
1
|
+
{"version":3,"file":"TemplateCard.js","sources":["../../../../src/features/webbuilder/components/TemplateCard.tsx"],"sourcesContent":["/**\n * One starter template: its cover, name and category - the web StudioSidebar\n * card. Tapping it hands the template to the composer (the web \"Use template\").\n */\nimport React, { useState } from 'react';\nimport { StyleSheet, Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { Image } from 'expo-image';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { DIM, Tap } from '../../inbox/components/Tap';\nimport type { WebBuilderTemplate } from '../workflow';\nimport type { WebBuilderTheme } from '../webbuilderTheme';\n\nexport function TemplateCover({\n uri,\n theme,\n style,\n}: {\n uri?: string | null;\n theme: WebBuilderTheme;\n style?: StyleProp<ViewStyle>;\n}) {\n const [failed, setFailed] = useState(false);\n return (\n <View style={[styles.cover, { backgroundColor: theme.chip }, style]}>\n {uri && !failed ? (\n <Image\n source={{ uri }}\n style={StyleSheet.absoluteFill}\n contentFit=\"cover\"\n contentPosition=\"top\"\n cachePolicy=\"memory-disk\"\n transition={150}\n onError={() => setFailed(true)}\n accessibilityIgnoresInvertColors\n />\n ) : (\n <MaterialCommunityIcons name=\"page-layout-body\" size={26} color={theme.faint} />\n )}\n </View>\n );\n}\n\nexport function TemplateCard({\n template,\n onPress,\n theme,\n width,\n selected = false,\n}: {\n template: WebBuilderTemplate;\n onPress: () => void;\n theme: WebBuilderTheme;\n width: number;\n selected?: boolean;\n}) {\n return (\n <Tap\n onPress={onPress}\n accessibilityRole=\"button\"\n accessibilityState={{ selected }}\n accessibilityLabel={`Use template ${template.title}`}\n style={[styles.card, { width }]}\n pressedStyle={DIM}\n testID={`webbuilder-template-${template.id}`}\n >\n <View style={[styles.frame, { borderColor: selected ? theme.accent : theme.border }]}>\n <TemplateCover uri={template.cover} theme={theme} />\n {selected ? (\n <View style={[styles.check, { backgroundColor: theme.accent }]}>\n <MaterialCommunityIcons name=\"check\" size={14} color={theme.accentInk} />\n </View>\n ) : null}\n </View>\n <Text style={[styles.title, { color: theme.ink }]} numberOfLines={1}>\n {template.title}\n </Text>\n {template.category ? (\n <Text style={[styles.meta, { color: theme.muted }]} numberOfLines={1}>\n {template.category}\n </Text>\n ) : null}\n </Tap>\n );\n}\n\nconst styles = StyleSheet.create({\n card: { gap: 4 },\n frame: { borderRadius: 14, borderWidth: StyleSheet.hairlineWidth, overflow: 'hidden', marginBottom: 6 },\n cover: { aspectRatio: 16 / 10, alignItems: 'center', justifyContent: 'center' },\n check: {\n position: 'absolute',\n top: 8,\n right: 8,\n width: 22,\n height: 22,\n borderRadius: 11,\n alignItems: 'center',\n justifyContent: 'center',\n },\n title: { fontSize: 14, fontWeight: '600' },\n meta: { fontSize: 12 },\n});\n"],"names":[],"mappings":"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"}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
+
const CHAT_STARTERS = [{
|
|
2
|
+
id: "cafe",
|
|
3
|
+
label: "Caf\xE9 website with menu",
|
|
4
|
+
prompt: "Build a website for a neighbourhood specialty coffee shop: hero, menu with prices, opening hours, location map and an Instagram-style gallery. Warm, modern look."
|
|
5
|
+
}, {
|
|
6
|
+
id: "saas",
|
|
7
|
+
label: "SaaS landing page",
|
|
8
|
+
prompt: "Build a landing page for a project-management SaaS: hero with product screenshot, features, pricing (3 tiers), testimonials, FAQ and a sign-up call to action."
|
|
9
|
+
}, {
|
|
10
|
+
id: "portfolio",
|
|
11
|
+
label: "Photographer portfolio",
|
|
12
|
+
prompt: "Build a portfolio for a wedding photographer: full-bleed gallery, about, packages with prices, and a contact form for booking enquiries. Elegant, minimal."
|
|
13
|
+
}, {
|
|
14
|
+
id: "booking",
|
|
15
|
+
label: "Booking app with database",
|
|
16
|
+
prompt: "Build a booking app for a yoga studio: class schedule, book a spot (saved in a database), and an admin page listing bookings."
|
|
17
|
+
}];
|
|
1
18
|
function webbuilderAgentSuggestions(hasBuild, siteTitle) {
|
|
2
|
-
if (!hasBuild)
|
|
3
|
-
return [{
|
|
4
|
-
id: "landing",
|
|
5
|
-
label: "Landing page",
|
|
6
|
-
prompt: "A landing page with a hero, features, pricing and a sign-up form."
|
|
7
|
-
}, {
|
|
8
|
-
id: "store",
|
|
9
|
-
label: "Online store",
|
|
10
|
-
prompt: "An online store with a product grid, product pages and a cart."
|
|
11
|
-
}, {
|
|
12
|
-
id: "portfolio",
|
|
13
|
-
label: "Portfolio",
|
|
14
|
-
prompt: "A portfolio with an about section, selected work and a contact form."
|
|
15
|
-
}];
|
|
16
|
-
}
|
|
19
|
+
if (!hasBuild) return [...CHAT_STARTERS];
|
|
17
20
|
const name = siteTitle ? ` on ${siteTitle}` : "";
|
|
18
21
|
return [{
|
|
19
22
|
id: "mobile",
|
|
@@ -37,6 +40,11 @@ function wakePreviewPrompt(build) {
|
|
|
37
40
|
if (!(build == null ? void 0 : build.previewUrl) && !(build == null ? void 0 : build.sandboxId)) {
|
|
38
41
|
return "The live preview is not responding. Restart the dev server for this site so the preview loads again.";
|
|
39
42
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
return [`The live preview of this site${build.title ? ` ("${build.title}")` : ""} is not responding`, build.previewUrl ? ` at ${build.previewUrl}` : "", build.sandboxId ? ` (sandbox ${build.sandboxId})` : "", ". Reconnect that sandbox and restart its dev server.", build.snapshotId ? ` If that sandbox is gone, boot from its latest restore point (snapshotId ${build.snapshotId}).` : " If the sandbox no longer exists, recreate the site in a new sandbox from its latest saved version.", " Then send me the preview link."].join("");
|
|
44
|
+
}
|
|
45
|
+
function agentEditNote(build) {
|
|
46
|
+
return `[Open build] The user is looking at the live preview of "${build.title || "this site"}" that you built${build.sandboxId ? ` (sandbox ${build.sandboxId}${build.previewUrl ? `, ${build.previewUrl}` : ""})` : ""}. Apply their request to that site in that sandbox - do not start a new one - and reply with the preview link.${build.snapshotId ? ` If that sandbox is gone, boot from its latest restore point (snapshotId ${build.snapshotId}).` : ""}`;
|
|
47
|
+
}
|
|
48
|
+
function agentPublishMessage(build, projectName, customDomain) {
|
|
49
|
+
return `Publish "${build.title || "this site"}" as ${projectName}${""}.`;
|
|
50
|
+
}export{CHAT_STARTERS,agentEditNote,agentPublishMessage,wakePreviewPrompt,webbuilderAgentSuggestions};//# sourceMappingURL=webbuilderPrompts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webbuilderPrompts.js","sources":["../../../src/features/webbuilder/webbuilderPrompts.ts"],"sourcesContent":["/**\n * Follow-ups for the Web Builder agent: the first asks of a site with nothing\n * built yet, and the edits people make most after a first build.\n */\nimport type { AgentChatSuggestion } from '../agent-chat';\n\
|
|
1
|
+
{"version":3,"file":"webbuilderPrompts.js","sources":["../../../src/features/webbuilder/webbuilderPrompts.ts"],"sourcesContent":["/**\n * Follow-ups for the Web Builder agent: the first asks of a site with nothing\n * built yet, and the edits people make most after a first build.\n */\nimport type { AgentChatSuggestion } from '../agent-chat';\n\n/**\n * Starter prompts in an empty chat (web `CHAT_STARTERS`, Lovable): complete briefs\n * that build on one tap - specific enough that the agent asks nothing.\n */\nexport const CHAT_STARTERS: readonly AgentChatSuggestion[] = [\n {\n id: 'cafe',\n label: 'Café website with menu',\n prompt: 'Build a website for a neighbourhood specialty coffee shop: hero, menu with prices, opening hours, location map and an Instagram-style gallery. Warm, modern look.',\n },\n {\n id: 'saas',\n label: 'SaaS landing page',\n prompt: 'Build a landing page for a project-management SaaS: hero with product screenshot, features, pricing (3 tiers), testimonials, FAQ and a sign-up call to action.',\n },\n {\n id: 'portfolio',\n label: 'Photographer portfolio',\n prompt: 'Build a portfolio for a wedding photographer: full-bleed gallery, about, packages with prices, and a contact form for booking enquiries. Elegant, minimal.',\n },\n {\n id: 'booking',\n label: 'Booking app with database',\n prompt: 'Build a booking app for a yoga studio: class schedule, book a spot (saved in a database), and an admin page listing bookings.',\n },\n];\n\nexport function webbuilderAgentSuggestions(hasBuild: boolean, siteTitle: string | null): AgentChatSuggestion[] {\n if (!hasBuild) return [...CHAT_STARTERS];\n const name = siteTitle ? ` on ${siteTitle}` : '';\n return [\n { id: 'mobile', label: 'Make it great on phones', prompt: `Make every page${name} look great on phones.` },\n { id: 'colors', label: 'New color scheme', prompt: 'Give the site a fresh, modern color scheme.' },\n { id: 'section', label: 'Add a section', prompt: 'Add a testimonials section above the footer.' },\n { id: 'fix', label: 'Fix what is broken', prompt: 'Check the site for errors and fix anything broken.' },\n ];\n}\n\ntype BuildRef = {\n title?: string;\n previewUrl: string | null;\n sandboxId: string | null;\n snapshotId?: string | null;\n};\n\n/**\n * Sent when the preview does not answer: the sandbox slept or was reclaimed (E2B\n * sandboxes live about an hour after the last change). It names the build and its\n * restore point, so the agent restarts THIS site or reopens it from the snapshot.\n */\nexport function wakePreviewPrompt(build: BuildRef | null): string {\n if (!build?.previewUrl && !build?.sandboxId) {\n return 'The live preview is not responding. Restart the dev server for this site so the preview loads again.';\n }\n return [\n `The live preview of this site${build.title ? ` (\"${build.title}\")` : ''} is not responding`,\n build.previewUrl ? ` at ${build.previewUrl}` : '',\n build.sandboxId ? ` (sandbox ${build.sandboxId})` : '',\n '. Reconnect that sandbox and restart its dev server.',\n build.snapshotId\n ? ` If that sandbox is gone, boot from its latest restore point (snapshotId ${build.snapshotId}).`\n : ' If the sandbox no longer exists, recreate the site in a new sandbox from its latest saved version.',\n ' Then send me the preview link.',\n ].join('');\n}\n\n/**\n * The note a change to an agent-chat build carries (web `agentEditForChat`): the\n * site lives in the agent's sandbox, so the change goes to that sandbox - never a\n * new one - with the restore point for when it expired.\n */\nexport function agentEditNote(build: BuildRef): string {\n return `[Open build] The user is looking at the live preview of \"${build.title || 'this site'}\" that you built${\n build.sandboxId ? ` (sandbox ${build.sandboxId}${build.previewUrl ? `, ${build.previewUrl}` : ''})` : ''\n }. Apply their request to that site in that sandbox - do not start a new one - and reply with the preview link.${\n build.snapshotId\n ? ` If that sandbox is gone, boot from its latest restore point (snapshotId ${build.snapshotId}).`\n : ''\n }`;\n}\n\n/** What Publish asks the agent for an agent-chat build (web `agentPublishMessage`). */\nexport function agentPublishMessage(build: BuildRef, projectName: string, customDomain?: string): string {\n return `Publish \"${build.title || 'this site'}\" as ${projectName}${customDomain ? ` on ${customDomain}` : ''}.`;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {useState,useEffect,useCallback}from'react';import AsyncStorage from'@react-native-async-storage/async-storage';var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
const STORAGE_KEY = "yantra.webbuilder.restored.v1";
|
|
21
|
+
const MAX_ENTRIES = 50;
|
|
22
|
+
function applyRestored(builds, restored) {
|
|
23
|
+
if (!Object.keys(restored).length) return builds;
|
|
24
|
+
return builds.map((build) => {
|
|
25
|
+
const next = restored[build.key];
|
|
26
|
+
return next ? __spreadProps(__spreadValues({}, build), {
|
|
27
|
+
sandboxId: next.sandboxId,
|
|
28
|
+
previewUrl: next.previewUrl
|
|
29
|
+
}) : build;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function useRestoredSandboxes() {
|
|
33
|
+
const [restored, setRestored] = useState({});
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
let alive = true;
|
|
36
|
+
AsyncStorage.getItem(STORAGE_KEY).then((raw) => {
|
|
37
|
+
if (!alive || !raw) return;
|
|
38
|
+
const parsed = JSON.parse(raw);
|
|
39
|
+
if (parsed && typeof parsed === "object") setRestored((cur) => __spreadValues(__spreadValues({}, parsed), cur));
|
|
40
|
+
}).catch(() => void 0);
|
|
41
|
+
return () => {
|
|
42
|
+
alive = false;
|
|
43
|
+
};
|
|
44
|
+
}, []);
|
|
45
|
+
const remember = useCallback((buildKey, sandboxId, previewUrl) => {
|
|
46
|
+
setRestored((cur) => {
|
|
47
|
+
const entries = Object.entries(__spreadProps(__spreadValues({}, cur), {
|
|
48
|
+
[buildKey]: {
|
|
49
|
+
sandboxId,
|
|
50
|
+
previewUrl,
|
|
51
|
+
at: Date.now()
|
|
52
|
+
}
|
|
53
|
+
})).sort((a, b) => b[1].at - a[1].at).slice(0, MAX_ENTRIES);
|
|
54
|
+
const next = Object.fromEntries(entries);
|
|
55
|
+
AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(next)).catch(() => void 0);
|
|
56
|
+
return next;
|
|
57
|
+
});
|
|
58
|
+
}, []);
|
|
59
|
+
return {
|
|
60
|
+
restored,
|
|
61
|
+
remember
|
|
62
|
+
};
|
|
63
|
+
}export{applyRestored,useRestoredSandboxes};//# sourceMappingURL=useRestoredSandboxes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRestoredSandboxes.js","sources":["../../../src/screens/WebBuilderNative/useRestoredSandboxes.ts"],"sourcesContent":["/**\n * The sandboxes a build's restore point was booted into on this device (web\n * `writeRestoredSandbox`/`readRestoredSandbox`). The chat's posts still name the\n * expired sandbox, and this backend has no `aiCodePersistSandboxIdentity`, so the\n * new id and preview are remembered here, keyed by the build, and laid over the\n * builds read from the posts.\n */\nimport { useCallback, useEffect, useState } from 'react';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\nimport type { WebBuilderBuild } from '../../features/webbuilder/types';\n\nconst STORAGE_KEY = 'yantra.webbuilder.restored.v1';\n/** Restored sandboxes expire too; older entries are not worth keeping. */\nconst MAX_ENTRIES = 50;\n\nexport interface RestoredSandbox {\n sandboxId: string;\n previewUrl: string;\n at: number;\n}\n\ntype RestoredMap = Record<string, RestoredSandbox>;\n\nexport function applyRestored(builds: WebBuilderBuild[], restored: RestoredMap): WebBuilderBuild[] {\n if (!Object.keys(restored).length) return builds;\n return builds.map((build) => {\n const next = restored[build.key];\n return next ? { ...build, sandboxId: next.sandboxId, previewUrl: next.previewUrl } : build;\n });\n}\n\nexport function useRestoredSandboxes() {\n const [restored, setRestored] = useState<RestoredMap>({});\n useEffect(() => {\n let alive = true;\n AsyncStorage.getItem(STORAGE_KEY)\n .then((raw) => {\n if (!alive || !raw) return;\n const parsed = JSON.parse(raw) as RestoredMap;\n if (parsed && typeof parsed === 'object') setRestored((cur) => ({ ...parsed, ...cur }));\n })\n .catch(() => undefined);\n return () => {\n alive = false;\n };\n }, []);\n\n const remember = useCallback((buildKey: string, sandboxId: string, previewUrl: string) => {\n setRestored((cur) => {\n const entries = Object.entries({ ...cur, [buildKey]: { sandboxId, previewUrl, at: Date.now() } })\n .sort((a, b) => b[1].at - a[1].at)\n .slice(0, MAX_ENTRIES);\n const next = Object.fromEntries(entries);\n AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(next)).catch(() => undefined);\n return next;\n });\n }, []);\n\n return { restored, remember };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAUA,MAAM,WAAc,GAAA,+BAAA;AAEpB,MAAM,WAAc,GAAA,EAAA;AAOJ,SAAA,aAAA,CAAc,QAA2B,QAA0C,EAAA;AACjG,EAAA,IAAI,CAAC,MAAO,CAAA,IAAA,CAAK,QAAQ,CAAA,CAAE,QAAe,OAAA,MAAA;AAC1C,EAAO,OAAA,MAAA,CAAO,IAAI,CAAS,KAAA,KAAA;AACzB,IAAM,MAAA,IAAA,GAAO,QAAS,CAAA,KAAA,CAAM,GAAG,CAAA;AAC/B,IAAO,OAAA,IAAA,GAAO,iCACT,KADS,CAAA,EAAA;AAAA,MAEZ,WAAW,IAAK,CAAA,SAAA;AAAA,MAChB,YAAY,IAAK,CAAA;AAAA,KACf,CAAA,GAAA,KAAA;AAAA,GACL,CAAA;AACH;AACO,SAAS,oBAAuB,GAAA;AACrC,EAAA,MAAM,CAAC,QAAU,EAAA,WAAW,CAAI,GAAA,QAAA,CAAsB,EAAE,CAAA;AACxD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,KAAQ,GAAA,IAAA;AACZ,IAAA,YAAA,CAAa,OAAQ,CAAA,WAAW,CAAE,CAAA,IAAA,CAAK,CAAO,GAAA,KAAA;AAC5C,MAAI,IAAA,CAAC,KAAS,IAAA,CAAC,GAAK,EAAA;AACpB,MAAM,MAAA,MAAA,GAAS,IAAK,CAAA,KAAA,CAAM,GAAG,CAAA;AAC7B,MAAI,IAAA,MAAA,IAAU,OAAO,MAAW,KAAA,QAAA,cAAsB,CAAQ,GAAA,KAAA,cAAA,CAAA,cAAA,CAAA,EAAA,EACzD,SACA,GACH,CAAA,CAAA;AAAA,KACH,CAAA,CAAE,KAAM,CAAA,MAAM,MAAS,CAAA;AACxB,IAAA,OAAO,MAAM;AACX,MAAQ,KAAA,GAAA,KAAA;AAAA,KACV;AAAA,GACF,EAAG,EAAE,CAAA;AACL,EAAA,MAAM,QAAW,GAAA,WAAA,CAAY,CAAC,QAAA,EAAkB,WAAmB,UAAuB,KAAA;AACxF,IAAA,WAAA,CAAY,CAAO,GAAA,KAAA;AACjB,MAAA,MAAM,OAAU,GAAA,MAAA,CAAO,OAAQ,CAAA,aAAA,CAAA,cAAA,CAAA,EAAA,EAC1B,GAD0B,CAAA,EAAA;AAAA,QAE7B,CAAC,QAAQ,GAAG;AAAA,UACV,SAAA;AAAA,UACA,UAAA;AAAA,UACA,EAAA,EAAI,KAAK,GAAI;AAAA;AACf,QACD,CAAE,CAAA,IAAA,CAAK,CAAC,CAAA,EAAG,MAAM,CAAE,CAAA,CAAC,CAAE,CAAA,EAAA,GAAK,EAAE,CAAC,CAAA,CAAE,EAAE,CAAE,CAAA,KAAA,CAAM,GAAG,WAAW,CAAA;AACzD,MAAM,MAAA,IAAA,GAAO,MAAO,CAAA,WAAA,CAAY,OAAO,CAAA;AACvC,MAAa,YAAA,CAAA,OAAA,CAAQ,aAAa,IAAK,CAAA,SAAA,CAAU,IAAI,CAAC,CAAA,CAAE,KAAM,CAAA,MAAM,MAAS,CAAA;AAC7E,MAAO,OAAA,IAAA;AAAA,KACR,CAAA;AAAA,GACH,EAAG,EAAE,CAAA;AACL,EAAO,OAAA;AAAA,IACL,QAAA;AAAA,IACA;AAAA,GACF;AACF"}
|
|
@@ -1,9 +1,67 @@
|
|
|
1
|
-
import {useState,useRef,useCallback
|
|
2
|
-
const MIN_FILL =
|
|
1
|
+
import {useState,useRef,useEffect,useCallback}from'react';import {useApolloClient}from'@apollo/client/index.js';import AsyncStorage from'@react-native-async-storage/async-storage';import {MessagesDocument,GetChannelsByUserWithLastMessageDocument}from'common/graphql';import {getThreadMessagesQueryVariables,getHistoryChannelsQueryVariables}from'../../hooks/useChatApi.js';import {lastMessageHasCover,projectFromPosts}from'../../features/webbuilder/buildFragments.js';const GALLERY_PAGE_SIZE = 10;
|
|
2
|
+
const MIN_FILL = 8;
|
|
3
3
|
const MAX_PAGES_PER_LOAD = 4;
|
|
4
|
+
const READ_CONCURRENCY = 4;
|
|
5
|
+
const SNAPSHOT_KEY = "yantra.webbuilder.projects.v1";
|
|
6
|
+
const SNAPSHOT_MAX = 40;
|
|
7
|
+
const memory = /* @__PURE__ */ new Map();
|
|
8
|
+
function sortProjects(list) {
|
|
9
|
+
return [...list].sort((a, b) => b.updatedAt - a.updatedAt);
|
|
10
|
+
}
|
|
11
|
+
function stampOf(channel) {
|
|
12
|
+
var _a, _b;
|
|
13
|
+
return String((_b = (_a = channel.updatedAt) != null ? _a : channel.createdAt) != null ? _b : "");
|
|
14
|
+
}
|
|
15
|
+
async function readSnapshot(org) {
|
|
16
|
+
try {
|
|
17
|
+
const raw = await AsyncStorage.getItem(`${SNAPSHOT_KEY}:${org}`);
|
|
18
|
+
const list = raw ? JSON.parse(raw) : [];
|
|
19
|
+
return Array.isArray(list) ? list.filter((p) => (p == null ? void 0 : p.channelId) && p.title) : [];
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function writeSnapshot(org, projects) {
|
|
25
|
+
AsyncStorage.setItem(`${SNAPSHOT_KEY}:${org}`, JSON.stringify(projects.slice(0, SNAPSHOT_MAX))).catch(() => void 0);
|
|
26
|
+
}
|
|
27
|
+
async function eachLimited(items, limit, work) {
|
|
28
|
+
let next = 0;
|
|
29
|
+
const lane = async () => {
|
|
30
|
+
while (next < items.length) {
|
|
31
|
+
const item = items[next];
|
|
32
|
+
next += 1;
|
|
33
|
+
await work(item);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
await Promise.all(Array.from({
|
|
37
|
+
length: Math.min(limit, items.length)
|
|
38
|
+
}, lane));
|
|
39
|
+
}
|
|
40
|
+
async function readChannels(client, orgName, skip, fetchPolicy) {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
const result = await client.query({
|
|
43
|
+
query: GetChannelsByUserWithLastMessageDocument,
|
|
44
|
+
variables: getHistoryChannelsQueryVariables(orgName, {
|
|
45
|
+
limit: GALLERY_PAGE_SIZE,
|
|
46
|
+
skip
|
|
47
|
+
}),
|
|
48
|
+
fetchPolicy,
|
|
49
|
+
errorPolicy: "all",
|
|
50
|
+
// Same slot as chat history: the type policy merges both into one list.
|
|
51
|
+
context: {
|
|
52
|
+
cacheKey: "chat-history-channels-list"
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return ((_b = (_a = result.data) == null ? void 0 : _a.channelsByUser) != null ? _b : []).filter((c) => c == null ? void 0 : c.id);
|
|
56
|
+
}
|
|
4
57
|
function useWebBuilderGallery(orgName, cdnOrigin) {
|
|
58
|
+
var _a;
|
|
5
59
|
const client = useApolloClient();
|
|
6
|
-
const
|
|
60
|
+
const remembered = orgName ? memory.get(orgName) : void 0;
|
|
61
|
+
const [projects, setProjects] = useState(() => {
|
|
62
|
+
var _a2;
|
|
63
|
+
return (_a2 = remembered == null ? void 0 : remembered.projects) != null ? _a2 : [];
|
|
64
|
+
});
|
|
7
65
|
const [loading, setLoading] = useState(false);
|
|
8
66
|
const [loadingMore, setLoadingMore] = useState(false);
|
|
9
67
|
const [hasMore, setHasMore] = useState(false);
|
|
@@ -11,96 +69,151 @@ function useWebBuilderGallery(orgName, cdnOrigin) {
|
|
|
11
69
|
const skipRef = useRef(0);
|
|
12
70
|
const busyRef = useRef(false);
|
|
13
71
|
const runRef = useRef(0);
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
72
|
+
const knownRef = useRef((_a = remembered == null ? void 0 : remembered.known) != null ? _a : /* @__PURE__ */ new Map());
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!orgName) return;
|
|
75
|
+
memory.set(orgName, {
|
|
76
|
+
projects,
|
|
77
|
+
known: knownRef.current
|
|
78
|
+
});
|
|
79
|
+
if (projects.length) writeSnapshot(orgName, projects);
|
|
80
|
+
}, [orgName, projects]);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
var _a2;
|
|
83
|
+
if (!orgName || ((_a2 = memory.get(orgName)) == null ? void 0 : _a2.projects.length)) return void 0;
|
|
84
|
+
let live = true;
|
|
85
|
+
readSnapshot(orgName).then((saved) => {
|
|
86
|
+
if (live && saved.length) setProjects((prev) => prev.length ? prev : saved);
|
|
87
|
+
}).catch(() => void 0);
|
|
88
|
+
return () => {
|
|
89
|
+
live = false;
|
|
90
|
+
};
|
|
91
|
+
}, [orgName]);
|
|
92
|
+
const projectFor = useCallback(async (channel, fetchPolicy) => {
|
|
93
|
+
var _a2, _b, _c, _d;
|
|
94
|
+
const id = (_a2 = channel.id) != null ? _a2 : "";
|
|
17
95
|
if (!id) return null;
|
|
18
96
|
if (lastMessageHasCover(channel)) {
|
|
19
97
|
const fromLast = projectFromPosts(channel, [channel.lastMessage], cdnOrigin);
|
|
20
98
|
if (fromLast) return fromLast;
|
|
21
99
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return projectFromPosts(channel, withLast, cdnOrigin);
|
|
35
|
-
} catch (err) {
|
|
36
|
-
console.warn("[WebBuilderNative] could not read builds of", id, err);
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
100
|
+
const result = await client.query({
|
|
101
|
+
query: MessagesDocument,
|
|
102
|
+
variables: getThreadMessagesQueryVariables(id),
|
|
103
|
+
fetchPolicy,
|
|
104
|
+
errorPolicy: "all",
|
|
105
|
+
context: {
|
|
106
|
+
cacheKey: `messages-list:${id}`
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
const posts = (_d = (_c = (_b = result.data) == null ? void 0 : _b.messages) == null ? void 0 : _c.data) != null ? _d : [];
|
|
110
|
+
const withLast = channel.lastMessage ? [...posts, channel.lastMessage] : posts;
|
|
111
|
+
return projectFromPosts(channel, withLast, cdnOrigin);
|
|
39
112
|
}, [client, cdnOrigin]);
|
|
40
|
-
const load = useCallback(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
for (let page = 0; page < MAX_PAGES_PER_LOAD; page += 1) {
|
|
57
|
-
const result = await client.query({
|
|
58
|
-
query: GetChannelsByUserWithLastMessageDocument,
|
|
59
|
-
variables: getHistoryChannelsQueryVariables(orgName, {
|
|
60
|
-
limit: GALLERY_PAGE_SIZE,
|
|
61
|
-
skip: skipRef.current
|
|
62
|
-
}),
|
|
63
|
-
// cache-first served a stale first page on the web; always ask.
|
|
64
|
-
fetchPolicy: "network-only",
|
|
65
|
-
context: {
|
|
66
|
-
cacheKey: "webbuilder-gallery"
|
|
67
|
-
}
|
|
113
|
+
const load = useCallback(
|
|
114
|
+
async (mode) => {
|
|
115
|
+
if (!orgName || busyRef.current) return;
|
|
116
|
+
busyRef.current = true;
|
|
117
|
+
const run = runRef.current + 1;
|
|
118
|
+
runRef.current = run;
|
|
119
|
+
const stale = () => runRef.current !== run;
|
|
120
|
+
const known = knownRef.current;
|
|
121
|
+
const seen = /* @__PURE__ */ new Set();
|
|
122
|
+
const put = (project, channelId) => {
|
|
123
|
+
if (stale()) return;
|
|
124
|
+
setProjects((prev) => {
|
|
125
|
+
const byId = new Map(prev.map((p) => [p.channelId, p]));
|
|
126
|
+
if (project) byId.set(channelId, project);
|
|
127
|
+
else byId.delete(channelId);
|
|
128
|
+
return sortProjects(byId.values());
|
|
68
129
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
130
|
+
};
|
|
131
|
+
if (mode === "reset") {
|
|
132
|
+
skipRef.current = 0;
|
|
133
|
+
setLoading(projects.length === 0);
|
|
134
|
+
} else {
|
|
135
|
+
setLoadingMore(true);
|
|
75
136
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
137
|
+
setError(null);
|
|
138
|
+
try {
|
|
139
|
+
if (mode === "reset") {
|
|
140
|
+
const cached = await readChannels(client, orgName, 0, "cache-only").catch(() => []);
|
|
141
|
+
await eachLimited(cached, READ_CONCURRENCY, async (channel) => {
|
|
142
|
+
var _a2;
|
|
143
|
+
const id = (_a2 = channel.id) != null ? _a2 : "";
|
|
144
|
+
if (known.has(id)) return;
|
|
145
|
+
const project = await projectFor(channel, "cache-only").catch(() => null);
|
|
146
|
+
if (project) {
|
|
147
|
+
known.set(id, {
|
|
148
|
+
stamp: stampOf(channel),
|
|
149
|
+
project
|
|
150
|
+
});
|
|
151
|
+
put(project, id);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
if (!stale() && projects.length === 0 && known.size) setLoading(false);
|
|
155
|
+
}
|
|
156
|
+
let more = false;
|
|
157
|
+
const tally = {
|
|
158
|
+
found: 0
|
|
159
|
+
};
|
|
160
|
+
for (let page = 0; page < MAX_PAGES_PER_LOAD; page += 1) {
|
|
161
|
+
const channels = await readChannels(client, orgName, skipRef.current, "network-only");
|
|
162
|
+
if (stale()) return;
|
|
163
|
+
skipRef.current += channels.length;
|
|
164
|
+
more = channels.length >= GALLERY_PAGE_SIZE;
|
|
165
|
+
await eachLimited(channels, READ_CONCURRENCY, async (channel) => {
|
|
166
|
+
var _a2;
|
|
167
|
+
const id = (_a2 = channel.id) != null ? _a2 : "";
|
|
168
|
+
seen.add(id);
|
|
169
|
+
const stamp = stampOf(channel);
|
|
170
|
+
const prior = known.get(id);
|
|
171
|
+
if (prior && prior.stamp === stamp) {
|
|
172
|
+
if (prior.project) tally.found += 1;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const project = await projectFor(channel, "network-only").catch(() => {
|
|
176
|
+
var _a3;
|
|
177
|
+
return (_a3 = prior == null ? void 0 : prior.project) != null ? _a3 : null;
|
|
178
|
+
});
|
|
179
|
+
known.set(id, {
|
|
180
|
+
stamp,
|
|
181
|
+
project
|
|
182
|
+
});
|
|
183
|
+
if (project) tally.found += 1;
|
|
184
|
+
put(project, id);
|
|
185
|
+
});
|
|
186
|
+
if (!more || tally.found >= MIN_FILL) break;
|
|
187
|
+
}
|
|
188
|
+
if (stale()) return;
|
|
189
|
+
if (mode === "reset") {
|
|
190
|
+
setProjects((prev) => prev.filter((p) => seen.has(p.channelId) || p.updatedAt > Date.now() - 6e4));
|
|
191
|
+
}
|
|
192
|
+
setHasMore(more);
|
|
193
|
+
} catch (err) {
|
|
194
|
+
console.warn("[WebBuilderNative] could not load projects:", err);
|
|
195
|
+
if (!stale()) setError("Check your connection and try again.");
|
|
196
|
+
} finally {
|
|
197
|
+
busyRef.current = false;
|
|
198
|
+
if (!stale()) {
|
|
199
|
+
setLoading(false);
|
|
200
|
+
setLoadingMore(false);
|
|
201
|
+
}
|
|
92
202
|
}
|
|
93
|
-
}
|
|
94
|
-
|
|
203
|
+
},
|
|
204
|
+
// `projects.length` only steers the first spinner; reading it stale is harmless.
|
|
205
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
206
|
+
[client, orgName, projectFor]
|
|
207
|
+
);
|
|
95
208
|
useEffect(() => {
|
|
96
|
-
|
|
209
|
+
load("reset").catch(() => void 0);
|
|
97
210
|
}, [load]);
|
|
98
211
|
const refresh = useCallback(() => {
|
|
99
212
|
busyRef.current = false;
|
|
100
|
-
|
|
213
|
+
load("reset").catch(() => void 0);
|
|
101
214
|
}, [load]);
|
|
102
215
|
const loadMore = useCallback(() => {
|
|
103
|
-
if (hasMore)
|
|
216
|
+
if (hasMore) load("more").catch(() => void 0);
|
|
104
217
|
}, [hasMore, load]);
|
|
105
218
|
const upsert = useCallback((project) => {
|
|
106
219
|
setProjects((prev) => {
|
|
@@ -108,8 +221,7 @@ function useWebBuilderGallery(orgName, cdnOrigin) {
|
|
|
108
221
|
if (existing && existing.title === project.title && existing.coverUrl === project.coverUrl && existing.liveUrl === project.liveUrl && existing.updatedAt === project.updatedAt) {
|
|
109
222
|
return prev;
|
|
110
223
|
}
|
|
111
|
-
|
|
112
|
-
return [project, ...rest].sort((a, b) => b.updatedAt - a.updatedAt);
|
|
224
|
+
return sortProjects([project, ...prev.filter((p) => p.channelId !== project.channelId)]);
|
|
113
225
|
});
|
|
114
226
|
}, []);
|
|
115
227
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWebBuilderGallery.js","sources":["../../../src/screens/WebBuilderNative/useWebBuilderGallery.ts"],"sourcesContent":["/**\n * \"Recent builds\" - the web surface's gallery hydrate, on the host.\n *\n * Same steps as the web (`realServices` gallery load): page the person's AI\n * assistant chats 10 at a time, newest first; for each, read its posts (unless\n * the last post already carries the build's screenshot) and keep the chat only\n * if it holds a build. A chat that never built anything is not a site, which\n * is why history's \"build\" label alone is not used.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useApolloClient } from '@apollo/client';\nimport { GetChannelsByUserWithLastMessageDocument, MessagesDocument } from 'common/graphql';\nimport { getHistoryChannelsQueryVariables, getThreadMessagesQueryVariables } from '../../hooks/useChatApi';\nimport { lastMessageHasCover, projectFromPosts, type ChannelLike } from '../../features/webbuilder/buildFragments';\nimport type { WebBuilderProject } from '../../features/webbuilder/types';\n\n/** The web gallery's page size (`GALLERY_PAGE_SIZE`). */\nexport const GALLERY_PAGE_SIZE = 10;\n/** Keep paging (up to this many pages per call) while a page yields too few builds to fill the screen. */\nconst MIN_FILL = 6;\nconst MAX_PAGES_PER_LOAD = 4;\n\ntype Loose = Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n\nexport interface WebBuilderGallery {\n projects: WebBuilderProject[];\n loading: boolean;\n loadingMore: boolean;\n hasMore: boolean;\n error: string | null;\n refresh: () => void;\n loadMore: () => void;\n /** Put (or update) one site - the open one, as its posts change. */\n upsert: (project: WebBuilderProject) => void;\n}\n\nexport function useWebBuilderGallery(orgName: string, cdnOrigin: string): WebBuilderGallery {\n const client = useApolloClient();\n const [projects, setProjects] = useState<WebBuilderProject[]>([]);\n const [loading, setLoading] = useState(false);\n const [loadingMore, setLoadingMore] = useState(false);\n const [hasMore, setHasMore] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const skipRef = useRef(0);\n const busyRef = useRef(false);\n const runRef = useRef(0);\n\n const projectFor = useCallback(\n async (channel: ChannelLike): Promise<WebBuilderProject | null> => {\n const id = channel.id ?? '';\n if (!id) return null;\n // The web skips the thread read only when the last post already has a cover.\n if (lastMessageHasCover(channel)) {\n const fromLast = projectFromPosts(channel, [channel.lastMessage], cdnOrigin);\n if (fromLast) return fromLast;\n }\n try {\n const result = await client.query({\n query: MessagesDocument,\n variables: getThreadMessagesQueryVariables(id),\n fetchPolicy: 'network-only',\n errorPolicy: 'all',\n context: { cacheKey: `messages-list:${id}` },\n });\n const posts = ((result.data as Loose)?.messages?.data ?? []) as unknown[];\n const withLast = channel.lastMessage ? [...posts, channel.lastMessage] : posts;\n return projectFromPosts(channel, withLast, cdnOrigin);\n } catch (err) {\n console.warn('[WebBuilderNative] could not read builds of', id, err);\n return null;\n }\n },\n [client, cdnOrigin],\n );\n\n const load = useCallback(\n async (mode: 'reset' | 'more') => {\n if (!orgName || busyRef.current) return;\n busyRef.current = true;\n const run = runRef.current + 1;\n runRef.current = run;\n if (mode === 'reset') {\n skipRef.current = 0;\n setLoading(true);\n } else {\n setLoadingMore(true);\n }\n setError(null);\n const found: WebBuilderProject[] = [];\n let more = false;\n try {\n for (let page = 0; page < MAX_PAGES_PER_LOAD; page += 1) {\n // eslint-disable-next-line no-await-in-loop -- pages are sequential by design\n const result = await client.query({\n query: GetChannelsByUserWithLastMessageDocument,\n variables: getHistoryChannelsQueryVariables(orgName, {\n limit: GALLERY_PAGE_SIZE,\n skip: skipRef.current,\n }),\n // cache-first served a stale first page on the web; always ask.\n fetchPolicy: 'network-only',\n context: { cacheKey: 'webbuilder-gallery' },\n });\n const channels = (((result.data as Loose)?.channelsByUser ?? []) as ChannelLike[]).filter(\n (c) => c?.id,\n );\n skipRef.current += channels.length;\n more = channels.length >= GALLERY_PAGE_SIZE;\n // eslint-disable-next-line no-await-in-loop\n const rows = await Promise.all(channels.map((channel) => projectFor(channel)));\n found.push(...rows.filter((row): row is WebBuilderProject => row !== null));\n if (!more || found.length >= MIN_FILL) break;\n }\n if (runRef.current !== run) return;\n setProjects((prev) => {\n const base = mode === 'reset' ? [] : prev;\n const byId = new Map(base.map((p) => [p.channelId, p]));\n for (const row of found) byId.set(row.channelId, row);\n return [...byId.values()].sort((a, b) => b.updatedAt - a.updatedAt);\n });\n setHasMore(more);\n } catch (err) {\n console.warn('[WebBuilderNative] could not load builds:', err);\n if (runRef.current === run) setError('Check your connection and try again.');\n } finally {\n busyRef.current = false;\n if (runRef.current === run) {\n setLoading(false);\n setLoadingMore(false);\n }\n }\n },\n [client, orgName, projectFor],\n );\n\n useEffect(() => {\n void load('reset');\n }, [load]);\n\n const refresh = useCallback(() => {\n busyRef.current = false;\n void load('reset');\n }, [load]);\n const loadMore = useCallback(() => {\n if (hasMore) void load('more');\n }, [hasMore, load]);\n\n const upsert = useCallback((project: WebBuilderProject) => {\n setProjects((prev) => {\n const existing = prev.find((p) => p.channelId === project.channelId);\n if (\n existing &&\n existing.title === project.title &&\n existing.coverUrl === project.coverUrl &&\n existing.liveUrl === project.liveUrl &&\n existing.updatedAt === project.updatedAt\n ) {\n return prev;\n }\n const rest = prev.filter((p) => p.channelId !== project.channelId);\n return [project, ...rest].sort((a, b) => b.updatedAt - a.updatedAt);\n });\n }, []);\n\n return { projects, loading, loadingMore, hasMore, error, refresh, loadMore, upsert };\n}\n"],"names":[],"mappings":"+YAiBO,MAAM,iBAAoB,GAAA;AAEjC,MAAM,QAAW,GAAA,CAAA;AACjB,MAAM,kBAAqB,GAAA,CAAA;AAcX,SAAA,oBAAA,CAAqB,SAAiB,SAAsC,EAAA;AAC1F,EAAA,MAAM,SAAS,eAAgB,EAAA;AAC/B,EAAA,MAAM,CAAC,QAAU,EAAA,WAAW,CAAI,GAAA,QAAA,CAA8B,EAAE,CAAA;AAChE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,KAAK,CAAA;AACpD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAwB,IAAI,CAAA;AACtD,EAAM,MAAA,OAAA,GAAU,OAAO,CAAC,CAAA;AACxB,EAAM,MAAA,OAAA,GAAU,OAAO,KAAK,CAAA;AAC5B,EAAM,MAAA,MAAA,GAAS,OAAO,CAAC,CAAA;AACvB,EAAM,MAAA,UAAA,GAAa,WAAY,CAAA,OAAO,OAA4D,KAAA;AA5CpG,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AA6CI,IAAM,MAAA,EAAA,GAAA,CAAK,EAAQ,GAAA,OAAA,CAAA,EAAA,KAAR,IAAc,GAAA,EAAA,GAAA,EAAA;AACzB,IAAI,IAAA,CAAC,IAAW,OAAA,IAAA;AAEhB,IAAI,IAAA,mBAAA,CAAoB,OAAO,CAAG,EAAA;AAChC,MAAA,MAAM,WAAW,gBAAiB,CAAA,OAAA,EAAS,CAAC,OAAQ,CAAA,WAAW,GAAG,SAAS,CAAA;AAC3E,MAAA,IAAI,UAAiB,OAAA,QAAA;AAAA;AAEvB,IAAI,IAAA;AACF,MAAM,MAAA,MAAA,GAAS,MAAM,MAAA,CAAO,KAAM,CAAA;AAAA,QAChC,KAAO,EAAA,gBAAA;AAAA,QACP,SAAA,EAAW,gCAAgC,EAAE,CAAA;AAAA,QAC7C,WAAa,EAAA,cAAA;AAAA,QACb,WAAa,EAAA,KAAA;AAAA,QACb,OAAS,EAAA;AAAA,UACP,QAAA,EAAU,iBAAiB,EAAE,CAAA;AAAA;AAC/B,OACD,CAAA;AACD,MAAM,MAAA,KAAA,GAAA,CAAU,wBAAO,IAAP,KAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAuB,aAAvB,IAAiC,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,IAAA,KAAjC,YAAyC,EAAC;AAC1D,MAAM,MAAA,QAAA,GAAW,QAAQ,WAAc,GAAA,CAAC,GAAG,KAAO,EAAA,OAAA,CAAQ,WAAW,CAAI,GAAA,KAAA;AACzE,MAAO,OAAA,gBAAA,CAAiB,OAAS,EAAA,QAAA,EAAU,SAAS,CAAA;AAAA,aAC7C,GAAK,EAAA;AACZ,MAAQ,OAAA,CAAA,IAAA,CAAK,6CAA+C,EAAA,EAAA,EAAI,GAAG,CAAA;AACnE,MAAO,OAAA,IAAA;AAAA;AACT,GACC,EAAA,CAAC,MAAQ,EAAA,SAAS,CAAC,CAAA;AACtB,EAAM,MAAA,IAAA,GAAO,WAAY,CAAA,OAAO,IAA2B,KAAA;AAtE7D,IAAA,IAAA,EAAA,EAAA,EAAA;AAuEI,IAAI,IAAA,CAAC,OAAW,IAAA,OAAA,CAAQ,OAAS,EAAA;AACjC,IAAA,OAAA,CAAQ,OAAU,GAAA,IAAA;AAClB,IAAM,MAAA,GAAA,GAAM,OAAO,OAAU,GAAA,CAAA;AAC7B,IAAA,MAAA,CAAO,OAAU,GAAA,GAAA;AACjB,IAAA,IAAI,SAAS,OAAS,EAAA;AACpB,MAAA,OAAA,CAAQ,OAAU,GAAA,CAAA;AAClB,MAAA,UAAA,CAAW,IAAI,CAAA;AAAA,KACV,MAAA;AACL,MAAA,cAAA,CAAe,IAAI,CAAA;AAAA;AAErB,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,MAAM,QAA6B,EAAC;AACpC,IAAA,IAAI,IAAO,GAAA,KAAA;AACX,IAAI,IAAA;AACF,MAAA,KAAA,IAAS,IAAO,GAAA,CAAA,EAAG,IAAO,GAAA,kBAAA,EAAoB,QAAQ,CAAG,EAAA;AAEvD,QAAM,MAAA,MAAA,GAAS,MAAM,MAAA,CAAO,KAAM,CAAA;AAAA,UAChC,KAAO,EAAA,wCAAA;AAAA,UACP,SAAA,EAAW,iCAAiC,OAAS,EAAA;AAAA,YACnD,KAAO,EAAA,iBAAA;AAAA,YACP,MAAM,OAAQ,CAAA;AAAA,WACf,CAAA;AAAA;AAAA,UAED,WAAa,EAAA,cAAA;AAAA,UACb,OAAS,EAAA;AAAA,YACP,QAAU,EAAA;AAAA;AACZ,SACD,CAAA;AACD,QAAM,MAAA,QAAA,GAAA,CAAA,CAAc,EAAO,GAAA,CAAA,EAAA,GAAA,MAAA,CAAA,IAAA,KAAP,IAAuB,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,cAAA,KAAvB,IAAyC,GAAA,EAAA,GAAA,EAAsB,EAAA,MAAA,CAAO,CAAK,CAAA,KAAA,CAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,CAAA,CAAG,EAAE,CAAA;AACpG,QAAA,OAAA,CAAQ,WAAW,QAAS,CAAA,MAAA;AAC5B,QAAA,IAAA,GAAO,SAAS,MAAU,IAAA,iBAAA;AAE1B,QAAM,MAAA,IAAA,GAAO,MAAM,OAAA,CAAQ,GAAI,CAAA,QAAA,CAAS,IAAI,CAAW,OAAA,KAAA,UAAA,CAAW,OAAO,CAAC,CAAC,CAAA;AAC3E,QAAM,KAAA,CAAA,IAAA,CAAK,GAAG,IAAK,CAAA,MAAA,CAAO,CAAC,GAAkC,KAAA,GAAA,KAAQ,IAAI,CAAC,CAAA;AAC1E,QAAA,IAAI,CAAC,IAAA,IAAQ,KAAM,CAAA,MAAA,IAAU,QAAU,EAAA;AAAA;AAEzC,MAAI,IAAA,MAAA,CAAO,YAAY,GAAK,EAAA;AAC5B,MAAA,WAAA,CAAY,CAAQ,IAAA,KAAA;AAClB,QAAA,MAAM,IAAO,GAAA,IAAA,KAAS,OAAU,GAAA,EAAK,GAAA,IAAA;AACrC,QAAM,MAAA,IAAA,GAAO,IAAI,GAAA,CAAI,IAAK,CAAA,GAAA,CAAI,CAAK,CAAA,KAAA,CAAC,CAAE,CAAA,SAAA,EAAW,CAAC,CAAC,CAAC,CAAA;AACpD,QAAA,KAAA,MAAW,OAAO,KAAO,EAAA,IAAA,CAAK,GAAI,CAAA,GAAA,CAAI,WAAW,GAAG,CAAA;AACpD,QAAA,OAAO,CAAC,GAAG,IAAK,CAAA,MAAA,EAAQ,CAAA,CAAE,IAAK,CAAA,CAAC,CAAG,EAAA,CAAA,KAAM,CAAE,CAAA,SAAA,GAAY,EAAE,SAAS,CAAA;AAAA,OACnE,CAAA;AACD,MAAA,UAAA,CAAW,IAAI,CAAA;AAAA,aACR,GAAK,EAAA;AACZ,MAAQ,OAAA,CAAA,IAAA,CAAK,6CAA6C,GAAG,CAAA;AAC7D,MAAA,IAAI,MAAO,CAAA,OAAA,KAAY,GAAK,EAAA,QAAA,CAAS,sCAAsC,CAAA;AAAA,KAC3E,SAAA;AACA,MAAA,OAAA,CAAQ,OAAU,GAAA,KAAA;AAClB,MAAI,IAAA,MAAA,CAAO,YAAY,GAAK,EAAA;AAC1B,QAAA,UAAA,CAAW,KAAK,CAAA;AAChB,QAAA,cAAA,CAAe,KAAK,CAAA;AAAA;AACtB;AACF,GACC,EAAA,CAAC,MAAQ,EAAA,OAAA,EAAS,UAAU,CAAC,CAAA;AAChC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,KAAK,KAAK,OAAO,CAAA;AAAA,GACnB,EAAG,CAAC,IAAI,CAAC,CAAA;AACT,EAAM,MAAA,OAAA,GAAU,YAAY,MAAM;AAChC,IAAA,OAAA,CAAQ,OAAU,GAAA,KAAA;AAClB,IAAA,KAAK,KAAK,OAAO,CAAA;AAAA,GACnB,EAAG,CAAC,IAAI,CAAC,CAAA;AACT,EAAM,MAAA,QAAA,GAAW,YAAY,MAAM;AACjC,IAAI,IAAA,OAAA,EAAc,KAAA,IAAA,CAAK,MAAM,CAAA;AAAA,GAC5B,EAAA,CAAC,OAAS,EAAA,IAAI,CAAC,CAAA;AAClB,EAAM,MAAA,MAAA,GAAS,WAAY,CAAA,CAAC,OAA+B,KAAA;AACzD,IAAA,WAAA,CAAY,CAAQ,IAAA,KAAA;AAClB,MAAA,MAAM,WAAW,IAAK,CAAA,IAAA,CAAK,OAAK,CAAE,CAAA,SAAA,KAAc,QAAQ,SAAS,CAAA;AACjE,MAAA,IAAI,YAAY,QAAS,CAAA,KAAA,KAAU,OAAQ,CAAA,KAAA,IAAS,SAAS,QAAa,KAAA,OAAA,CAAQ,QAAY,IAAA,QAAA,CAAS,YAAY,OAAQ,CAAA,OAAA,IAAW,QAAS,CAAA,SAAA,KAAc,QAAQ,SAAW,EAAA;AAC9K,QAAO,OAAA,IAAA;AAAA;AAET,MAAA,MAAM,OAAO,IAAK,CAAA,MAAA,CAAO,OAAK,CAAE,CAAA,SAAA,KAAc,QAAQ,SAAS,CAAA;AAC/D,MAAA,OAAO,CAAC,OAAA,EAAS,GAAG,IAAI,CAAE,CAAA,IAAA,CAAK,CAAC,CAAA,EAAG,CAAM,KAAA,CAAA,CAAE,SAAY,GAAA,CAAA,CAAE,SAAS,CAAA;AAAA,KACnE,CAAA;AAAA,GACH,EAAG,EAAE,CAAA;AACL,EAAO,OAAA;AAAA,IACL,QAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACF;AACF"}
|
|
1
|
+
{"version":3,"file":"useWebBuilderGallery.js","sources":["../../../src/screens/WebBuilderNative/useWebBuilderGallery.ts"],"sourcesContent":["/**\n * Projects - the web surface's gallery hydrate, on the host, made fast.\n *\n * Same rule as the web (`realServices` gallery load): page the person's AI\n * assistant chats 10 at a time, newest first, and keep a chat only if its posts\n * hold a build. What makes it quick on a phone:\n *\n * - The last list paints at once: kept in memory across visits and on disk for a\n * cold start (per workspace).\n * - Reads go through the Apollo cache first. The app's type policies\n * (`graphql/typePolicies.ts`) keep `channelsByUser` as one merged list per\n * workspace and `messages` per channel, so a cached page is complete and\n * paints without the network.\n * - The refresh re-reads a chat's thread only when the chat changed\n * (`updatedAt`); an unchanged chat keeps the project it already gave. A typical\n * refresh is one list request, not one per chat.\n * - Thread reads run a few at a time and each project appears as soon as it is\n * known, newest first.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useApolloClient, type ApolloClient, type FetchPolicy } from '@apollo/client';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\nimport { GetChannelsByUserWithLastMessageDocument, MessagesDocument } from 'common/graphql';\nimport { getHistoryChannelsQueryVariables, getThreadMessagesQueryVariables } from '../../hooks/useChatApi';\nimport { lastMessageHasCover, projectFromPosts, type ChannelLike } from '../../features/webbuilder/buildFragments';\nimport type { WebBuilderProject } from '../../features/webbuilder/types';\n\n/** The web gallery's page size (`GALLERY_PAGE_SIZE`). */\nexport const GALLERY_PAGE_SIZE = 10;\n/** Keep paging (up to this many pages per load) while pages yield too few projects to fill the screen. */\nconst MIN_FILL = 8;\nconst MAX_PAGES_PER_LOAD = 4;\n/** Thread reads in flight at once: enough to be quick, few enough to keep the app responsive. */\nconst READ_CONCURRENCY = 4;\nconst SNAPSHOT_KEY = 'yantra.webbuilder.projects.v1';\nconst SNAPSHOT_MAX = 40;\n\ntype Loose = Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n/** What a chat last gave: its project (or none) at the `updatedAt` it was read for. */\ninterface Known {\n stamp: string;\n project: WebBuilderProject | null;\n}\n\n/** Per-workspace memory: survives leaving and reopening Web Builder in one app run. */\nconst memory = new Map<string, { projects: WebBuilderProject[]; known: Map<string, Known> }>();\n\nexport interface WebBuilderGallery {\n projects: WebBuilderProject[];\n loading: boolean;\n loadingMore: boolean;\n hasMore: boolean;\n error: string | null;\n refresh: () => void;\n loadMore: () => void;\n /** Put (or update) one site - the open one, as its posts change. */\n upsert: (project: WebBuilderProject) => void;\n}\n\nfunction sortProjects(list: Iterable<WebBuilderProject>): WebBuilderProject[] {\n return [...list].sort((a, b) => b.updatedAt - a.updatedAt);\n}\n\nfunction stampOf(channel: ChannelLike): string {\n return String(channel.updatedAt ?? channel.createdAt ?? '');\n}\n\nasync function readSnapshot(org: string): Promise<WebBuilderProject[]> {\n try {\n const raw = await AsyncStorage.getItem(`${SNAPSHOT_KEY}:${org}`);\n const list = raw ? (JSON.parse(raw) as WebBuilderProject[]) : [];\n return Array.isArray(list) ? list.filter((p) => p?.channelId && p.title) : [];\n } catch {\n return [];\n }\n}\n\nfunction writeSnapshot(org: string, projects: WebBuilderProject[]): void {\n AsyncStorage.setItem(`${SNAPSHOT_KEY}:${org}`, JSON.stringify(projects.slice(0, SNAPSHOT_MAX))).catch(\n () => undefined,\n );\n}\n\n/** Runs `work` over `items`, at most `limit` at a time. */\nasync function eachLimited<T>(items: T[], limit: number, work: (item: T) => Promise<void>): Promise<void> {\n let next = 0;\n const lane = async (): Promise<void> => {\n while (next < items.length) {\n const item = items[next];\n next += 1;\n // eslint-disable-next-line no-await-in-loop -- one lane works through its share in order\n await work(item);\n }\n };\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, lane));\n}\n\nasync function readChannels(\n client: ApolloClient<object>,\n orgName: string,\n skip: number,\n fetchPolicy: FetchPolicy,\n): Promise<ChannelLike[]> {\n const result = await client.query({\n query: GetChannelsByUserWithLastMessageDocument,\n variables: getHistoryChannelsQueryVariables(orgName, { limit: GALLERY_PAGE_SIZE, skip }),\n fetchPolicy,\n errorPolicy: 'all',\n // Same slot as chat history: the type policy merges both into one list.\n context: { cacheKey: 'chat-history-channels-list' },\n });\n return (((result.data as Loose)?.channelsByUser ?? []) as ChannelLike[]).filter((c) => c?.id);\n}\n\nexport function useWebBuilderGallery(orgName: string, cdnOrigin: string): WebBuilderGallery {\n const client = useApolloClient();\n const remembered = orgName ? memory.get(orgName) : undefined;\n const [projects, setProjects] = useState<WebBuilderProject[]>(() => remembered?.projects ?? []);\n const [loading, setLoading] = useState(false);\n const [loadingMore, setLoadingMore] = useState(false);\n const [hasMore, setHasMore] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const skipRef = useRef(0);\n const busyRef = useRef(false);\n const runRef = useRef(0);\n const knownRef = useRef<Map<string, Known>>(remembered?.known ?? new Map());\n\n // Keep memory and the disk snapshot in step with what is shown.\n useEffect(() => {\n if (!orgName) return;\n memory.set(orgName, { projects, known: knownRef.current });\n if (projects.length) writeSnapshot(orgName, projects);\n }, [orgName, projects]);\n\n // A cold start paints the last list from disk while the cache and network catch up.\n useEffect(() => {\n if (!orgName || memory.get(orgName)?.projects.length) return undefined;\n let live = true;\n readSnapshot(orgName)\n .then((saved) => {\n if (live && saved.length) setProjects((prev) => (prev.length ? prev : saved));\n })\n .catch(() => undefined);\n return () => {\n live = false;\n };\n }, [orgName]);\n\n const projectFor = useCallback(\n async (channel: ChannelLike, fetchPolicy: FetchPolicy): Promise<WebBuilderProject | null> => {\n const id = channel.id ?? '';\n if (!id) return null;\n // The web skips the thread read when the last post already has the screenshot.\n if (lastMessageHasCover(channel)) {\n const fromLast = projectFromPosts(channel, [channel.lastMessage], cdnOrigin);\n if (fromLast) return fromLast;\n }\n const result = await client.query({\n query: MessagesDocument,\n variables: getThreadMessagesQueryVariables(id),\n fetchPolicy,\n errorPolicy: 'all',\n context: { cacheKey: `messages-list:${id}` },\n });\n const posts = ((result.data as Loose)?.messages?.data ?? []) as unknown[];\n const withLast = channel.lastMessage ? [...posts, channel.lastMessage] : posts;\n return projectFromPosts(channel, withLast, cdnOrigin);\n },\n [client, cdnOrigin],\n );\n\n const load = useCallback(\n async (mode: 'reset' | 'more') => {\n if (!orgName || busyRef.current) return;\n busyRef.current = true;\n const run = runRef.current + 1;\n runRef.current = run;\n const stale = () => runRef.current !== run;\n const known = knownRef.current;\n const seen = new Set<string>();\n const put = (project: WebBuilderProject | null, channelId: string) => {\n if (stale()) return;\n setProjects((prev) => {\n const byId = new Map(prev.map((p) => [p.channelId, p]));\n if (project) byId.set(channelId, project);\n else byId.delete(channelId);\n return sortProjects(byId.values());\n });\n };\n\n if (mode === 'reset') {\n skipRef.current = 0;\n setLoading(projects.length === 0);\n } else {\n setLoadingMore(true);\n }\n setError(null);\n\n try {\n // 1. From the cache: instant when the type policies already hold the list.\n if (mode === 'reset') {\n const cached = await readChannels(client, orgName, 0, 'cache-only').catch(() => []);\n await eachLimited(cached, READ_CONCURRENCY, async (channel) => {\n const id = channel.id ?? '';\n if (known.has(id)) return;\n const project = await projectFor(channel, 'cache-only').catch(() => null);\n if (project) {\n known.set(id, { stamp: stampOf(channel), project });\n put(project, id);\n }\n });\n if (!stale() && projects.length === 0 && known.size) setLoading(false);\n }\n\n // 2. From the network: the list, then only the threads that changed.\n let more = false;\n const tally = { found: 0 };\n for (let page = 0; page < MAX_PAGES_PER_LOAD; page += 1) {\n // eslint-disable-next-line no-await-in-loop -- pages are sequential by design\n const channels = await readChannels(client, orgName, skipRef.current, 'network-only');\n if (stale()) return;\n skipRef.current += channels.length;\n more = channels.length >= GALLERY_PAGE_SIZE;\n // eslint-disable-next-line no-await-in-loop\n await eachLimited(channels, READ_CONCURRENCY, async (channel) => {\n const id = channel.id ?? '';\n seen.add(id);\n const stamp = stampOf(channel);\n const prior = known.get(id);\n if (prior && prior.stamp === stamp) {\n if (prior.project) tally.found += 1;\n return;\n }\n const project = await projectFor(channel, 'network-only').catch(() => prior?.project ?? null);\n known.set(id, { stamp, project });\n if (project) tally.found += 1;\n put(project, id);\n });\n if (!more || tally.found >= MIN_FILL) break;\n }\n if (stale()) return;\n if (mode === 'reset') {\n // A chat no longer in the first pages (deleted, moved) drops off the list.\n setProjects((prev) =>\n prev.filter((p) => seen.has(p.channelId) || p.updatedAt > Date.now() - 60_000),\n );\n }\n setHasMore(more);\n } catch (err) {\n console.warn('[WebBuilderNative] could not load projects:', err);\n if (!stale()) setError('Check your connection and try again.');\n } finally {\n busyRef.current = false;\n if (!stale()) {\n setLoading(false);\n setLoadingMore(false);\n }\n }\n },\n // `projects.length` only steers the first spinner; reading it stale is harmless.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [client, orgName, projectFor],\n );\n\n useEffect(() => {\n load('reset').catch(() => undefined);\n }, [load]);\n\n const refresh = useCallback(() => {\n busyRef.current = false;\n load('reset').catch(() => undefined);\n }, [load]);\n const loadMore = useCallback(() => {\n if (hasMore) load('more').catch(() => undefined);\n }, [hasMore, load]);\n\n const upsert = useCallback((project: WebBuilderProject) => {\n setProjects((prev) => {\n const existing = prev.find((p) => p.channelId === project.channelId);\n if (\n existing &&\n existing.title === project.title &&\n existing.coverUrl === project.coverUrl &&\n existing.liveUrl === project.liveUrl &&\n existing.updatedAt === project.updatedAt\n ) {\n return prev;\n }\n return sortProjects([project, ...prev.filter((p) => p.channelId !== project.channelId)]);\n });\n }, []);\n\n return { projects, loading, loadingMore, hasMore, error, refresh, loadMore, upsert };\n}\n"],"names":["_a"],"mappings":"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"}
|