From a053861a8b5d9bc0f742b79b12c0882a01ee1eb0 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 28 Jan 2026 17:58:00 +1100 Subject: [PATCH] Initial commit of PostIt app --- ATTRIBUTIONS.md | 3 + README.md | 11 + guidelines/Guidelines.md | 61 ++ index.html | 15 + package.json | 88 +++ postcss.config.mjs | 15 + src/app/App.tsx | 123 +++ src/app/components/Auth.tsx | 209 +++++ src/app/components/ChatList.tsx | 103 +++ src/app/components/ChatView.tsx | 161 ++++ src/app/components/Feed.tsx | 138 ++++ src/app/components/Post.tsx | 143 ++++ src/app/components/Profile.tsx | 232 ++++++ src/app/components/SimpleChat.tsx | 195 +++++ src/app/components/SimpleFeed.tsx | 291 +++++++ .../components/figma/ImageWithFallback.tsx | 27 + src/app/components/ui/accordion.tsx | 66 ++ src/app/components/ui/alert-dialog.tsx | 157 ++++ src/app/components/ui/alert.tsx | 66 ++ src/app/components/ui/aspect-ratio.tsx | 11 + src/app/components/ui/avatar.tsx | 53 ++ src/app/components/ui/badge.tsx | 46 ++ src/app/components/ui/breadcrumb.tsx | 109 +++ src/app/components/ui/button.tsx | 58 ++ src/app/components/ui/calendar.tsx | 75 ++ src/app/components/ui/card.tsx | 92 +++ src/app/components/ui/carousel.tsx | 241 ++++++ src/app/components/ui/chart.tsx | 353 +++++++++ src/app/components/ui/checkbox.tsx | 32 + src/app/components/ui/collapsible.tsx | 33 + src/app/components/ui/command.tsx | 177 +++++ src/app/components/ui/context-menu.tsx | 252 ++++++ src/app/components/ui/dialog.tsx | 135 ++++ src/app/components/ui/drawer.tsx | 132 ++++ src/app/components/ui/dropdown-menu.tsx | 257 +++++++ src/app/components/ui/form.tsx | 168 ++++ src/app/components/ui/hover-card.tsx | 44 ++ src/app/components/ui/input-otp.tsx | 77 ++ src/app/components/ui/input.tsx | 21 + src/app/components/ui/label.tsx | 24 + src/app/components/ui/menubar.tsx | 276 +++++++ src/app/components/ui/navigation-menu.tsx | 168 ++++ src/app/components/ui/pagination.tsx | 127 +++ src/app/components/ui/popover.tsx | 48 ++ src/app/components/ui/progress.tsx | 31 + src/app/components/ui/radio-group.tsx | 45 ++ src/app/components/ui/resizable.tsx | 56 ++ src/app/components/ui/scroll-area.tsx | 58 ++ src/app/components/ui/select.tsx | 189 +++++ src/app/components/ui/separator.tsx | 28 + src/app/components/ui/sheet.tsx | 139 ++++ src/app/components/ui/sidebar.tsx | 726 ++++++++++++++++++ src/app/components/ui/skeleton.tsx | 13 + src/app/components/ui/slider.tsx | 63 ++ src/app/components/ui/sonner.tsx | 25 + src/app/components/ui/switch.tsx | 31 + src/app/components/ui/table.tsx | 116 +++ src/app/components/ui/tabs.tsx | 66 ++ src/app/components/ui/textarea.tsx | 18 + src/app/components/ui/toggle-group.tsx | 73 ++ src/app/components/ui/toggle.tsx | 47 ++ src/app/components/ui/tooltip.tsx | 61 ++ src/app/components/ui/use-mobile.ts | 21 + src/app/components/ui/utils.ts | 6 + src/main.tsx | 7 + src/styles/fonts.css | 0 src/styles/index.css | 3 + src/styles/tailwind.css | 4 + src/styles/theme.css | 181 +++++ vite.config.ts | 19 + 70 files changed, 7139 insertions(+) create mode 100644 ATTRIBUTIONS.md create mode 100644 README.md create mode 100644 guidelines/Guidelines.md create mode 100644 index.html create mode 100644 package.json create mode 100644 postcss.config.mjs create mode 100644 src/app/App.tsx create mode 100644 src/app/components/Auth.tsx create mode 100644 src/app/components/ChatList.tsx create mode 100644 src/app/components/ChatView.tsx create mode 100644 src/app/components/Feed.tsx create mode 100644 src/app/components/Post.tsx create mode 100644 src/app/components/Profile.tsx create mode 100644 src/app/components/SimpleChat.tsx create mode 100644 src/app/components/SimpleFeed.tsx create mode 100644 src/app/components/figma/ImageWithFallback.tsx create mode 100644 src/app/components/ui/accordion.tsx create mode 100644 src/app/components/ui/alert-dialog.tsx create mode 100644 src/app/components/ui/alert.tsx create mode 100644 src/app/components/ui/aspect-ratio.tsx create mode 100644 src/app/components/ui/avatar.tsx create mode 100644 src/app/components/ui/badge.tsx create mode 100644 src/app/components/ui/breadcrumb.tsx create mode 100644 src/app/components/ui/button.tsx create mode 100644 src/app/components/ui/calendar.tsx create mode 100644 src/app/components/ui/card.tsx create mode 100644 src/app/components/ui/carousel.tsx create mode 100644 src/app/components/ui/chart.tsx create mode 100644 src/app/components/ui/checkbox.tsx create mode 100644 src/app/components/ui/collapsible.tsx create mode 100644 src/app/components/ui/command.tsx create mode 100644 src/app/components/ui/context-menu.tsx create mode 100644 src/app/components/ui/dialog.tsx create mode 100644 src/app/components/ui/drawer.tsx create mode 100644 src/app/components/ui/dropdown-menu.tsx create mode 100644 src/app/components/ui/form.tsx create mode 100644 src/app/components/ui/hover-card.tsx create mode 100644 src/app/components/ui/input-otp.tsx create mode 100644 src/app/components/ui/input.tsx create mode 100644 src/app/components/ui/label.tsx create mode 100644 src/app/components/ui/menubar.tsx create mode 100644 src/app/components/ui/navigation-menu.tsx create mode 100644 src/app/components/ui/pagination.tsx create mode 100644 src/app/components/ui/popover.tsx create mode 100644 src/app/components/ui/progress.tsx create mode 100644 src/app/components/ui/radio-group.tsx create mode 100644 src/app/components/ui/resizable.tsx create mode 100644 src/app/components/ui/scroll-area.tsx create mode 100644 src/app/components/ui/select.tsx create mode 100644 src/app/components/ui/separator.tsx create mode 100644 src/app/components/ui/sheet.tsx create mode 100644 src/app/components/ui/sidebar.tsx create mode 100644 src/app/components/ui/skeleton.tsx create mode 100644 src/app/components/ui/slider.tsx create mode 100644 src/app/components/ui/sonner.tsx create mode 100644 src/app/components/ui/switch.tsx create mode 100644 src/app/components/ui/table.tsx create mode 100644 src/app/components/ui/tabs.tsx create mode 100644 src/app/components/ui/textarea.tsx create mode 100644 src/app/components/ui/toggle-group.tsx create mode 100644 src/app/components/ui/toggle.tsx create mode 100644 src/app/components/ui/tooltip.tsx create mode 100644 src/app/components/ui/use-mobile.ts create mode 100644 src/app/components/ui/utils.ts create mode 100644 src/main.tsx create mode 100644 src/styles/fonts.css create mode 100644 src/styles/index.css create mode 100644 src/styles/tailwind.css create mode 100644 src/styles/theme.css create mode 100644 vite.config.ts diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md new file mode 100644 index 0000000..9b7cd4e --- /dev/null +++ b/ATTRIBUTIONS.md @@ -0,0 +1,3 @@ +This Figma Make file includes components from [shadcn/ui](https://ui.shadcn.com/) used under [MIT license](https://github.com/shadcn-ui/ui/blob/main/LICENSE.md). + +This Figma Make file includes photos from [Unsplash](https://unsplash.com) used under [license](https://unsplash.com/license). \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d5bd79 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ + + # Personal Social Media App + + This is a code bundle for Personal Social Media App. The original project is available at https://www.figma.com/design/N6x68TAOmCaKWT2LMuy8Bv/Personal-Social-Media-App. + + ## Running the code + + Run `npm i` to install the dependencies. + + Run `npm run dev` to start the development server. + \ No newline at end of file diff --git a/guidelines/Guidelines.md b/guidelines/Guidelines.md new file mode 100644 index 0000000..110f117 --- /dev/null +++ b/guidelines/Guidelines.md @@ -0,0 +1,61 @@ +**Add your own guidelines here** + diff --git a/index.html b/index.html new file mode 100644 index 0000000..d157589 --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + Personal Social Media App + + + +
+ + + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..c404cfb --- /dev/null +++ b/package.json @@ -0,0 +1,88 @@ +{ + "name": "@figma/my-make-file", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "dependencies": { + "@emotion/react": "11.14.0", + "@emotion/styled": "11.14.1", + "@mui/icons-material": "7.3.5", + "@mui/material": "7.3.5", + "@popperjs/core": "2.11.8", + "@radix-ui/react-accordion": "1.2.3", + "@radix-ui/react-alert-dialog": "1.1.6", + "@radix-ui/react-aspect-ratio": "1.1.2", + "@radix-ui/react-avatar": "1.1.3", + "@radix-ui/react-checkbox": "1.1.4", + "@radix-ui/react-collapsible": "1.1.3", + "@radix-ui/react-context-menu": "2.2.6", + "@radix-ui/react-dialog": "1.1.6", + "@radix-ui/react-dropdown-menu": "2.1.6", + "@radix-ui/react-hover-card": "1.1.6", + "@radix-ui/react-label": "2.1.2", + "@radix-ui/react-menubar": "1.1.6", + "@radix-ui/react-navigation-menu": "1.2.5", + "@radix-ui/react-popover": "1.1.6", + "@radix-ui/react-progress": "1.1.2", + "@radix-ui/react-radio-group": "1.2.3", + "@radix-ui/react-scroll-area": "1.2.3", + "@radix-ui/react-select": "2.1.6", + "@radix-ui/react-separator": "1.1.2", + "@radix-ui/react-slider": "1.2.3", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-switch": "1.1.3", + "@radix-ui/react-tabs": "1.1.3", + "@radix-ui/react-toggle-group": "1.1.2", + "@radix-ui/react-toggle": "1.1.2", + "@radix-ui/react-tooltip": "1.1.8", + "class-variance-authority": "0.7.1", + "clsx": "2.1.1", + "cmdk": "1.1.1", + "date-fns": "3.6.0", + "embla-carousel-react": "8.6.0", + "input-otp": "1.4.2", + "lucide-react": "0.487.0", + "motion": "12.23.24", + "next-themes": "0.4.6", + "react-day-picker": "8.10.1", + "react-dnd": "16.0.1", + "react-dnd-html5-backend": "16.0.1", + "react-hook-form": "7.55.0", + "react-popper": "2.3.0", + "react-resizable-panels": "2.1.7", + "react-responsive-masonry": "2.7.1", + "react-slick": "0.31.0", + "recharts": "2.15.2", + "sonner": "2.0.3", + "tailwind-merge": "3.2.0", + "tw-animate-css": "1.3.8", + "vaul": "1.1.2" + }, + "devDependencies": { + "@tailwindcss/vite": "4.1.12", + "@vitejs/plugin-react": "4.7.0", + "tailwindcss": "4.1.12", + "vite": "6.3.5" + }, + "peerDependencies": { + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + }, + "pnpm": { + "overrides": { + "vite": "6.3.5" + } + } +} \ No newline at end of file diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..531dbec --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,15 @@ +/** + * PostCSS Configuration + * + * Tailwind CSS v4 (via @tailwindcss/vite) automatically sets up all required + * PostCSS plugins — you do NOT need to include `tailwindcss` or `autoprefixer` here. + * + * This file only exists for adding additional PostCSS plugins, if needed. + * For example: + * + * import postcssNested from 'postcss-nested' + * export default { plugins: [postcssNested()] } + * + * Otherwise, you can leave this file empty. + */ +export default {} diff --git a/src/app/App.tsx b/src/app/App.tsx new file mode 100644 index 0000000..7202687 --- /dev/null +++ b/src/app/App.tsx @@ -0,0 +1,123 @@ +import { useState } from 'react'; +import { Home, MessageCircle, Plus, User } from 'lucide-react'; +import { SimpleFeed } from '@/app/components/SimpleFeed'; +import { SimpleChatList, SimpleChatView } from '@/app/components/SimpleChat'; +import { SignIn, Register } from '@/app/components/Auth'; +import { Profile } from '@/app/components/Profile'; + +type View = 'feed' | 'chats' | 'chat' | 'profile'; +type AuthView = 'signin' | 'register' | null; + +function SimpleButton({ children, onClick, className, active }: { children: React.ReactNode; onClick?: () => void; className?: string; active?: boolean }) { + return ( + + ); +} + +export default function App() { + const [currentView, setCurrentView] = useState('feed'); + const [selectedChatId, setSelectedChatId] = useState(null); + const [showCreatePost, setShowCreatePost] = useState(false); + const [authView, setAuthView] = useState('signin'); + const [isAuthenticated, setIsAuthenticated] = useState(false); + const [username, setUsername] = useState(''); + + const handleSelectChat = (chatId: string) => { + setSelectedChatId(chatId); + setCurrentView('chat'); + }; + + const handleBackToChats = () => { + setSelectedChatId(null); + setCurrentView('chats'); + }; + + const handleCreatePostComplete = () => { + setShowCreatePost(false); + }; + + const handleLogin = (user: string) => { + setUsername(user); + setIsAuthenticated(true); + setAuthView(null); + }; + + const handleLogout = () => { + setIsAuthenticated(false); + setAuthView('signin'); + setCurrentView('feed'); + setUsername(''); + }; + + // Show auth screens if not authenticated + if (!isAuthenticated) { + if (authView === 'signin') { + return setAuthView('register')} />; + } + if (authView === 'register') { + return setAuthView('signin')} />; + } + } + + return ( +
+ {/* Main Content */} +
+ {currentView === 'feed' && } + {currentView === 'chats' && } + {currentView === 'chat' && selectedChatId && ( + + )} + {currentView === 'profile' && } +
+ + {/* Bottom Navigation */} +
+
+ setCurrentView('feed')} + className="flex-1 h-full rounded-none" + active={currentView === 'feed'} + > + + + + setCurrentView('chats')} + className="flex-1 h-full rounded-none" + active={currentView === 'chats' || currentView === 'chat'} + > + + + + {/* Centered Plus Button */} + + + {/* Spacer for centered plus button */} +
+ + setCurrentView('profile')} + className="flex-1 h-full rounded-none" + active={currentView === 'profile'} + > + + +
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/components/Auth.tsx b/src/app/components/Auth.tsx new file mode 100644 index 0000000..c6f413a --- /dev/null +++ b/src/app/components/Auth.tsx @@ -0,0 +1,209 @@ +import { useState } from 'react'; +import { Mail, Lock, User, Eye, EyeOff } from 'lucide-react'; + +interface AuthProps { + onLogin: (username: string) => void; +} + +export function SignIn({ onLogin, onSwitchToRegister }: AuthProps & { onSwitchToRegister: () => void }) { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (email && password) { + // Mock login - just use email username part + const username = email.split('@')[0]; + onLogin(username); + } + }; + + return ( +
+
+
+

Welcome Back

+

Sign in to continue

+
+ +
+
+ +
+ + setEmail(e.target.value)} + placeholder="your@email.com" + className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" + required + /> +
+
+ +
+ +
+ + setPassword(e.target.value)} + placeholder="••••••••" + className="w-full pl-10 pr-12 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" + required + /> + +
+
+ + +
+ +
+

+ Don't have an account?{' '} + +

+
+
+
+ ); +} + +export function Register({ onLogin, onSwitchToSignIn }: AuthProps & { onSwitchToSignIn: () => void }) { + const [username, setUsername] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (username && email && password && password === confirmPassword) { + onLogin(username); + } + }; + + return ( +
+
+
+

Create Account

+

Join us today

+
+ +
+
+ +
+ + setUsername(e.target.value)} + placeholder="username" + className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" + required + /> +
+
+ +
+ +
+ + setEmail(e.target.value)} + placeholder="your@email.com" + className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" + required + /> +
+
+ +
+ +
+ + setPassword(e.target.value)} + placeholder="••••••••" + className="w-full pl-10 pr-12 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" + required + /> + +
+
+ +
+ +
+ + setConfirmPassword(e.target.value)} + placeholder="••••••••" + className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" + required + /> +
+
+ + {password && confirmPassword && password !== confirmPassword && ( +

Passwords don't match

+ )} + + +
+ +
+

+ Already have an account?{' '} + +

+
+
+
+ ); +} diff --git a/src/app/components/ChatList.tsx b/src/app/components/ChatList.tsx new file mode 100644 index 0000000..7f31955 --- /dev/null +++ b/src/app/components/ChatList.tsx @@ -0,0 +1,103 @@ +import { Avatar, AvatarFallback, AvatarImage } from '@/app/components/ui/avatar'; +import { Button } from '@/app/components/ui/button'; + +interface ChatListItemProps { + id: string; + name: string; + avatar: string; + lastMessage: string; + timestamp: string; + unread?: boolean; + onSelect: (id: string) => void; +} + +function ChatListItem({ id, name, avatar, lastMessage, timestamp, unread, onSelect }: ChatListItemProps) { + return ( + + ); +} + +interface ChatListProps { + onSelectChat: (chatId: string) => void; +} + +const chats = [ + { + id: '1', + name: 'explorer_life', + avatar: 'https://images.unsplash.com/photo-1695485121912-25c7ea05119c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMG1hbiUyMGNhc3VhbHxlbnwxfHx8fDE3Njk0Nzc3MTV8MA&ixlib=rb-4.1.0&q=80&w=400', + lastMessage: 'That photo was incredible!', + timestamp: '5m', + unread: true + }, + { + id: '2', + name: 'coffee_addict', + avatar: 'https://images.unsplash.com/photo-1594318223885-20dc4b889f9e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMHdvbWFuJTIwc21pbGluZ3xlbnwxfHx8fDE3Njk0Nzc3MTR8MA&ixlib=rb-4.1.0&q=80&w=400', + lastMessage: 'Want to meet up for coffee tomorrow?', + timestamp: '1h', + unread: true + }, + { + id: '3', + name: 'beach_vibes', + avatar: 'https://images.unsplash.com/photo-1695485121912-25c7ea05119c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMG1hbiUyMGNhc3VhbHxlbnwxfHx8fDE3Njk0Nzc3MTV8MA&ixlib=rb-4.1.0&q=80&w=400', + lastMessage: 'Thanks for the recommendation!', + timestamp: '3h', + unread: false + }, + { + id: '4', + name: 'mountain_hiker', + avatar: 'https://images.unsplash.com/photo-1594318223885-20dc4b889f9e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMHdvbWFuJTIwc21pbGluZ3xlbnwxfHx8fDE3Njk0Nzc3MTR8MA&ixlib=rb-4.1.0&q=80&w=400', + lastMessage: "Let's plan a hike soon!", + timestamp: '1d', + unread: false + }, + { + id: '5', + name: 'photo_lover', + avatar: 'https://images.unsplash.com/photo-1695485121912-25c7ea05119c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMG1hbiUyMGNhc3VhbHxlbnwxfHx8fDE3Njk0Nzc3MTV8MA&ixlib=rb-4.1.0&q=80&w=400', + lastMessage: 'Check out my new camera gear!', + timestamp: '2d', + unread: false + } +]; + +export function ChatList({ onSelectChat }: ChatListProps) { + return ( +
+ {/* Header */} +
+

Messages

+
+ + {/* Chat List */} +
+ {chats.map((chat) => ( + + ))} +
+
+ ); +} diff --git a/src/app/components/ChatView.tsx b/src/app/components/ChatView.tsx new file mode 100644 index 0000000..f40ec54 --- /dev/null +++ b/src/app/components/ChatView.tsx @@ -0,0 +1,161 @@ +import { useState } from 'react'; +import { ArrowLeft, Send, Smile, Image as ImageIcon } from 'lucide-react'; +import { Avatar, AvatarFallback, AvatarImage } from '@/app/components/ui/avatar'; +import { Button } from '@/app/components/ui/button'; +import { Input } from '@/app/components/ui/input'; + +interface Message { + id: string; + text: string; + sender: 'me' | 'them'; + timestamp: string; +} + +interface ChatViewProps { + chatId: string; + onBack: () => void; +} + +const chatData: Record = { + '1': { + name: 'explorer_life', + avatar: 'https://images.unsplash.com/photo-1695485121912-25c7ea05119c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMG1hbiUyMGNhc3VhbHxlbnwxfHx8fDE3Njk0Nzc3MTV8MA&ixlib=rb-4.1.0&q=80&w=400', + messages: [ + { id: '1', text: 'Hey! How are you?', sender: 'them', timestamp: '10:30 AM' }, + { id: '2', text: "I'm great! How about you?", sender: 'me', timestamp: '10:32 AM' }, + { id: '3', text: 'That photo was incredible!', sender: 'them', timestamp: '10:35 AM' } + ] + }, + '2': { + name: 'coffee_addict', + avatar: 'https://images.unsplash.com/photo-1594318223885-20dc4b889f9e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMHdvbWFuJTIwc21pbGluZ3xlbnwxfHx8fDE3Njk0Nzc3MTR8MA&ixlib=rb-4.1.0&q=80&w=400', + messages: [ + { id: '1', text: 'Good morning! ☕', sender: 'them', timestamp: '8:15 AM' }, + { id: '2', text: 'Morning! Just made some coffee', sender: 'me', timestamp: '8:20 AM' }, + { id: '3', text: 'Want to meet up for coffee tomorrow?', sender: 'them', timestamp: '9:00 AM' } + ] + }, + '3': { + name: 'beach_vibes', + avatar: 'https://images.unsplash.com/photo-1695485121912-25c7ea05119c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMG1hbiUyMGNhc3VhbHxlbnwxfHx8fDE3Njk0Nzc3MTV8MA&ixlib=rb-4.1.0&q=80&w=400', + messages: [ + { id: '1', text: 'Thanks for the beach spot recommendation!', sender: 'them', timestamp: 'Yesterday' }, + { id: '2', text: 'No problem! Did you enjoy it?', sender: 'me', timestamp: 'Yesterday' }, + { id: '3', text: 'Thanks for the recommendation!', sender: 'them', timestamp: 'Yesterday' } + ] + }, + '4': { + name: 'mountain_hiker', + avatar: 'https://images.unsplash.com/photo-1594318223885-20dc4b889f9e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMHdvbWFuJTIwc21pbGluZ3xlbnwxfHx8fDE3Njk0Nzc3MTR8MA&ixlib=rb-4.1.0&q=80&w=400', + messages: [ + { id: '1', text: 'That summit view was amazing! ⛰️', sender: 'them', timestamp: '2 days ago' }, + { id: '2', text: 'Right?! The sunrise was perfect', sender: 'me', timestamp: '2 days ago' }, + { id: '3', text: "Let's plan a hike soon!", sender: 'them', timestamp: '2 days ago' } + ] + }, + '5': { + name: 'photo_lover', + avatar: 'https://images.unsplash.com/photo-1695485121912-25c7ea05119c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMG1hbiUyMGNhc3VhbHxlbnwxfHx8fDE3Njk0Nzc3MTV8MA&ixlib=rb-4.1.0&q=80&w=400', + messages: [ + { id: '1', text: 'Check out my new camera gear! 📷', sender: 'them', timestamp: '3 days ago' }, + { id: '2', text: 'Nice! What did you get?', sender: 'me', timestamp: '3 days ago' } + ] + } +}; + +export function ChatView({ chatId, onBack }: ChatViewProps) { + const chat = chatData[chatId]; + const [messages, setMessages] = useState(chat?.messages || []); + const [newMessage, setNewMessage] = useState(''); + + if (!chat) { + return ( +
+

Chat not found

+
+ ); + } + + const handleSendMessage = () => { + if (newMessage.trim()) { + const message: Message = { + id: Date.now().toString(), + text: newMessage, + sender: 'me', + timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) + }; + setMessages([...messages, message]); + setNewMessage(''); + } + }; + + return ( +
+ {/* Header */} +
+ + + + {chat.name[0]} + + {chat.name} +
+ + {/* Messages */} +
+ {messages.map((message) => ( +
+
+
+

{message.text}

+
+ + {message.timestamp} + +
+
+ ))} +
+ + {/* Input */} +
+
+ + setNewMessage(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && handleSendMessage()} + className="flex-1" + /> + + +
+
+
+ ); +} diff --git a/src/app/components/Feed.tsx b/src/app/components/Feed.tsx new file mode 100644 index 0000000..3407e3d --- /dev/null +++ b/src/app/components/Feed.tsx @@ -0,0 +1,138 @@ +import { useState } from 'react'; +import { Plus, Image } from 'lucide-react'; +import { Post } from '@/app/components/Post'; +import { Button } from '@/app/components/ui/button'; +import { Input } from '@/app/components/ui/input'; +import { Textarea } from '@/app/components/ui/textarea'; + +const initialPosts = [ + { + id: '1', + username: 'explorer_life', + userAvatar: 'https://images.unsplash.com/photo-1695485121912-25c7ea05119c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMG1hbiUyMGNhc3VhbHxlbnwxfHx8fDE3Njk0Nzc3MTV8MA&ixlib=rb-4.1.0&q=80&w=400', + imageUrl: 'https://images.unsplash.com/photo-1727942019403-cf4aecfc3276?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx1cmJhbiUyMGNpdHklMjBzdW5zZXQlMjBza3lsaW5lfGVufDF8fHx8MTc2OTU3MTI2MXww&ixlib=rb-4.1.0&q=80&w=1080', + caption: 'Golden hour in the city 🌆', + likes: 342, + comments: [ + { username: 'photo_lover', text: 'Amazing shot! 📸' }, + { username: 'travel_bug', text: 'Where is this?' } + ], + timestamp: '2 hours ago' + }, + { + id: '2', + username: 'coffee_addict', + userAvatar: 'https://images.unsplash.com/photo-1594318223885-20dc4b889f9e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMHdvbWFuJTIwc21pbGluZ3xlbnwxfHx8fDE3Njk0Nzc3MTR8MA&ixlib=rb-4.1.0&q=80&w=400', + imageUrl: 'https://images.unsplash.com/photo-1760163630058-aa71c91783bf?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxjb2ZmZWUlMjBsYXR0ZSUyMG1vcm5pbmd8ZW58MXx8fHwxNzY5NTcxMjYxfDA&ixlib=rb-4.1.0&q=80&w=1080', + caption: 'Perfect way to start the morning ☕', + likes: 156, + comments: [], + timestamp: '5 hours ago' + }, + { + id: '3', + username: 'beach_vibes', + userAvatar: 'https://images.unsplash.com/photo-1695485121912-25c7ea05119c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMG1hbiUyMGNhc3VhbHxlbnwxfHx8fDE3Njk0Nzc3MTV8MA&ixlib=rb-4.1.0&q=80&w=400', + imageUrl: 'https://images.unsplash.com/photo-1661953029179-e1b0dc900490?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxiZWFjaCUyMG9jZWFuJTIwd2F2ZXN8ZW58MXx8fHwxNzY5NDg3Mjc2fDA&ixlib=rb-4.1.0&q=80&w=1080', + caption: 'Ocean therapy 🌊', + likes: 523, + comments: [ + { username: 'nature_love', text: 'So peaceful!' } + ], + timestamp: '1 day ago' + }, + { + id: '4', + username: 'mountain_hiker', + userAvatar: 'https://images.unsplash.com/photo-1594318223885-20dc4b889f9e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMHdvbWFuJTIwc21pbGluZ3xlbnwxfHx8fDE3Njk0Nzc3MTR8MA&ixlib=rb-4.1.0&q=80&w=400', + imageUrl: 'https://images.unsplash.com/photo-1609373066983-cee8662ea93f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtb3VudGFpbiUyMGhpa2luZyUyMGFkdmVudHVyZXxlbnwxfHx8fDE3Njk0NzM4NDd8MA&ixlib=rb-4.1.0&q=80&w=1080', + caption: 'Adventure awaits at the summit ⛰️', + likes: 789, + comments: [ + { username: 'outdoor_fan', text: 'Goals! 🎯' }, + { username: 'hiking_crew', text: 'Take me with you!' } + ], + timestamp: '2 days ago' + } +]; + +export function Feed() { + const [posts, setPosts] = useState(initialPosts); + const [showCreatePost, setShowCreatePost] = useState(false); + const [newCaption, setNewCaption] = useState(''); + const [newImageUrl, setNewImageUrl] = useState(''); + + const handleCreatePost = () => { + if (newCaption.trim() && newImageUrl.trim()) { + const newPost = { + id: Date.now().toString(), + username: 'You', + userAvatar: 'https://images.unsplash.com/photo-1695485121912-25c7ea05119c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwb3J0cmFpdCUyMG1hbiUyMGNhc3VhbHxlbnwxfHx8fDE3Njk0Nzc3MTV8MA&ixlib=rb-4.1.0&q=80&w=400', + imageUrl: newImageUrl, + caption: newCaption, + likes: 0, + comments: [], + timestamp: 'Just now' + }; + setPosts([newPost, ...posts]); + setNewCaption(''); + setNewImageUrl(''); + setShowCreatePost(false); + } + }; + + return ( +
+ {/* Create Post Button */} +
+ {!showCreatePost ? ( + + ) : ( +
+
+

Create New Post

+ +
+
+ +
+ setNewImageUrl(e.target.value)} + /> + +
+
+
+ +