@memori.ai/memori-react
Version:
[](https://www.npmjs.com/package/@memori.ai/memori-react)   => {
if (typeof metric === 'number' && Number.isFinite(metric))
return metric;
if (!metric || typeof metric !== 'object')
return undefined;
if (typeof metric.parsedValue === 'number' &&
Number.isFinite(metric.parsedValue)) {
return metric.parsedValue;
}
if (typeof metric.source === 'string') {
const parsed = Number(metric.source);
if (Number.isFinite(parsed))
return parsed;
}
return undefined;
};
const formatMetricValue = (value, locale) => new Intl.NumberFormat(locale, {
minimumFractionDigits: 0,
maximumFractionDigits: Math.abs(value) >= 1 ? 3 : 4,
}).format(value);
const formatImpactInReadableUnit = (value, metricType, locale) => {
const absValue = Math.abs(value);
if (metricType === 'energy') {
if (absValue >= 1)
return `${formatMetricValue(value, locale)} kWh`;
const wh = value * 1000;
if (Math.abs(wh) >= 1)
return `${formatMetricValue(wh, locale)} Wh`;
return `${formatMetricValue(wh * 1000, locale)} mWh`;
}
if (metricType === 'co2') {
if (absValue >= 1)
return `${formatMetricValue(value, locale)} kg`;
const g = value * 1000;
if (Math.abs(g) >= 1)
return `${formatMetricValue(g, locale)} g`;
return `${formatMetricValue(g * 1000, locale)} mg`;
}
if (absValue >= 1)
return `${formatMetricValue(value, locale)} L`;
const ml = value * 1000;
if (Math.abs(ml) >= 1)
return `${formatMetricValue(ml, locale)} mL`;
return `${formatMetricValue(ml * 1000, locale)} μL`;
};
const MobileSessionPanel = ({ open, onClose, presentation = 'sheet', title, loginToken, userName, user, apiClient, userEmail, birthDate, avatarURL, actions, logoutLabel = 'Log out', onLogout, onKnownFactsOpen, onLocationEnable, onLocationDisable, knownFactsPageTitle = 'Known facts', sharePageTitle = 'Share', locationPageTitle = 'Location tracking', backLabel = 'Back', locationStatusLabel = 'Status', locationPlace, locationUnknownLabel = 'Unknown position', locationEnableLabel = 'Use current position', locationDisableLabel = 'Disable location sharing', knownFactsDescription, knownFactsCtaLabel = 'Open full known facts', knownFactsCountLabel, shareContent, knownFactsDisabled = false, knownFactsHint, showSessionInfo = false, history = [], aiUsageTitle, isLoggedIn = false, loginLabel = 'Log in', onLogin, venue, setVenue, }) => {
const panelRef = useRef(null);
const dialogRef = useRef(null);
const panelTitleId = 'mobile-session-panel-title';
const touchStartYRef = useRef(0);
const [dragOffset, setDragOffset] = useState(0);
const [activeView, setActiveView] = useState('session');
const { uploadAsset, pwlUpdateUser } = (apiClient === null || apiClient === void 0 ? void 0 : apiClient.backend) || {};
const { add } = useAlertManager();
const { t, i18n } = useTranslation();
const currentLocale = i18n.language || navigator.language || 'en';
const sustainabilityTotals = useMemo(() => {
const totals = { energy: 0, gwp: 0, wcf: 0 };
history.forEach(line => {
var _a, _b, _c, _d;
const energyImpact = (_a = line.llmUsage) === null || _a === void 0 ? void 0 : _a.energyImpact;
if (!energyImpact)
return;
totals.energy += (_b = getMetricValue(energyImpact.energy)) !== null && _b !== void 0 ? _b : 0;
totals.gwp += (_c = getMetricValue(energyImpact.gwp)) !== null && _c !== void 0 ? _c : 0;
totals.wcf += (_d = getMetricValue(energyImpact.wcf)) !== null && _d !== void 0 ? _d : 0;
});
return totals;
}, [history]);
const hasSustainabilityData = useMemo(() => history.some(line => {
var _a;
return !!((_a = line.llmUsage) === null || _a === void 0 ? void 0 : _a.energyImpact);
}), [history]);
const hasChatConsumptionData = useMemo(() => history.some(line => !!line.llmUsage), [history]);
const aiUsageSubtitle = hasSustainabilityData
? `${formatImpactInReadableUnit(sustainabilityTotals.energy, 'energy', currentLocale)} • ${formatImpactInReadableUnit(sustainabilityTotals.gwp, 'co2', currentLocale)}`
: t('widget.noData', { defaultValue: 'Nessun dato disponibile' });
const resolvedKnownFactsHint = knownFactsHint || t('widget.knownFactsHint') || 'What I remember about you';
const resolvedAiUsageTitle = aiUsageTitle || t('widget.aiConsumption') || 'AI usage';
useEffect(() => {
if (!open)
return;
const onEscape = (event) => {
if (event.key === 'Escape')
onClose();
};
document.addEventListener('keydown', onEscape);
return () => document.removeEventListener('keydown', onEscape);
}, [open, onClose]);
useEffect(() => {
var _a;
if (!open)
return;
const previouslyFocused = document.activeElement;
const root = dialogRef.current;
if (!root)
return;
const handleKeyDown = (event) => {
if (event.key !== 'Tab')
return;
const focusableElements = root.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (!focusableElements.length)
return;
const first = focusableElements[0];
const last = focusableElements[focusableElements.length - 1];
const active = document.activeElement;
if (event.shiftKey && active === first) {
event.preventDefault();
last.focus();
}
else if (!event.shiftKey && active === last) {
event.preventDefault();
first.focus();
}
};
document.addEventListener('keydown', handleKeyDown);
(_a = panelRef.current) === null || _a === void 0 ? void 0 : _a.focus();
return () => {
document.removeEventListener('keydown', handleKeyDown);
previouslyFocused === null || previouslyFocused === void 0 ? void 0 : previouslyFocused.focus();
};
}, [open]);
useEffect(() => {
if (open)
setActiveView('session');
}, [open]);
if (!open)
return null;
const updateAvatar = async (avatar) => {
if (!uploadAsset || !pwlUpdateUser) {
add(createAlertOptions({
description: t('login.avatarUploadError'),
severity: 'error',
}));
return;
}
if (avatar && loginToken) {
const reader = new FileReader();
reader.onload = async (e) => {
var _a, _b, _c, _d;
try {
const { asset: avatarAsset, ...resp } = await uploadAsset((_a = avatar.name) !== null && _a !== void 0 ? _a : 'avatar', (_b = e.target) === null || _b === void 0 ? void 0 : _b.result, loginToken !== null && loginToken !== void 0 ? loginToken : '');
if (resp.resultCode !== 0) {
console.error('[updateAvatar] Upload failed:', resp);
add(createAlertOptions({
description: t(getErrori18nKey(resp.resultCode)),
severity: 'error',
}));
}
else if (avatarAsset) {
let newUser = {
userID: (_c = user === null || user === void 0 ? void 0 : user.userID) !== null && _c !== void 0 ? _c : '',
avatarURL: avatarAsset.assetURL,
};
const { ...updateResp } = await pwlUpdateUser(loginToken !== null && loginToken !== void 0 ? loginToken : '', (_d = user === null || user === void 0 ? void 0 : user.userID) !== null && _d !== void 0 ? _d : '', newUser);
if (updateResp.resultCode !== 0) {
add(createAlertOptions({
description: t(getErrori18nKey(updateResp.resultCode)),
severity: 'error',
}));
}
else {
add(createAlertOptions({
description: t('login.avatarUploadSuccess'),
severity: 'success',
}));
}
}
}
catch (e) {
let err = e;
console.error('[updateAvatar] Error:', err);
if (err === null || err === void 0 ? void 0 : err.message)
add(createAlertOptions({
description: err.message,
severity: 'error',
}));
}
};
reader.readAsDataURL(avatar);
}
else {
console.error('[updateAvatar] Missing avatar or login token', {
avatar,
loginToken,
});
add(createAlertOptions({
description: t('login.avatarUploadError'),
severity: 'error',
}));
}
};
const isPopover = presentation === 'popover';
const isLocationEnabled = Boolean(locationPlace &&
locationPlace.trim().length > 0 &&
locationPlace !== locationUnknownLabel);
const visibleActions = actions.filter(action => {
const normalizedKey = action.key.toLowerCase();
const normalizedTitle = action.title.toLowerCase();
const isKnownFactsAction = action.view === 'knownFacts';
const isAudioAction = normalizedKey.includes('audio') ||
normalizedTitle.includes('audio') ||
normalizedTitle.includes('sound');
return !isKnownFactsAction && !isAudioAction;
});
return (_jsxs("div", { ref: dialogRef, className: `memori-mobile-session-panel--overlay ${isPopover ? 'memori-mobile-session-panel--overlay-popover' : ''}`, role: "presentation", children: [!isPopover && (_jsx("button", { type: "button", className: "memori-mobile-session-panel--backdrop", "aria-label": String(t('close', { defaultValue: 'Close' })), onClick: onClose })), _jsxs("section", { ref: panelRef, tabIndex: -1, className: `memori-mobile-session-panel ${isPopover ? 'memori-mobile-session-panel--popover' : ''}`, role: "dialog", "aria-modal": "true", "aria-labelledby": panelTitleId, onClick: event => event.stopPropagation(), onTouchStart: isPopover
? undefined
: event => {
touchStartYRef.current = event.touches[0].clientY;
setDragOffset(0);
}, onTouchMove: isPopover
? undefined
: event => {
const delta = event.touches[0].clientY - touchStartYRef.current;
if (delta > 0)
setDragOffset(delta);
}, onTouchEnd: isPopover
? undefined
: () => {
if (dragOffset > 90) {
onClose();
}
setDragOffset(0);
}, style: {
transform: dragOffset ? `translateY(${dragOffset}px)` : undefined,
}, children: [!isPopover && _jsx("div", { className: "memori-mobile-session-panel--handle" }), activeView === 'session' ? (_jsxs(_Fragment, { children: [isLoggedIn && (_jsxs("div", { className: "memori-mobile-session-panel--user", children: [_jsx("div", { className: "memori-dropdown--avatar-wrap", children: avatarURL ? (_jsxs(_Fragment, { children: [_jsx("img", { src: avatarURL, alt: userName || userEmail, className: "memori-dropdown--avatar" }), _jsx("span", { className: "memori-dropdown--avatar-overlay", "aria-hidden": true, children: _jsx(Camera, { size: 20, strokeWidth: 2 }) }), _jsx("label", { htmlFor: "mobile-session-avatar-upload", className: "sr-only", children: t('login.changeAvatar', {
defaultValue: 'Change profile picture',
}) }), _jsx("input", { type: "file", name: "avatar", id: "mobile-session-avatar-upload", className: "memori-dropdown--avatar-input", onChange: e => {
var _a, _b;
return updateAvatar((_b = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : null);
}, accept: imgMimeTypes.join(', ') })] })) : (_jsx(_Fragment, { children: _jsxs("div", { className: "memori-dropdown--avatar-placeholder", children: [_jsx("span", { className: "memori-dropdown--avatar-initial", children: (userName || userEmail || 'U')
.charAt(0)
.toUpperCase() }), _jsx("span", { className: "memori-dropdown--avatar-overlay", "aria-hidden": true, children: _jsx(Camera, { size: 20, strokeWidth: 2 }) }), _jsx("label", { htmlFor: "mobile-session-avatar-upload-placeholder", className: "sr-only", children: t('login.changeAvatar', {
defaultValue: 'Change profile picture',
}) }), _jsx("input", { type: "file", name: "avatar", id: "mobile-session-avatar-upload-placeholder", className: "memori-dropdown--avatar-input", onChange: e => {
var _a, _b;
return updateAvatar((_b = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : null);
}, accept: imgMimeTypes.join(', ') })] }) })) }), _jsxs("div", { className: "memori-dropdown--user-details", children: [_jsx("h3", { className: "memori-dropdown--user-name", children: userName }), _jsx("p", { className: "memori-dropdown--user-email", children: userEmail }), _jsx("div", { className: "memori-dropdown--user-badge", children: birthDate
? new Date(birthDate).toLocaleDateString()
: 'Not set' })] })] })), _jsx("h2", { id: panelTitleId, className: "memori-mobile-session-panel--section-title", children: title }), showSessionInfo && (_jsxs("ul", { className: "memori-mobile-session-panel--actions memori-mobile-session-panel--session-info", children: [isLoggedIn && (_jsx("li", { children: _jsxs(Button, { variant: "toolbar", size: "sm", className: "memori-mobile-session-panel--action", disabled: knownFactsDisabled, onClick: () => {
if (knownFactsDisabled)
return;
onKnownFactsOpen === null || onKnownFactsOpen === void 0 ? void 0 : onKnownFactsOpen();
}, children: [_jsx("span", { className: "memori-mobile-session-panel--action-icon", children: _jsx(Brain, { size: 18 }) }), _jsxs("span", { className: "memori-mobile-session-panel--action-copy", children: [_jsx("span", { className: "memori-mobile-session-panel--action-title", children: knownFactsPageTitle }), _jsx("span", { className: "memori-mobile-session-panel--action-subtitle", children: resolvedKnownFactsHint })] }), _jsx("span", { className: "memori-mobile-session-panel--action-trailing", children: _jsx(ChevronRight, { size: 16 }) })] }) })), _jsx("li", { children: _jsxs(Button, { variant: "toolbar", size: "sm", className: "memori-mobile-session-panel--action", disabled: !hasChatConsumptionData, onClick: () => {
if (!hasChatConsumptionData)
return;
setActiveView('aiUsage');
}, children: [_jsx("span", { className: "memori-mobile-session-panel--action-icon", children: _jsx(GasStation, {}) }), _jsxs("span", { className: "memori-mobile-session-panel--action-copy", children: [_jsx("span", { className: "memori-mobile-session-panel--action-title", children: resolvedAiUsageTitle }), _jsx("span", { className: "memori-mobile-session-panel--action-subtitle", children: aiUsageSubtitle })] }), _jsx("span", { className: "memori-mobile-session-panel--action-trailing", children: _jsx(ChevronRight, { size: 16 }) })] }) })] })), _jsx("ul", { className: "memori-mobile-session-panel--actions", children: visibleActions.map(action => (_jsx("li", { children: (() => {
const isKnownFactsAction = action.view === 'knownFacts';
const isShareAction = action.view === 'share';
const isActionDisabled = action.disabled || (isKnownFactsAction && !isLoggedIn);
return (_jsxs(Button, { variant: "toolbar", size: "sm", className: "memori-mobile-session-panel--action", disabled: isActionDisabled, onClick: () => {
var _a;
if (isActionDisabled)
return;
if (action.view === 'knownFacts') {
if (onKnownFactsOpen) {
onKnownFactsOpen();
return;
}
}
if (action.view) {
setActiveView(action.view);
return;
}
(_a = action.onClick) === null || _a === void 0 ? void 0 : _a.call(action);
}, children: [_jsx("span", { className: "memori-mobile-session-panel--action-icon", children: action.icon }), _jsxs("span", { className: "memori-mobile-session-panel--action-copy", children: [_jsx("span", { className: "memori-mobile-session-panel--action-title", children: action.title }), action.subtitle && (_jsx("span", { className: "memori-mobile-session-panel--action-subtitle", children: action.subtitle }))] }), (isShareAction || action.view === 'location') && (_jsx("span", { className: "memori-mobile-session-panel--action-trailing", children: _jsx(ChevronRight, { size: 16 }) }))] }));
})() }, action.key))) }), isLoggedIn && (_jsxs(Button, { variant: "toolbar", size: "sm", shape: "default", className: "memori-mobile-session-panel--logout", onClick: onLogout, children: [_jsx("span", { className: "memori-mobile-session-panel--action-icon", children: _jsx(LogOut, { size: 18 }) }), _jsx("span", { className: "memori-mobile-session-panel--logout-label", children: logoutLabel })] })), !isLoggedIn && (_jsx("div", { className: "memori-mobile-session-panel--login-cta-wrap", children: _jsxs(Button, { variant: "toolbar", size: "sm", className: "memori-mobile-session-panel--login-cta", onClick: onLogin, children: [_jsx("span", { className: "memori-mobile-session-panel--action-icon", children: _jsx(LogIn, { size: 18 }) }), loginLabel] }) }))] })) : (_jsxs("div", { className: "memori-mobile-session-panel--page", children: [_jsxs(Button, { variant: "toolbar", size: "sm", className: "memori-mobile-session-panel--back", onClick: () => setActiveView('session'), children: [_jsx(ChevronLeft, { size: 16 }), backLabel] }), _jsx("h3", { className: "memori-mobile-session-panel--page-title", children: activeView === 'location'
? locationPageTitle
: activeView === 'share'
? sharePageTitle
: activeView === 'aiUsage'
? resolvedAiUsageTitle
: knownFactsPageTitle }), activeView === 'location' ? (_jsx("div", { className: "memori-mobile-session-panel--page-content memori-mobile-session-panel--location-content", children: setVenue ? (_jsx(PositionPopoverContent, { venue: venue, setVenue: setVenue })) : (_jsxs(_Fragment, { children: [_jsx("p", { className: "memori-mobile-session-panel--meta-label", children: locationStatusLabel }), _jsx("p", { className: "memori-mobile-session-panel--meta-value", children: locationPlace || locationUnknownLabel }), _jsxs("div", { className: "memori-mobile-session-panel--page-actions", children: [_jsx(Button, { variant: "toolbar", size: "sm", className: `memori-mobile-session-panel--page-btn ${isLocationEnabled
? 'memori-mobile-session-panel--page-btn-active'
: ''}`, onClick: onLocationEnable, children: _jsx("span", { children: locationEnableLabel }) }), _jsx(Button, { variant: "toolbar", size: "sm", className: "memori-mobile-session-panel--page-btn memori-mobile-session-panel--page-btn-secondary", onClick: onLocationDisable, children: locationDisableLabel })] })] })) })) : activeView === 'share' ? (_jsx("div", { className: "memori-mobile-session-panel--page-content memori-mobile-session-panel--share-content", children: shareContent })) : activeView === 'aiUsage' ? (_jsx("div", { className: "memori-mobile-session-panel--page-content memori-mobile-session-panel--ai-usage-content", children: _jsx(ChatConsumptionContent, { history: history, showTitle: false, className: "memori-mobile-session-panel--ai-usage" }) })) : (_jsxs("div", { className: "memori-mobile-session-panel--page-content", children: [knownFactsDescription && (_jsx("p", { className: "memori-mobile-session-panel--known-facts-description", children: knownFactsDescription })), knownFactsCountLabel && (_jsx("p", { className: "memori-mobile-session-panel--known-facts-count", children: knownFactsCountLabel })), _jsx(Button, { variant: "toolbar", size: "sm", className: "memori-mobile-session-panel--page-btn", disabled: knownFactsDisabled, onClick: onKnownFactsOpen, children: knownFactsCtaLabel })] }))] }))] })] }));
};
export default MobileSessionPanel;
//# sourceMappingURL=MobileSessionPanel.js.map