Skip to content

Commit 0c178ff

Browse files
committedSep 5, 2024
Bookmarks added
1 parent d6132be commit 0c178ff

File tree

9 files changed

+222
-29
lines changed

9 files changed

+222
-29
lines changed
 

‎app/(tabs)/_layout.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { View, Text,Image,ImageSourcePropType} from 'react-native'
2-
import {Tabs,Redirect} from 'expo-router'
2+
import {Tabs} from 'expo-router'
33
import {icons} from '../../constants';
44

55
interface TabIconProps {

‎app/(tabs)/bookmark.tsx

+45-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,51 @@
1-
import { View, Text } from 'react-native'
2-
import React from 'react'
1+
import { View, Text, FlatList } from 'react-native'
2+
import React, { useEffect, useState } from 'react'
3+
import { SafeAreaView } from 'react-native-safe-area-context'
4+
import { getLikedPosts } from '@/lib/appwrite';
5+
import useAppwrite from '@/lib/useAppwrite';
6+
import VideoCard from '@/components/VideoCard';
7+
import SearchInput from '@/components/SearchInput';
8+
import EmptyState from '@/components/EmptyState';
39

410
const Bookmark = () => {
11+
// const { query } = useLocalSearchParams();
12+
const { data: likedPosts, refetch } = useAppwrite(getLikedPosts);
13+
14+
const [refreshng, setrefreshng] = useState(false)
15+
// console.log(posts)
16+
useEffect(() => {
17+
refetch()
18+
19+
}, [])
20+
521
return (
6-
<View>
7-
<Text>Bookmark</Text>
8-
</View>
22+
<SafeAreaView className="px-4 my-6 bg-primary h-full">
23+
<FlatList
24+
// data={[]}
25+
data={likedPosts}
26+
keyExtractor={(item: { $id: string }) => item.$id}//(item) => item.$id
27+
renderItem={({ item }: { item: { $id: string; title: string; thumbnail: string; video: string; creator: { username: string; avatar: string; } } }) => (
28+
<View>
29+
<Text>Video will be here</Text>
30+
<VideoCard Video={item} /></View>
31+
)}
32+
ListHeaderComponent={() => (
33+
<View className='flex my-6 px-4 '>
34+
35+
<Text className='font-psemibold text-xl text-white' >Saved Videos</Text>
36+
<View className='mt-3 mb-8'>
37+
<SearchInput initialQuery={''} />
38+
</View>
39+
</View>
40+
)}
41+
ListEmptyComponent={() => (
42+
<EmptyState
43+
title="No videos Found"
44+
subtitle="No Videos found for this Search Query" />
45+
)
46+
}
47+
/>
48+
</SafeAreaView>
949
)
1050
}
1151

‎app/(tabs)/home.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { View, Text, FlatList, Image, RefreshControl, Alert } from 'react-native'
2-
import React, { useEffect, useState } from 'react'
2+
import React, { useState } from 'react'
33
import { SafeAreaView } from 'react-native-safe-area-context'
44
import { images } from '../../constants'
55
import SearchInput from '@/components/SearchInput'

‎app/(tabs)/profile.tsx

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { View, FlatList, TouchableOpacity, Image } from 'react-native'
2-
import React, { useState } from 'react'
2+
import React from 'react'
33
import { SafeAreaView } from 'react-native-safe-area-context'
44
import EmptyState from '@/components/EmptyState'
5-
import { getUserPosts, signOut } from '@/lib/appwrite'
5+
import { getUserPosts, signOut } from '@/lib/appwrite'
66
import useAppwrite from '@/lib/useAppwrite'
77
import VideoCard from '@/components/VideoCard'
88
import { useGlobalContext } from '@/context/GlobalProvider'
@@ -16,22 +16,22 @@ const Profile = () => {
1616
const { setisLoggedIn, User, setUser, } = useGlobalContext();
1717
const { data: posts, refetch } = useAppwrite(() => getUserPosts(User.$id));//This time not paasing function declaration but calling the function. That's why we are using callback function.
1818

19-
const logout = async() => {
19+
const logout = async () => {
2020
await signOut();
2121
setUser(null);
2222
setisLoggedIn(false);
23-
router.replace('/sign-in')
23+
router.replace('/sign-in')
2424
}
2525

2626
//console.log(posts)
2727
return (
2828
<SafeAreaView className='bg-primary h-full'>
2929
<FlatList
3030
data={posts}
31-
31+
3232
keyExtractor={(item: { $id: string }) => item.$id}
3333
renderItem={({ item }: { item: { $id: string; title: string; thumbnail: string; video: string; creator: { username: string; avatar: string; } } }) => (
34-
34+
3535
<VideoCard
3636
Video={item}
3737
/>
@@ -61,7 +61,7 @@ router.replace('/sign-in')
6161
/>
6262
<View className='mt-5 flex-row'>
6363
<InfoBox
64-
title={posts?.length||0}
64+
title={posts?.length || 0}
6565
subtitle='Posts'
6666
containerStyle='mr-10'
6767
titleStyle='text-xl'

‎assets/icons/heart.png

1.46 KB
Loading

‎assets/icons/heartfill.png

1.57 KB
Loading

‎components/VideoCard.tsx

+22-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import React, { useState } from 'react'
33
import { icons } from '@/constants'
44
import { ResizeMode, Video } from 'expo-av';
55
import WebView from 'react-native-webview';
6+
import { likePost } from '@/lib/appwrite';
7+
import { useGlobalContext } from '@/context/GlobalProvider';
68

79
interface VideoCardProps {
810
Video: {
@@ -20,6 +22,18 @@ const VideoCard = ({ Video: { title, thumbnail, video, creator: { username, avat
2022

2123
const [play, setplay] = useState(false);
2224
const [isLoaded, setIsLoaded] = useState(false);
25+
const [liked, setliked] = useState(false)
26+
const { User } = useGlobalContext();
27+
28+
const LikedVideo = async () => {
29+
setliked(!liked);
30+
31+
if (liked) {
32+
await likePost(video, User.$id, true); // unlike the video
33+
} else {
34+
await likePost(video, User.$id); // like the video
35+
}
36+
};
2337

2438
return (
2539
<View className='flex-col items-center px-4 mb-14'>
@@ -37,9 +51,13 @@ const [isLoaded, setIsLoaded] = useState(false);
3751
>{username}</Text>
3852
</View>
3953
</View>
40-
<View className='p-2'>
41-
<Image source={icons.menu} className='w-5 h-5' resizeMode='contain'/>
42-
</View>
54+
<TouchableOpacity
55+
activeOpacity={0.7}
56+
onPress={LikedVideo}
57+
className='p-2'>
58+
{liked?(<Image source={icons.heartfill} className='w-6 h-6' resizeMode='contain'/>):(<Image source={icons.heart} className='w-6 h-6' resizeMode='contain'/>)}
59+
60+
</TouchableOpacity>
4361
</View>
4462
{
4563
play?(
@@ -75,6 +93,7 @@ resizeMode='cover'/>
7593
<Image source={icons.play}
7694
className='w-12 h-12 absolute'
7795
resizeMode='contain'/>
96+
7897
</TouchableOpacity>)
7998
}
8099
</View>

‎constants/icons.js

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import logout from "../assets/icons/logout.png";
1111
import eyeHide from "../assets/icons/eye-hide.png";
1212
import eye from "../assets/icons/eye.png";
1313
import play from "../assets/icons/play.png";
14+
import heart from "../assets/icons/heart.png";
15+
import heartfill from "../assets/icons/heartfill.png";
1416

1517
export default {
1618
play,
@@ -26,4 +28,6 @@ export default {
2628
logout,
2729
eyeHide,
2830
eye,
31+
heart,
32+
heartfill,
2933
};

‎lib/appwrite.js

+142-12
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ const avatars = new Avatars(client);
3636
const databases = new Databases(client);
3737
const storage = new Storage(client);
3838

39+
40+
//Creating Sessions For User
3941
export const createUser = async (email, password, username) => {
4042
try {
4143
// Create a new account
@@ -71,7 +73,6 @@ export const createUser = async (email, password, username) => {
7173
}
7274
};
7375

74-
7576
export const signIn = async (email, password) => {
7677
try {
7778
// Try to get the current session
@@ -91,7 +92,7 @@ export const signIn = async (email, password) => {
9192
};
9293

9394

94-
// Ensure this function is properly defined
95+
// Getiing Current Logged User
9596
export const getCurrentUser = async () => {
9697
try {
9798
const currentAccount = await account.get();
@@ -108,52 +109,53 @@ export const getCurrentUser = async () => {
108109
}
109110
};
110111

112+
//User Feed
111113
export const getAllPosts = async () => {
112114
try {
113115
const posts = await databases.listDocuments(
114116
databaseId,
115117
videoCollectionId,
118+
[Query.orderDesc('$createdAt')]
116119
);
117120
return posts.documents;
118121
} catch (error) {
119122
throw new Error(error.message || 'Error fetching posts');
120123
}
121124
}
122125

123-
124-
export const getLatestPosts = async () => {
126+
export const getUserPosts = async (userID) => {
125127
try {
126128
const posts = await databases.listDocuments(
127129
databaseId,
128130
videoCollectionId,
129-
[Query.orderDesc('$createdAt', Query.limit(7))]
131+
[Query.equal('creator', userID),Query.orderDesc('$createdAt')]
130132
);
131133
return posts.documents;
132134
} catch (error) {
133135
throw new Error(error.message || 'Error fetching posts');
134136
}
135137
}
136138

137-
138-
export const searchPosts = async (query) => {
139+
export const getLatestPosts = async () => {
139140
try {
140141
const posts = await databases.listDocuments(
141142
databaseId,
142143
videoCollectionId,
143-
[Query.search('title', query)]
144+
[Query.orderDesc('$createdAt', Query.limit(7))]
144145
);
145146
return posts.documents;
146147
} catch (error) {
147148
throw new Error(error.message || 'Error fetching posts');
148149
}
149150
}
150151

151-
export const getUserPosts = async (userID) => {
152+
//User Action
153+
export const searchPosts = async (query) => {
152154
try {
153155
const posts = await databases.listDocuments(
154156
databaseId,
155157
videoCollectionId,
156-
[Query.equal('creator', userID)]
158+
[Query.search('title', query)]
157159
);
158160
return posts.documents;
159161
} catch (error) {
@@ -162,6 +164,7 @@ export const getUserPosts = async (userID) => {
162164
}
163165

164166

167+
//Clearing Sessions Created
165168
export const signOut = async () => {
166169
try {
167170
const session = await account.deleteSession('current');
@@ -171,7 +174,7 @@ export const signOut = async () => {
171174
}
172175
}
173176

174-
177+
//Creating a Post and Submitting to DB
175178
export const getFilePreview = async (fileId, type) => {
176179
let fileUrl;
177180
try {
@@ -238,4 +241,131 @@ export const CreateVideo = async (form) => {
238241
console.log(error);
239242
throw new Error(error.message || 'Error creating video');
240243
}
241-
}
244+
}
245+
246+
//Bookmarks Logics
247+
const getUserDocument = async (userId, collectionId) => {
248+
try {
249+
const userDocument = await databases.getDocument(databaseId, collectionId, userId);
250+
console.log('userDocument:', userDocument);
251+
252+
// Set default roleID if it's null or undefined
253+
userDocument.roleID = userDocument.roleID || 'user';
254+
255+
return userDocument;
256+
} catch (error) {
257+
console.error('Error getting user document:', error);
258+
return null;
259+
}
260+
};
261+
export const likePost = async (videoUrl, userId, unlike = false) => {
262+
try {
263+
// Step 1: Get the current user
264+
const user = await getCurrentUser();
265+
266+
// Step 2: Get the video document
267+
const videoDocument = await getVideoDocument(videoUrl);
268+
269+
// Step 3: Get the user document
270+
const userDocument = await getUserDocument(user.$id, user.$collectionId);
271+
272+
// Step 4: Check if the user has already liked/unliked the video
273+
if (unlike && (!userDocument.LikedVideos || !userDocument.LikedVideos.includes(videoDocument.$id))) {
274+
console.log('User has not liked the video');
275+
return;
276+
}
277+
278+
if (!unlike && userDocument.LikedVideos && userDocument.LikedVideos.includes(videoDocument.$id)) {
279+
console.log('User has already liked the video');
280+
return;
281+
}
282+
283+
// Step 5: Update the user document with LikedVideos
284+
await updateUserDocument(userDocument, videoDocument.$id, unlike);
285+
} catch (error) {
286+
console.error('Error liking post:', error);
287+
} finally {
288+
console.log('Like post function completed');
289+
}
290+
};
291+
292+
// Helper function to get the video document
293+
const getVideoDocument = async (videoUrl) => {
294+
const videoDocument = await databases.listDocuments(
295+
databaseId,
296+
videoCollectionId,
297+
[Query.equal('video', videoUrl)],
298+
);
299+
300+
if (videoDocument.documents.length > 0) {
301+
return videoDocument.documents[0];
302+
} else {
303+
throw new Error('Video document not found');
304+
}
305+
};
306+
307+
// Helper function to get the user document
308+
const updateUserDocument = async (userDocument, videoDocumentId, unlike = false) => {
309+
if (!userDocument) {
310+
console.error('User document not found');
311+
return;
312+
}
313+
314+
let likedVideos = userDocument.LikedVideos || [];
315+
316+
if (unlike) {
317+
likedVideos = likedVideos.filter((id) => id !== videoDocumentId);
318+
} else {
319+
likedVideos.push(videoDocumentId);
320+
}
321+
322+
const newUserDocument = {
323+
accountid: userDocument.accountid,
324+
avatar: userDocument.avatar,
325+
email: userDocument.email,
326+
roleID: userDocument.roleID || 'user', // Set roleID to 'user' if it's null or undefined
327+
username: userDocument.username,
328+
LikedVideos: likedVideos,
329+
};
330+
331+
console.log('newUserDocument:', newUserDocument); // Add this line
332+
333+
try {
334+
await databases.updateDocument(
335+
databaseId,
336+
userDocument.$collectionId,
337+
userDocument.$id,
338+
{ data: newUserDocument },
339+
);
340+
} catch (error) {
341+
console.error('Error updating user document:', error);
342+
}
343+
};
344+
345+
346+
export const getLikedPosts = async () => {
347+
try {
348+
// Get the current user
349+
const user = await getCurrentUser();
350+
351+
if (!user || !user.LikedVideos) {
352+
return [];
353+
}
354+
355+
// Fetch the liked posts using the LikedVideos attribute
356+
const likedPosts = await Promise.all(
357+
user.LikedVideos.map(async (id) => {
358+
const post = await databases.getDocument(
359+
databaseId,
360+
videoCollectionId,
361+
id,
362+
);
363+
return post;
364+
}),
365+
);
366+
367+
return likedPosts;
368+
} catch (error) {
369+
throw new Error(error.message || 'Error fetching liked posts');
370+
}
371+
};

0 commit comments

Comments
 (0)
Please sign in to comment.