Update App
This commit is contained in:
@@ -1,9 +1,50 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useDevice } from "@/lib/context/DeviceContext";
|
import { useDevice } from "@/lib/context/DeviceContext";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import Post from "../../lib/components/Post";
|
||||||
|
|
||||||
function Mobile() {
|
function Mobile() {
|
||||||
const { isAuthenticated, session } = useDevice();
|
const { isAuthenticated, session } = useDevice();
|
||||||
|
const [friendsPostsData, setFriendsPosts] = useState<any[]>([]);
|
||||||
|
|
||||||
|
function getFriendsPosts(friendId: any) {
|
||||||
|
fetch(`/api/users/${friendId}/posts`)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data.posts) {
|
||||||
|
setFriendsPosts([...friendsPostsData, ...data.posts]);
|
||||||
|
} else {
|
||||||
|
console.error("No posts found for friend ID:", friendId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("Error fetching friend's posts:", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch friends' posts
|
||||||
|
useEffect(() => {
|
||||||
|
if (isAuthenticated && session?.friends) {
|
||||||
|
let friendsIds = session.friends.map((friend: any) => friend.id);
|
||||||
|
|
||||||
|
// use /api/users/:id to get friend data
|
||||||
|
friendsIds.forEach((friendId: any) => {
|
||||||
|
fetch(`/api/users/${friendId}`)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data.user) {
|
||||||
|
getFriendsPosts(data.user.id);
|
||||||
|
} else {
|
||||||
|
console.error("No user data found for friend ID:", friendId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("Error fetching friend data:", err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [isAuthenticated, session?.friends]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="flex flex-col gap-[32px] row-start-2 items-center mt-10 text-white">
|
<main className="flex flex-col gap-[32px] row-start-2 items-center mt-10 text-white">
|
||||||
@@ -24,7 +65,25 @@ function Mobile() {
|
|||||||
<a href="/auth/login?screen_hint=login">Log in</a>
|
<a href="/auth/login?screen_hint=login">Log in</a>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : (
|
||||||
|
<div className="w-full px-6">
|
||||||
|
<h2 className="text-2xl font-bold text-[color:var(--color-warning-300)] mb-4">
|
||||||
|
Activity Feed
|
||||||
|
</h2>
|
||||||
|
<div className="space-y-6">
|
||||||
|
{friendsPostsData.map((post, index) => (
|
||||||
|
<Post
|
||||||
|
key={index}
|
||||||
|
post={post}
|
||||||
|
allowReactions={true} // Allow reactions for friends' posts
|
||||||
|
onLike={() => console.log("Liked post:", post.id)}
|
||||||
|
onWarning={() => console.log("Warned post:", post.id)}
|
||||||
|
onDelete={() => {}} // No delete option for friends' posts
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -48,6 +107,6 @@ function Web() {
|
|||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const { isMobile, isSafari } = useDevice();
|
const { isMobile, isSafari } = useDevice();
|
||||||
if (isMobile && isSafari) return Mobile();
|
if (isMobile && isSafari) return <Mobile />;
|
||||||
else return Web();
|
else return <Web />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,7 +201,6 @@ export default function PostsPage() {
|
|||||||
allowReactions={false}
|
allowReactions={false}
|
||||||
key={index}
|
key={index}
|
||||||
post={post}
|
post={post}
|
||||||
userReactions={userReactions[index] || { liked: false, warned: false }}
|
|
||||||
onLike={() => handleLike(index)}
|
onLike={() => handleLike(index)}
|
||||||
onWarning={() => handleWarning(index)}
|
onWarning={() => handleWarning(index)}
|
||||||
onDelete={() => handleDelete(index)} // Pass the delete handler
|
onDelete={() => handleDelete(index)} // Pass the delete handler
|
||||||
|
|||||||
42
src/app/api/user/[id]/posts/route.ts
Normal file
42
src/app/api/user/[id]/posts/route.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { auth0 } from "../../../../../lib/scripts/auth0";
|
||||||
|
import { db } from "@/lib/scripts/db";
|
||||||
|
|
||||||
|
async function authenticateUser() {
|
||||||
|
const session = await auth0.getSession();
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
return NextResponse.json({ message: "No session found" }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionUser = session.user;
|
||||||
|
let userData = await db.users.findByEmail(sessionUser.email as string);
|
||||||
|
if (!userData) return NextResponse.json({ message: "User not found" }, { status: 404 });
|
||||||
|
|
||||||
|
return userData != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET(req: Request, { params }: any) {
|
||||||
|
try {
|
||||||
|
if (!(await authenticateUser())) return;
|
||||||
|
|
||||||
|
const { id } = params;
|
||||||
|
|
||||||
|
// Find the user by ID
|
||||||
|
const user = await db.users.findById(id);
|
||||||
|
if (!user) {
|
||||||
|
return NextResponse.json({ message: "User not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch all posts for the user
|
||||||
|
const posts = await db.posts.getAllByUserId(id);
|
||||||
|
if (!posts || posts.length === 0) {
|
||||||
|
return NextResponse.json({ message: "No posts found for this user" }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({ posts }, { status: 200 });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching user posts:", error);
|
||||||
|
return NextResponse.json({ message: "Internal server error" }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,25 @@
|
|||||||
import type { MetadataRoute } from "next";
|
import type { MetadataRoute } from "next";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function manifest(): MetadataRoute.Manifest {
|
export default function manifest(): MetadataRoute.Manifest {
|
||||||
return {
|
return {
|
||||||
name: "Healthify",
|
name: "Drink Happy",
|
||||||
short_name: "Healthify",
|
short_name: "Drink Happy",
|
||||||
description:
|
description: "Track your drinks, earn points, and improve your health with Drink Happy!",
|
||||||
"Healthy APP",
|
start_url: "/",
|
||||||
start_url: "/",
|
display: "standalone",
|
||||||
display: "standalone",
|
background_color: "#1E293B", // Matches the app's dark theme
|
||||||
background_color: "#000000",
|
theme_color: "#1E293B", // Matches the app's dark theme
|
||||||
theme_color: "#000000",
|
icons: [
|
||||||
icons: [
|
{
|
||||||
{
|
src: "/cappylogosmall.png",
|
||||||
src: "/vercel.svg",
|
sizes: "192x192",
|
||||||
sizes: "192x192",
|
type: "image/png",
|
||||||
type: "image/svg",
|
},
|
||||||
},
|
{
|
||||||
],
|
src: "/cappylogosmall.png",
|
||||||
};
|
sizes: "512x512",
|
||||||
|
type: "image/png",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,27 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Drinkify",
|
title: "Drink Happy - Health Check & Game",
|
||||||
description: "Drinkify health check app / game",
|
description: "Drink Happy is a fun and engaging app to track your drink choices, earn points, and improve your health!",
|
||||||
|
keywords: ["Drink Happy", "Health", "Game", "Drink Tracker", "Healthy Lifestyle"],
|
||||||
|
themeColor: "#1E293B", // Matches the app's dark theme
|
||||||
|
appleWebApp: {
|
||||||
|
title: "Drink Happy",
|
||||||
|
statusBarStyle: "black-translucent",
|
||||||
|
capable: true,
|
||||||
|
},
|
||||||
|
manifest: "/manifest.json",
|
||||||
|
openGraph: {
|
||||||
|
title: "Drink Happy - Health Check & Game",
|
||||||
|
description: "Drink Happy is a fun and engaging app to track your drink choices, earn points, and improve your health!",
|
||||||
|
siteName: "Drink Happy",
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: "/cappylogosmall.png",
|
||||||
|
width: 512,
|
||||||
|
height: 512,
|
||||||
|
alt: "Drink Happy Logo",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
@@ -14,7 +14,6 @@ interface PostProps {
|
|||||||
onLike: () => void;
|
onLike: () => void;
|
||||||
onWarning: () => void;
|
onWarning: () => void;
|
||||||
onDelete: () => void; // Callback for deleting the post
|
onDelete: () => void; // Callback for deleting the post
|
||||||
userReactions: { liked: boolean; warned: boolean };
|
|
||||||
allowReactions: boolean; // New property to toggle reactions
|
allowReactions: boolean; // New property to toggle reactions
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,10 +22,12 @@ export default function Post({
|
|||||||
onLike,
|
onLike,
|
||||||
onWarning,
|
onWarning,
|
||||||
onDelete,
|
onDelete,
|
||||||
userReactions,
|
|
||||||
allowReactions,
|
allowReactions,
|
||||||
}: PostProps) {
|
}: PostProps) {
|
||||||
const [userData, setUserData] = useState<{ username: string; avatar: number } | null>({username: "Loading...", avatar: 1});
|
const [userData, setUserData] = useState<{ username: string; avatar: number } | null>({
|
||||||
|
username: "Loading...",
|
||||||
|
avatar: 1,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Fetch the username and avatar based on the userId
|
// Fetch the username and avatar based on the userId
|
||||||
@@ -89,30 +90,28 @@ export default function Post({
|
|||||||
|
|
||||||
{/* Post Actions */}
|
{/* Post Actions */}
|
||||||
<div className="flex gap-4 items-center">
|
<div className="flex gap-4 items-center">
|
||||||
{allowReactions && (
|
<button
|
||||||
<>
|
onClick={onLike}
|
||||||
<button
|
disabled={!allowReactions} // Disable button if allowReactions is false
|
||||||
onClick={onLike}
|
className={`px-3 py-1 rounded text-sm ${
|
||||||
className={`px-3 py-1 rounded text-sm ${
|
allowReactions
|
||||||
userReactions.liked
|
? "bg-success-600 hover:bg-primary-600 text-white"
|
||||||
? "bg-success-800"
|
: "bg-gray-500 text-gray-300 cursor-not-allowed"
|
||||||
: "bg-success-600 hover:bg-primary-600"
|
}`}
|
||||||
} text-white`}
|
>
|
||||||
>
|
👍 Like ({post.reactions.filter((reaction) => reaction.liked).length})
|
||||||
👍 Like ({post.reactions.filter((reaction) => reaction.liked).length})
|
</button>
|
||||||
</button>
|
<button
|
||||||
<button
|
onClick={onWarning}
|
||||||
onClick={onWarning}
|
disabled={!allowReactions} // Disable button if allowReactions is false
|
||||||
className={`px-3 py-1 rounded text-sm ${
|
className={`px-3 py-1 rounded text-sm ${
|
||||||
userReactions.warned
|
allowReactions
|
||||||
? "bg-red-800"
|
? "bg-primary-500 hover:bg-red-600 text-white"
|
||||||
: "bg-primary-500 hover:bg-red-600"
|
: "bg-gray-500 text-gray-300 cursor-not-allowed"
|
||||||
} text-white`}
|
}`}
|
||||||
>
|
>
|
||||||
😭 Stop drinking that ({post.reactions.filter((reaction) => reaction.warned).length})
|
😭 Stop drinking that ({post.reactions.filter((reaction) => reaction.warned).length})
|
||||||
</button>
|
</button>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
className="px-3 py-1 rounded text-sm bg-red-600 hover:bg-red-700 text-white"
|
className="px-3 py-1 rounded text-sm bg-red-600 hover:bg-red-700 text-white"
|
||||||
|
|||||||
Reference in New Issue
Block a user