@interactify-live/pindo-wizard-react-native
Version:
React Native PindoWizard component for media capture and interaction management
312 lines (311 loc) • 15.4 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const react_native_1 = require("react-native");
const CameraComponent_1 = __importDefault(require("./CameraComponent"));
const components_1 = require("./components");
const MediaThumbnails_1 = __importDefault(require("./components/MediaThumbnails"));
const utils_1 = require("./utils");
const PindoWizard = ({ onFinish, uploadFile: _uploadFile, handleUpload, initialData = [], settings: _settings = {
isVideoActive: false,
isInteractionActive: false,
isCoverActive: false,
numMedias: 1,
flashEnabled: false,
}, }) => {
const [currentStep, setCurrentStep] = (0, react_1.useState)(initialData.length > 0 ? 'interactions' : 'camera');
const [medias, setMedias] = (0, react_1.useState)(initialData.map(data => ({
...data,
interactions: data.interactions || [],
})));
const [activeMediaIndex, setActiveMediaIndex] = (0, react_1.useState)(0);
const [activeInteraction, setActiveInteraction] = (0, react_1.useState)(-1);
const [coverIndex, setCoverIndex] = (0, react_1.useState)(0);
const [isAddTextModalOpen, setIsAddTextModalOpen] = (0, react_1.useState)(false);
const [isEditTextModalOpen, setIsEditTextModalOpen] = (0, react_1.useState)(false);
const [pendingText, setPendingText] = (0, react_1.useState)('');
const [editingInteraction, setEditingInteraction] = (0, react_1.useState)(null);
const handleCameraCapture = (0, react_1.useCallback)(async (uri, type) => {
const newMedia = {
id: `media-${Date.now()}`,
type: type === 'photo' ? 'image' : 'video',
url: uri,
localUri: uri,
interactions: [],
isUploading: true,
isUploaded: false,
};
setMedias(prev => {
const newMedias = [...prev, newMedia];
setActiveMediaIndex(newMedias.length - 1);
return newMedias;
});
setCurrentStep('interactions');
// Start upload if handleUpload is provided
if (handleUpload) {
try {
const uploadResult = await handleUpload({ uri, type });
setMedias(prev => prev.map((media, index) => index === prev.length - 1
? {
...media,
url: uploadResult.url,
thumbnail: uploadResult.thumbnail,
isUploading: false,
isUploaded: true,
response: uploadResult,
}
: media));
}
catch (error) {
console.error('Upload failed:', error);
setMedias(prev => prev.map((media, index) => index === prev.length - 1
? {
...media,
isUploading: false,
isUploaded: false,
}
: media));
}
}
else {
// If no upload handler provided, mark as uploaded immediately
setMedias(prev => prev.map((media, index) => index === prev.length - 1
? {
...media,
isUploading: false,
isUploaded: true,
}
: media));
}
}, [handleUpload]);
const handleGallerySelect = (0, react_1.useCallback)(async (uri, type) => {
const newMedia = {
id: `media-${Date.now()}`,
type: type === 'photo' ? 'image' : 'video',
url: uri,
localUri: uri,
interactions: [],
isUploading: true,
isUploaded: false,
};
setMedias(prev => {
const newMedias = [...prev, newMedia];
setActiveMediaIndex(newMedias.length - 1);
return newMedias;
});
setCurrentStep('interactions');
// Start upload if handleUpload is provided
if (handleUpload) {
try {
const uploadResult = await handleUpload({ uri, type });
setMedias(prev => prev.map((media, index) => index === prev.length - 1
? {
...media,
url: uploadResult.url,
thumbnail: uploadResult.thumbnail,
isUploading: false,
isUploaded: true,
response: uploadResult,
}
: media));
}
catch (error) {
console.error('Upload failed:', error);
setMedias(prev => prev.map((media, index) => index === prev.length - 1
? {
...media,
isUploading: false,
isUploaded: false,
}
: media));
}
}
else {
// If no upload handler provided, mark as uploaded immediately
setMedias(prev => prev.map((media, index) => index === prev.length - 1
? {
...media,
isUploading: false,
isUploaded: true,
}
: media));
}
}, [handleUpload]);
const handleCameraClose = (0, react_1.useCallback)(() => {
if (medias.length > 0) {
setCurrentStep('interactions');
}
}, [medias.length]);
const handleAddTextInteraction = (0, react_1.useCallback)(() => {
setPendingText('');
setIsAddTextModalOpen(true);
}, []);
const handleEditTextInteraction = (0, react_1.useCallback)((interaction) => {
setEditingInteraction(interaction);
setIsEditTextModalOpen(true);
}, []);
const handleSaveTextInteraction = (0, react_1.useCallback)((textData) => {
const newInteraction = {
interaction: 'text',
payload: {
text: textData.text,
size: textData.size,
color: textData.color,
background: textData.background,
borderRadius: textData.borderRadius,
},
geometric: {
x: 300, // Default x position
y: 300, // Default y position
width: textData.width,
height: textData.height,
},
};
setMedias(prev => prev.map((media, index) => {
if (index === activeMediaIndex) {
return {
...media,
interactions: [...media.interactions, newInteraction],
};
}
return media;
}));
setIsAddTextModalOpen(false);
}, [activeMediaIndex]);
const handleSaveEditTextInteraction = (0, react_1.useCallback)((textData) => {
if (!editingInteraction)
return;
const updatedInteraction = {
...editingInteraction,
payload: {
text: textData.text,
size: textData.size,
color: textData.color,
background: textData.background,
borderRadius: textData.borderRadius,
},
geometric: {
...editingInteraction.geometric,
width: textData.width,
height: textData.height,
},
};
setMedias(prev => prev.map((media, index) => {
if (index === activeMediaIndex) {
return {
...media,
interactions: media.interactions.map((interaction, idx) => idx === activeInteraction ? updatedInteraction : interaction),
};
}
return media;
}));
setIsEditTextModalOpen(false);
setEditingInteraction(null);
setActiveInteraction(-1);
}, [activeMediaIndex, activeInteraction, editingInteraction]);
const handleFinish = (0, react_1.useCallback)(() => {
const mediaWithInteractions = medias.map(media => ({
id: media.id,
type: media.type,
url: media.url,
thumbnail: media.thumbnail,
interactions: media.interactions,
response: media.response,
}));
onFinish(mediaWithInteractions);
}, [medias, onFinish]);
const hasAnyActiveInteraction = activeInteraction !== -1;
if (currentStep === 'camera') {
return ((0, jsx_runtime_1.jsx)(react_native_1.SafeAreaView, { style: {
flex: 1,
backgroundColor: '#000',
paddingTop: react_native_1.Platform.OS === 'android' ? react_native_1.StatusBar.currentHeight : 0,
}, children: (0, jsx_runtime_1.jsx)(CameraComponent_1.default, { onCapture: handleCameraCapture, onGallerySelect: handleGallerySelect, onClose: handleCameraClose, settings: _settings }) }));
}
return ((0, jsx_runtime_1.jsx)(react_native_1.SafeAreaView, { style: {
flex: 1,
backgroundColor: '#262626',
paddingTop: react_native_1.Platform.OS === 'android' ? react_native_1.StatusBar.currentHeight : 0,
}, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.container, children: [(0, jsx_runtime_1.jsx)(components_1.InteractionHeader, { activeMedia: activeMediaIndex, setActiveMedia: setActiveMediaIndex, setMedias: updater => {
setMedias(prev => {
const updated = typeof updater === 'function' ? updater(prev) : updater;
if (updated.length === 0) {
setCurrentStep('camera');
}
return updated;
});
}, _hasAnyActiveInteraction: hasAnyActiveInteraction, _activeInteraction: activeInteraction, _setActiveInteraction: setActiveInteraction, _medias: medias, onAddInteraction: handleAddTextInteraction, _onDeleteActiveInteraction: () => {
if (activeInteraction >= 0) {
setMedias(prev => prev.map((media, i) => {
if (i === activeMediaIndex) {
return {
...media,
interactions: media.interactions.filter((_, j) => j !== activeInteraction),
};
}
return media;
}));
setActiveInteraction(-1);
}
}, coverIndex: coverIndex, setCoverIndex: setCoverIndex, settings: {
isVideoActive: _settings.isVideoActive || false,
isInteractionActive: _settings.isInteractionActive || false,
isCoverActive: _settings.isCoverActive || false,
numMedias: _settings.numMedias || 1,
} }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.content, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.mainContentWrapper, children: (0, jsx_runtime_1.jsx)(components_1.MainContent, { medias: medias, setMedias: setMedias, activeMedia: activeMediaIndex, _activeInteraction: activeInteraction, setActiveInteraction: setActiveInteraction, coverIndex: coverIndex, setCoverIndex: setCoverIndex, onEditInteraction: handleEditTextInteraction, settings: {
isVideoActive: _settings.isVideoActive || false,
isInteractionActive: _settings.isInteractionActive || false,
isCoverActive: _settings.isCoverActive || false,
numMedias: _settings.numMedias || 1,
} }) }) }), _settings.numMedias && _settings.numMedias > 1 && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.mediaThumbnailsWrapper, children: (0, jsx_runtime_1.jsx)(MediaThumbnails_1.default, { medias: medias, activeMedia: activeMediaIndex, setActiveMedia: setActiveMediaIndex, setActiveInteraction: setActiveInteraction, coverIndex: coverIndex, setCurrentStep: setCurrentStep, settings: {
isVideoActive: _settings.isVideoActive || false,
isInteractionActive: _settings.isInteractionActive || false,
isCoverActive: _settings.isCoverActive || false,
numMedias: _settings.numMedias || 1,
} }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.pageIndicator, children: (0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: styles.pageIndicatorText, children: [(0, utils_1.toPersianNumbers)(activeMediaIndex + 1), " /", ' ', (0, utils_1.toPersianNumbers)(_settings.numMedias || 1)] }) })] })), (0, jsx_runtime_1.jsx)(components_1.BottomControls, { medias: medias, activeMedia: activeMediaIndex, onFinish: handleFinish }), (0, jsx_runtime_1.jsx)(components_1.TextInputModal, { isOpen: isAddTextModalOpen, initialText: pendingText, initialColor: "#FFFFFF", initialBackground: "#000000", initialBgOpacity: 0.6, onSave: handleSaveTextInteraction, onCancel: () => setIsAddTextModalOpen(false), title: "\u0627\u0641\u0632\u0648\u062F\u0646 \u0645\u062A\u0646" }), (0, jsx_runtime_1.jsx)(components_1.TextInputModal, { isOpen: isEditTextModalOpen, initialText: editingInteraction?.payload?.text || '', initialColor: editingInteraction?.payload?.color || '#FFFFFF', initialBackground: editingInteraction?.payload?.background || '#000000', initialBgOpacity: 0.6, initialSize: editingInteraction?.payload?.size || 16, initialBorderRadius: editingInteraction?.payload?.borderRadius || 8, onSave: handleSaveEditTextInteraction, onCancel: () => {
setIsEditTextModalOpen(false);
setEditingInteraction(null);
}, title: "\u0648\u06CC\u0631\u0627\u06CC\u0634 \u0645\u062A\u0646" })] }) }));
};
const styles = react_native_1.StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#262626',
},
content: {
flex: 1,
paddingHorizontal: 16,
paddingBottom: 16,
},
mainContentWrapper: {
flex: 1,
borderWidth: 2,
borderColor: 'rgba(175, 177, 182, 1)',
borderRadius: 8,
overflow: 'visible',
},
mediaThumbnailsWrapper: {
height: 80,
paddingHorizontal: 16,
paddingBottom: 12,
},
pageIndicator: {
alignItems: 'center',
marginBottom: 12,
marginTop: 5,
},
pageIndicatorText: {
color: '#ffffff',
fontSize: 10,
borderWidth: 1.5,
borderColor: 'rgba(255, 255, 255, 1)',
borderRadius: 8,
paddingHorizontal: 8,
paddingVertical: 4,
},
});
exports.default = PindoWizard;