managarten/apps-archived/memoro/apps/mobile/components/molecules/StableMemoTitle.tsx
Till-JS 61d181fbc2 chore: archive inactive projects to apps-archived/
Move inactive projects out of active workspace:
- bauntown (community website)
- maerchenzauber (AI story generation)
- memoro (voice memo app)
- news (news aggregation)
- nutriphi (nutrition tracking)
- reader (reading app)
- uload (URL shortener)
- wisekeep (AI wisdom extraction)

Update CLAUDE.md documentation:
- Add presi to active projects
- Document archived projects section
- Update workspace configuration

Archived apps can be re-activated by moving back to apps/

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 07:03:59 +01:00

109 lines
2.7 KiB
TypeScript

import React, { useEffect, useState, useRef } from 'react';
import { Text, TextProps, Animated } from 'react-native';
import { useMemoProcessing } from '~/features/memos/contexts/MemoProcessingContext';
interface StableMemoTitleProps extends TextProps {
memoId: string;
titleClasses?: string;
numberOfLines?: number;
ellipsizeMode?: 'head' | 'middle' | 'tail' | 'clip';
}
/**
* A specialized component that renders ONLY the memo title
* and updates it without causing parent re-renders
*/
const StableMemoTitle: React.FC<StableMemoTitleProps> = ({
memoId,
titleClasses,
numberOfLines = 2,
ellipsizeMode = 'tail',
style,
...rest
}) => {
// Get the display title from context
const { getDisplayTitle, registerForUpdates } = useMemoProcessing();
// Local state for the title (will only update this component)
const [title, setTitle] = useState(() => getDisplayTitle(memoId) || 'New Recording');
// Animation value for smooth transitions
const fadeAnim = useRef(new Animated.Value(1)).current;
// Direct polling for title updates to ensure we don't miss any
useEffect(() => {
// Poll for title updates every 500ms
const pollInterval = setInterval(() => {
const newTitle = getDisplayTitle(memoId);
if (newTitle && newTitle !== title) {
console.debug(`🔄 StableMemoTitle: Updating title from "${title}" to "${newTitle}"`);
// Fade out
Animated.timing(fadeAnim, {
toValue: 0,
duration: 100,
useNativeDriver: true,
}).start(() => {
// Update title
setTitle(newTitle);
// Fade in
Animated.timing(fadeAnim, {
toValue: 1,
duration: 100,
useNativeDriver: true,
}).start();
});
}
}, 500);
// Also register for immediate updates from the context
const unregister = registerForUpdates(() => {
const newTitle = getDisplayTitle(memoId);
if (newTitle && newTitle !== title) {
console.debug(
`🔔 StableMemoTitle: Context update - title from "${title}" to "${newTitle}"`
);
// Fade out
Animated.timing(fadeAnim, {
toValue: 0,
duration: 100,
useNativeDriver: true,
}).start(() => {
// Update title
setTitle(newTitle);
// Fade in
Animated.timing(fadeAnim, {
toValue: 1,
duration: 100,
useNativeDriver: true,
}).start();
});
}
});
// Cleanup
return () => {
clearInterval(pollInterval);
unregister();
};
}, [memoId, getDisplayTitle, registerForUpdates, title, fadeAnim]);
return (
<Animated.View style={{ opacity: fadeAnim }}>
<Text
className={titleClasses}
numberOfLines={numberOfLines}
ellipsizeMode={ellipsizeMode}
style={style}
{...rest}
>
{title}
</Text>
</Animated.View>
);
};
export default React.memo(StableMemoTitle);