@teamvortexsoftware/vortex-react-native 0.0.13 → 1.0.1
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/LICENSE +201 -0
- package/README.md +1227 -11
- package/dist/InviteFormCore-D4HkMMo0.d.mts +721 -0
- package/dist/InviteFormCore-D9oUCbu7.d.ts +721 -0
- package/dist/VortexClient.js +192 -0
- package/dist/VortexClient.js.map +1 -0
- package/dist/VortexDeferredLinks.js +127 -0
- package/dist/VortexDeferredLinks.js.map +1 -0
- package/dist/clientInfo.js +45 -0
- package/dist/clientInfo.js.map +1 -0
- package/dist/components/ContactsPickerModal.js +182 -0
- package/dist/components/ContactsPickerModal.js.map +1 -0
- package/dist/components/InviteFormCore.js +2141 -0
- package/dist/components/InviteFormCore.js.map +1 -0
- package/dist/components/InviteFormMobile.js +463 -0
- package/dist/components/InviteFormMobile.js.map +1 -0
- package/dist/components/InviteFormWeb.js +295 -0
- package/dist/components/InviteFormWeb.js.map +1 -0
- package/dist/components/PlacedItemToolbar.js +147 -0
- package/dist/components/PlacedItemToolbar.js.map +1 -0
- package/dist/components/ShareButtons.js +1 -0
- package/dist/components/ShareButtons.js.map +1 -0
- package/dist/components/VrtxContactsImport.js +234 -0
- package/dist/components/VrtxContactsImport.js.map +1 -0
- package/dist/components/VrtxEmailInvitations.js +341 -0
- package/dist/components/VrtxEmailInvitations.js.map +1 -0
- package/dist/components/VrtxFindFriends.js +400 -0
- package/dist/components/VrtxFindFriends.js.map +1 -0
- package/dist/components/VrtxHeading.js +58 -0
- package/dist/components/VrtxHeading.js.map +1 -0
- package/dist/components/VrtxIncomingInvitations.js +657 -0
- package/dist/components/VrtxIncomingInvitations.js.map +1 -0
- package/dist/components/VrtxInvitationSuggestions.js +506 -0
- package/dist/components/VrtxInvitationSuggestions.js.map +1 -0
- package/dist/components/VrtxInviteContacts.js +512 -0
- package/dist/components/VrtxInviteContacts.js.map +1 -0
- package/dist/components/VrtxOutgoingInvitations.js +572 -0
- package/dist/components/VrtxOutgoingInvitations.js.map +1 -0
- package/dist/components/VrtxSearchBox.js +487 -0
- package/dist/components/VrtxSearchBox.js.map +1 -0
- package/dist/components/VrtxSelect.js +27 -0
- package/dist/components/VrtxSelect.js.map +1 -0
- package/dist/components/VrtxShareOptions.js +435 -0
- package/dist/components/VrtxShareOptions.js.map +1 -0
- package/dist/components/VrtxSubmit.js +132 -0
- package/dist/components/VrtxSubmit.js.map +1 -0
- package/dist/components/VrtxText.js +146 -0
- package/dist/components/VrtxText.js.map +1 -0
- package/dist/constants/mockData.d.mts +7 -0
- package/dist/constants/mockData.d.ts +7 -0
- package/dist/constants/mockData.js +48 -0
- package/dist/constants/mockData.js.map +1 -0
- package/dist/constants/mockData.mjs +22 -0
- package/dist/constants/mockData.mjs.map +1 -0
- package/dist/context/VortexModulesContext.js +135 -0
- package/dist/context/VortexModulesContext.js.map +1 -0
- package/dist/hooks/useInvitationFormLogic.d.mts +2 -0
- package/dist/hooks/useInvitationFormLogic.d.ts +2 -0
- package/dist/hooks/useInvitationFormLogic.js +300 -0
- package/dist/hooks/useInvitationFormLogic.js.map +1 -0
- package/dist/hooks/useInvitationFormLogic.mjs +276 -0
- package/dist/hooks/useInvitationFormLogic.mjs.map +1 -0
- package/dist/hooks/usePrefetchWidgetConfiguration.js +117 -0
- package/dist/hooks/usePrefetchWidgetConfiguration.js.map +1 -0
- package/dist/hooks/useThemeStyles.js +2 -0
- package/dist/hooks/useThemeStyles.js.map +1 -0
- package/dist/hooks/useVortexInvite.js +467 -56
- package/dist/hooks/useVortexInvite.js.map +1 -0
- package/dist/index-web.d.mts +93 -0
- package/dist/index-web.d.ts +93 -0
- package/dist/index-web.js +7397 -0
- package/dist/index-web.js.map +1 -0
- package/dist/index-web.mjs +7445 -0
- package/dist/index-web.mjs.map +1 -0
- package/dist/index.d.mts +656 -0
- package/dist/index.d.ts +656 -0
- package/dist/index.js +10205 -4
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +10244 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types/VortexClient.d.ts +106 -0
- package/dist/types/VortexClient.d.ts.map +1 -0
- package/dist/types/VortexDeferredLinks.d.ts +73 -0
- package/dist/types/VortexDeferredLinks.d.ts.map +1 -0
- package/dist/types/clientInfo.d.ts +5 -0
- package/dist/types/clientInfo.d.ts.map +1 -0
- package/dist/types/components/ContactsPickerModal.d.ts +18 -0
- package/dist/types/components/ContactsPickerModal.d.ts.map +1 -0
- package/dist/types/components/InviteFormCore.d.ts +166 -0
- package/dist/types/components/InviteFormCore.d.ts.map +1 -0
- package/dist/types/components/InviteFormMobile.d.ts +42 -0
- package/dist/types/components/InviteFormMobile.d.ts.map +1 -0
- package/dist/types/components/InviteFormWeb.d.ts +87 -0
- package/dist/types/components/InviteFormWeb.d.ts.map +1 -0
- package/dist/types/components/PlacedItemToolbar.d.ts +16 -0
- package/dist/types/components/PlacedItemToolbar.d.ts.map +1 -0
- package/dist/types/components/VrtxContactsImport.d.ts +14 -0
- package/dist/types/components/VrtxContactsImport.d.ts.map +1 -0
- package/dist/types/components/VrtxEmailInvitations.d.ts +31 -0
- package/dist/types/components/VrtxEmailInvitations.d.ts.map +1 -0
- package/dist/types/components/VrtxFindFriends.d.ts +25 -0
- package/dist/types/components/VrtxFindFriends.d.ts.map +1 -0
- package/dist/types/components/VrtxHeading.d.ts +6 -0
- package/dist/types/components/VrtxHeading.d.ts.map +1 -0
- package/dist/types/components/VrtxIncomingInvitations.d.ts +27 -0
- package/dist/types/components/VrtxIncomingInvitations.d.ts.map +1 -0
- package/dist/types/components/VrtxInvitationSuggestions.d.ts +25 -0
- package/dist/types/components/VrtxInvitationSuggestions.d.ts.map +1 -0
- package/dist/types/components/VrtxInviteContacts.d.ts +24 -0
- package/dist/types/components/VrtxInviteContacts.d.ts.map +1 -0
- package/dist/types/components/VrtxOutgoingInvitations.d.ts +27 -0
- package/dist/types/components/VrtxOutgoingInvitations.d.ts.map +1 -0
- package/dist/types/components/VrtxSearchBox.d.ts +28 -0
- package/dist/types/components/VrtxSearchBox.d.ts.map +1 -0
- package/dist/types/components/VrtxSelect.d.ts +6 -0
- package/dist/types/components/VrtxSelect.d.ts.map +1 -0
- package/dist/types/components/VrtxShareOptions.d.ts +41 -0
- package/dist/types/components/VrtxShareOptions.d.ts.map +1 -0
- package/dist/types/components/VrtxSubmit.d.ts +18 -0
- package/dist/types/components/VrtxSubmit.d.ts.map +1 -0
- package/dist/types/components/VrtxText.d.ts +8 -0
- package/dist/types/components/VrtxText.d.ts.map +1 -0
- package/dist/types/constants/mockData.d.ts +4 -0
- package/dist/types/constants/mockData.d.ts.map +1 -0
- package/dist/types/context/VortexModulesContext.d.ts +238 -0
- package/dist/types/context/VortexModulesContext.d.ts.map +1 -0
- package/dist/types/findFriends.js +10 -0
- package/dist/types/findFriends.js.map +1 -0
- package/dist/types/hooks/useInvitationFormLogic.d.ts +55 -0
- package/dist/types/hooks/useInvitationFormLogic.d.ts.map +1 -0
- package/dist/types/hooks/usePrefetchWidgetConfiguration.d.ts +39 -0
- package/dist/types/hooks/usePrefetchWidgetConfiguration.d.ts.map +1 -0
- package/dist/types/hooks/useThemeStyles.d.ts +1 -0
- package/dist/types/hooks/useThemeStyles.d.ts.map +1 -1
- package/dist/types/hooks/useVortexInvite.d.ts +48 -6
- package/dist/types/hooks/useVortexInvite.d.ts.map +1 -1
- package/dist/types/index-web.d.ts +23 -0
- package/dist/types/index-web.d.ts.map +1 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/invitations.js +13 -0
- package/dist/types/invitations.js.map +1 -0
- package/dist/types/inviteContacts.js +14 -0
- package/dist/types/inviteContacts.js.map +1 -0
- package/dist/{shared/InvitationResult.js → types/platformOperations.js} +1 -0
- package/dist/types/platformOperations.js.map +1 -0
- package/dist/types/searchBox.js +11 -0
- package/dist/types/searchBox.js.map +1 -0
- package/dist/types/types/findFriends.d.ts +101 -0
- package/dist/types/types/findFriends.d.ts.map +1 -0
- package/dist/types/types/invitations.d.ts +301 -0
- package/dist/types/types/invitations.d.ts.map +1 -0
- package/dist/types/types/inviteContacts.d.ts +86 -0
- package/dist/types/types/inviteContacts.d.ts.map +1 -0
- package/dist/types/types/platformOperations.d.ts +185 -0
- package/dist/types/types/platformOperations.d.ts.map +1 -0
- package/dist/types/types/searchBox.d.ts +69 -0
- package/dist/types/types/searchBox.d.ts.map +1 -0
- package/dist/types/types/unfurlConfig.d.ts +34 -0
- package/dist/types/types/unfurlConfig.d.ts.map +1 -0
- package/dist/types/unfurlConfig.js +21 -0
- package/dist/types/unfurlConfig.js.map +1 -0
- package/dist/types/utils/analytics.d.ts +54 -0
- package/dist/types/utils/analytics.d.ts.map +1 -0
- package/dist/types/utils/configCache.d.ts +34 -0
- package/dist/types/utils/configCache.d.ts.map +1 -0
- package/dist/types/utils/contactUtils.d.ts +9 -0
- package/dist/types/utils/contactUtils.d.ts.map +1 -0
- package/dist/types/utils/featureWarnings.d.ts +56 -0
- package/dist/types/utils/featureWarnings.d.ts.map +1 -0
- package/dist/types/utils/formUtils.d.ts +11 -3
- package/dist/types/utils/formUtils.d.ts.map +1 -1
- package/dist/types/utils/gradientUtils.d.ts +67 -0
- package/dist/types/utils/gradientUtils.d.ts.map +1 -0
- package/dist/types/utils/invitationEvents.d.ts +21 -0
- package/dist/types/utils/invitationEvents.d.ts.map +1 -0
- package/dist/types/utils/moduleLoaders.d.ts +115 -0
- package/dist/types/utils/moduleLoaders.d.ts.map +1 -0
- package/dist/types/utils/moduleLoaders.web.d.ts +73 -0
- package/dist/types/utils/moduleLoaders.web.d.ts.map +1 -0
- package/dist/types/utils/nameUtils.d.ts +15 -0
- package/dist/types/utils/nameUtils.d.ts.map +1 -0
- package/dist/types/utils/themeUtils.d.ts +3 -1
- package/dist/types/utils/themeUtils.d.ts.map +1 -1
- package/dist/types/vortexInvite.d.ts +145 -5
- package/dist/types/vortexInvite.d.ts.map +1 -1
- package/dist/useInvitationFormLogic-Ct73M19B.d.mts +242 -0
- package/dist/useInvitationFormLogic-Ct73M19B.d.ts +242 -0
- package/dist/utils/analytics.js +92 -0
- package/dist/utils/analytics.js.map +1 -0
- package/dist/utils/configCache.js +68 -0
- package/dist/utils/configCache.js.map +1 -0
- package/dist/utils/contactUtils.d.mts +12 -0
- package/dist/utils/contactUtils.d.ts +12 -0
- package/dist/utils/contactUtils.js +37 -0
- package/dist/utils/contactUtils.js.map +1 -0
- package/dist/utils/contactUtils.mjs +12 -0
- package/dist/utils/contactUtils.mjs.map +1 -0
- package/dist/utils/featureWarnings.js +214 -0
- package/dist/utils/featureWarnings.js.map +1 -0
- package/dist/utils/formUtils.js +161 -51
- package/dist/utils/formUtils.js.map +1 -0
- package/dist/utils/gradientUtils.js +120 -0
- package/dist/utils/gradientUtils.js.map +1 -0
- package/dist/utils/invitationEvents.js +45 -0
- package/dist/utils/invitationEvents.js.map +1 -0
- package/dist/utils/moduleLoaders.js +275 -0
- package/dist/utils/moduleLoaders.js.map +1 -0
- package/dist/utils/moduleLoaders.web.js +72 -0
- package/dist/utils/moduleLoaders.web.js.map +1 -0
- package/dist/utils/nameUtils.js +51 -0
- package/dist/utils/nameUtils.js.map +1 -0
- package/dist/utils/themeUtils.js +117 -32
- package/dist/utils/themeUtils.js.map +1 -0
- package/dist/vortexInvite.js +78 -167
- package/dist/vortexInvite.js.map +1 -0
- package/package.json +69 -31
- package/dist/components/Clipboard.js +0 -64
- package/dist/shared/api.js +0 -90
- package/dist/tests/TestVortexInvite.js +0 -134
- package/dist/types/components/Clipboard.d.ts +0 -16
- package/dist/types/components/Clipboard.d.ts.map +0 -1
- package/dist/types/shared/InvitationResult.d.ts +0 -24
- package/dist/types/shared/InvitationResult.d.ts.map +0 -1
- package/dist/types/shared/api.d.ts +0 -14
- package/dist/types/shared/api.d.ts.map +0 -1
- package/dist/types/tests/TestVortexInvite.d.ts +0 -4
- package/dist/types/tests/TestVortexInvite.d.ts.map +0 -1
|
@@ -0,0 +1,2141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.InviteFormCore = InviteFormCore;
|
|
46
|
+
const react_1 = __importStar(require("react"));
|
|
47
|
+
const react_native_1 = require("react-native");
|
|
48
|
+
const useInvitationFormLogic_1 = require("../hooks/useInvitationFormLogic");
|
|
49
|
+
const contactUtils_1 = require("../utils/contactUtils");
|
|
50
|
+
const VrtxShareOptions_1 = require("./VrtxShareOptions");
|
|
51
|
+
const VrtxContactsImport_1 = require("./VrtxContactsImport");
|
|
52
|
+
const VrtxEmailInvitations_1 = require("./VrtxEmailInvitations");
|
|
53
|
+
const VrtxFindFriends_1 = require("./VrtxFindFriends");
|
|
54
|
+
const VrtxIncomingInvitations_1 = require("./VrtxIncomingInvitations");
|
|
55
|
+
const VrtxOutgoingInvitations_1 = require("./VrtxOutgoingInvitations");
|
|
56
|
+
const VrtxInvitationSuggestions_1 = require("./VrtxInvitationSuggestions");
|
|
57
|
+
const VrtxInviteContacts_1 = require("./VrtxInviteContacts");
|
|
58
|
+
const VrtxHeading_1 = require("./VrtxHeading");
|
|
59
|
+
const VrtxText_1 = require("./VrtxText");
|
|
60
|
+
const VrtxSelect_1 = require("./VrtxSelect");
|
|
61
|
+
const VrtxSubmit_1 = require("./VrtxSubmit");
|
|
62
|
+
const VrtxSearchBox_1 = require("./VrtxSearchBox");
|
|
63
|
+
const PlacedItemToolbar_1 = require("./PlacedItemToolbar");
|
|
64
|
+
const isWeb = react_native_1.Platform.OS === 'web';
|
|
65
|
+
// Parse CSS linear-gradient to extract first color for fallback
|
|
66
|
+
function parseGradientFirstColor(gradientString) {
|
|
67
|
+
if (!gradientString || !gradientString.includes('linear-gradient')) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const stopsRegex = /(rgba?\([^)]+\)|#[0-9a-fA-F]{3,8}|[a-z]+)\s+(\d+)%/i;
|
|
71
|
+
const match = stopsRegex.exec(gradientString);
|
|
72
|
+
if (match) {
|
|
73
|
+
return match[1].trim();
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const ButtonWrapper = ({ children, style, gradientString, onPress, disabled, wrapperStyle, }) => {
|
|
78
|
+
const styleArray = Array.isArray(style) ? style : [style];
|
|
79
|
+
// On web, use CSS gradients directly
|
|
80
|
+
if (gradientString && isWeb) {
|
|
81
|
+
return (<react_native_1.TouchableOpacity style={[wrapperStyle, ...styleArray, { background: gradientString }]} onPress={onPress} disabled={disabled} activeOpacity={0.7}>
|
|
82
|
+
{children}
|
|
83
|
+
</react_native_1.TouchableOpacity>);
|
|
84
|
+
}
|
|
85
|
+
// On native, use first color from gradient as solid background
|
|
86
|
+
const fallbackColor = gradientString ? parseGradientFirstColor(gradientString) : null;
|
|
87
|
+
const finalStyle = fallbackColor
|
|
88
|
+
? [wrapperStyle, ...styleArray, { backgroundColor: fallbackColor }]
|
|
89
|
+
: [wrapperStyle, ...styleArray];
|
|
90
|
+
return (<react_native_1.TouchableOpacity style={finalStyle} onPress={onPress} disabled={disabled} activeOpacity={0.7}>
|
|
91
|
+
{children}
|
|
92
|
+
</react_native_1.TouchableOpacity>);
|
|
93
|
+
};
|
|
94
|
+
function InviteFormCore({ renderIcon, renderQRCode, platformOperations, fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif', containerStyle = {}, widgetConfiguration, isEditMode = false, isDragging = false, onComponentSelect, onDrop, onSetDropTarget, onClearDropTarget, onComponentDelete, getShareableInviteLink,
|
|
95
|
+
// Analytics callbacks (optional - no-op for preview mode)
|
|
96
|
+
onShareLinkClick, onEmailFieldFocus, onEmailFieldBlur, onEmailValidation, onEmailInvitationsSubmitted, onEmailValidationError, onEmailSubmitError,
|
|
97
|
+
// Find Friends configuration
|
|
98
|
+
findFriendsConfig, createUserIdInvitation,
|
|
99
|
+
// Incoming/Outgoing Invitations configuration
|
|
100
|
+
incomingInvitationsConfig, outgoingInvitationsConfig,
|
|
101
|
+
// Invitation Suggestions configuration
|
|
102
|
+
invitationSuggestionsConfig,
|
|
103
|
+
// Invite Contacts configuration
|
|
104
|
+
inviteContactsConfig,
|
|
105
|
+
// Search Box configuration
|
|
106
|
+
searchBoxConfig,
|
|
107
|
+
// API configuration
|
|
108
|
+
apiUrl, jwt, }) {
|
|
109
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
110
|
+
// Get the form structure from widget configuration
|
|
111
|
+
const getFormStructure = () => {
|
|
112
|
+
var _a, _b, _c;
|
|
113
|
+
try {
|
|
114
|
+
const formData = (_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.form']) === null || _c === void 0 ? void 0 : _c.value;
|
|
115
|
+
if (formData && typeof formData === 'object' && formData.root) {
|
|
116
|
+
return formData.root;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
console.error('[InviteFormCore] Failed to parse form structure:', err);
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
};
|
|
124
|
+
const formStructure = getFormStructure();
|
|
125
|
+
if (!formStructure) {
|
|
126
|
+
console.error('[InviteFormCore] No form structure found in widget configuration. ' +
|
|
127
|
+
'The widget may not have a form configured, or the configuration format is unexpected. ' +
|
|
128
|
+
'Expected path: configuration.props["vortex.components.form"].value.root');
|
|
129
|
+
}
|
|
130
|
+
// Extract form title from widget configuration (mobile-specific key takes priority)
|
|
131
|
+
const resolvedTitle = ((_c = (_b = (_a = formStructure === null || formStructure === void 0 ? void 0 : formStructure.settings) === null || _a === void 0 ? void 0 : _a.customizations) === null || _b === void 0 ? void 0 : _b['mobile.formTitle']) === null || _c === void 0 ? void 0 : _c.textContent) ||
|
|
132
|
+
((_f = (_e = (_d = formStructure === null || formStructure === void 0 ? void 0 : formStructure.settings) === null || _d === void 0 ? void 0 : _d.customizations) === null || _e === void 0 ? void 0 : _e.formTitle) === null || _f === void 0 ? void 0 : _f.textContent) ||
|
|
133
|
+
null;
|
|
134
|
+
// Extract title theme styles from root theme options
|
|
135
|
+
const resolvedTitleStyle = (() => {
|
|
136
|
+
var _a;
|
|
137
|
+
const themeOptions = (_a = formStructure === null || formStructure === void 0 ? void 0 : formStructure.theme) === null || _a === void 0 ? void 0 : _a.options;
|
|
138
|
+
if (!Array.isArray(themeOptions))
|
|
139
|
+
return undefined;
|
|
140
|
+
const getVal = (key) => { var _a; return ((_a = themeOptions.find((o) => o.key === key)) === null || _a === void 0 ? void 0 : _a.value) || undefined; };
|
|
141
|
+
const color = getVal('--vrtx-form-title-color');
|
|
142
|
+
const fontSize = getVal('--vrtx-form-title-font-size');
|
|
143
|
+
const fontWeight = getVal('--vrtx-form-title-font-weight');
|
|
144
|
+
const fontFamily = getVal('--vrtx-form-title-font-family');
|
|
145
|
+
if (!color && !fontSize && !fontWeight && !fontFamily)
|
|
146
|
+
return undefined;
|
|
147
|
+
return Object.assign(Object.assign(Object.assign(Object.assign({}, (color && { color })), (fontSize && { fontSize: parseFloat(fontSize) || undefined })), (fontWeight && { fontWeight })), (fontFamily && { fontFamily }));
|
|
148
|
+
})();
|
|
149
|
+
// Helper function to find email invitations block in form structure
|
|
150
|
+
const findEmailInvitationsBlock = () => {
|
|
151
|
+
if (!formStructure)
|
|
152
|
+
return null;
|
|
153
|
+
const searchChildren = (children) => {
|
|
154
|
+
if (!Array.isArray(children))
|
|
155
|
+
return null;
|
|
156
|
+
for (const child of children) {
|
|
157
|
+
if (child.type === 'block' &&
|
|
158
|
+
child.subtype === 'vrtx-email-invitations' &&
|
|
159
|
+
child.hidden !== true) {
|
|
160
|
+
return child;
|
|
161
|
+
}
|
|
162
|
+
if (child.children) {
|
|
163
|
+
const found = searchChildren(child.children);
|
|
164
|
+
if (found)
|
|
165
|
+
return found;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
};
|
|
170
|
+
if (formStructure.children) {
|
|
171
|
+
return searchChildren(formStructure.children);
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
};
|
|
175
|
+
const emailInvitationsBlock = findEmailInvitationsBlock();
|
|
176
|
+
// Helper function to find submit button block in form structure
|
|
177
|
+
const findSubmitButtonBlock = () => {
|
|
178
|
+
if (!formStructure)
|
|
179
|
+
return null;
|
|
180
|
+
const searchChildren = (children) => {
|
|
181
|
+
if (!Array.isArray(children))
|
|
182
|
+
return null;
|
|
183
|
+
for (const child of children) {
|
|
184
|
+
if (child.type === 'block' && child.subtype === 'vrtx-submit') {
|
|
185
|
+
return child;
|
|
186
|
+
}
|
|
187
|
+
if (child.children) {
|
|
188
|
+
const found = searchChildren(child.children);
|
|
189
|
+
if (found)
|
|
190
|
+
return found;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
};
|
|
195
|
+
if (formStructure.children) {
|
|
196
|
+
return searchChildren(formStructure.children);
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
};
|
|
200
|
+
const submitButtonBlock = findSubmitButtonBlock();
|
|
201
|
+
// Helper function to find contacts-import block in form structure
|
|
202
|
+
const findContactsImportBlock = () => {
|
|
203
|
+
if (!formStructure)
|
|
204
|
+
return null;
|
|
205
|
+
const searchChildren = (children) => {
|
|
206
|
+
if (!Array.isArray(children))
|
|
207
|
+
return null;
|
|
208
|
+
for (const child of children) {
|
|
209
|
+
if (child.type === 'block' && child.subtype === 'vrtx-contacts-import') {
|
|
210
|
+
return child;
|
|
211
|
+
}
|
|
212
|
+
if (child.children) {
|
|
213
|
+
const found = searchChildren(child.children);
|
|
214
|
+
if (found)
|
|
215
|
+
return found;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
};
|
|
220
|
+
if (formStructure.children) {
|
|
221
|
+
return searchChildren(formStructure.children);
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
};
|
|
225
|
+
const contactsImportBlock = findContactsImportBlock();
|
|
226
|
+
// Helper to get customization text from contacts-import block
|
|
227
|
+
const getContactsCustomization = (key, defaultValue) => {
|
|
228
|
+
var _a, _b, _c;
|
|
229
|
+
const textContent = (_c = (_b = (_a = contactsImportBlock === null || contactsImportBlock === void 0 ? void 0 : contactsImportBlock.settings) === null || _a === void 0 ? void 0 : _a.customizations) === null || _b === void 0 ? void 0 : _b[key]) === null || _c === void 0 ? void 0 : _c.textContent;
|
|
230
|
+
if (textContent === null || textContent === undefined) {
|
|
231
|
+
return defaultValue;
|
|
232
|
+
}
|
|
233
|
+
return textContent;
|
|
234
|
+
};
|
|
235
|
+
// Extract styles from email invitations block for "Add by Email" button
|
|
236
|
+
const emailInvitationsStyle = (emailInvitationsBlock === null || emailInvitationsBlock === void 0 ? void 0 : emailInvitationsBlock.style) || {};
|
|
237
|
+
// Extract gradient string and fallback color
|
|
238
|
+
// On native, 'background' is a CSS property that doesn't work — convert solid colors to 'backgroundColor'
|
|
239
|
+
const rawEmailBackground = emailInvitationsStyle.background || null;
|
|
240
|
+
const isEmailGradient = rawEmailBackground === null || rawEmailBackground === void 0 ? void 0 : rawEmailBackground.includes('gradient');
|
|
241
|
+
const emailInvitationsGradientString = isEmailGradient ? rawEmailBackground : null;
|
|
242
|
+
const emailInvitationsFallbackColor = isEmailGradient
|
|
243
|
+
? parseGradientFirstColor(rawEmailBackground)
|
|
244
|
+
: rawEmailBackground || emailInvitationsStyle.backgroundColor;
|
|
245
|
+
const emailInvitationsTextColor = emailInvitationsStyle.color || '#333';
|
|
246
|
+
// Helper function to check if native Contacts import is enabled
|
|
247
|
+
const isNativeContactsEnabled = () => {
|
|
248
|
+
var _a, _b, _c;
|
|
249
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
const components = widgetConfiguration.configuration.props['vortex.components'].value;
|
|
253
|
+
return (Array.isArray(components) &&
|
|
254
|
+
components.includes('vortex.components.importcontacts.providers.importcontacts'));
|
|
255
|
+
};
|
|
256
|
+
// Helper function to check if Google Contacts import is enabled
|
|
257
|
+
const isGoogleContactsEnabled = () => {
|
|
258
|
+
var _a, _b, _c;
|
|
259
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
const components = widgetConfiguration.configuration.props['vortex.components'].value;
|
|
263
|
+
return (Array.isArray(components) &&
|
|
264
|
+
components.includes('vortex.components.importcontacts.providers.google'));
|
|
265
|
+
};
|
|
266
|
+
// Helper function to check if Copy Link is enabled
|
|
267
|
+
const isCopyLinkEnabled = () => {
|
|
268
|
+
var _a, _b, _c;
|
|
269
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
273
|
+
return Array.isArray(shareOptions) && shareOptions.includes('copyLink');
|
|
274
|
+
};
|
|
275
|
+
// Helper function to check if Share (native share sheet) is enabled
|
|
276
|
+
const isShareEnabled = () => {
|
|
277
|
+
var _a, _b, _c;
|
|
278
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
282
|
+
return Array.isArray(shareOptions) && shareOptions.includes('nativeShareSheet');
|
|
283
|
+
};
|
|
284
|
+
// Helper function to check if Email Invitations is enabled
|
|
285
|
+
// Check if the form structure contains a vrtx-email-invitations block
|
|
286
|
+
const isEmailInvitationsEnabled = () => {
|
|
287
|
+
var _a, _b, _c;
|
|
288
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
const components = widgetConfiguration.configuration.props['vortex.components'].value;
|
|
292
|
+
return Array.isArray(components) && components.includes('vortex.components.emailinvitations');
|
|
293
|
+
};
|
|
294
|
+
// Helper function to check if Email share is enabled
|
|
295
|
+
const isEmailShareEnabled = () => {
|
|
296
|
+
var _a, _b, _c;
|
|
297
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
301
|
+
return Array.isArray(shareOptions) && shareOptions.includes('email');
|
|
302
|
+
};
|
|
303
|
+
// Helper function to check if SMS is enabled
|
|
304
|
+
const isSmsEnabled = () => {
|
|
305
|
+
var _a, _b, _c;
|
|
306
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
310
|
+
return Array.isArray(shareOptions) && shareOptions.includes('sms');
|
|
311
|
+
};
|
|
312
|
+
// Helper function to check if X/Twitter DMs is enabled
|
|
313
|
+
const isTwitterDmsEnabled = () => {
|
|
314
|
+
var _a, _b, _c;
|
|
315
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
319
|
+
return Array.isArray(shareOptions) && shareOptions.includes('twitterDms');
|
|
320
|
+
};
|
|
321
|
+
// Helper function to check if Instagram DMs is enabled
|
|
322
|
+
const isInstagramDmsEnabled = () => {
|
|
323
|
+
var _a, _b, _c;
|
|
324
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
328
|
+
return Array.isArray(shareOptions) && shareOptions.includes('instagramDms');
|
|
329
|
+
};
|
|
330
|
+
// Helper function to check if WhatsApp is enabled
|
|
331
|
+
const isWhatsAppEnabled = () => {
|
|
332
|
+
var _a, _b, _c;
|
|
333
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
337
|
+
return Array.isArray(shareOptions) && shareOptions.includes('whatsApp');
|
|
338
|
+
};
|
|
339
|
+
// Helper function to check if Line is enabled
|
|
340
|
+
const isLineEnabled = () => {
|
|
341
|
+
var _a, _b, _c;
|
|
342
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
346
|
+
return Array.isArray(shareOptions) && shareOptions.includes('line');
|
|
347
|
+
};
|
|
348
|
+
// Helper function to check if Line LIFF is enabled
|
|
349
|
+
const isLineLiffEnabled = () => {
|
|
350
|
+
var _a, _b, _c;
|
|
351
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
355
|
+
return Array.isArray(shareOptions) && shareOptions.includes('lineLiff');
|
|
356
|
+
};
|
|
357
|
+
// Helper function to check if QR Code is enabled
|
|
358
|
+
const isQrCodeEnabled = () => {
|
|
359
|
+
var _a, _b, _c;
|
|
360
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
364
|
+
return Array.isArray(shareOptions) && shareOptions.includes('qrCode');
|
|
365
|
+
};
|
|
366
|
+
// Helper function to check if Facebook Messenger is enabled
|
|
367
|
+
const isFacebookMessengerEnabled = () => {
|
|
368
|
+
var _a, _b, _c;
|
|
369
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
373
|
+
return Array.isArray(shareOptions) && shareOptions.includes('facebookMessenger');
|
|
374
|
+
};
|
|
375
|
+
// Helper function to check if Telegram is enabled
|
|
376
|
+
const isTelegramEnabled = () => {
|
|
377
|
+
var _a, _b, _c;
|
|
378
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
382
|
+
return Array.isArray(shareOptions) && shareOptions.includes('telegram');
|
|
383
|
+
};
|
|
384
|
+
// Helper function to check if Discord is enabled
|
|
385
|
+
const isDiscordEnabled = () => {
|
|
386
|
+
var _a, _b, _c;
|
|
387
|
+
if (!((_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value)) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
const shareOptions = widgetConfiguration.configuration.props['vortex.components.share.options'].value;
|
|
391
|
+
return Array.isArray(shareOptions) && shareOptions.includes('discord');
|
|
392
|
+
};
|
|
393
|
+
// Helper function to get share options order from configuration
|
|
394
|
+
const getShareOptionsOrder = () => {
|
|
395
|
+
var _a, _b, _c;
|
|
396
|
+
const shareOptions = (_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.components.share.options']) === null || _c === void 0 ? void 0 : _c.value;
|
|
397
|
+
return Array.isArray(shareOptions) ? shareOptions : [];
|
|
398
|
+
};
|
|
399
|
+
// State for fetched contacts
|
|
400
|
+
const [contacts, setContacts] = (0, react_1.useState)([]);
|
|
401
|
+
const [loadingContacts, setLoadingContacts] = (0, react_1.useState)(false);
|
|
402
|
+
const [contactsFetchAttempted, setContactsFetchAttempted] = (0, react_1.useState)(false);
|
|
403
|
+
const [contactsError, setContactsError] = (0, react_1.useState)(null);
|
|
404
|
+
// State for fetched Google contacts
|
|
405
|
+
const [googleContacts, setGoogleContacts] = (0, react_1.useState)([]);
|
|
406
|
+
const [loadingGoogleContacts, setLoadingGoogleContacts] = (0, react_1.useState)(false);
|
|
407
|
+
const [googleContactsFetchAttempted, setGoogleContactsFetchAttempted] = (0, react_1.useState)(false);
|
|
408
|
+
const [googleContactsError, setGoogleContactsError] = (0, react_1.useState)(null);
|
|
409
|
+
// State for hover tracking in edit mode
|
|
410
|
+
const [hoveredComponentId, setHoveredComponentId] = (0, react_1.useState)(null);
|
|
411
|
+
const [selectedComponentId, setSelectedComponentId] = (0, react_1.useState)(null);
|
|
412
|
+
const logic = (0, useInvitationFormLogic_1.useInvitationFormLogic)(platformOperations, contacts, googleContacts, getShareableInviteLink);
|
|
413
|
+
// Destructure for easier access
|
|
414
|
+
const { view, emailInput, emails, copySuccess, shareSuccess, sendSuccess, searchQuery, invitedContactIds, invitedGoogleContactIds, googleSearchQuery, setEmailInput, setSearchQuery, setGoogleSearchQuery, loadingContactIds, loadingGoogleContactIds, loadingEmailInvite, loadingCopy, loadingShare, loadingInvitationLink, lastInvalidEmail, handleClose, handleSendInvitation, handleCopyLink, handleShare, handleEmailSubmit, handleRemoveEmail, handleSelectFromGoogle, handleSelectFromContacts, handleAddByEmail, handleShowQrCode, handleBackToMain, handleInviteContact, handleInviteGoogleContact, } = logic;
|
|
415
|
+
// Fetch contacts on-demand when the contacts view is opened
|
|
416
|
+
(0, react_1.useEffect)(() => {
|
|
417
|
+
// Reset fetch attempt when view changes away from contacts
|
|
418
|
+
if (view !== 'contacts' && contactsFetchAttempted) {
|
|
419
|
+
setContactsFetchAttempted(false);
|
|
420
|
+
setContactsError(null);
|
|
421
|
+
}
|
|
422
|
+
if (view === 'contacts' && !contactsFetchAttempted && !loadingContacts) {
|
|
423
|
+
setLoadingContacts(true);
|
|
424
|
+
setContactsFetchAttempted(true);
|
|
425
|
+
setContactsError(null); // Clear previous errors
|
|
426
|
+
platformOperations
|
|
427
|
+
.fetchContacts()
|
|
428
|
+
.then((fetchedContacts) => {
|
|
429
|
+
setContacts(fetchedContacts);
|
|
430
|
+
setContactsError(null);
|
|
431
|
+
})
|
|
432
|
+
.catch((err) => {
|
|
433
|
+
console.error('[Vortex] Failed to fetch contacts:', err);
|
|
434
|
+
setContactsError(err instanceof Error ? err : new Error(String(err)));
|
|
435
|
+
setContacts([]); // Clear contacts on error
|
|
436
|
+
})
|
|
437
|
+
.finally(() => setLoadingContacts(false));
|
|
438
|
+
}
|
|
439
|
+
}, [view, contactsFetchAttempted, loadingContacts]);
|
|
440
|
+
// Fetch Google contacts on-demand when the googleContacts view is opened
|
|
441
|
+
(0, react_1.useEffect)(() => {
|
|
442
|
+
// Reset fetch attempt when view changes away from googleContacts
|
|
443
|
+
if (view !== 'googleContacts' && googleContactsFetchAttempted) {
|
|
444
|
+
setGoogleContactsFetchAttempted(false);
|
|
445
|
+
setGoogleContactsError(null);
|
|
446
|
+
}
|
|
447
|
+
if (view === 'googleContacts' && !googleContactsFetchAttempted && !loadingGoogleContacts) {
|
|
448
|
+
setLoadingGoogleContacts(true);
|
|
449
|
+
setGoogleContactsFetchAttempted(true);
|
|
450
|
+
setGoogleContactsError(null); // Clear previous errors
|
|
451
|
+
platformOperations
|
|
452
|
+
.fetchGoogleContacts()
|
|
453
|
+
.then((fetchedContacts) => {
|
|
454
|
+
setGoogleContacts(fetchedContacts);
|
|
455
|
+
setGoogleContactsError(null);
|
|
456
|
+
})
|
|
457
|
+
.catch((err) => {
|
|
458
|
+
console.error('[Vortex] Failed to fetch Google contacts:', err);
|
|
459
|
+
setGoogleContactsError(err instanceof Error ? err : new Error(String(err)));
|
|
460
|
+
setGoogleContacts([]); // Clear contacts on error
|
|
461
|
+
})
|
|
462
|
+
.finally(() => setLoadingGoogleContacts(false));
|
|
463
|
+
}
|
|
464
|
+
}, [view, googleContactsFetchAttempted, loadingGoogleContacts]);
|
|
465
|
+
// Filter contacts using shared utility
|
|
466
|
+
const filteredContacts = (0, contactUtils_1.filterContacts)(contacts, searchQuery);
|
|
467
|
+
const filteredGoogleContacts = (0, contactUtils_1.filterContacts)(googleContacts, googleSearchQuery);
|
|
468
|
+
// Wrapper functions with haptic feedback and analytics tracking
|
|
469
|
+
const handleCopyLinkWithHaptics = () => __awaiter(this, void 0, void 0, function* () {
|
|
470
|
+
var _a;
|
|
471
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
472
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('copyLink');
|
|
473
|
+
yield handleCopyLink();
|
|
474
|
+
});
|
|
475
|
+
const handleShareWithHaptics = () => __awaiter(this, void 0, void 0, function* () {
|
|
476
|
+
var _a;
|
|
477
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
478
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaNativeShare');
|
|
479
|
+
yield handleShare();
|
|
480
|
+
});
|
|
481
|
+
const handleSelectFromContactsWithHaptics = () => __awaiter(this, void 0, void 0, function* () {
|
|
482
|
+
var _a;
|
|
483
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
484
|
+
handleSelectFromContacts();
|
|
485
|
+
});
|
|
486
|
+
const handleSelectFromGoogleWithHaptics = () => __awaiter(this, void 0, void 0, function* () {
|
|
487
|
+
var _a;
|
|
488
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
489
|
+
handleSelectFromGoogle();
|
|
490
|
+
});
|
|
491
|
+
const handleAddByEmailWithHaptics = () => __awaiter(this, void 0, void 0, function* () {
|
|
492
|
+
var _a;
|
|
493
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
494
|
+
handleAddByEmail();
|
|
495
|
+
});
|
|
496
|
+
const handleInviteContactWithHaptics = (contactId) => __awaiter(this, void 0, void 0, function* () {
|
|
497
|
+
var _a;
|
|
498
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
499
|
+
yield handleInviteContact(contactId);
|
|
500
|
+
});
|
|
501
|
+
const handleInviteGoogleContactWithHaptics = (contactId) => __awaiter(this, void 0, void 0, function* () {
|
|
502
|
+
var _a;
|
|
503
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
504
|
+
yield handleInviteGoogleContact(contactId);
|
|
505
|
+
});
|
|
506
|
+
const handleSendInvitationWithHaptics = () => __awaiter(this, void 0, void 0, function* () {
|
|
507
|
+
var _a;
|
|
508
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
509
|
+
yield handleSendInvitation();
|
|
510
|
+
});
|
|
511
|
+
const handleRetryContacts = () => __awaiter(this, void 0, void 0, function* () {
|
|
512
|
+
var _a;
|
|
513
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
514
|
+
// Clear the error and reset fetch attempt to trigger a new fetch
|
|
515
|
+
setContactsError(null);
|
|
516
|
+
setContactsFetchAttempted(false);
|
|
517
|
+
});
|
|
518
|
+
const handleOpenSettings = () => __awaiter(this, void 0, void 0, function* () {
|
|
519
|
+
var _a;
|
|
520
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
521
|
+
try {
|
|
522
|
+
yield react_native_1.Linking.openSettings();
|
|
523
|
+
}
|
|
524
|
+
catch (err) {
|
|
525
|
+
console.warn('[Vortex] Failed to open settings:', err);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
const handleEmailShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
529
|
+
var _a, _b;
|
|
530
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
531
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaEmail');
|
|
532
|
+
yield ((_b = platformOperations.shareViaEmail) === null || _b === void 0 ? void 0 : _b.call(platformOperations));
|
|
533
|
+
});
|
|
534
|
+
const handleSmsShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
535
|
+
var _a, _b;
|
|
536
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
537
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaSMS');
|
|
538
|
+
yield ((_b = platformOperations.shareViaSms) === null || _b === void 0 ? void 0 : _b.call(platformOperations));
|
|
539
|
+
});
|
|
540
|
+
const handleTwitterShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
541
|
+
var _a, _b;
|
|
542
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
543
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaTwitter');
|
|
544
|
+
yield ((_b = platformOperations.shareViaTwitter) === null || _b === void 0 ? void 0 : _b.call(platformOperations));
|
|
545
|
+
});
|
|
546
|
+
const handleInstagramShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
547
|
+
var _a, _b;
|
|
548
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
549
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaInstagram');
|
|
550
|
+
yield ((_b = platformOperations.shareViaInstagram) === null || _b === void 0 ? void 0 : _b.call(platformOperations));
|
|
551
|
+
});
|
|
552
|
+
const handleWhatsAppShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
553
|
+
var _a, _b;
|
|
554
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
555
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaWhatsApp');
|
|
556
|
+
yield ((_b = platformOperations.shareViaWhatsApp) === null || _b === void 0 ? void 0 : _b.call(platformOperations));
|
|
557
|
+
});
|
|
558
|
+
const handleLineShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
559
|
+
var _a, _b;
|
|
560
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
561
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaLine');
|
|
562
|
+
yield ((_b = platformOperations.shareViaLine) === null || _b === void 0 ? void 0 : _b.call(platformOperations));
|
|
563
|
+
});
|
|
564
|
+
const handleLineLiffShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
565
|
+
var _a, _b;
|
|
566
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
567
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaLineLiff');
|
|
568
|
+
yield ((_b = platformOperations.shareViaLineLiff) === null || _b === void 0 ? void 0 : _b.call(platformOperations));
|
|
569
|
+
});
|
|
570
|
+
const handleFacebookMessengerShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
571
|
+
var _a, _b;
|
|
572
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
573
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaFacebookMessenger');
|
|
574
|
+
yield ((_b = platformOperations.shareViaFacebookMessenger) === null || _b === void 0 ? void 0 : _b.call(platformOperations));
|
|
575
|
+
});
|
|
576
|
+
const handleTelegramShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
577
|
+
var _a, _b;
|
|
578
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
579
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaTelegram');
|
|
580
|
+
yield ((_b = platformOperations.shareViaTelegram) === null || _b === void 0 ? void 0 : _b.call(platformOperations));
|
|
581
|
+
});
|
|
582
|
+
const handleDiscordShare = () => __awaiter(this, void 0, void 0, function* () {
|
|
583
|
+
var _a, _b;
|
|
584
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
585
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaDiscord');
|
|
586
|
+
yield ((_b = platformOperations.shareViaDiscord) === null || _b === void 0 ? void 0 : _b.call(platformOperations));
|
|
587
|
+
});
|
|
588
|
+
const handleQrCode = () => __awaiter(this, void 0, void 0, function* () {
|
|
589
|
+
var _a;
|
|
590
|
+
yield ((_a = platformOperations.triggerHaptic) === null || _a === void 0 ? void 0 : _a.call(platformOperations, 'light'));
|
|
591
|
+
onShareLinkClick === null || onShareLinkClick === void 0 ? void 0 : onShareLinkClick('shareViaQrCode');
|
|
592
|
+
handleShowQrCode();
|
|
593
|
+
});
|
|
594
|
+
// Block renderer functions - render individual components based on block type
|
|
595
|
+
const renderShareOptionsBlock = (block) => {
|
|
596
|
+
return (<VrtxShareOptions_1.VrtxShareOptions block={block} renderIcon={renderIcon} isCopyLinkEnabled={isCopyLinkEnabled} isShareEnabled={isShareEnabled} isEmailShareEnabled={isEmailShareEnabled} isSmsEnabled={isSmsEnabled} isTwitterDmsEnabled={isTwitterDmsEnabled} isInstagramDmsEnabled={isInstagramDmsEnabled} isWhatsAppEnabled={isWhatsAppEnabled} isLineEnabled={isLineEnabled} isLineLiffEnabled={isLineLiffEnabled} isQrCodeEnabled={isQrCodeEnabled} isFacebookMessengerEnabled={isFacebookMessengerEnabled} isTelegramEnabled={isTelegramEnabled} isDiscordEnabled={isDiscordEnabled} handleCopyLink={handleCopyLink} handleShare={handleShare} handleEmailShare={handleEmailShare} handleSmsShare={handleSmsShare} handleTwitterShare={handleTwitterShare} handleInstagramShare={handleInstagramShare} handleWhatsAppShare={handleWhatsAppShare} handleLineShare={handleLineShare} handleLineLiffShare={handleLineLiffShare} handleQrCode={handleQrCode} handleFacebookMessengerShare={handleFacebookMessengerShare} handleTelegramShare={handleTelegramShare} handleDiscordShare={handleDiscordShare} triggerHaptic={platformOperations.triggerHaptic} loadingCopy={loadingCopy} loadingShare={loadingShare} copySuccess={copySuccess} shareSuccess={shareSuccess} isEditMode={isEditMode} shareOptionsOrder={getShareOptionsOrder()}/>);
|
|
597
|
+
};
|
|
598
|
+
const renderContactsImportBlock = (block) => {
|
|
599
|
+
return (<VrtxContactsImport_1.VrtxContactsImport block={block} renderIcon={renderIcon} isNativeContactsEnabled={isNativeContactsEnabled} isGoogleContactsEnabled={isGoogleContactsEnabled} handleSelectFromContacts={handleSelectFromContacts} handleSelectFromGoogle={handleSelectFromGoogle} triggerHaptic={platformOperations.triggerHaptic} isEditMode={isEditMode}/>);
|
|
600
|
+
};
|
|
601
|
+
const renderEmailInvitationsBlock = (block) => {
|
|
602
|
+
// Don't pass submitButtonBlock - we'll render it separately
|
|
603
|
+
return (<VrtxEmailInvitations_1.VrtxEmailInvitations block={block} view={view} emails={emails} emailInput={emailInput} setEmailInput={setEmailInput} handleEmailSubmit={handleEmailSubmit} handleRemoveEmail={handleRemoveEmail} submitButtonBlock={undefined} handleSendInvitation={handleSendInvitation} triggerHaptic={platformOperations.triggerHaptic} loadingEmailInvite={loadingEmailInvite} sendSuccess={sendSuccess} lastInvalidEmail={lastInvalidEmail} EditableWrapper={EditableWrapper} onEmailFieldFocus={onEmailFieldFocus} onEmailFieldBlur={onEmailFieldBlur} handleAddByEmail={handleAddByEmail} renderIcon={renderIcon} isEditMode={isEditMode}/>);
|
|
604
|
+
};
|
|
605
|
+
const renderHeadingBlock = (block) => {
|
|
606
|
+
return <VrtxHeading_1.VrtxHeading block={block}/>;
|
|
607
|
+
};
|
|
608
|
+
// Stable text update handler that doesn't depend on block
|
|
609
|
+
const handleTextUpdate = (0, react_1.useCallback)((blockId, updates) => {
|
|
610
|
+
// Dispatch a custom event that PageBuilderMobile can listen to
|
|
611
|
+
const updateEvent = new CustomEvent('vortex-update-text', {
|
|
612
|
+
detail: {
|
|
613
|
+
blockId,
|
|
614
|
+
textContent: updates.textContent,
|
|
615
|
+
},
|
|
616
|
+
bubbles: true,
|
|
617
|
+
});
|
|
618
|
+
// Dispatch from document if we're on web
|
|
619
|
+
if (isWeb && document) {
|
|
620
|
+
document.dispatchEvent(updateEvent);
|
|
621
|
+
}
|
|
622
|
+
}, []); // Empty deps - this handler never needs to change
|
|
623
|
+
const renderTextBlock = (block) => {
|
|
624
|
+
return <VrtxText_1.VrtxText block={block} isEditMode={isEditMode} onUpdate={handleTextUpdate}/>;
|
|
625
|
+
};
|
|
626
|
+
const renderSelectBlock = (block) => {
|
|
627
|
+
return <VrtxSelect_1.VrtxSelect block={block}/>;
|
|
628
|
+
};
|
|
629
|
+
const renderSubmitBlock = (block) => {
|
|
630
|
+
var _a;
|
|
631
|
+
return (<VrtxSubmit_1.VrtxSubmit block={block} view={view} handleSendInvitation={handleSendInvitation} triggerHaptic={platformOperations.triggerHaptic} loadingEmailInvite={loadingEmailInvite} sendSuccess={sendSuccess} emailCount={emails.length} emailBlockCustomizations={(_a = emailInvitationsBlock === null || emailInvitationsBlock === void 0 ? void 0 : emailInvitationsBlock.settings) === null || _a === void 0 ? void 0 : _a.customizations} emailBlockStyle={emailInvitationsBlock === null || emailInvitationsBlock === void 0 ? void 0 : emailInvitationsBlock.style}/>);
|
|
632
|
+
};
|
|
633
|
+
const renderFindFriendsBlock = (block) => {
|
|
634
|
+
var _a, _b, _c, _d;
|
|
635
|
+
// Extract theme colors from widget configuration
|
|
636
|
+
const themeOptions = (_d = (_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.theme']) === null || _c === void 0 ? void 0 : _c.value) === null || _d === void 0 ? void 0 : _d.options;
|
|
637
|
+
const getThemeValue = (key) => {
|
|
638
|
+
const option = themeOptions === null || themeOptions === void 0 ? void 0 : themeOptions.find((opt) => opt.key === key);
|
|
639
|
+
return (option === null || option === void 0 ? void 0 : option.value) || undefined;
|
|
640
|
+
};
|
|
641
|
+
const extractBorderColor = (borderValue) => {
|
|
642
|
+
if (!borderValue)
|
|
643
|
+
return undefined;
|
|
644
|
+
const parts = borderValue.trim().split(/\s+/);
|
|
645
|
+
if (parts.length === 3)
|
|
646
|
+
return parts[2];
|
|
647
|
+
return undefined;
|
|
648
|
+
};
|
|
649
|
+
const theme = {
|
|
650
|
+
primaryBackground: getThemeValue('--vrtx-submit-primary-background') ||
|
|
651
|
+
getThemeValue('--vrtx-button-primary-background') ||
|
|
652
|
+
getThemeValue('--color-primary-background'),
|
|
653
|
+
primaryForeground: getThemeValue('--vrtx-submit-primary-color') || getThemeValue('--color-primary-foreground'),
|
|
654
|
+
secondaryBackground: getThemeValue('--vrtx-submit-secondary-background') ||
|
|
655
|
+
getThemeValue('--vrtx-button-secondary-background') ||
|
|
656
|
+
getThemeValue('--color-secondary-background'),
|
|
657
|
+
secondaryForeground: getThemeValue('--vrtx-submit-secondary-color') ||
|
|
658
|
+
getThemeValue('--vrtx-button-secondary-color') ||
|
|
659
|
+
getThemeValue('--color-secondary-foreground'),
|
|
660
|
+
foreground: getThemeValue('--vrtx-root-color') || getThemeValue('--color-foreground'),
|
|
661
|
+
border: extractBorderColor(getThemeValue('--vrtx-root-border')) ||
|
|
662
|
+
extractBorderColor(getThemeValue('--vrtx-input-border')) ||
|
|
663
|
+
getThemeValue('--color-border'),
|
|
664
|
+
};
|
|
665
|
+
return (<VrtxFindFriends_1.VrtxFindFriends block={block} findFriendsConfig={findFriendsConfig} createUserIdInvitation={createUserIdInvitation} triggerHaptic={platformOperations.triggerHaptic} theme={theme}/>);
|
|
666
|
+
};
|
|
667
|
+
const renderIncomingInvitationsBlock = (block) => {
|
|
668
|
+
var _a, _b, _c, _d;
|
|
669
|
+
// Extract theme colors from widget configuration
|
|
670
|
+
const themeOptions = (_d = (_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.theme']) === null || _c === void 0 ? void 0 : _c.value) === null || _d === void 0 ? void 0 : _d.options;
|
|
671
|
+
const getThemeValue = (key) => {
|
|
672
|
+
const option = themeOptions === null || themeOptions === void 0 ? void 0 : themeOptions.find((opt) => opt.key === key);
|
|
673
|
+
return (option === null || option === void 0 ? void 0 : option.value) || undefined;
|
|
674
|
+
};
|
|
675
|
+
const extractBorderColor = (borderValue) => {
|
|
676
|
+
if (!borderValue)
|
|
677
|
+
return undefined;
|
|
678
|
+
const parts = borderValue.trim().split(/\s+/);
|
|
679
|
+
if (parts.length === 3)
|
|
680
|
+
return parts[2];
|
|
681
|
+
return undefined;
|
|
682
|
+
};
|
|
683
|
+
const theme = {
|
|
684
|
+
primaryBackground: getThemeValue('--vrtx-submit-primary-background') ||
|
|
685
|
+
getThemeValue('--vrtx-button-primary-background') ||
|
|
686
|
+
getThemeValue('--color-primary-background'),
|
|
687
|
+
primaryForeground: getThemeValue('--vrtx-submit-primary-color') || getThemeValue('--color-primary-foreground'),
|
|
688
|
+
secondaryBackground: getThemeValue('--vrtx-submit-secondary-background') ||
|
|
689
|
+
getThemeValue('--vrtx-button-secondary-background') ||
|
|
690
|
+
getThemeValue('--color-secondary-background'),
|
|
691
|
+
secondaryForeground: getThemeValue('--vrtx-submit-secondary-color') ||
|
|
692
|
+
getThemeValue('--vrtx-button-secondary-color') ||
|
|
693
|
+
getThemeValue('--color-secondary-foreground'),
|
|
694
|
+
foreground: getThemeValue('--vrtx-root-color') || getThemeValue('--color-foreground'),
|
|
695
|
+
border: extractBorderColor(getThemeValue('--vrtx-root-border')) ||
|
|
696
|
+
extractBorderColor(getThemeValue('--vrtx-input-border')) ||
|
|
697
|
+
getThemeValue('--color-border'),
|
|
698
|
+
};
|
|
699
|
+
return (<VrtxIncomingInvitations_1.VrtxIncomingInvitations block={block} incomingInvitationsConfig={incomingInvitationsConfig} apiUrl={apiUrl} jwt={jwt} triggerHaptic={platformOperations.triggerHaptic} theme={theme}/>);
|
|
700
|
+
};
|
|
701
|
+
const renderOutgoingInvitationsBlock = (block) => {
|
|
702
|
+
var _a, _b, _c, _d;
|
|
703
|
+
// Extract theme colors from widget configuration
|
|
704
|
+
const themeOptions = (_d = (_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.theme']) === null || _c === void 0 ? void 0 : _c.value) === null || _d === void 0 ? void 0 : _d.options;
|
|
705
|
+
const getThemeValue = (key) => {
|
|
706
|
+
const option = themeOptions === null || themeOptions === void 0 ? void 0 : themeOptions.find((opt) => opt.key === key);
|
|
707
|
+
return (option === null || option === void 0 ? void 0 : option.value) || undefined;
|
|
708
|
+
};
|
|
709
|
+
const extractBorderColor = (borderValue) => {
|
|
710
|
+
if (!borderValue)
|
|
711
|
+
return undefined;
|
|
712
|
+
const parts = borderValue.trim().split(/\s+/);
|
|
713
|
+
if (parts.length === 3)
|
|
714
|
+
return parts[2];
|
|
715
|
+
return undefined;
|
|
716
|
+
};
|
|
717
|
+
const theme = {
|
|
718
|
+
primaryBackground: getThemeValue('--vrtx-submit-primary-background') ||
|
|
719
|
+
getThemeValue('--vrtx-button-primary-background') ||
|
|
720
|
+
getThemeValue('--color-primary-background'),
|
|
721
|
+
primaryForeground: getThemeValue('--vrtx-submit-primary-color') || getThemeValue('--color-primary-foreground'),
|
|
722
|
+
secondaryBackground: getThemeValue('--vrtx-submit-secondary-background') ||
|
|
723
|
+
getThemeValue('--vrtx-button-secondary-background') ||
|
|
724
|
+
getThemeValue('--color-secondary-background'),
|
|
725
|
+
secondaryForeground: getThemeValue('--vrtx-submit-secondary-color') ||
|
|
726
|
+
getThemeValue('--vrtx-button-secondary-color') ||
|
|
727
|
+
getThemeValue('--color-secondary-foreground'),
|
|
728
|
+
foreground: getThemeValue('--vrtx-root-color') || getThemeValue('--color-foreground'),
|
|
729
|
+
border: extractBorderColor(getThemeValue('--vrtx-root-border')) ||
|
|
730
|
+
extractBorderColor(getThemeValue('--vrtx-input-border')) ||
|
|
731
|
+
getThemeValue('--color-border'),
|
|
732
|
+
};
|
|
733
|
+
return (<VrtxOutgoingInvitations_1.VrtxOutgoingInvitations block={block} outgoingInvitationsConfig={outgoingInvitationsConfig} apiUrl={apiUrl} jwt={jwt} triggerHaptic={platformOperations.triggerHaptic} theme={theme}/>);
|
|
734
|
+
};
|
|
735
|
+
const renderSearchBoxBlock = (block) => {
|
|
736
|
+
var _a, _b, _c, _d;
|
|
737
|
+
// Extract theme colors from widget configuration
|
|
738
|
+
const themeOptions = (_d = (_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.theme']) === null || _c === void 0 ? void 0 : _c.value) === null || _d === void 0 ? void 0 : _d.options;
|
|
739
|
+
const getThemeValue = (key) => {
|
|
740
|
+
const option = themeOptions === null || themeOptions === void 0 ? void 0 : themeOptions.find((opt) => opt.key === key);
|
|
741
|
+
return (option === null || option === void 0 ? void 0 : option.value) || undefined;
|
|
742
|
+
};
|
|
743
|
+
const theme = {
|
|
744
|
+
primaryBackground: getThemeValue('--color-primary-background'),
|
|
745
|
+
primaryForeground: getThemeValue('--color-primary-foreground'),
|
|
746
|
+
secondaryBackground: getThemeValue('--color-secondary-background'),
|
|
747
|
+
secondaryForeground: getThemeValue('--color-secondary-foreground'),
|
|
748
|
+
foreground: getThemeValue('--color-foreground'),
|
|
749
|
+
border: getThemeValue('--color-border'),
|
|
750
|
+
};
|
|
751
|
+
return (<VrtxSearchBox_1.VrtxSearchBox block={block} searchBoxConfig={searchBoxConfig} createUserIdInvitation={createUserIdInvitation} triggerHaptic={platformOperations.triggerHaptic} renderIcon={renderIcon} theme={theme}/>);
|
|
752
|
+
};
|
|
753
|
+
const renderInvitationSuggestionsBlock = (block) => {
|
|
754
|
+
var _a, _b, _c, _d;
|
|
755
|
+
// Extract theme colors from widget configuration
|
|
756
|
+
const themeOptions = (_d = (_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.theme']) === null || _c === void 0 ? void 0 : _c.value) === null || _d === void 0 ? void 0 : _d.options;
|
|
757
|
+
const getThemeValue = (key) => {
|
|
758
|
+
const option = themeOptions === null || themeOptions === void 0 ? void 0 : themeOptions.find((opt) => opt.key === key);
|
|
759
|
+
return (option === null || option === void 0 ? void 0 : option.value) || undefined;
|
|
760
|
+
};
|
|
761
|
+
const extractBorderColor = (borderValue) => {
|
|
762
|
+
if (!borderValue)
|
|
763
|
+
return undefined;
|
|
764
|
+
const parts = borderValue.trim().split(/\s+/);
|
|
765
|
+
if (parts.length === 3)
|
|
766
|
+
return parts[2];
|
|
767
|
+
return undefined;
|
|
768
|
+
};
|
|
769
|
+
const theme = {
|
|
770
|
+
primaryBackground: getThemeValue('--vrtx-submit-primary-background') ||
|
|
771
|
+
getThemeValue('--vrtx-button-primary-background') ||
|
|
772
|
+
getThemeValue('--color-primary-background'),
|
|
773
|
+
primaryForeground: getThemeValue('--vrtx-submit-primary-color') || getThemeValue('--color-primary-foreground'),
|
|
774
|
+
secondaryBackground: getThemeValue('--vrtx-submit-secondary-background') ||
|
|
775
|
+
getThemeValue('--vrtx-button-secondary-background') ||
|
|
776
|
+
getThemeValue('--color-secondary-background'),
|
|
777
|
+
secondaryForeground: getThemeValue('--vrtx-submit-secondary-color') ||
|
|
778
|
+
getThemeValue('--vrtx-button-secondary-color') ||
|
|
779
|
+
getThemeValue('--color-secondary-foreground'),
|
|
780
|
+
foreground: getThemeValue('--vrtx-root-color') || getThemeValue('--color-foreground'),
|
|
781
|
+
border: extractBorderColor(getThemeValue('--vrtx-root-border')) ||
|
|
782
|
+
extractBorderColor(getThemeValue('--vrtx-input-border')) ||
|
|
783
|
+
getThemeValue('--color-border'),
|
|
784
|
+
};
|
|
785
|
+
return (<VrtxInvitationSuggestions_1.VrtxInvitationSuggestions block={block} invitationSuggestionsConfig={invitationSuggestionsConfig} createUserIdInvitation={createUserIdInvitation} triggerHaptic={platformOperations.triggerHaptic} theme={theme}/>);
|
|
786
|
+
};
|
|
787
|
+
const renderInviteContactsBlock = (block) => {
|
|
788
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
789
|
+
// Extract theme colors from widget configuration
|
|
790
|
+
const themeOptions = (_d = (_c = (_b = (_a = widgetConfiguration === null || widgetConfiguration === void 0 ? void 0 : widgetConfiguration.configuration) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b['vortex.theme']) === null || _c === void 0 ? void 0 : _c.value) === null || _d === void 0 ? void 0 : _d.options;
|
|
791
|
+
const getThemeValue = (key) => {
|
|
792
|
+
const option = themeOptions === null || themeOptions === void 0 ? void 0 : themeOptions.find((opt) => opt.key === key);
|
|
793
|
+
return (option === null || option === void 0 ? void 0 : option.value) || undefined;
|
|
794
|
+
};
|
|
795
|
+
const extractBorderColor = (borderValue) => {
|
|
796
|
+
if (!borderValue)
|
|
797
|
+
return undefined;
|
|
798
|
+
const parts = borderValue.trim().split(/\s+/);
|
|
799
|
+
if (parts.length === 3)
|
|
800
|
+
return parts[2];
|
|
801
|
+
return undefined;
|
|
802
|
+
};
|
|
803
|
+
const theme = {
|
|
804
|
+
primaryBackground: getThemeValue('--vrtx-submit-primary-background') ||
|
|
805
|
+
getThemeValue('--vrtx-button-primary-background') ||
|
|
806
|
+
getThemeValue('--color-primary-background'),
|
|
807
|
+
primaryForeground: getThemeValue('--vrtx-submit-primary-color') || getThemeValue('--color-primary-foreground'),
|
|
808
|
+
secondaryBackground: getThemeValue('--vrtx-submit-secondary-background') ||
|
|
809
|
+
getThemeValue('--vrtx-button-secondary-background') ||
|
|
810
|
+
getThemeValue('--color-secondary-background'),
|
|
811
|
+
secondaryForeground: getThemeValue('--vrtx-submit-secondary-color') ||
|
|
812
|
+
getThemeValue('--vrtx-button-secondary-color') ||
|
|
813
|
+
getThemeValue('--color-secondary-foreground'),
|
|
814
|
+
foreground: getThemeValue('--vrtx-root-color') || getThemeValue('--color-foreground'),
|
|
815
|
+
border: extractBorderColor(getThemeValue('--vrtx-root-border')) ||
|
|
816
|
+
extractBorderColor(getThemeValue('--vrtx-input-border')) ||
|
|
817
|
+
getThemeValue('--color-border'),
|
|
818
|
+
};
|
|
819
|
+
// Get SMS message template from block settings
|
|
820
|
+
const smsMessageTemplate = (_g = (_f = (_e = block === null || block === void 0 ? void 0 : block.settings) === null || _e === void 0 ? void 0 : _e.customizations) === null || _f === void 0 ? void 0 : _f.smsMessage) === null || _g === void 0 ? void 0 : _g.textContent;
|
|
821
|
+
// Create SMS invitation function that calls the API
|
|
822
|
+
const createSmsInvitation = (phoneNumber, contactName) => __awaiter(this, void 0, void 0, function* () {
|
|
823
|
+
try {
|
|
824
|
+
if (platformOperations.createSmsInvitation) {
|
|
825
|
+
return yield platformOperations.createSmsInvitation(phoneNumber, contactName);
|
|
826
|
+
}
|
|
827
|
+
console.warn('[VrtxInviteContacts] No createSmsInvitation function provided');
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
catch (err) {
|
|
831
|
+
console.error('[VrtxInviteContacts] Failed to create SMS invitation:', err);
|
|
832
|
+
return null;
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
return (<VrtxInviteContacts_1.VrtxInviteContacts block={block} inviteContactsConfig={inviteContactsConfig} createSmsInvitation={createSmsInvitation} triggerHaptic={platformOperations.triggerHaptic} theme={theme} smsMessageTemplate={smsMessageTemplate}/>);
|
|
836
|
+
};
|
|
837
|
+
const renderBlock = (block) => {
|
|
838
|
+
if (!block)
|
|
839
|
+
return null;
|
|
840
|
+
// Skip rendering if block is hidden
|
|
841
|
+
if (block.hidden === true) {
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
const content = (() => {
|
|
845
|
+
switch (block.subtype) {
|
|
846
|
+
case 'vrtx-share-options':
|
|
847
|
+
return renderShareOptionsBlock(block);
|
|
848
|
+
case 'vrtx-contacts-import':
|
|
849
|
+
return renderContactsImportBlock(block);
|
|
850
|
+
case 'vrtx-email-invitations':
|
|
851
|
+
return renderEmailInvitationsBlock(block);
|
|
852
|
+
case 'vrtx-find-friends':
|
|
853
|
+
return renderFindFriendsBlock(block);
|
|
854
|
+
case 'vrtx-incoming-invitations':
|
|
855
|
+
return renderIncomingInvitationsBlock(block);
|
|
856
|
+
case 'vrtx-outgoing-invitations':
|
|
857
|
+
return renderOutgoingInvitationsBlock(block);
|
|
858
|
+
case 'vrtx-invitation-suggestions':
|
|
859
|
+
return renderInvitationSuggestionsBlock(block);
|
|
860
|
+
case 'vrtx-invite-contacts':
|
|
861
|
+
return renderInviteContactsBlock(block);
|
|
862
|
+
case 'vrtx-search-box':
|
|
863
|
+
return renderSearchBoxBlock(block);
|
|
864
|
+
case 'vrtx-heading':
|
|
865
|
+
return renderHeadingBlock(block);
|
|
866
|
+
case 'vrtx-text':
|
|
867
|
+
return renderTextBlock(block);
|
|
868
|
+
case 'vrtx-select':
|
|
869
|
+
return null; //renderSelectBlock(block);
|
|
870
|
+
case 'vrtx-submit':
|
|
871
|
+
return renderSubmitBlock(block);
|
|
872
|
+
default:
|
|
873
|
+
console.warn(`here [InvitationFormCore] Unknown block type: ${block.subtype}`);
|
|
874
|
+
return null;
|
|
875
|
+
}
|
|
876
|
+
})();
|
|
877
|
+
if (!content)
|
|
878
|
+
return null;
|
|
879
|
+
// Wrap in EditableWrapper for edit mode
|
|
880
|
+
return (<EditableWrapper key={block.id} componentId={block.id} componentType={block.type} node={block}>
|
|
881
|
+
{content}
|
|
882
|
+
</EditableWrapper>);
|
|
883
|
+
};
|
|
884
|
+
const renderColumn = (column, filterGroup, excludeGroups) => {
|
|
885
|
+
if (!column || !column.children)
|
|
886
|
+
return null;
|
|
887
|
+
// If filtering by include group, check if this column has any matching blocks
|
|
888
|
+
if (filterGroup) {
|
|
889
|
+
const hasMatchingBlocks = column.children.some((child) => {
|
|
890
|
+
var _a, _b, _c;
|
|
891
|
+
if (child.type === 'block') {
|
|
892
|
+
const blockGroup = (_c = (_b = (_a = child.meta) === null || _a === void 0 ? void 0 : _a.source) === null || _b === void 0 ? void 0 : _b.group) === null || _c === void 0 ? void 0 : _c.name;
|
|
893
|
+
return blockGroup === filterGroup;
|
|
894
|
+
}
|
|
895
|
+
return false;
|
|
896
|
+
});
|
|
897
|
+
// Skip rendering this column if it has no matching blocks
|
|
898
|
+
if (!hasMatchingBlocks) {
|
|
899
|
+
return null;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
// If excluding groups, check if this column has any blocks NOT in excluded groups
|
|
903
|
+
if (excludeGroups && excludeGroups.length > 0) {
|
|
904
|
+
const hasNonExcludedBlocks = column.children.some((child) => {
|
|
905
|
+
var _a, _b, _c;
|
|
906
|
+
if (child.type === 'block') {
|
|
907
|
+
const blockGroup = (_c = (_b = (_a = child.meta) === null || _a === void 0 ? void 0 : _a.source) === null || _b === void 0 ? void 0 : _b.group) === null || _c === void 0 ? void 0 : _c.name;
|
|
908
|
+
return !excludeGroups.includes(blockGroup);
|
|
909
|
+
}
|
|
910
|
+
return false;
|
|
911
|
+
});
|
|
912
|
+
// Skip rendering this column if all its blocks are excluded
|
|
913
|
+
if (!hasNonExcludedBlocks) {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return (<EditableWrapper key={column.id} componentId={column.id} componentType={column.type} node={column}>
|
|
918
|
+
<react_native_1.View style={{ flex: 1 }}>
|
|
919
|
+
{column.children.map((child) => {
|
|
920
|
+
var _a, _b, _c, _d, _e, _f;
|
|
921
|
+
if (child.type === 'block') {
|
|
922
|
+
// Skip submit button blocks - they're rendered after email invitations
|
|
923
|
+
if (child.subtype === 'vrtx-submit') {
|
|
924
|
+
return null;
|
|
925
|
+
}
|
|
926
|
+
// If filterGroup is provided, only render blocks from that group
|
|
927
|
+
if (filterGroup) {
|
|
928
|
+
const blockGroup = (_c = (_b = (_a = child.meta) === null || _a === void 0 ? void 0 : _a.source) === null || _b === void 0 ? void 0 : _b.group) === null || _c === void 0 ? void 0 : _c.name;
|
|
929
|
+
if (blockGroup !== filterGroup) {
|
|
930
|
+
return null;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
// If excludeGroups is provided, skip blocks from those groups
|
|
934
|
+
if (excludeGroups && excludeGroups.length > 0) {
|
|
935
|
+
const blockGroup = (_f = (_e = (_d = child.meta) === null || _d === void 0 ? void 0 : _d.source) === null || _e === void 0 ? void 0 : _e.group) === null || _f === void 0 ? void 0 : _f.name;
|
|
936
|
+
if (excludeGroups.includes(blockGroup)) {
|
|
937
|
+
return null;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
// Special handling for email invitations block - render submit button separately
|
|
941
|
+
if (child.subtype === 'vrtx-email-invitations' && submitButtonBlock) {
|
|
942
|
+
return (<react_1.default.Fragment key={child.id}>
|
|
943
|
+
{renderBlock(child)}
|
|
944
|
+
{renderBlock(submitButtonBlock)}
|
|
945
|
+
</react_1.default.Fragment>);
|
|
946
|
+
}
|
|
947
|
+
return renderBlock(child);
|
|
948
|
+
}
|
|
949
|
+
return null;
|
|
950
|
+
})}
|
|
951
|
+
</react_native_1.View>
|
|
952
|
+
</EditableWrapper>);
|
|
953
|
+
};
|
|
954
|
+
const renderRow = (row, index, totalRows, filterGroup, excludeGroups) => {
|
|
955
|
+
var _a, _b, _c, _d;
|
|
956
|
+
if (!row)
|
|
957
|
+
return null;
|
|
958
|
+
// Check if row has any visible content (columns with blocks)
|
|
959
|
+
const hasVisibleContent = (_a = row.children) === null || _a === void 0 ? void 0 : _a.some((child) => {
|
|
960
|
+
if (child.type === 'column' && child.children) {
|
|
961
|
+
return child.children.some((blockChild) => blockChild.type === 'block');
|
|
962
|
+
}
|
|
963
|
+
return false;
|
|
964
|
+
});
|
|
965
|
+
// In non-edit mode, skip rows without children entirely
|
|
966
|
+
if (!row.children && !isEditMode)
|
|
967
|
+
return null;
|
|
968
|
+
// If filtering by include group, check if this row has any columns with matching blocks
|
|
969
|
+
if (filterGroup) {
|
|
970
|
+
const hasMatchingContent = (_b = row.children) === null || _b === void 0 ? void 0 : _b.some((child) => {
|
|
971
|
+
if (child.type === 'column' && child.children) {
|
|
972
|
+
return child.children.some((blockChild) => {
|
|
973
|
+
var _a, _b, _c;
|
|
974
|
+
if (blockChild.type === 'block') {
|
|
975
|
+
const blockGroup = (_c = (_b = (_a = blockChild.meta) === null || _a === void 0 ? void 0 : _a.source) === null || _b === void 0 ? void 0 : _b.group) === null || _c === void 0 ? void 0 : _c.name;
|
|
976
|
+
return blockGroup === filterGroup;
|
|
977
|
+
}
|
|
978
|
+
return false;
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
return false;
|
|
982
|
+
});
|
|
983
|
+
// Skip rendering this row if it has no matching content
|
|
984
|
+
if (!hasMatchingContent) {
|
|
985
|
+
return null;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
// If excluding groups, check if this row has any columns with non-excluded blocks
|
|
989
|
+
if (excludeGroups && excludeGroups.length > 0) {
|
|
990
|
+
const hasNonExcludedContent = (_c = row.children) === null || _c === void 0 ? void 0 : _c.some((child) => {
|
|
991
|
+
if (child.type === 'column' && child.children) {
|
|
992
|
+
return child.children.some((blockChild) => {
|
|
993
|
+
var _a, _b, _c;
|
|
994
|
+
if (blockChild.type === 'block') {
|
|
995
|
+
const blockGroup = (_c = (_b = (_a = blockChild.meta) === null || _a === void 0 ? void 0 : _a.source) === null || _b === void 0 ? void 0 : _b.group) === null || _c === void 0 ? void 0 : _c.name;
|
|
996
|
+
return !excludeGroups.includes(blockGroup);
|
|
997
|
+
}
|
|
998
|
+
return false;
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
return false;
|
|
1002
|
+
});
|
|
1003
|
+
// Skip rendering this row if all its blocks are excluded
|
|
1004
|
+
// BUT in edit mode, allow empty rows through so they can be selected
|
|
1005
|
+
if (!hasNonExcludedContent && !(isEditMode && !hasVisibleContent)) {
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
// Determine if this is an empty row (no visible content)
|
|
1010
|
+
const isEmptyRow = !hasVisibleContent;
|
|
1011
|
+
return (<react_1.default.Fragment key={row.id}>
|
|
1012
|
+
{/* Drop zone before this row */}
|
|
1013
|
+
{isEditMode && isDragging && (<react_native_1.View
|
|
1014
|
+
// @ts-ignore
|
|
1015
|
+
data-drop-zone="between-rows" data-insertion-index={index} style={{
|
|
1016
|
+
height: 40,
|
|
1017
|
+
// borderWidth: 2,
|
|
1018
|
+
// borderColor: '#4CAF50',
|
|
1019
|
+
// borderStyle: 'dashed',
|
|
1020
|
+
// borderRadius: 4,
|
|
1021
|
+
// backgroundColor: 'rgba(76, 175, 80, 0.1)',
|
|
1022
|
+
marginVertical: 8,
|
|
1023
|
+
alignItems: 'center',
|
|
1024
|
+
justifyContent: 'center',
|
|
1025
|
+
}}>
|
|
1026
|
+
<react_native_1.Text style={{ color: '#4CAF50', fontSize: 12, fontWeight: '600' }}>
|
|
1027
|
+
{/* Drop here to insert at position {index} */}
|
|
1028
|
+
</react_native_1.Text>
|
|
1029
|
+
</react_native_1.View>)}
|
|
1030
|
+
|
|
1031
|
+
{/* The actual row */}
|
|
1032
|
+
<EditableWrapper componentId={row.id} componentType={row.type} node={row}>
|
|
1033
|
+
<react_native_1.View style={[
|
|
1034
|
+
styles.section,
|
|
1035
|
+
// In edit mode, empty rows get a minimum height so they can be selected
|
|
1036
|
+
isEditMode && isEmptyRow && styles.emptyRowEditMode,
|
|
1037
|
+
]}>
|
|
1038
|
+
{(_d = row.children) === null || _d === void 0 ? void 0 : _d.map((child) => {
|
|
1039
|
+
if (child.type === 'column') {
|
|
1040
|
+
return renderColumn(child, filterGroup, excludeGroups);
|
|
1041
|
+
}
|
|
1042
|
+
return null;
|
|
1043
|
+
})}
|
|
1044
|
+
</react_native_1.View>
|
|
1045
|
+
</EditableWrapper>
|
|
1046
|
+
|
|
1047
|
+
{/* Drop zone after last row */}
|
|
1048
|
+
{isEditMode && isDragging && index === totalRows - 1 && (<react_native_1.View
|
|
1049
|
+
// @ts-ignore
|
|
1050
|
+
data-drop-zone="between-rows" data-insertion-index={totalRows} style={{
|
|
1051
|
+
height: 40,
|
|
1052
|
+
borderWidth: 2,
|
|
1053
|
+
borderColor: '#4CAF50',
|
|
1054
|
+
borderStyle: 'dashed',
|
|
1055
|
+
borderRadius: 4,
|
|
1056
|
+
backgroundColor: 'rgba(76, 175, 80, 0.1)',
|
|
1057
|
+
marginVertical: 8,
|
|
1058
|
+
alignItems: 'center',
|
|
1059
|
+
justifyContent: 'center',
|
|
1060
|
+
}}>
|
|
1061
|
+
<react_native_1.Text style={{ color: '#4CAF50', fontSize: 12, fontWeight: '600' }}>
|
|
1062
|
+
Drop here to insert at position {totalRows}
|
|
1063
|
+
</react_native_1.Text>
|
|
1064
|
+
</react_native_1.View>)}
|
|
1065
|
+
</react_1.default.Fragment>);
|
|
1066
|
+
};
|
|
1067
|
+
// Helper function to get component label
|
|
1068
|
+
const getComponentLabel = (type) => {
|
|
1069
|
+
const labelMap = {
|
|
1070
|
+
'vrtx-share-options': 'Share Options',
|
|
1071
|
+
'vrtx-contacts-import': 'Contacts Import',
|
|
1072
|
+
'vrtx-email-invitations': 'Email Invitations',
|
|
1073
|
+
'vrtx-find-friends': 'Find Friends',
|
|
1074
|
+
'vrtx-incoming-invitations': 'Incoming Invitations',
|
|
1075
|
+
'vrtx-outgoing-invitations': 'Outgoing Invitations',
|
|
1076
|
+
'vrtx-invitation-suggestions': 'Invitation Suggestions',
|
|
1077
|
+
'vrtx-heading': 'Heading',
|
|
1078
|
+
'vrtx-text': 'Text',
|
|
1079
|
+
'vrtx-select': 'Select',
|
|
1080
|
+
'vrtx-submit': 'Submit',
|
|
1081
|
+
'vrtx-form-label': 'Label',
|
|
1082
|
+
'vrtx-row': 'Row',
|
|
1083
|
+
row: 'Row',
|
|
1084
|
+
column: 'Column',
|
|
1085
|
+
block: 'Block',
|
|
1086
|
+
};
|
|
1087
|
+
return labelMap[type] || type;
|
|
1088
|
+
};
|
|
1089
|
+
// EditableWrapper component for hover highlighting and drag-drop in edit mode
|
|
1090
|
+
// IMPORTANT: Memoized via useMemo to maintain stable component identity across re-renders.
|
|
1091
|
+
// Without this, every parent state change (e.g., typing in email input) would create a new
|
|
1092
|
+
// component type, causing React to unmount/remount all children — which blurs TextInputs.
|
|
1093
|
+
const EditableWrapper = react_1.default.useMemo(() => {
|
|
1094
|
+
const Wrapper = ({ children, componentId, componentType, node, flex, }) => {
|
|
1095
|
+
const wrapperRef = react_1.default.useRef(null);
|
|
1096
|
+
const labelRef = react_1.default.useRef(null);
|
|
1097
|
+
const leftMarginRef = react_1.default.useRef(null);
|
|
1098
|
+
const rightMarginRef = react_1.default.useRef(null);
|
|
1099
|
+
const outlineRef = react_1.default.useRef(null);
|
|
1100
|
+
// For rows on web, allow margin-based hover/selection
|
|
1101
|
+
const isRow = componentType === 'row';
|
|
1102
|
+
const isColumn = componentType === 'column';
|
|
1103
|
+
const isBlock = !isRow && !isColumn;
|
|
1104
|
+
// State for row margin hover
|
|
1105
|
+
const [isRowMarginHovered, setIsRowMarginHovered] = react_1.default.useState(false);
|
|
1106
|
+
// Determine hover and selection states
|
|
1107
|
+
const isHovered = isBlock
|
|
1108
|
+
? hoveredComponentId === componentId
|
|
1109
|
+
: isRow && isWeb
|
|
1110
|
+
? isRowMarginHovered
|
|
1111
|
+
: false;
|
|
1112
|
+
const isSelected = isBlock
|
|
1113
|
+
? selectedComponentId === componentId
|
|
1114
|
+
: isRow && isWeb
|
|
1115
|
+
? selectedComponentId === componentId
|
|
1116
|
+
: false;
|
|
1117
|
+
const showLabel = isEditMode && (isHovered || isSelected);
|
|
1118
|
+
// Create margin detection boxes for rows on web
|
|
1119
|
+
react_1.default.useEffect(() => {
|
|
1120
|
+
if (!isWeb || !isRow || !isEditMode) {
|
|
1121
|
+
// Clean up margin detectors if they exist
|
|
1122
|
+
if (leftMarginRef.current && leftMarginRef.current.parentNode) {
|
|
1123
|
+
leftMarginRef.current.parentNode.removeChild(leftMarginRef.current);
|
|
1124
|
+
leftMarginRef.current = null;
|
|
1125
|
+
}
|
|
1126
|
+
if (rightMarginRef.current && rightMarginRef.current.parentNode) {
|
|
1127
|
+
rightMarginRef.current.parentNode.removeChild(rightMarginRef.current);
|
|
1128
|
+
rightMarginRef.current = null;
|
|
1129
|
+
}
|
|
1130
|
+
if (outlineRef.current && outlineRef.current.parentNode) {
|
|
1131
|
+
outlineRef.current.parentNode.removeChild(outlineRef.current);
|
|
1132
|
+
outlineRef.current = null;
|
|
1133
|
+
}
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
if (!wrapperRef.current)
|
|
1137
|
+
return;
|
|
1138
|
+
// Find the outermost container with padding - this creates the margin areas
|
|
1139
|
+
// Walk up the DOM tree until we find a container that's significantly wider than the row
|
|
1140
|
+
let scrollContainer = wrapperRef.current.parentElement;
|
|
1141
|
+
const rowWidth = wrapperRef.current.getBoundingClientRect().width;
|
|
1142
|
+
let attempts = 0;
|
|
1143
|
+
while (scrollContainer && attempts < 15) {
|
|
1144
|
+
const containerWidth = scrollContainer.getBoundingClientRect().width;
|
|
1145
|
+
// If this container is significantly wider than the row (has margins/padding)
|
|
1146
|
+
if (containerWidth > rowWidth + 20) {
|
|
1147
|
+
break;
|
|
1148
|
+
}
|
|
1149
|
+
scrollContainer = scrollContainer.parentElement;
|
|
1150
|
+
attempts++;
|
|
1151
|
+
}
|
|
1152
|
+
// Fallback: use the widest ancestor we can find
|
|
1153
|
+
if (!scrollContainer || scrollContainer.getBoundingClientRect().width <= rowWidth) {
|
|
1154
|
+
scrollContainer = wrapperRef.current.parentElement;
|
|
1155
|
+
while (scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.parentElement) {
|
|
1156
|
+
const parent = scrollContainer.parentElement;
|
|
1157
|
+
if (parent.getBoundingClientRect().width > scrollContainer.getBoundingClientRect().width) {
|
|
1158
|
+
scrollContainer = parent;
|
|
1159
|
+
}
|
|
1160
|
+
else {
|
|
1161
|
+
break;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
if (!scrollContainer)
|
|
1166
|
+
return;
|
|
1167
|
+
// Create margin detectors and outline if they don't exist
|
|
1168
|
+
if (!leftMarginRef.current) {
|
|
1169
|
+
leftMarginRef.current = document.createElement('div');
|
|
1170
|
+
document.body.appendChild(leftMarginRef.current);
|
|
1171
|
+
}
|
|
1172
|
+
if (!rightMarginRef.current) {
|
|
1173
|
+
rightMarginRef.current = document.createElement('div');
|
|
1174
|
+
document.body.appendChild(rightMarginRef.current);
|
|
1175
|
+
}
|
|
1176
|
+
if (!outlineRef.current) {
|
|
1177
|
+
outlineRef.current = document.createElement('div');
|
|
1178
|
+
document.body.appendChild(outlineRef.current);
|
|
1179
|
+
}
|
|
1180
|
+
const updateMarginDetectors = () => {
|
|
1181
|
+
if (!wrapperRef.current || !scrollContainer)
|
|
1182
|
+
return;
|
|
1183
|
+
const rowRect = wrapperRef.current.getBoundingClientRect();
|
|
1184
|
+
const containerRect = scrollContainer.getBoundingClientRect();
|
|
1185
|
+
// Update outline box (full width)
|
|
1186
|
+
if (outlineRef.current) {
|
|
1187
|
+
Object.assign(outlineRef.current.style, {
|
|
1188
|
+
position: 'fixed',
|
|
1189
|
+
top: `${rowRect.top}px`,
|
|
1190
|
+
left: `${containerRect.left}px`,
|
|
1191
|
+
width: `${containerRect.width}px`,
|
|
1192
|
+
height: `${rowRect.height}px`,
|
|
1193
|
+
outline: isRowMarginHovered || isSelected
|
|
1194
|
+
? `3px solid ${isSelected ? '#1166c2' : '#72b3f3'}`
|
|
1195
|
+
: 'none',
|
|
1196
|
+
outlineOffset: '0px',
|
|
1197
|
+
borderRadius: '8px',
|
|
1198
|
+
pointerEvents: 'none',
|
|
1199
|
+
zIndex: '1199',
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
// Update left margin detector
|
|
1203
|
+
if (leftMarginRef.current) {
|
|
1204
|
+
const leftWidth = rowRect.left - containerRect.left;
|
|
1205
|
+
Object.assign(leftMarginRef.current.style, {
|
|
1206
|
+
position: 'fixed',
|
|
1207
|
+
top: `${rowRect.top}px`,
|
|
1208
|
+
left: `${containerRect.left}px`,
|
|
1209
|
+
width: `${leftWidth}px`,
|
|
1210
|
+
height: `${rowRect.height}px`,
|
|
1211
|
+
cursor: 'pointer',
|
|
1212
|
+
pointerEvents: 'auto',
|
|
1213
|
+
zIndex: '1199',
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
// Update right margin detector
|
|
1217
|
+
if (rightMarginRef.current) {
|
|
1218
|
+
const rightWidth = containerRect.right - rowRect.right;
|
|
1219
|
+
Object.assign(rightMarginRef.current.style, {
|
|
1220
|
+
position: 'fixed',
|
|
1221
|
+
top: `${rowRect.top}px`,
|
|
1222
|
+
left: `${rowRect.right}px`,
|
|
1223
|
+
width: `${rightWidth}px`,
|
|
1224
|
+
height: `${rowRect.height}px`,
|
|
1225
|
+
cursor: 'pointer',
|
|
1226
|
+
pointerEvents: 'auto',
|
|
1227
|
+
zIndex: '1199',
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
// Set up event handlers for margin detectors
|
|
1232
|
+
const handleMarginEnter = () => {
|
|
1233
|
+
setIsRowMarginHovered(true);
|
|
1234
|
+
};
|
|
1235
|
+
const handleMarginLeave = () => {
|
|
1236
|
+
setIsRowMarginHovered(false);
|
|
1237
|
+
};
|
|
1238
|
+
const handleMarginClick = (e) => {
|
|
1239
|
+
e.stopPropagation();
|
|
1240
|
+
setSelectedComponentId(componentId);
|
|
1241
|
+
if (onComponentSelect) {
|
|
1242
|
+
onComponentSelect({
|
|
1243
|
+
id: componentId,
|
|
1244
|
+
type: componentType,
|
|
1245
|
+
element: node || null,
|
|
1246
|
+
styles: {},
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
leftMarginRef.current.addEventListener('mouseenter', handleMarginEnter);
|
|
1251
|
+
leftMarginRef.current.addEventListener('mouseleave', handleMarginLeave);
|
|
1252
|
+
leftMarginRef.current.addEventListener('click', handleMarginClick);
|
|
1253
|
+
rightMarginRef.current.addEventListener('mouseenter', handleMarginEnter);
|
|
1254
|
+
rightMarginRef.current.addEventListener('mouseleave', handleMarginLeave);
|
|
1255
|
+
rightMarginRef.current.addEventListener('click', handleMarginClick);
|
|
1256
|
+
updateMarginDetectors();
|
|
1257
|
+
window.addEventListener('scroll', updateMarginDetectors, true);
|
|
1258
|
+
window.addEventListener('resize', updateMarginDetectors);
|
|
1259
|
+
return () => {
|
|
1260
|
+
window.removeEventListener('scroll', updateMarginDetectors, true);
|
|
1261
|
+
window.removeEventListener('resize', updateMarginDetectors);
|
|
1262
|
+
if (leftMarginRef.current) {
|
|
1263
|
+
leftMarginRef.current.removeEventListener('mouseenter', handleMarginEnter);
|
|
1264
|
+
leftMarginRef.current.removeEventListener('mouseleave', handleMarginLeave);
|
|
1265
|
+
leftMarginRef.current.removeEventListener('click', handleMarginClick);
|
|
1266
|
+
if (leftMarginRef.current.parentNode) {
|
|
1267
|
+
leftMarginRef.current.parentNode.removeChild(leftMarginRef.current);
|
|
1268
|
+
}
|
|
1269
|
+
leftMarginRef.current = null;
|
|
1270
|
+
}
|
|
1271
|
+
if (rightMarginRef.current) {
|
|
1272
|
+
rightMarginRef.current.removeEventListener('mouseenter', handleMarginEnter);
|
|
1273
|
+
rightMarginRef.current.removeEventListener('mouseleave', handleMarginLeave);
|
|
1274
|
+
rightMarginRef.current.removeEventListener('click', handleMarginClick);
|
|
1275
|
+
if (rightMarginRef.current.parentNode) {
|
|
1276
|
+
rightMarginRef.current.parentNode.removeChild(rightMarginRef.current);
|
|
1277
|
+
}
|
|
1278
|
+
rightMarginRef.current = null;
|
|
1279
|
+
}
|
|
1280
|
+
if (outlineRef.current && outlineRef.current.parentNode) {
|
|
1281
|
+
outlineRef.current.parentNode.removeChild(outlineRef.current);
|
|
1282
|
+
outlineRef.current = null;
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
}, [
|
|
1286
|
+
isWeb,
|
|
1287
|
+
isRow,
|
|
1288
|
+
isEditMode,
|
|
1289
|
+
isRowMarginHovered,
|
|
1290
|
+
isSelected,
|
|
1291
|
+
componentId,
|
|
1292
|
+
componentType,
|
|
1293
|
+
node,
|
|
1294
|
+
onComponentSelect,
|
|
1295
|
+
]);
|
|
1296
|
+
// Create and manage a portal-like label element
|
|
1297
|
+
react_1.default.useEffect(() => {
|
|
1298
|
+
if (!isWeb || !showLabel) {
|
|
1299
|
+
// Clean up label if it exists
|
|
1300
|
+
if (labelRef.current && labelRef.current.parentNode) {
|
|
1301
|
+
labelRef.current.parentNode.removeChild(labelRef.current);
|
|
1302
|
+
labelRef.current = null;
|
|
1303
|
+
}
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
if (!wrapperRef.current)
|
|
1307
|
+
return;
|
|
1308
|
+
// Find the container using the same logic as margin detectors
|
|
1309
|
+
let scrollContainer = null;
|
|
1310
|
+
if (isRow) {
|
|
1311
|
+
// For rows, find the outermost container with padding
|
|
1312
|
+
scrollContainer = wrapperRef.current.parentElement;
|
|
1313
|
+
const rowWidth = wrapperRef.current.getBoundingClientRect().width;
|
|
1314
|
+
let attempts = 0;
|
|
1315
|
+
while (scrollContainer && attempts < 15) {
|
|
1316
|
+
const containerWidth = scrollContainer.getBoundingClientRect().width;
|
|
1317
|
+
if (containerWidth > rowWidth + 20) {
|
|
1318
|
+
break;
|
|
1319
|
+
}
|
|
1320
|
+
scrollContainer = scrollContainer.parentElement;
|
|
1321
|
+
attempts++;
|
|
1322
|
+
}
|
|
1323
|
+
if (!scrollContainer || scrollContainer.getBoundingClientRect().width <= rowWidth) {
|
|
1324
|
+
scrollContainer = wrapperRef.current.parentElement;
|
|
1325
|
+
while (scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.parentElement) {
|
|
1326
|
+
const parent = scrollContainer.parentElement;
|
|
1327
|
+
if (parent.getBoundingClientRect().width > scrollContainer.getBoundingClientRect().width) {
|
|
1328
|
+
scrollContainer = parent;
|
|
1329
|
+
}
|
|
1330
|
+
else {
|
|
1331
|
+
break;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
// Create label element if it doesn't exist
|
|
1337
|
+
if (!labelRef.current) {
|
|
1338
|
+
labelRef.current = document.createElement('div');
|
|
1339
|
+
document.body.appendChild(labelRef.current);
|
|
1340
|
+
}
|
|
1341
|
+
const updateLabelPosition = () => {
|
|
1342
|
+
if (!wrapperRef.current || !labelRef.current)
|
|
1343
|
+
return;
|
|
1344
|
+
const rect = wrapperRef.current.getBoundingClientRect();
|
|
1345
|
+
const label = labelRef.current;
|
|
1346
|
+
// For rows on web, position at extreme left of container
|
|
1347
|
+
let leftPos = rect.left + 5;
|
|
1348
|
+
if (isRow && scrollContainer) {
|
|
1349
|
+
const containerRect = scrollContainer.getBoundingClientRect();
|
|
1350
|
+
leftPos = containerRect.left;
|
|
1351
|
+
}
|
|
1352
|
+
// Update styles
|
|
1353
|
+
Object.assign(label.style, {
|
|
1354
|
+
position: 'fixed',
|
|
1355
|
+
left: `${leftPos}px`,
|
|
1356
|
+
top: `${rect.top - 25}px`,
|
|
1357
|
+
height: '25px',
|
|
1358
|
+
display: 'flex',
|
|
1359
|
+
alignItems: 'center',
|
|
1360
|
+
backgroundColor: isSelected ? '#1166c2' : '#72b3f3',
|
|
1361
|
+
color: 'white',
|
|
1362
|
+
padding: '0 12px',
|
|
1363
|
+
fontSize: '11px',
|
|
1364
|
+
fontWeight: '600',
|
|
1365
|
+
borderRadius: '2px 2px 0 0',
|
|
1366
|
+
whiteSpace: 'nowrap',
|
|
1367
|
+
zIndex: '1200',
|
|
1368
|
+
pointerEvents: 'none',
|
|
1369
|
+
userSelect: 'none',
|
|
1370
|
+
boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.3)',
|
|
1371
|
+
});
|
|
1372
|
+
label.textContent = getComponentLabel((node === null || node === void 0 ? void 0 : node.subtype) || componentType);
|
|
1373
|
+
};
|
|
1374
|
+
updateLabelPosition();
|
|
1375
|
+
window.addEventListener('scroll', updateLabelPosition, true);
|
|
1376
|
+
window.addEventListener('resize', updateLabelPosition);
|
|
1377
|
+
return () => {
|
|
1378
|
+
window.removeEventListener('scroll', updateLabelPosition, true);
|
|
1379
|
+
window.removeEventListener('resize', updateLabelPosition);
|
|
1380
|
+
if (labelRef.current && labelRef.current.parentNode) {
|
|
1381
|
+
labelRef.current.parentNode.removeChild(labelRef.current);
|
|
1382
|
+
labelRef.current = null;
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
}, [showLabel, isSelected, node, componentType, isWeb, isRow, isEditMode]);
|
|
1386
|
+
// Use the toolbar hook to manage the delete button
|
|
1387
|
+
(0, PlacedItemToolbar_1.usePlacedItemToolbar)({
|
|
1388
|
+
wrapperRef,
|
|
1389
|
+
isSelected,
|
|
1390
|
+
componentId,
|
|
1391
|
+
componentType,
|
|
1392
|
+
isRow,
|
|
1393
|
+
nodeSubtype: node === null || node === void 0 ? void 0 : node.subtype,
|
|
1394
|
+
});
|
|
1395
|
+
if (!isEditMode)
|
|
1396
|
+
return <>{children}</>;
|
|
1397
|
+
// Helper function to check if element is inside a contentEditable element
|
|
1398
|
+
const isInsideContentEditable = (target) => {
|
|
1399
|
+
var _a, _b;
|
|
1400
|
+
if (!target)
|
|
1401
|
+
return false;
|
|
1402
|
+
let el = target;
|
|
1403
|
+
let depth = 0;
|
|
1404
|
+
while (el && depth < 20) {
|
|
1405
|
+
if (((_a = el.getAttribute) === null || _a === void 0 ? void 0 : _a.call(el, 'data-slate-editor')) === 'true') {
|
|
1406
|
+
return true;
|
|
1407
|
+
}
|
|
1408
|
+
if (el.contentEditable === 'true' || ((_b = el.getAttribute) === null || _b === void 0 ? void 0 : _b.call(el, 'contenteditable')) === 'true') {
|
|
1409
|
+
return true;
|
|
1410
|
+
}
|
|
1411
|
+
el = el.parentElement;
|
|
1412
|
+
depth++;
|
|
1413
|
+
}
|
|
1414
|
+
return false;
|
|
1415
|
+
};
|
|
1416
|
+
const handleClick = (e) => {
|
|
1417
|
+
var _a, _b, _c, _d, _e;
|
|
1418
|
+
// Don't handle clicks on columns - let them pass through to child blocks
|
|
1419
|
+
// Rows are allowed so empty rows can be selected and deleted
|
|
1420
|
+
if (isColumn) {
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
const target = (e === null || e === void 0 ? void 0 : e.target) || ((_a = e === null || e === void 0 ? void 0 : e.nativeEvent) === null || _a === void 0 ? void 0 : _a.target);
|
|
1424
|
+
// Check if the click is inside a contentEditable element
|
|
1425
|
+
if (isInsideContentEditable(target)) {
|
|
1426
|
+
// If the component is already selected, allow the click to proceed for text editing
|
|
1427
|
+
if (isSelected) {
|
|
1428
|
+
console.log('[EditableWrapper] Allowing click inside contentEditable element (already selected)');
|
|
1429
|
+
(_b = e === null || e === void 0 ? void 0 : e.stopPropagation) === null || _b === void 0 ? void 0 : _b.call(e);
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
// If not selected, we'll select it first (don't return early)
|
|
1433
|
+
console.log('[EditableWrapper] Selecting component with contentEditable element');
|
|
1434
|
+
}
|
|
1435
|
+
if (target) {
|
|
1436
|
+
let el = target;
|
|
1437
|
+
let depth = 0;
|
|
1438
|
+
while (el && depth < 20) {
|
|
1439
|
+
// Check for circle button attribute
|
|
1440
|
+
if (((_c = el.getAttribute) === null || _c === void 0 ? void 0 : _c.call(el, 'data-circle-button')) === 'true') {
|
|
1441
|
+
console.log('[EditableWrapper] Ignoring circle button click - found data attribute');
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
el = el.parentElement;
|
|
1445
|
+
depth++;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
// Ignore clicks from circle button (legacy flag check)
|
|
1449
|
+
if ((e === null || e === void 0 ? void 0 : e._circleButtonClick) || ((_d = e === null || e === void 0 ? void 0 : e.nativeEvent) === null || _d === void 0 ? void 0 : _d._circleButtonClick)) {
|
|
1450
|
+
console.log('[EditableWrapper] Ignoring circle button click - found flag');
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
// Only handle once - prevent double firing from both capture and bubble
|
|
1454
|
+
if (e === null || e === void 0 ? void 0 : e._editable_handled)
|
|
1455
|
+
return;
|
|
1456
|
+
if (e)
|
|
1457
|
+
e._editable_handled = true;
|
|
1458
|
+
(_e = e === null || e === void 0 ? void 0 : e.stopPropagation) === null || _e === void 0 ? void 0 : _e.call(e);
|
|
1459
|
+
console.log('[EditableWrapper] Clicked:', componentId, componentType, 'onComponentSelect exists:', !!onComponentSelect);
|
|
1460
|
+
setSelectedComponentId(componentId);
|
|
1461
|
+
if (onComponentSelect) {
|
|
1462
|
+
console.log('[EditableWrapper] Calling onComponentSelect with:', {
|
|
1463
|
+
id: componentId,
|
|
1464
|
+
type: componentType,
|
|
1465
|
+
element: node || null,
|
|
1466
|
+
});
|
|
1467
|
+
onComponentSelect({
|
|
1468
|
+
id: componentId,
|
|
1469
|
+
type: componentType,
|
|
1470
|
+
element: node || null,
|
|
1471
|
+
styles: {},
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
else {
|
|
1475
|
+
console.warn('[EditableWrapper] onComponentSelect is not defined!');
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1478
|
+
return (<div ref={wrapperRef} data-component-id={componentId} data-component-type={componentType} style={Object.assign({ position: 'relative' }, (flex && { flex: 1 }))} onMouseEnter={isColumn
|
|
1479
|
+
? undefined
|
|
1480
|
+
: isColumn
|
|
1481
|
+
? undefined
|
|
1482
|
+
: (e) => {
|
|
1483
|
+
if (!isInsideContentEditable(e.target)) {
|
|
1484
|
+
setHoveredComponentId(componentId);
|
|
1485
|
+
}
|
|
1486
|
+
}} onMouseLeave={isColumn
|
|
1487
|
+
? undefined
|
|
1488
|
+
: isColumn
|
|
1489
|
+
? undefined
|
|
1490
|
+
: (e) => {
|
|
1491
|
+
if (!isInsideContentEditable(e.target)) {
|
|
1492
|
+
setHoveredComponentId(null);
|
|
1493
|
+
}
|
|
1494
|
+
}} onClick={handleClick}>
|
|
1495
|
+
{children}
|
|
1496
|
+
{/* Selection border - only for blocks, not rows (rows use full-width outline) */}
|
|
1497
|
+
{showLabel && !isRow && (<>
|
|
1498
|
+
<div style={{
|
|
1499
|
+
position: 'absolute',
|
|
1500
|
+
top: 0,
|
|
1501
|
+
left: 0,
|
|
1502
|
+
right: 0,
|
|
1503
|
+
bottom: 0,
|
|
1504
|
+
border: `3px solid ${isSelected ? '#1166c2' : '#72b3f3'}`,
|
|
1505
|
+
borderRadius: '8px',
|
|
1506
|
+
pointerEvents: 'none',
|
|
1507
|
+
zIndex: 1,
|
|
1508
|
+
}}/>
|
|
1509
|
+
</>)}
|
|
1510
|
+
</div>);
|
|
1511
|
+
};
|
|
1512
|
+
return Wrapper;
|
|
1513
|
+
}, [isEditMode, hoveredComponentId, selectedComponentId, onComponentSelect, onComponentDelete]);
|
|
1514
|
+
const styles = createStyles(fontFamily);
|
|
1515
|
+
const handleHeaderButtonPress = () => {
|
|
1516
|
+
if (view === 'main') {
|
|
1517
|
+
handleClose();
|
|
1518
|
+
}
|
|
1519
|
+
else {
|
|
1520
|
+
handleBackToMain();
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
// Deselect when clicking on empty space (web only)
|
|
1524
|
+
react_1.default.useEffect(() => {
|
|
1525
|
+
if (!isEditMode || !isWeb)
|
|
1526
|
+
return;
|
|
1527
|
+
const handleDocumentClick = (e) => {
|
|
1528
|
+
let el = e.target;
|
|
1529
|
+
while (el) {
|
|
1530
|
+
// If the click is inside an editable element, do nothing
|
|
1531
|
+
if (el.getAttribute) {
|
|
1532
|
+
if (el.getAttribute('data-component-id') ||
|
|
1533
|
+
el.getAttribute('data-circle-button') === 'true') {
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
el = el.parentElement;
|
|
1538
|
+
}
|
|
1539
|
+
// Click was on empty space, deselect
|
|
1540
|
+
setSelectedComponentId(null);
|
|
1541
|
+
};
|
|
1542
|
+
document.addEventListener('click', handleDocumentClick, true);
|
|
1543
|
+
return () => {
|
|
1544
|
+
document.removeEventListener('click', handleDocumentClick, true);
|
|
1545
|
+
};
|
|
1546
|
+
}, [isEditMode]);
|
|
1547
|
+
// Handle component deletion (web only)
|
|
1548
|
+
react_1.default.useEffect(() => {
|
|
1549
|
+
if (!isWeb || !isEditMode)
|
|
1550
|
+
return;
|
|
1551
|
+
const handleDeleteEvent = (e) => {
|
|
1552
|
+
const customEvent = e;
|
|
1553
|
+
const { id, type } = customEvent.detail;
|
|
1554
|
+
if (onComponentDelete) {
|
|
1555
|
+
onComponentDelete(id, type);
|
|
1556
|
+
}
|
|
1557
|
+
// Also deselect the component
|
|
1558
|
+
setSelectedComponentId(null);
|
|
1559
|
+
};
|
|
1560
|
+
document.addEventListener('vortex-delete-component', handleDeleteEvent, true);
|
|
1561
|
+
return () => {
|
|
1562
|
+
document.removeEventListener('vortex-delete-component', handleDeleteEvent, true);
|
|
1563
|
+
};
|
|
1564
|
+
}, [isEditMode, onComponentDelete]);
|
|
1565
|
+
// Extract root form styles from formStructure
|
|
1566
|
+
// Transform CSS properties to React Native equivalents (e.g., 'background' -> 'backgroundColor')
|
|
1567
|
+
const rawRootStyle = (formStructure === null || formStructure === void 0 ? void 0 : formStructure.style) || {};
|
|
1568
|
+
const rootFormStyle = {};
|
|
1569
|
+
for (const [key, value] of Object.entries(rawRootStyle)) {
|
|
1570
|
+
if (key === 'background' && typeof value === 'string' && !value.includes('gradient')) {
|
|
1571
|
+
// Convert CSS 'background' to RN 'backgroundColor' for solid colors
|
|
1572
|
+
rootFormStyle.backgroundColor = value;
|
|
1573
|
+
}
|
|
1574
|
+
else if (key !== 'background') {
|
|
1575
|
+
// Pass through other properties (skip gradients which need special handling)
|
|
1576
|
+
rootFormStyle[key] = value;
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
return (<react_native_1.View style={Object.assign(Object.assign(Object.assign({}, styles.container), containerStyle), rootFormStyle)}>
|
|
1580
|
+
{/* Header with Close or Back button */}
|
|
1581
|
+
<react_native_1.View style={Object.assign(Object.assign({}, styles.header), rootFormStyle)}>
|
|
1582
|
+
<react_native_1.View style={[styles.headerButton, { minWidth: 44, minHeight: 44, justifyContent: 'center' }]} onStartShouldSetResponder={() => {
|
|
1583
|
+
return true;
|
|
1584
|
+
}} onResponderGrant={() => {
|
|
1585
|
+
// Handle responder grant
|
|
1586
|
+
}} onResponderRelease={() => {
|
|
1587
|
+
handleHeaderButtonPress();
|
|
1588
|
+
}}>
|
|
1589
|
+
{renderIcon({
|
|
1590
|
+
name: view === 'main' ? 'close' : 'arrow-back',
|
|
1591
|
+
size: 24,
|
|
1592
|
+
color: (resolvedTitleStyle === null || resolvedTitleStyle === void 0 ? void 0 : resolvedTitleStyle.color) || '#666',
|
|
1593
|
+
opacity: 0.75,
|
|
1594
|
+
})}
|
|
1595
|
+
</react_native_1.View>
|
|
1596
|
+
{resolvedTitle && (<react_native_1.Text style={[
|
|
1597
|
+
styles.headerTitle,
|
|
1598
|
+
resolvedTitleStyle && Object.assign(Object.assign(Object.assign(Object.assign({}, (resolvedTitleStyle.color && { color: resolvedTitleStyle.color })), (resolvedTitleStyle.fontSize && { fontSize: resolvedTitleStyle.fontSize })), (resolvedTitleStyle.fontWeight && { fontWeight: resolvedTitleStyle.fontWeight })), (resolvedTitleStyle.fontFamily && { fontFamily: resolvedTitleStyle.fontFamily })),
|
|
1599
|
+
]}>
|
|
1600
|
+
{resolvedTitle}
|
|
1601
|
+
</react_native_1.Text>)}
|
|
1602
|
+
{/* Empty spacer to balance the header when title is present */}
|
|
1603
|
+
{resolvedTitle && <react_native_1.View style={[styles.headerButton, { minWidth: 44, minHeight: 44 }]}/>}
|
|
1604
|
+
</react_native_1.View>
|
|
1605
|
+
|
|
1606
|
+
<react_native_1.ScrollView style={Object.assign(Object.assign({}, styles.scrollContent), rootFormStyle)} contentContainerStyle={rootFormStyle} showsVerticalScrollIndicator={false}>
|
|
1607
|
+
{view === 'main' && formStructure && (<>
|
|
1608
|
+
{/* <EditableWrapper componentId="title-text" componentType="text">
|
|
1609
|
+
<Text style={styles.title}>Invite people to join your team</Text>
|
|
1610
|
+
</EditableWrapper> */}
|
|
1611
|
+
|
|
1612
|
+
{/* Dynamic rendering from formStructure */}
|
|
1613
|
+
{(_g = formStructure.children) === null || _g === void 0 ? void 0 : _g.map((child, index) => {
|
|
1614
|
+
if (child.type === 'row') {
|
|
1615
|
+
return renderRow(child, index, formStructure.children.length);
|
|
1616
|
+
}
|
|
1617
|
+
return null;
|
|
1618
|
+
})}
|
|
1619
|
+
</>)}
|
|
1620
|
+
|
|
1621
|
+
{view === 'main' && !formStructure && (<react_native_1.View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }}>
|
|
1622
|
+
<react_native_1.Text style={{ fontSize: 50, marginBottom: 16 }}>⚠️</react_native_1.Text>
|
|
1623
|
+
<react_native_1.Text style={{ fontSize: 18, fontWeight: 'bold', color: '#d9534f', marginBottom: 8, fontFamily }}>
|
|
1624
|
+
Configuration Error
|
|
1625
|
+
</react_native_1.Text>
|
|
1626
|
+
<react_native_1.Text style={{ fontSize: 14, color: '#666', textAlign: 'center', fontFamily }}>
|
|
1627
|
+
Unable to load form structure from widget configuration. Please check your widget configuration and try again.
|
|
1628
|
+
</react_native_1.Text>
|
|
1629
|
+
</react_native_1.View>)}
|
|
1630
|
+
|
|
1631
|
+
{view === 'email' && formStructure && (<>
|
|
1632
|
+
<react_native_1.View style={styles.section}>
|
|
1633
|
+
{/* Dynamic rendering from formStructure - only email-related blocks */}
|
|
1634
|
+
{(_h = formStructure.children) === null || _h === void 0 ? void 0 : _h.map((child, index) => {
|
|
1635
|
+
if (child.type === 'row') {
|
|
1636
|
+
// Render all rows, but filter blocks inside to only show grp-email-invitations
|
|
1637
|
+
return renderRow(child, index, formStructure.children.length, 'grp-email-invitations');
|
|
1638
|
+
}
|
|
1639
|
+
return null;
|
|
1640
|
+
})}
|
|
1641
|
+
</react_native_1.View>
|
|
1642
|
+
</>)}
|
|
1643
|
+
|
|
1644
|
+
{view === 'email' && !formStructure && (<react_native_1.View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }}>
|
|
1645
|
+
<react_native_1.Text style={{ fontSize: 50, marginBottom: 16 }}>⚠️</react_native_1.Text>
|
|
1646
|
+
<react_native_1.Text style={{ fontSize: 18, fontWeight: 'bold', color: '#d9534f', marginBottom: 8, fontFamily }}>
|
|
1647
|
+
Configuration Error
|
|
1648
|
+
</react_native_1.Text>
|
|
1649
|
+
<react_native_1.Text style={{ fontSize: 14, color: '#666', textAlign: 'center', fontFamily }}>
|
|
1650
|
+
Unable to load form structure from widget configuration. Please check your widget configuration and try again.
|
|
1651
|
+
</react_native_1.Text>
|
|
1652
|
+
</react_native_1.View>)}
|
|
1653
|
+
|
|
1654
|
+
{view === 'contacts' && (<>
|
|
1655
|
+
<react_native_1.Text style={styles.title}>{getContactsCustomization('importContacts.title', 'Add from Contacts')}</react_native_1.Text>
|
|
1656
|
+
|
|
1657
|
+
<react_native_1.View style={styles.section}>
|
|
1658
|
+
<react_native_1.TextInput style={styles.input} placeholder={getContactsCustomization('importContacts.searchPlaceholder', 'Search contacts...')} placeholderTextColor="#999" value={searchQuery} onChangeText={setSearchQuery} autoCapitalize="none" autoCorrect={false} returnKeyType="search"/>
|
|
1659
|
+
|
|
1660
|
+
{loadingContacts ? (<react_native_1.View style={styles.loadingContainer}>
|
|
1661
|
+
<react_native_1.ActivityIndicator size="large" color="#6291d5"/>
|
|
1662
|
+
<react_native_1.Text style={styles.loadingText}>{getContactsCustomization('importContacts.loadingText', 'Loading contacts...')}</react_native_1.Text>
|
|
1663
|
+
</react_native_1.View>) : contactsError ? (<react_native_1.View style={styles.errorContainer}>
|
|
1664
|
+
<react_native_1.Text style={styles.errorTitle}>{getContactsCustomization('importContacts.errorTitle', 'Unable to Access Contacts')}</react_native_1.Text>
|
|
1665
|
+
<react_native_1.Text style={styles.errorMessage}>{contactsError.message}</react_native_1.Text>
|
|
1666
|
+
{contactsError.message.includes('Settings') && (<>
|
|
1667
|
+
<react_native_1.Text style={styles.errorHint}>
|
|
1668
|
+
To grant access: Open Settings → Privacy & Security → Contacts → Enable for
|
|
1669
|
+
this app
|
|
1670
|
+
</react_native_1.Text>
|
|
1671
|
+
<react_native_1.View style={styles.errorActions}>
|
|
1672
|
+
<react_native_1.TouchableOpacity style={[styles.button, styles.secondaryButton, styles.halfButton]} onPress={handleOpenSettings}>
|
|
1673
|
+
<react_native_1.Text style={styles.secondaryButtonText}>{getContactsCustomization('importContacts.openSettingsButton', 'Open Settings')}</react_native_1.Text>
|
|
1674
|
+
</react_native_1.TouchableOpacity>
|
|
1675
|
+
<react_native_1.TouchableOpacity style={[styles.button, styles.secondaryButton, styles.halfButton]} onPress={handleRetryContacts}>
|
|
1676
|
+
<react_native_1.Text style={styles.secondaryButtonText}>{getContactsCustomization('importContacts.retryButton', 'Retry')}</react_native_1.Text>
|
|
1677
|
+
</react_native_1.TouchableOpacity>
|
|
1678
|
+
</react_native_1.View>
|
|
1679
|
+
</>)}
|
|
1680
|
+
{!contactsError.message.includes('Settings') && (<react_native_1.TouchableOpacity style={[styles.button, styles.secondaryButton, { marginTop: 16 }]} onPress={handleRetryContacts}>
|
|
1681
|
+
<react_native_1.Text style={styles.secondaryButtonText}>{getContactsCustomization('importContacts.tryAgainButton', 'Try Again')}</react_native_1.Text>
|
|
1682
|
+
</react_native_1.TouchableOpacity>)}
|
|
1683
|
+
<react_native_1.Text style={styles.errorDetails}>
|
|
1684
|
+
{contactsError.message || 'Unknown error occurred'}
|
|
1685
|
+
</react_native_1.Text>
|
|
1686
|
+
</react_native_1.View>) : (<react_native_1.View style={styles.contactsList}>
|
|
1687
|
+
{filteredContacts.length === 0 ? (<react_native_1.Text style={styles.emptyText}>
|
|
1688
|
+
{searchQuery
|
|
1689
|
+
? getContactsCustomization('importContacts.emptySearchState', 'No contacts match your search')
|
|
1690
|
+
: getContactsCustomization('importContacts.emptyState', 'No contacts with email addresses found')}
|
|
1691
|
+
</react_native_1.Text>) : (filteredContacts.map((contact) => {
|
|
1692
|
+
const isInvited = invitedContactIds.has(contact.id);
|
|
1693
|
+
const isLoading = loadingContactIds.has(contact.id);
|
|
1694
|
+
const initials = contact.name
|
|
1695
|
+
.split(' ')
|
|
1696
|
+
.map((part) => part[0])
|
|
1697
|
+
.join('')
|
|
1698
|
+
.toUpperCase()
|
|
1699
|
+
.slice(0, 2);
|
|
1700
|
+
return (<react_native_1.View key={contact.id} style={styles.contactItem}>
|
|
1701
|
+
{contact.imageUri ? (<react_native_1.Image source={{ uri: contact.imageUri }} style={styles.contactAvatar}/>) : (<react_native_1.View style={styles.contactAvatarPlaceholder}>
|
|
1702
|
+
<react_native_1.Text style={styles.contactAvatarInitials}>{initials}</react_native_1.Text>
|
|
1703
|
+
</react_native_1.View>)}
|
|
1704
|
+
<react_native_1.View style={styles.contactInfo}>
|
|
1705
|
+
<react_native_1.Text style={styles.contactName}>{contact.name}</react_native_1.Text>
|
|
1706
|
+
<react_native_1.Text style={styles.contactEmail}>{contact.email}</react_native_1.Text>
|
|
1707
|
+
</react_native_1.View>
|
|
1708
|
+
{isInvited ? (<react_native_1.Text style={styles.invitedText}>{getContactsCustomization('importContacts.invitedStatus', '✓ Invited!')}</react_native_1.Text>) : (<react_native_1.TouchableOpacity style={styles.inviteButton} onPress={() => handleInviteContactWithHaptics(contact.id)} disabled={isLoading}>
|
|
1709
|
+
{isLoading ? (<react_native_1.ActivityIndicator size="small" color="#333"/>) : (<react_native_1.Text style={styles.inviteButtonText}>{getContactsCustomization('importContacts.inviteButton', 'Invite')}</react_native_1.Text>)}
|
|
1710
|
+
</react_native_1.TouchableOpacity>)}
|
|
1711
|
+
</react_native_1.View>);
|
|
1712
|
+
}))}
|
|
1713
|
+
</react_native_1.View>)}
|
|
1714
|
+
</react_native_1.View>
|
|
1715
|
+
</>)}
|
|
1716
|
+
|
|
1717
|
+
{view === 'googleContacts' && (<>
|
|
1718
|
+
<react_native_1.Text style={styles.title}>{getContactsCustomization('google.title', 'Add from Google Contacts')}</react_native_1.Text>
|
|
1719
|
+
|
|
1720
|
+
<react_native_1.View style={styles.section}>
|
|
1721
|
+
<react_native_1.TextInput style={styles.input} placeholder={getContactsCustomization('google.searchPlaceholder', 'Search contacts...')} placeholderTextColor="#999" value={googleSearchQuery} onChangeText={setGoogleSearchQuery} autoCapitalize="none" autoCorrect={false} returnKeyType="search"/>
|
|
1722
|
+
|
|
1723
|
+
{loadingGoogleContacts ? (<react_native_1.View style={styles.loadingContainer}>
|
|
1724
|
+
<react_native_1.ActivityIndicator size="large" color="#6291d5"/>
|
|
1725
|
+
<react_native_1.Text style={styles.loadingText}>{getContactsCustomization('google.loadingText', 'Loading Google contacts...')}</react_native_1.Text>
|
|
1726
|
+
</react_native_1.View>) : googleContactsError ? (<react_native_1.View style={styles.errorContainer}>
|
|
1727
|
+
<react_native_1.Text style={styles.errorTitle}>{getContactsCustomization('google.errorTitle', 'Unable to Access Google Contacts')}</react_native_1.Text>
|
|
1728
|
+
<react_native_1.Text style={styles.errorMessage}>{googleContactsError.message}</react_native_1.Text>
|
|
1729
|
+
<react_native_1.TouchableOpacity style={[styles.button, styles.secondaryButton, { marginTop: 16 }]} onPress={handleRetryContacts}>
|
|
1730
|
+
<react_native_1.Text style={styles.secondaryButtonText}>{getContactsCustomization('google.tryAgainButton', 'Try Again')}</react_native_1.Text>
|
|
1731
|
+
</react_native_1.TouchableOpacity>
|
|
1732
|
+
<react_native_1.Text style={styles.errorDetails}>
|
|
1733
|
+
{googleContactsError.message || 'Unknown error occurred'}
|
|
1734
|
+
</react_native_1.Text>
|
|
1735
|
+
</react_native_1.View>) : (<react_native_1.View style={styles.contactsList}>
|
|
1736
|
+
{filteredGoogleContacts.length === 0 ? (<react_native_1.Text style={styles.emptyText}>
|
|
1737
|
+
{googleSearchQuery
|
|
1738
|
+
? getContactsCustomization('google.emptySearchState', 'No contacts match your search')
|
|
1739
|
+
: getContactsCustomization('google.emptyState', 'No Google contacts with email addresses found')}
|
|
1740
|
+
</react_native_1.Text>) : (filteredGoogleContacts.map((contact) => {
|
|
1741
|
+
const isInvited = invitedGoogleContactIds.has(contact.id);
|
|
1742
|
+
const isLoading = loadingGoogleContactIds.has(contact.id);
|
|
1743
|
+
const initials = contact.name
|
|
1744
|
+
.split(' ')
|
|
1745
|
+
.map((part) => part[0])
|
|
1746
|
+
.join('')
|
|
1747
|
+
.toUpperCase()
|
|
1748
|
+
.slice(0, 2);
|
|
1749
|
+
return (<react_native_1.View key={contact.id} style={styles.contactItem}>
|
|
1750
|
+
{contact.imageUri ? (<react_native_1.Image source={{ uri: contact.imageUri }} style={styles.contactAvatar}/>) : (<react_native_1.View style={styles.contactAvatarPlaceholder}>
|
|
1751
|
+
<react_native_1.Text style={styles.contactAvatarInitials}>{initials}</react_native_1.Text>
|
|
1752
|
+
</react_native_1.View>)}
|
|
1753
|
+
<react_native_1.View style={styles.contactInfo}>
|
|
1754
|
+
<react_native_1.Text style={styles.contactName}>{contact.name}</react_native_1.Text>
|
|
1755
|
+
<react_native_1.Text style={styles.contactEmail}>{contact.email}</react_native_1.Text>
|
|
1756
|
+
</react_native_1.View>
|
|
1757
|
+
{isInvited ? (<react_native_1.Text style={styles.invitedText}>{getContactsCustomization('google.invitedStatus', '✓ Invited!')}</react_native_1.Text>) : (<react_native_1.TouchableOpacity style={styles.inviteButton} onPress={() => handleInviteGoogleContactWithHaptics(contact.id)} disabled={isLoading}>
|
|
1758
|
+
{isLoading ? (<react_native_1.ActivityIndicator size="small" color="#333"/>) : (<react_native_1.Text style={styles.inviteButtonText}>{getContactsCustomization('google.inviteButton', 'Invite')}</react_native_1.Text>)}
|
|
1759
|
+
</react_native_1.TouchableOpacity>)}
|
|
1760
|
+
</react_native_1.View>);
|
|
1761
|
+
}))}
|
|
1762
|
+
</react_native_1.View>)}
|
|
1763
|
+
</react_native_1.View>
|
|
1764
|
+
</>)}
|
|
1765
|
+
|
|
1766
|
+
{view === 'qrcode' && (<react_native_1.View style={styles.section}>
|
|
1767
|
+
<react_native_1.View style={styles.qrCodeViewContainer}>
|
|
1768
|
+
{react_native_1.Platform.OS === 'web' ? (
|
|
1769
|
+
// In web preview mode, show placeholder QR code immediately for demo
|
|
1770
|
+
renderQRCode ? (renderQRCode({
|
|
1771
|
+
value: logic.invitationLink,
|
|
1772
|
+
size: 250,
|
|
1773
|
+
})) : (<react_native_1.Text style={styles.errorMessage}>QR Code rendering not available</react_native_1.Text>)) : loadingInvitationLink ||
|
|
1774
|
+
logic.invitationLink === 'https://app.vortexsoftware.com/invite/abc123' ? (
|
|
1775
|
+
// In native mode, show loading while fetching real link
|
|
1776
|
+
<react_native_1.View style={styles.loadingContainer}>
|
|
1777
|
+
<react_native_1.ActivityIndicator size="large" color="#6291d5"/>
|
|
1778
|
+
<react_native_1.Text style={styles.loadingText}>Generating QR Code...</react_native_1.Text>
|
|
1779
|
+
</react_native_1.View>) : renderQRCode ? (renderQRCode({
|
|
1780
|
+
value: logic.invitationLink,
|
|
1781
|
+
size: 250,
|
|
1782
|
+
})) : (<react_native_1.Text style={styles.errorMessage}>QR Code rendering not available</react_native_1.Text>)}
|
|
1783
|
+
</react_native_1.View>
|
|
1784
|
+
</react_native_1.View>)}
|
|
1785
|
+
</react_native_1.ScrollView>
|
|
1786
|
+
</react_native_1.View>);
|
|
1787
|
+
}
|
|
1788
|
+
const createStyles = (fontFamily) => react_native_1.StyleSheet.create({
|
|
1789
|
+
container: {
|
|
1790
|
+
flex: 1,
|
|
1791
|
+
paddingTop: 8,
|
|
1792
|
+
backgroundColor: '#fff',
|
|
1793
|
+
},
|
|
1794
|
+
handle: {
|
|
1795
|
+
width: 40,
|
|
1796
|
+
height: 4,
|
|
1797
|
+
backgroundColor: '#ddd',
|
|
1798
|
+
borderRadius: 2,
|
|
1799
|
+
alignSelf: 'center',
|
|
1800
|
+
marginBottom: 2,
|
|
1801
|
+
},
|
|
1802
|
+
header: {
|
|
1803
|
+
flexDirection: 'row',
|
|
1804
|
+
justifyContent: 'space-between',
|
|
1805
|
+
alignItems: 'center',
|
|
1806
|
+
paddingHorizontal: 16,
|
|
1807
|
+
paddingVertical: 4,
|
|
1808
|
+
zIndex: 10,
|
|
1809
|
+
backgroundColor: '#fff',
|
|
1810
|
+
},
|
|
1811
|
+
headerButton: {
|
|
1812
|
+
padding: 4,
|
|
1813
|
+
},
|
|
1814
|
+
headerTitle: {
|
|
1815
|
+
fontFamily,
|
|
1816
|
+
fontSize: 17,
|
|
1817
|
+
fontWeight: '600',
|
|
1818
|
+
color: '#1a1a1a',
|
|
1819
|
+
textAlign: 'center',
|
|
1820
|
+
flex: 1,
|
|
1821
|
+
},
|
|
1822
|
+
sendButton: {
|
|
1823
|
+
paddingHorizontal: 8,
|
|
1824
|
+
paddingVertical: 8,
|
|
1825
|
+
},
|
|
1826
|
+
sendButtonText: {
|
|
1827
|
+
fontFamily,
|
|
1828
|
+
color: '#2196F3',
|
|
1829
|
+
fontSize: 16,
|
|
1830
|
+
fontWeight: '600',
|
|
1831
|
+
},
|
|
1832
|
+
scrollContent: {
|
|
1833
|
+
flex: 1,
|
|
1834
|
+
paddingHorizontal: 20,
|
|
1835
|
+
// backgroundColor: 'none',
|
|
1836
|
+
},
|
|
1837
|
+
title: {
|
|
1838
|
+
fontFamily,
|
|
1839
|
+
fontSize: 18,
|
|
1840
|
+
fontWeight: '700',
|
|
1841
|
+
color: '#1a1a1a',
|
|
1842
|
+
marginTop: 16,
|
|
1843
|
+
marginBottom: 20,
|
|
1844
|
+
textAlign: 'center',
|
|
1845
|
+
},
|
|
1846
|
+
subtitle: {
|
|
1847
|
+
fontFamily,
|
|
1848
|
+
fontSize: 13,
|
|
1849
|
+
color: '#666',
|
|
1850
|
+
marginBottom: 20,
|
|
1851
|
+
lineHeight: 18,
|
|
1852
|
+
},
|
|
1853
|
+
// Sections
|
|
1854
|
+
section: {
|
|
1855
|
+
marginBottom: 10,
|
|
1856
|
+
},
|
|
1857
|
+
emptyRowEditMode: {
|
|
1858
|
+
minHeight: 40,
|
|
1859
|
+
backgroundColor: 'rgba(200, 200, 200, 0.2)',
|
|
1860
|
+
borderWidth: 1,
|
|
1861
|
+
borderColor: 'rgba(150, 150, 150, 0.3)',
|
|
1862
|
+
borderStyle: 'dashed',
|
|
1863
|
+
borderRadius: 4,
|
|
1864
|
+
},
|
|
1865
|
+
sectionTitle: {
|
|
1866
|
+
fontFamily,
|
|
1867
|
+
fontSize: 16,
|
|
1868
|
+
fontWeight: '700',
|
|
1869
|
+
color: '#1a1a1a',
|
|
1870
|
+
marginBottom: 12,
|
|
1871
|
+
},
|
|
1872
|
+
// Email chips
|
|
1873
|
+
emailChipsContainer: {
|
|
1874
|
+
flexDirection: 'row',
|
|
1875
|
+
flexWrap: 'wrap',
|
|
1876
|
+
gap: 8,
|
|
1877
|
+
marginBottom: 12,
|
|
1878
|
+
},
|
|
1879
|
+
emailChip: {
|
|
1880
|
+
flexDirection: 'row',
|
|
1881
|
+
alignItems: 'center',
|
|
1882
|
+
backgroundColor: '#e3f2fd',
|
|
1883
|
+
borderRadius: 16,
|
|
1884
|
+
paddingVertical: 6,
|
|
1885
|
+
paddingLeft: 12,
|
|
1886
|
+
paddingRight: 8,
|
|
1887
|
+
borderWidth: 1,
|
|
1888
|
+
borderColor: '#90caf9',
|
|
1889
|
+
},
|
|
1890
|
+
emailChipText: {
|
|
1891
|
+
fontFamily,
|
|
1892
|
+
fontSize: 13,
|
|
1893
|
+
color: '#1565c0',
|
|
1894
|
+
marginRight: 6,
|
|
1895
|
+
},
|
|
1896
|
+
emailChipRemove: {
|
|
1897
|
+
fontFamily,
|
|
1898
|
+
fontSize: 20,
|
|
1899
|
+
color: '#1565c0',
|
|
1900
|
+
fontWeight: '600',
|
|
1901
|
+
lineHeight: 20,
|
|
1902
|
+
},
|
|
1903
|
+
// Input
|
|
1904
|
+
input: {
|
|
1905
|
+
fontFamily,
|
|
1906
|
+
borderWidth: 1,
|
|
1907
|
+
borderColor: '#ddd',
|
|
1908
|
+
borderRadius: 8,
|
|
1909
|
+
paddingHorizontal: 14,
|
|
1910
|
+
paddingVertical: 10,
|
|
1911
|
+
fontSize: 15,
|
|
1912
|
+
color: '#1a1a1a',
|
|
1913
|
+
backgroundColor: '#fafafa',
|
|
1914
|
+
marginBottom: 12,
|
|
1915
|
+
},
|
|
1916
|
+
// Buttons
|
|
1917
|
+
button: {
|
|
1918
|
+
borderRadius: 8,
|
|
1919
|
+
paddingVertical: 12,
|
|
1920
|
+
paddingHorizontal: 16,
|
|
1921
|
+
alignItems: 'center',
|
|
1922
|
+
justifyContent: 'center',
|
|
1923
|
+
flexDirection: 'row',
|
|
1924
|
+
},
|
|
1925
|
+
secondaryButton: {
|
|
1926
|
+
backgroundColor: '#f5f5f5',
|
|
1927
|
+
borderWidth: 1,
|
|
1928
|
+
borderColor: '#e0e0e0',
|
|
1929
|
+
},
|
|
1930
|
+
secondaryButtonText: {
|
|
1931
|
+
fontFamily,
|
|
1932
|
+
color: '#333',
|
|
1933
|
+
fontSize: 14,
|
|
1934
|
+
fontWeight: '600',
|
|
1935
|
+
},
|
|
1936
|
+
tertiaryButton: {
|
|
1937
|
+
backgroundColor: '#f5f5f5',
|
|
1938
|
+
borderWidth: 1,
|
|
1939
|
+
borderColor: '#e0e0e0',
|
|
1940
|
+
},
|
|
1941
|
+
tertiaryButtonText: {
|
|
1942
|
+
fontFamily,
|
|
1943
|
+
color: '#333',
|
|
1944
|
+
fontSize: 11,
|
|
1945
|
+
fontWeight: '600',
|
|
1946
|
+
},
|
|
1947
|
+
actionButtons: {
|
|
1948
|
+
flexDirection: 'row',
|
|
1949
|
+
gap: 10,
|
|
1950
|
+
},
|
|
1951
|
+
halfButton: {
|
|
1952
|
+
flex: 1,
|
|
1953
|
+
},
|
|
1954
|
+
shareButtonsContainer: {
|
|
1955
|
+
flexDirection: 'column',
|
|
1956
|
+
gap: 10,
|
|
1957
|
+
},
|
|
1958
|
+
contactButtonsContainer: {
|
|
1959
|
+
flexDirection: 'column',
|
|
1960
|
+
gap: 10,
|
|
1961
|
+
},
|
|
1962
|
+
fullButton: {
|
|
1963
|
+
width: '100%',
|
|
1964
|
+
},
|
|
1965
|
+
buttonIconContainer: {
|
|
1966
|
+
marginRight: 8,
|
|
1967
|
+
},
|
|
1968
|
+
// Contacts list
|
|
1969
|
+
contactsList: {
|
|
1970
|
+
gap: 0,
|
|
1971
|
+
},
|
|
1972
|
+
contactItem: {
|
|
1973
|
+
flexDirection: 'row',
|
|
1974
|
+
alignItems: 'center',
|
|
1975
|
+
paddingVertical: 12,
|
|
1976
|
+
},
|
|
1977
|
+
contactAvatar: {
|
|
1978
|
+
width: 44,
|
|
1979
|
+
height: 44,
|
|
1980
|
+
borderRadius: 22,
|
|
1981
|
+
},
|
|
1982
|
+
contactAvatarPlaceholder: {
|
|
1983
|
+
width: 44,
|
|
1984
|
+
height: 44,
|
|
1985
|
+
borderRadius: 22,
|
|
1986
|
+
backgroundColor: '#6291d5',
|
|
1987
|
+
alignItems: 'center',
|
|
1988
|
+
justifyContent: 'center',
|
|
1989
|
+
},
|
|
1990
|
+
contactAvatarInitials: {
|
|
1991
|
+
fontSize: 16,
|
|
1992
|
+
fontWeight: '600',
|
|
1993
|
+
color: '#ffffff',
|
|
1994
|
+
},
|
|
1995
|
+
contactInfo: {
|
|
1996
|
+
flex: 1,
|
|
1997
|
+
marginLeft: 12,
|
|
1998
|
+
marginRight: 12,
|
|
1999
|
+
},
|
|
2000
|
+
contactName: {
|
|
2001
|
+
fontFamily,
|
|
2002
|
+
fontSize: 15,
|
|
2003
|
+
fontWeight: '600',
|
|
2004
|
+
color: '#1a1a1a',
|
|
2005
|
+
marginBottom: 4,
|
|
2006
|
+
},
|
|
2007
|
+
contactEmail: {
|
|
2008
|
+
fontFamily,
|
|
2009
|
+
fontSize: 13,
|
|
2010
|
+
color: '#666',
|
|
2011
|
+
},
|
|
2012
|
+
inviteButton: {
|
|
2013
|
+
paddingVertical: 8,
|
|
2014
|
+
paddingHorizontal: 16,
|
|
2015
|
+
backgroundColor: '#f5f5f5',
|
|
2016
|
+
borderWidth: 1,
|
|
2017
|
+
borderColor: '#e0e0e0',
|
|
2018
|
+
borderRadius: 6,
|
|
2019
|
+
minWidth: 80,
|
|
2020
|
+
alignItems: 'center',
|
|
2021
|
+
},
|
|
2022
|
+
inviteButtonText: {
|
|
2023
|
+
fontFamily,
|
|
2024
|
+
fontSize: 13,
|
|
2025
|
+
fontWeight: '600',
|
|
2026
|
+
color: '#333',
|
|
2027
|
+
},
|
|
2028
|
+
invitedText: {
|
|
2029
|
+
fontFamily,
|
|
2030
|
+
fontSize: 13,
|
|
2031
|
+
fontWeight: '600',
|
|
2032
|
+
color: '#666',
|
|
2033
|
+
},
|
|
2034
|
+
successMessageContainer: {
|
|
2035
|
+
paddingVertical: 12,
|
|
2036
|
+
alignItems: 'center',
|
|
2037
|
+
},
|
|
2038
|
+
// Loading state
|
|
2039
|
+
loadingContainer: {
|
|
2040
|
+
paddingVertical: 40,
|
|
2041
|
+
alignItems: 'center',
|
|
2042
|
+
justifyContent: 'center',
|
|
2043
|
+
},
|
|
2044
|
+
loadingText: {
|
|
2045
|
+
fontFamily,
|
|
2046
|
+
fontSize: 14,
|
|
2047
|
+
color: '#666',
|
|
2048
|
+
marginTop: 12,
|
|
2049
|
+
},
|
|
2050
|
+
emptyText: {
|
|
2051
|
+
fontFamily,
|
|
2052
|
+
fontSize: 14,
|
|
2053
|
+
color: '#999',
|
|
2054
|
+
textAlign: 'center',
|
|
2055
|
+
paddingVertical: 20,
|
|
2056
|
+
},
|
|
2057
|
+
// Error state
|
|
2058
|
+
errorContainer: {
|
|
2059
|
+
paddingVertical: 40,
|
|
2060
|
+
paddingHorizontal: 20,
|
|
2061
|
+
alignItems: 'center',
|
|
2062
|
+
justifyContent: 'center',
|
|
2063
|
+
},
|
|
2064
|
+
errorTitle: {
|
|
2065
|
+
fontFamily,
|
|
2066
|
+
fontSize: 16,
|
|
2067
|
+
fontWeight: '600',
|
|
2068
|
+
color: '#d9534f',
|
|
2069
|
+
marginBottom: 12,
|
|
2070
|
+
textAlign: 'center',
|
|
2071
|
+
},
|
|
2072
|
+
errorMessage: {
|
|
2073
|
+
fontFamily,
|
|
2074
|
+
fontSize: 14,
|
|
2075
|
+
color: '#666',
|
|
2076
|
+
marginBottom: 16,
|
|
2077
|
+
textAlign: 'center',
|
|
2078
|
+
lineHeight: 20,
|
|
2079
|
+
},
|
|
2080
|
+
errorHint: {
|
|
2081
|
+
fontFamily,
|
|
2082
|
+
fontSize: 13,
|
|
2083
|
+
color: '#666',
|
|
2084
|
+
marginTop: 12,
|
|
2085
|
+
marginBottom: 8,
|
|
2086
|
+
textAlign: 'center',
|
|
2087
|
+
lineHeight: 18,
|
|
2088
|
+
backgroundColor: '#f0f8ff',
|
|
2089
|
+
padding: 12,
|
|
2090
|
+
borderRadius: 6,
|
|
2091
|
+
borderWidth: 1,
|
|
2092
|
+
borderColor: '#b3d9ff',
|
|
2093
|
+
},
|
|
2094
|
+
errorActions: {
|
|
2095
|
+
flexDirection: 'row',
|
|
2096
|
+
gap: 10,
|
|
2097
|
+
marginTop: 16,
|
|
2098
|
+
marginBottom: 8,
|
|
2099
|
+
width: '100%',
|
|
2100
|
+
},
|
|
2101
|
+
errorDetails: {
|
|
2102
|
+
fontFamily,
|
|
2103
|
+
fontSize: 12,
|
|
2104
|
+
color: '#999',
|
|
2105
|
+
textAlign: 'center',
|
|
2106
|
+
fontStyle: 'italic',
|
|
2107
|
+
},
|
|
2108
|
+
// QR Code view
|
|
2109
|
+
qrCodeViewContainer: {
|
|
2110
|
+
alignItems: 'center',
|
|
2111
|
+
justifyContent: 'center',
|
|
2112
|
+
padding: 20,
|
|
2113
|
+
backgroundColor: '#fff',
|
|
2114
|
+
borderRadius: 10,
|
|
2115
|
+
marginVertical: 10,
|
|
2116
|
+
},
|
|
2117
|
+
// Edit mode circle button
|
|
2118
|
+
editModeCircleButton: {
|
|
2119
|
+
position: 'absolute',
|
|
2120
|
+
right: 8,
|
|
2121
|
+
top: '50%',
|
|
2122
|
+
transform: [{ translateY: -15 }],
|
|
2123
|
+
width: 30,
|
|
2124
|
+
height: 30,
|
|
2125
|
+
borderRadius: 15,
|
|
2126
|
+
backgroundColor: '#2196F3',
|
|
2127
|
+
justifyContent: 'center',
|
|
2128
|
+
alignItems: 'center',
|
|
2129
|
+
shadowColor: '#000',
|
|
2130
|
+
shadowOffset: { width: 0, height: 2 },
|
|
2131
|
+
shadowOpacity: 0.25,
|
|
2132
|
+
shadowRadius: 3.84,
|
|
2133
|
+
elevation: 5,
|
|
2134
|
+
},
|
|
2135
|
+
editModeCircleButtonText: {
|
|
2136
|
+
color: '#fff',
|
|
2137
|
+
fontSize: 16,
|
|
2138
|
+
fontWeight: 'bold',
|
|
2139
|
+
},
|
|
2140
|
+
});
|
|
2141
|
+
//# sourceMappingURL=InviteFormCore.js.map
|