Skip to content

Commit

Permalink
integrated follow user service and functionality to the app b00tc4mp#175
Browse files Browse the repository at this point in the history
  • Loading branch information
breadislife committed Nov 21, 2024
1 parent fc259b3 commit 83810ae
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 10 deletions.
1 change: 1 addition & 0 deletions staff/daniel-hernandez/project/api/services/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const query = (userId, query, types = [], limit = constants.DEFAULT_LIMIT, page
username: 1,
profileImage: 1,
followers: '$followersCount',
isFollowed: { $in: [user._id, '$followers'] }, // Bool to determine if the user is followed by the current user
relevance: 1 // Include relevance score in the results
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { useTrackStore } from '../../store/track';
import SpinningLoader from '../loaders/SpinningLoader';
import { ItemIcons } from '../../../assets/images/icons';
import formatSeconds from '../../utils/formatSeconds';
import usePlayerHandlers from '../../hooks/usePlayerHandlers';

const TrackItem = ({ item, onMore, onGeneralPress }) => {
const TrackItem = ({ item, onMore }) => {
const { currentTrackId } = useTrackStore();
const { handlePlay } = usePlayerHandlers();

return (
<Pressable key={item?.id} className="py-2 flex-row items-start w-[100%] px-5 active:bg-palette-80 bg-palette-90" onPress={() => onGeneralPress(item)}>
<Pressable key={item?.id} className="py-2 flex-row items-start w-[100%] px-5 active:bg-palette-80 bg-palette-90" onPress={() => handlePlay(item)}>
<View className="w-16 h-16 rounded-sm mr-3 justify-center">
{currentTrackId === item?.id && <SpinningLoader className="absolute" tintColor="#E36526" />}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ const UserItem = ({ item, onAdd, onGeneralPress }) => (
className="self-center h-5 w-5 ml-2"
onPress={event => {
event.stopPropagation();
onAdd(item);
onAdd(item.id);
}}
>
<Image source={ItemIcons.addIcon} className="self-center h-4 w-4 my-auto" resizeMode="contain" />
<Image source={item.isFollowed ? ItemIcons.checkIcon : ItemIcons.addIcon} className="self-center h-4 w-4 my-auto" resizeMode="contain" />
</Pressable>
</Pressable>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useState, useEffect, useCallback } from 'react';
import { View, Image, Text, SectionList, FlatList } from 'react-native';
import useNotification from '../../hooks/useNotification';
import usePlayerHandlers from '../../hooks/usePlayerHandlers';
import { useTrackStore } from '../../store/track';
import { UserItem, TrackItem, PlaylistItem, AlbumItem } from '../../components/items';
import SpinningLoader from '../../components/loaders/SpinningLoader';
Expand All @@ -17,7 +16,6 @@ const DEFAULT_PILL = { label: 'All', queryType: [...constants.queryTypes], limit
// TODO: refactor and componentize
const SearchScreen = () => {
const { notify, notificationTypes } = useNotification();
const { handlePlay } = usePlayerHandlers();
const { currentTrackId } = useTrackStore();

const [query, setQuery] = useState('');
Expand Down Expand Up @@ -105,15 +103,59 @@ const SearchScreen = () => {
}
};

const handleFollowUser = async id => {
setResults(pR => {
const updatedResults = { ...pR };
if (updatedResults.users) {
updatedResults.users = updatedResults.users.map(user => {
if (user.id === id) {
const following = user.isFollowed;
return {
...user,
isFollowed: !following,
followers: following ? parseInt(user.followers) - 1 : parseInt(user.followers) + 1
};
}
return user;
});
}
return updatedResults;
});

try {
await services.followUser(id);
} catch {
notify('Something went wrong..', notificationTypes.error);

setResults(pR => {
const revertedResults = { ...pR };
if (revertedResults.users) {
revertedResults.users = revertedResults.users.map(user => {
if (user.id === id) {
const followed = !user.isFollowed;
return {
...user,
isFollowed: followed,
followers: followed ? parseInt(user.followers) + 1 : parseInt(user.followers) - 1
};
}
return user;
});
}
return revertedResults;
});
}
};

const renderResult = useCallback(
({ item, section }) => {
const type = section?.key || selectedPill.label.toLowerCase();

switch (type) {
case 'users':
return <UserItem item={item} onAdd={() => {}} onGeneralPress={() => {}} />;
return <UserItem item={item} onAdd={handleFollowUser} onGeneralPress={() => {}} />;
case 'tracks':
return <TrackItem item={item} onMore={() => {}} onGeneralPress={handlePlay} />;
return <TrackItem item={item} onMore={() => {}} />;
case 'playlists':
return <PlaylistItem item={item} onMore={() => {}} onGeneralPress={() => {}} />;
case 'albums':
Expand Down
7 changes: 5 additions & 2 deletions staff/daniel-hernandez/project/app/src/services/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { storage } from './storage.js';
import { playback } from './playback';
import player from './player.js';

export { signIn, signUp, signOut, checkEmail, search, storage, playback, player };
import followUser from './user/followUser.js';

export { signIn, signUp, signOut, checkEmail, search, storage, playback, player, followUser };

export default {
signIn,
Expand All @@ -19,5 +21,6 @@ export default {
search,
storage,
playback,
player
player,
followUser
};
45 changes: 45 additions & 0 deletions staff/daniel-hernandez/project/app/src/services/user/followUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Config from 'react-native-config';
import * as SecureStore from 'expo-secure-store';
import validate from 'com/validation';
import errors, { FetchError, ParseError, RetrievalError } from 'com/errors';

const followUser = targetUserId => {
validate.inputs(targetUserId);
validate.objectId(targetUserId);

return (async () => {
let userToken, res, body;

try {
userToken = await SecureStore.getItemAsync(Config.USER_TOKEN_KEY);
} catch (error) {
throw new RetrievalError(`Token retrieval failed: ${error.message}`);
}

if (!userToken) throw new RetrievalError('No token found');

try {
res = await fetch(`${Config.API_URL}/api/v1/user/follow/${targetUserId}`, {
method: 'PATCH',
headers: { Authorization: `Bearer ${userToken}` }
});
} catch (error) {
throw new FetchError(`Fetch failed: ${error.message}`);
}

if (res.status === 204) return;

try {
body = await res.json();
} catch (error) {
throw new ParseError(`Error parse failed: ${error.message}`);
}

const { error, message } = body;
const constructor = errors[error];

throw new constructor(message);
})();
};

export default followUser;

0 comments on commit 83810ae

Please sign in to comment.