| import React, { useState, useEffect } from 'react'; |
| import { AIEntity } from './components/AIEntity'; |
| import { TopActions } from './components/TopActions'; |
| import { BottomNav } from './components/BottomNav'; |
| import { CustomizationButton } from './components/CustomizationButton'; |
| import { CharacterCustomizationDialog, CharacterPreferences } from './components/CharacterCustomizationDialog'; |
| import { ChatDialog } from './components/ChatDialog'; |
| import { RecordView } from './components/RecordView'; |
| import { CommunityView } from './components/CommunityView'; |
| import { MineView } from './components/MineView'; |
| import { MoodView } from './components/MoodView'; |
| import { InspirationView } from './components/InspirationView'; |
| import { TodoView } from './components/TodoView'; |
| import { HomeInput } from './components/HomeInput'; |
| import { |
| Tab, |
| MoodAction, |
| RecordItem, |
| RecordSource, |
| CommunityPost, |
| Profile, |
| DeviceStatus, |
| MoodItem, |
| MoodType, |
| InspirationItem, |
| TodoItem |
| } from './types'; |
| import { apiService } from './services/api'; |
| import { |
| transformRecord, |
| transformMood, |
| transformInspiration, |
| transformTodo |
| } from './utils/dataTransform'; |
|
|
| |
| const generateMockRecords = (): RecordItem[] => { |
| const now = Date.now(); |
| const hour = 60 * 60 * 1000; |
| const day = 24 * hour; |
| |
| return [ |
| |
| { |
| id: '1', |
| content: "早上6点醒来,窗外的鸟鸣声特别清脆。新的一天,充满期待。", |
| createdAt: now - hour * 2, |
| sourceType: RecordSource.MOOD |
| }, |
| { |
| id: '2', |
| content: "晨跑时看到日出,天空从深蓝渐变到橙粉色,美得让人想哭。", |
| createdAt: now - hour * 1.5, |
| sourceType: RecordSource.VOICE |
| }, |
| |
| { |
| id: '3', |
| content: "咖啡店里的音乐很好听,是一首法语歌。要记得回去搜一下。", |
| createdAt: now - hour, |
| sourceType: RecordSource.INSPIRATION |
| }, |
| { |
| id: '4', |
| content: "工作会议上提出的方案被采纳了,同事们的认可让我很开心。", |
| createdAt: now - hour * 0.5, |
| sourceType: RecordSource.MOOD |
| }, |
| |
| { |
| id: '5', |
| content: "午后的阳光透过百叶窗,在桌上投下一道道光影。时间仿佛静止了。", |
| createdAt: now - day - hour * 6, |
| sourceType: RecordSource.MANUAL |
| }, |
| { |
| id: '6', |
| content: "突然想到:如果每个人的记忆都能变成一本书,那会是什么样子?", |
| createdAt: now - day - hour * 5, |
| sourceType: RecordSource.INSPIRATION |
| }, |
| |
| { |
| id: '7', |
| content: "下班路上遇到一只流浪猫,蹲下来和它对视了很久。它的眼睛很清澈。", |
| createdAt: now - day - hour * 2, |
| sourceType: RecordSource.VOICE |
| }, |
| { |
| id: '8', |
| content: "晚餐做了番茄炒蛋,虽然简单,但吃得很满足。生活的幸福就是这样。", |
| createdAt: now - day - hour, |
| sourceType: RecordSource.MOOD |
| }, |
| |
| { |
| id: '9', |
| content: "今天有点焦虑,deadline临近,但还有很多工作没完成。深呼吸,一步一步来。", |
| createdAt: now - day * 2 - hour * 8, |
| sourceType: RecordSource.MOOD |
| }, |
| { |
| id: '10', |
| content: "中午和老朋友视频通话,虽然各自忙碌,但友谊依然温暖。", |
| createdAt: now - day * 2 - hour * 6, |
| sourceType: RecordSource.VOICE |
| }, |
| { |
| id: '11', |
| content: "读到一句话:'不要因为走得太远,而忘记为什么出发。' 很有共鸣。", |
| createdAt: now - day * 2 - hour * 3, |
| sourceType: RecordSource.MANUAL |
| }, |
| { |
| id: '12', |
| content: "晚上加班到很晚,但看到项目进展顺利,疲惫中带着一丝成就感。", |
| createdAt: now - day * 2 - hour, |
| sourceType: RecordSource.MOOD |
| }, |
| |
| { |
| id: '13', |
| content: "周末去了郊外,看到一大片向日葵田。金黄色的花海,治愈了整个人。", |
| createdAt: now - day * 3 - hour * 10, |
| sourceType: RecordSource.VOICE |
| }, |
| { |
| id: '14', |
| content: "在书店翻到一本旧书,扉页上有人写的字:'愿你永远保持好奇心'。", |
| createdAt: now - day * 3 - hour * 7, |
| sourceType: RecordSource.INSPIRATION |
| }, |
| { |
| id: '15', |
| content: "下午茶时间,点了一块抹茶蛋糕。甜食真的能让心情变好。", |
| createdAt: now - day * 3 - hour * 4, |
| sourceType: RecordSource.MOOD |
| }, |
| |
| { |
| id: '16', |
| content: "今天状态不太好,做什么都提不起劲。也许只是需要好好休息一下。", |
| createdAt: now - day * 4 - hour * 9, |
| sourceType: RecordSource.MOOD |
| }, |
| { |
| id: '17', |
| content: "妈妈打电话来,聊了很久。她说最近在学广场舞,听起来很开心。", |
| createdAt: now - day * 4 - hour * 5, |
| sourceType: RecordSource.VOICE |
| }, |
| { |
| id: '18', |
| content: "晚上看了一部老电影,《天使爱美丽》。生活需要一些小确幸和浪漫。", |
| createdAt: now - day * 4 - hour * 2, |
| sourceType: RecordSource.MANUAL |
| }, |
| |
| { |
| id: '19', |
| content: "早起去菜市场,看到各种新鲜的蔬菜水果,感受到生活的烟火气。", |
| createdAt: now - day * 5 - hour * 11, |
| sourceType: RecordSource.MOOD |
| }, |
| { |
| id: '20', |
| content: "灵感:设计一个'慢生活'主题的摄影集,记录日常中被忽略的美好瞬间。", |
| createdAt: now - day * 5 - hour * 8, |
| sourceType: RecordSource.INSPIRATION |
| }, |
| { |
| id: '21', |
| content: "下午在公园散步,看到一对老夫妻手牵手。希望自己老了也能这样。", |
| createdAt: now - day * 5 - hour * 4, |
| sourceType: RecordSource.VOICE |
| }, |
| |
| { |
| id: '22', |
| content: "工作上遇到了一些挫折,有点沮丧。但转念一想,这也是成长的机会。", |
| createdAt: now - day * 6 - hour * 10, |
| sourceType: RecordSource.MOOD |
| }, |
| { |
| id: '23', |
| content: "中午吃饭时,餐厅放的背景音乐是《月亮代表我的心》,突然很想家。", |
| createdAt: now - day * 6 - hour * 6, |
| sourceType: RecordSource.MANUAL |
| }, |
| { |
| id: '24', |
| content: "晚上和室友一起做饭,虽然厨艺不精,但笑声不断。这就是生活的乐趣。", |
| createdAt: now - day * 6 - hour * 2, |
| sourceType: RecordSource.VOICE |
| }, |
| |
| { |
| id: '25', |
| content: "今天是周一,新的一周开始。给自己定个小目标:每天进步一点点。", |
| createdAt: now - day * 7 - hour * 9, |
| sourceType: RecordSource.MOOD |
| }, |
| { |
| id: '26', |
| content: "路过花店,买了一束雏菊。白色的花瓣,简单却很美。", |
| createdAt: now - day * 7 - hour * 5, |
| sourceType: RecordSource.MANUAL |
| }, |
| { |
| id: '27', |
| content: "晚上写日记时想到:记录生活不是为了回忆,而是为了更好地活在当下。", |
| createdAt: now - day * 7 - hour * 1, |
| sourceType: RecordSource.INSPIRATION |
| }, |
| |
| { |
| id: '28', |
| content: "雨后的街道,空气中弥漫着泥土的清香。这是大自然的馈赠。", |
| createdAt: now - day * 10 - hour * 7, |
| sourceType: RecordSource.VOICE |
| }, |
| { |
| id: '29', |
| content: "完成了一个困扰很久的难题,那种豁然开朗的感觉太棒了!", |
| createdAt: now - day * 12 - hour * 4, |
| sourceType: RecordSource.MOOD |
| }, |
| { |
| id: '30', |
| content: "深夜听歌,突然被一句歌词击中:'我们都在时光里跌跌撞撞地成长'。", |
| createdAt: now - day * 14 - hour * 2, |
| sourceType: RecordSource.MANUAL |
| } |
| ]; |
| }; |
|
|
| |
| const generateMockPosts = (): CommunityPost[] => { |
| const now = Date.now(); |
| return [ |
| { |
| id: '101', |
| user: { name: '安静的观察者', avatarColor: 'bg-indigo-200' }, |
| content: "有人也觉得自己在等待一些还没发生的事情吗?这是一种对未来的奇怪怀念。", |
| createdAt: now - 1000 * 60 * 10, |
| likeCount: 12, |
| isLiked: false, |
| commentCount: 3 |
| }, |
| { |
| id: '102', |
| user: { name: '温柔的灵魂', avatarColor: 'bg-pink-200' }, |
| content: "泡了一杯茶,看着蒸汽升起看了5分钟。这是我今天最美好的时刻。", |
| createdAt: now - 1000 * 60 * 45, |
| likeCount: 28, |
| isLiked: true, |
| commentCount: 5 |
| }, |
| { |
| id: '103', |
| user: { name: '匿名', avatarColor: 'bg-teal-200' }, |
| content: "今天我试着对自己更温柔一些。这比对别人温柔更难。", |
| createdAt: now - 1000 * 60 * 60 * 2, |
| likeCount: 45, |
| isLiked: false, |
| commentCount: 8 |
| }, |
| { |
| id: '104', |
| user: { name: '云中漫步者', avatarColor: 'bg-blue-200' }, |
| content: "今天的日落特别粉。", |
| createdAt: now - 1000 * 60 * 60 * 5, |
| likeCount: 8, |
| isLiked: false, |
| commentCount: 0 |
| } |
| ]; |
| }; |
|
|
| |
| const mockProfile: Profile = { |
| name: "小雅", |
| birthday: "3月12日", |
| moodStatus: "感觉平静而专注", |
| }; |
|
|
| const mockDeviceStatus: DeviceStatus = { |
| isConnected: true, |
| batteryLevel: 82, |
| deviceName: "心灵伴侣吊坠" |
| }; |
|
|
| |
| const generateMockMoods = (): MoodItem[] => { |
| const now = Date.now(); |
| const hour = 60 * 60 * 1000; |
| return [ |
| |
| { |
| id: 'm1', |
| type: MoodType.HAPPY, |
| date: now - hour * 2, |
| intensity: 0.9, |
| x: 25, |
| y: 30 |
| }, |
| { |
| id: 'm2', |
| type: MoodType.HAPPY, |
| date: now - hour * 5, |
| intensity: 0.7, |
| x: 70, |
| y: 25 |
| }, |
| |
| { |
| id: 'm3', |
| type: MoodType.CALM, |
| date: now - hour * 1, |
| intensity: 0.8, |
| x: 35, |
| y: 45 |
| }, |
| { |
| id: 'm4', |
| type: MoodType.CALM, |
| date: now - hour * 8, |
| intensity: 0.6, |
| x: 55, |
| y: 60 |
| }, |
| { |
| id: 'm5', |
| type: MoodType.CALM, |
| date: now - hour * 12, |
| intensity: 0.75, |
| x: 20, |
| y: 70 |
| }, |
| |
| { |
| id: 'm6', |
| type: MoodType.TIRED, |
| date: now - hour * 3, |
| intensity: 0.5, |
| x: 65, |
| y: 50 |
| }, |
| { |
| id: 'm7', |
| type: MoodType.TIRED, |
| date: now - hour * 10, |
| intensity: 0.4, |
| x: 45, |
| y: 75 |
| }, |
| |
| { |
| id: 'm8', |
| type: MoodType.ANXIOUS, |
| date: now - hour * 4, |
| intensity: 0.6, |
| x: 80, |
| y: 40 |
| }, |
| { |
| id: 'm9', |
| type: MoodType.ANXIOUS, |
| date: now - hour * 6, |
| intensity: 0.55, |
| x: 30, |
| y: 55 |
| }, |
| |
| { |
| id: 'm10', |
| type: MoodType.HOPEFUL, |
| date: now, |
| intensity: 0.85, |
| x: 50, |
| y: 35 |
| }, |
| { |
| id: 'm11', |
| type: MoodType.HOPEFUL, |
| date: now - hour * 7, |
| intensity: 0.7, |
| x: 75, |
| y: 65 |
| }, |
| { |
| id: 'm12', |
| type: MoodType.HOPEFUL, |
| date: now - hour * 11, |
| intensity: 0.65, |
| x: 40, |
| y: 20 |
| } |
| ]; |
| }; |
|
|
| |
| const generateMockInspirations = (): InspirationItem[] => { |
| const now = Date.now(); |
| const hour = 60 * 60 * 1000; |
| const day = 24 * hour; |
| return [ |
| { |
| id: 'i1', |
| content: "如果云朵只是地球在做梦呢?", |
| createdAt: now - hour * 2, |
| tags: ['随想', '自然'] |
| }, |
| { |
| id: 'i2', |
| content: "设计概念:一个不显示数字的时钟,只用颜色代表一天的能量。", |
| createdAt: now - hour * 5, |
| tags: ['设计', '创意'] |
| }, |
| { |
| id: 'i3', |
| content: "旧书和咖啡的香气,是时光最温柔的记忆。", |
| createdAt: now - hour * 8, |
| tags: ['生活', '随想'] |
| }, |
| { |
| id: 'i4', |
| content: "记得在接电话前深呼吸,给自己三秒钟的准备时间。", |
| createdAt: now - day, |
| tags: ['提醒', '生活'] |
| }, |
| { |
| id: 'i5', |
| content: "也许每个人都是一座岛屿,而友谊是连接彼此的桥梁。", |
| createdAt: now - day - hour * 3, |
| tags: ['随想', '友情'] |
| }, |
| { |
| id: 'i6', |
| content: "学习新技能时,不要害怕犯错,错误是成长的阶梯。", |
| createdAt: now - day * 2, |
| tags: ['学习', '成长'] |
| }, |
| { |
| id: 'i7', |
| content: "工作灵感:用番茄工作法,25分钟专注,5分钟放松。", |
| createdAt: now - day * 2 - hour * 4, |
| tags: ['工作', '提醒'] |
| }, |
| { |
| id: 'i8', |
| content: "雨后的空气里藏着大地的秘密。", |
| createdAt: now - day * 3, |
| tags: ['自然', '随想'] |
| }, |
| { |
| id: 'i9', |
| content: "创意想法:设计一个记录每天小确幸的应用。", |
| createdAt: now - day * 3 - hour * 6, |
| tags: ['创意', '设计'] |
| }, |
| { |
| id: 'i10', |
| content: "真正的朋友,是那个在你沉默时也能理解你的人。", |
| createdAt: now - day * 4, |
| tags: ['友情', '生活'] |
| }, |
| { |
| id: 'i11', |
| content: "每天写三件感恩的事,心态会慢慢变得更积极。", |
| createdAt: now - day * 5, |
| tags: ['成长', '提醒'] |
| }, |
| { |
| id: 'i12', |
| content: "工作中遇到困难时,试着换个角度思考问题。", |
| createdAt: now - day * 5 - hour * 2, |
| tags: ['工作', '成长'] |
| }, |
| { |
| id: 'i13', |
| content: "学习笔记:费曼学习法 - 用简单的语言解释复杂的概念。", |
| createdAt: now - day * 6, |
| tags: ['学习', '工作'] |
| }, |
| { |
| id: 'i14', |
| content: "窗外的鸟鸣声,是大自然送给城市的礼物。", |
| createdAt: now - day * 7, |
| tags: ['自然', '生活'] |
| }, |
| { |
| id: 'i15', |
| content: "设计理念:少即是多,简洁才是最高级的美。", |
| createdAt: now - day * 8, |
| tags: ['设计', '随想'] |
| } |
| ]; |
| }; |
|
|
| |
| const generateMockTodos = (): TodoItem[] => { |
| const now = Date.now(); |
| const hour = 60 * 60 * 1000; |
| const day = 24 * hour; |
| return [ |
| { |
| id: 't1', |
| title: "慢慢喝一杯水", |
| createdAt: now, |
| scheduledAt: now + hour / 2, |
| isDone: false, |
| category: 'health' |
| }, |
| { |
| id: 't2', |
| title: "读《小王子》20页", |
| createdAt: now - hour, |
| scheduledAt: now + hour * 2, |
| isDone: false, |
| category: 'life' |
| }, |
| { |
| id: 't3', |
| title: "回复小雪关于项目的邮件", |
| createdAt: now - hour * 4, |
| scheduledAt: now + day, |
| isDone: false, |
| category: 'work' |
| }, |
| { |
| id: 't4', |
| title: "整理学习笔记", |
| createdAt: now - hour * 6, |
| scheduledAt: now + day, |
| isDone: false, |
| category: 'study' |
| }, |
| { |
| id: 't5', |
| title: "晚上8点运动30分钟", |
| createdAt: now - day, |
| scheduledAt: now + hour * 8, |
| isDone: false, |
| category: 'health' |
| }, |
| { |
| id: 't6', |
| title: "准备明天的会议材料", |
| createdAt: now - day, |
| scheduledAt: now + day, |
| isDone: false, |
| category: 'work' |
| }, |
| { |
| id: 't7', |
| title: "买鲜花", |
| createdAt: now - day, |
| isDone: true, |
| category: 'life' |
| }, |
| { |
| id: 't8', |
| title: "完成英语作业", |
| createdAt: now - day * 2, |
| isDone: true, |
| category: 'study' |
| }, |
| { |
| id: 't9', |
| title: "给植物浇水", |
| createdAt: now - day * 2, |
| isDone: true, |
| category: 'life' |
| } |
| ]; |
| }; |
|
|
| export default function App() { |
| const [currentTab, setCurrentTab] = useState<Tab>(Tab.HOME); |
| const [records, setRecords] = useState<RecordItem[]>(generateMockRecords()); |
| const [posts, setPosts] = useState<CommunityPost[]>(generateMockPosts()); |
| const [moods, setMoods] = useState<MoodItem[]>(generateMockMoods()); |
| const [inspirations, setInspirations] = useState<InspirationItem[]>(generateMockInspirations()); |
| const [todos, setTodos] = useState<TodoItem[]>(generateMockTodos()); |
| const [loading, setLoading] = useState(true); |
| const [error, setError] = useState<string | null>(null); |
| const [characterImageUrl, setCharacterImageUrl] = useState<string | undefined>(); |
| const [characterPreferences, setCharacterPreferences] = useState<CharacterPreferences | undefined>(); |
| const [showCustomizationDialog, setShowCustomizationDialog] = useState(false); |
| const [showChatDialog, setShowChatDialog] = useState(false); |
| |
| |
| const [activeActionView, setActiveActionView] = useState<MoodAction | null>(null); |
|
|
| |
| useEffect(() => { |
| loadAllData(); |
| }, []); |
|
|
| const loadAllData = async () => { |
| try { |
| setLoading(true); |
| setError(null); |
|
|
| |
| const [recordsRes, moodsRes, inspirationsRes, todosRes, userConfigRes] = await Promise.all([ |
| apiService.getRecords().catch(() => ({ records: [] })), |
| apiService.getMoods().catch(() => ({ moods: [] })), |
| apiService.getInspirations().catch(() => ({ inspirations: [] })), |
| apiService.getTodos().catch(() => ({ todos: [] })), |
| apiService.getUserConfig().catch(() => null) |
| ]); |
|
|
| |
| if (recordsRes.records.length > 0) { |
| setRecords(recordsRes.records.map(transformRecord)); |
| } |
| |
| if (moodsRes.moods.length > 0) { |
| setMoods(moodsRes.moods.map((m, i) => transformMood(m, i))); |
| } |
| |
| if (inspirationsRes.inspirations.length > 0) { |
| setInspirations(inspirationsRes.inspirations.map(transformInspiration)); |
| } |
| |
| if (todosRes.todos.length > 0) { |
| setTodos(todosRes.todos.map(transformTodo)); |
| } |
|
|
| |
| if (userConfigRes?.character?.image_url) { |
| setCharacterImageUrl(userConfigRes.character.image_url); |
| } |
| |
| |
| if (userConfigRes?.character?.preferences) { |
| setCharacterPreferences(userConfigRes.character.preferences); |
| } |
|
|
| } catch (err) { |
| console.error('Failed to load data:', err); |
| setError('Failed to load data from server. Using mock data.'); |
| } finally { |
| setLoading(false); |
| } |
| }; |
|
|
| |
| const latestVoiceRecord = records |
| .filter(r => r.sourceType === RecordSource.VOICE) |
| .sort((a, b) => b.createdAt - a.createdAt)[0]?.content; |
|
|
| |
| const handleSendMessage = async (message: string): Promise<string> => { |
| try { |
| const response = await apiService.chatWithAI(message); |
| return response; |
| } catch (error) { |
| console.error('Failed to chat:', error); |
| return '抱歉,我现在有点累了,稍后再聊好吗?'; |
| } |
| }; |
|
|
| |
| const bgGradient = "bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-purple-100 via-pink-50 to-blue-50"; |
|
|
| const handleActionClick = (action: MoodAction) => { |
| console.log(`Action clicked: ${action}`); |
| if (action === MoodAction.MOOD) { |
| setActiveActionView(MoodAction.MOOD); |
| } else if (action === MoodAction.INSPIRATION) { |
| setActiveActionView(MoodAction.INSPIRATION); |
| } else if (action === MoodAction.TODO) { |
| setActiveActionView(MoodAction.TODO); |
| } |
| }; |
| |
| const closeActionView = () => { |
| setActiveActionView(null); |
| }; |
|
|
| const handleAddPost = (content: string) => { |
| const newPost: CommunityPost = { |
| id: Date.now().toString(), |
| user: { name: 'Me', avatarColor: 'bg-purple-300' }, |
| content: content, |
| createdAt: Date.now(), |
| likeCount: 0, |
| isLiked: false, |
| commentCount: 0 |
| }; |
| setPosts([newPost, ...posts]); |
| }; |
| |
| const handleAddInspiration = async (content: string, isVoice: boolean) => { |
| try { |
| |
| if (isVoice) { |
| |
| console.log('Voice input:', content); |
| } |
| |
| |
| const response = await apiService.processInput(undefined, content); |
| |
| |
| await loadAllData(); |
| |
| console.log('Inspiration added:', response); |
| } catch (error) { |
| console.error('Failed to add inspiration:', error); |
| alert('添加灵感失败,请重试'); |
| } |
| }; |
|
|
| const handleAddTodo = () => { |
| console.log("Add todo clicked"); |
| |
| }; |
| |
| const handleToggleTodo = async (id: string) => { |
| |
| const todo = todos.find(t => t.id === id); |
| if (!todo) return; |
|
|
| const newStatus = todo.isDone ? 'pending' : 'completed'; |
| |
| setTodos(todos.map(t => |
| t.id === id ? { ...t, isDone: !t.isDone } : t |
| )); |
|
|
| |
| try { |
| await apiService.updateTodoStatus(id, newStatus); |
| } catch (err) { |
| console.error('Failed to update todo:', err); |
| |
| setTodos(todos.map(t => |
| t.id === id ? { ...t, isDone: todo.isDone } : t |
| )); |
| } |
| }; |
|
|
| const handleGenerateCharacter = async (preferences: CharacterPreferences) => { |
| try { |
| console.log('Generating character with preferences:', preferences); |
| |
| const result = await apiService.generateCharacter(preferences); |
| |
| console.log('Character generated:', result); |
| |
| |
| setCharacterImageUrl(result.image_url); |
| setCharacterPreferences(result.preferences); |
| |
| |
| alert('AI 形象生成成功!'); |
| |
| } catch (error) { |
| console.error('Failed to generate character:', error); |
| throw error; |
| } |
| }; |
|
|
| const handleTabChange = (tab: Tab) => { |
| |
| if (activeActionView) { |
| setActiveActionView(null); |
| } |
| |
| setCurrentTab(tab); |
| }; |
|
|
| const handleOpenChat = () => { |
| setShowChatDialog(true); |
| }; |
|
|
| const isHome = currentTab === Tab.HOME; |
|
|
| return ( |
| <div className={`relative w-full h-screen overflow-hidden ${bgGradient} text-slate-700`}> |
| {/* Ambient noise texture overlay */} |
| <div className="absolute inset-0 opacity-[0.03] pointer-events-none mix-blend-multiply bg-[url('https://grainy-gradients.vercel.app/noise.svg')]"></div> |
| |
| {/* Main Content Area */} |
| <main className="relative flex flex-col h-full items-center justify-between"> |
| |
| {/* HOME VIEW */} |
| {isHome && !activeActionView && ( |
| <> |
| <TopActions onActionClick={handleActionClick} /> |
| |
| <div className="flex-1 flex flex-col items-center justify-center w-full relative pt-24 pb-12"> |
| <div className="absolute top-28 opacity-60 text-sm font-light tracking-widest text-slate-500 animate-pulse delay-700"> |
| I'm here with you |
| </div> |
| |
| <AIEntity |
| imageUrl={characterImageUrl} |
| latestRecord={latestVoiceRecord} |
| onGreeting={(greeting) => console.log('AI greeting:', greeting)} |
| onOpenChat={handleOpenChat} |
| /> |
| |
| {/* Home Input Component */} |
| <div className="mt-8 w-full"> |
| <HomeInput onRecordComplete={loadAllData} /> |
| </div> |
| </div> |
| |
| <CustomizationButton onClick={() => setShowCustomizationDialog(true)} /> |
| </> |
| )} |
| |
| {/* RECORD VIEW */} |
| {currentTab === Tab.RECORD && !activeActionView && ( |
| <RecordView items={records} /> |
| )} |
| |
| {/* COMMUNITY VIEW */} |
| {currentTab === Tab.COMMUNITY && !activeActionView && ( |
| <CommunityView posts={posts} onAddPost={handleAddPost} /> |
| )} |
| |
| {/* MINE VIEW */} |
| {currentTab === Tab.MINE && !activeActionView && ( |
| <MineView profile={mockProfile} deviceStatus={mockDeviceStatus} /> |
| )} |
| |
| {/* --- FULL SCREEN OVERLAYS --- */} |
| |
| {/* MOOD PAGE OVERLAY */} |
| {activeActionView === MoodAction.MOOD && ( |
| <MoodView |
| items={moods} |
| onClose={closeActionView} |
| characterImageUrl={characterImageUrl} |
| onSendMessage={handleSendMessage} |
| /> |
| )} |
| |
| {/* INSPIRATION PAGE OVERLAY */} |
| {activeActionView === MoodAction.INSPIRATION && ( |
| <InspirationView |
| items={inspirations} |
| onClose={closeActionView} |
| onAdd={handleAddInspiration} |
| characterImageUrl={characterImageUrl} |
| onSendMessage={handleSendMessage} |
| /> |
| )} |
| |
| {/* TODO PAGE OVERLAY */} |
| {activeActionView === MoodAction.TODO && ( |
| <TodoView |
| items={todos} |
| onClose={closeActionView} |
| onAdd={handleAddTodo} |
| onToggleItem={handleToggleTodo} |
| characterImageUrl={characterImageUrl} |
| onSendMessage={handleSendMessage} |
| /> |
| )} |
| |
| {/* Bottom Navigation - 始终显示,z-index 最高 */} |
| <BottomNav currentTab={currentTab} onTabChange={handleTabChange} /> |
| </main> |
|
|
| {} |
| <CharacterCustomizationDialog |
| isOpen={showCustomizationDialog} |
| onClose={() => setShowCustomizationDialog(false)} |
| onGenerate={handleGenerateCharacter} |
| onSelectHistory={(imageUrl) => { |
| setCharacterImageUrl(imageUrl); |
| }} |
| currentPreferences={characterPreferences} |
| currentImageUrl={characterImageUrl} |
| /> |
|
|
| {} |
| <ChatDialog |
| isOpen={showChatDialog} |
| onClose={() => setShowChatDialog(false)} |
| characterImageUrl={characterImageUrl} |
| onSendMessage={handleSendMessage} |
| /> |
| </div> |
| ); |
| } |