Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import MyStatusBar from '@app/features/commons/components/StatusBar';

import {BottomSheetHomeProvider} from '@app/containers/BottomSheetHomeContext';
import GlobalContext from '@app/containers';

export const queryClient = new QueryClient();
import {queryClient} from '@app/lib/react-query';

const App = () => {
return (
Expand Down
32 changes: 21 additions & 11 deletions android/fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,6 @@ platform :android do

desc "Deploy a new version to the Google Play"
lane :deploy do |options|
# Convertir el tipo de versión a número
version_map = {
'major' => '1',
'minor' => '2',
'patch' => '3'
}
version_type = version_map[options[:version_type]] || '3'

# Ejecutar el script con el tipo de versión como argumento
sh "echo #{version_type} | ./bump-release-numbers.sh"

# Leer las variables de GitHub Secrets directamente
store_file = ENV["MYAPP_UPLOAD_STORE_FILE"]
store_password = ENV["MYAPP_UPLOAD_STORE_PASSWORD"]
Expand Down Expand Up @@ -68,5 +57,26 @@ platform :android do
skip_upload_images: true,
skip_upload_screenshots: true
)

# --- AHORA ACTUALIZAMOS LA VERSIÓN PARA LA SIGUIENTE BUILD ---
version_map = {
'major' => '1',
'minor' => '2',
'patch' => '3'
}
version_type = version_map[options[:version_type]] || '3'

# Ejecutar el script con el tipo de versión como argumento
sh "echo #{version_type} | ./bump-release-numbers.sh"

# --- SUBIR LOS CAMBIOS A GIT ---
commit_message = "🔖 Bump versión de desarrollo automática después de release"
branch_name = sh("git rev-parse --abbrev-ref HEAD").strip

sh "git add ."
sh "git commit -m '#{commit_message}'"
sh "git push origin #{branch_name}"

puts "✅ Versión subida a Google Play y número de versión actualizado en Git."
end
end
16 changes: 12 additions & 4 deletions android/fastlane/bump-release-numbers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ fi
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION_NAME"

# Lee la opción desde stdin
echo "Seleccione el tipo de incremento:"
echo "1) Major"
echo "2) Minor"
echo "3) Patch"
read -r CHOICE

# Valida y procesa la opción
Expand Down Expand Up @@ -57,19 +61,23 @@ esac

# Forma la nueva versión
NEW_VERSION_NAME="$MAJOR.$MINOR.$PATCH"
NEW_VERSION_CODE=$((CURRENT_VERSION_CODE + 1))
NEW_VERSION_CODE="$MAJOR$MINOR$PATCH"

# Crea un archivo temporal
TMP_FILE=$(mktemp)

# Actualiza el archivo build.gradle usando sed con un archivo temporal
# Actualiza el archivo build.gradle usando sed con compatibilidad universal
sed "s/versionCode $CURRENT_VERSION_CODE/versionCode $NEW_VERSION_CODE/" "$GRADLE_FILE" > "$TMP_FILE"
sed -i "s/versionName \"$CURRENT_VERSION_NAME\"/versionName \"$NEW_VERSION_NAME\"/" "$TMP_FILE"
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/versionName \"$CURRENT_VERSION_NAME\"/versionName \"$NEW_VERSION_NAME\"/" "$TMP_FILE"
else
sed -i "s/versionName \"$CURRENT_VERSION_NAME\"/versionName \"$NEW_VERSION_NAME\"/" "$TMP_FILE"
fi

# Mueve el archivo temporal al original
mv "$TMP_FILE" "$GRADLE_FILE"

echo "versionCode actualizado de $CURRENT_VERSION_CODE a $NEW_VERSION_CODE"
echo "versionName actualizado de $CURRENT_VERSION_NAME a $NEW_VERSION_NAME"

exit 0
exit 0
Original file line number Diff line number Diff line change
@@ -1,42 +1,15 @@
import React, {useEffect, useMemo, useState} from 'react';

import {useMemo} from 'react';
import {useUserContext} from '@app/containers/UserContext';
import {useTheme} from '@app/features/commons/theme/hooks/useTheme';
import {usePlaylistAllTracks} from '@app/features/commons/hooks/usePlaylistAllTracks';
import {
isSavedPlaylistForNotify,
removePlaylistForNotify,
savePlaylistForNotify,
} from '@app/services/playlist';
import {queryClient} from '../../../../../../../App';
import {useToggleNotify} from '@app/features/commons/hooks/useToggleNotify';

export const useNotifyMeButton = (playlistId: string) => {
const {isDarkMode} = useTheme();

const [isSaved, setIsSaved] = useState(false);
const [loadingToggle, setLoadingToggle] = useState(false);
const [checkingSaved, setCheckingSaved] = useState(false);
const {tracks, hasNextPage} = usePlaylistAllTracks(playlistId);
const {user} = useUserContext();

const checkPlaylistForNotify = async () => {
if (user) {
setCheckingSaved(true);
const isSaved = await isSavedPlaylistForNotify(playlistId, user?.id);
setIsSaved(isSaved ?? false);
setCheckingSaved(false);
return isSaved;
}
};

useEffect(() => {
const init = async () => {
const isSaved = await checkPlaylistForNotify();
setIsSaved(isSaved ?? false);
};

init();
}, []);
const {toggleNotify, isSaved, checkingSaved, loadingToggle} =
useToggleNotify(playlistId);

const iconProps = useMemo(() => {
if (isSaved)
Expand All @@ -49,37 +22,18 @@ export const useNotifyMeButton = (playlistId: string) => {
color: 'gray',
iconName: 'material-symbols:notifications-off-outline-rounded',
};
}, [isSaved]);
}, [isSaved, isDarkMode]);

const togglePlaylistSave = async () => {
if (user) {
setLoadingToggle(true); // Show loading indicator

try {
if (!isSaved) {
// Always save the playlist, regardless of hasNextPage
await savePlaylistForNotify(playlistId, tracks, user?.id);
queryClient.invalidateQueries({queryKey: ['userPlaylists']});
setIsSaved(true);
} else {
await removePlaylistForNotify(playlistId, user?.id);
queryClient.invalidateQueries({queryKey: ['userPlaylists']});
setIsSaved(false);
}
} catch (error) {
console.error('Error saving/removing playlist:', error);
}

setLoadingToggle(false); // Hide loading indicator
}
toggleNotify(tracks);
};

return {
loadingToggle,
checkingSaved,
iconProps,
canSavePlaylist: hasNextPage,
checkPlaylistForNotify,
isSaved,
togglePlaylistSave,
};
};
11 changes: 7 additions & 4 deletions app/features/commons/components/Header/NotifyMeButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,20 @@ const NotifyMeButton = ({id}: Props) => {
checkingSaved,
} = useNotifyMeButton(id);

if (checkingSaved)
const showLoading = checkingSaved || canSavePlaylist;
const showSavingLoading = loadingToggle || checkingSaved || canSavePlaylist;

if (showLoading)
return (
<ActivityIndicator style={styles.inline} size="small" color="gray" />
);

return (
<View style={styles.inline}>
{loadingToggle && <ActivityIndicator size="small" color="gray" />}
{showSavingLoading && <ActivityIndicator size="small" color="gray" />}
<TouchableOpacity
onPress={togglePlaylistSave}
disabled={canSavePlaylist || loadingToggle}>
disabled={showSavingLoading}
onPress={togglePlaylistSave}>
<Monicon name={iconProps.iconName} size={26} color={iconProps.color} />
</TouchableOpacity>
</View>
Expand Down
89 changes: 89 additions & 0 deletions app/features/commons/hooks/useNotifyPlaylist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
import {
removePlaylistForNotify,
savePlaylistForNotify,
isSavedPlaylistForNotify,
} from '@app/services/playlist';
import {PlaylistItem, UserAddedPlaylistsResponse} from '@app/services/types';
import {QUERY_KEYS} from '@app/lib/queryKeys';
import {useUserContext} from '@app/containers/UserContext';

export const useNotifyStatus = (playlistId: string) => {
const {user} = useUserContext();

return useQuery({
queryKey: QUERY_KEYS.NOTIFY.playlist(playlistId, user?.id),
queryFn: () => isSavedPlaylistForNotify(playlistId, user?.id ?? ''),
enabled: !!user?.id,
staleTime: 5 * 60 * 1000, // 5 minutos
});
};

export const useSaveNotify = (playlistId: string) => {
const {user} = useUserContext();
const queryClient = useQueryClient();

return useMutation({
mutationFn: async ({tracks}: {tracks: PlaylistItem[]}) => {
if (!user?.id) throw new Error('User ID is required');
// Actualización optimista inmediata
return savePlaylistForNotify(playlistId, tracks, user?.id);
},
onSuccess: () => {
queryClient.setQueryData(
QUERY_KEYS.NOTIFY.playlist(playlistId, user?.id),
true,
);
queryClient.refetchQueries([QUERY_KEYS.USER_NOTIFIED_PLAYLISTS]);
},
onError: () => {
queryClient.setQueryData(
QUERY_KEYS.NOTIFY.playlist(playlistId, user?.id),
false,
);
},
});
};

export const useRemoveNotify = (playlistId: string) => {
const {user} = useUserContext();
const queryClient = useQueryClient();

return useMutation({
mutationFn: async () => {
if (!user?.id) throw new Error('User ID is required');
queryClient.setQueryData(
QUERY_KEYS.NOTIFY.playlist(playlistId, user?.id),
false,
);

return removePlaylistForNotify(playlistId, user?.id);
},
onSuccess: () => {
console.log(
'onsuccedss',
queryClient.getQueryData([QUERY_KEYS.USER_NOTIFIED_PLAYLISTS]),
);

queryClient.setQueryData<UserAddedPlaylistsResponse[]>(
[QUERY_KEYS.USER_NOTIFIED_PLAYLISTS],
oldData =>
oldData?.filter(item => item.playlistId !== playlistId) ?? [],
);
},
onError: () => {
console.log('onError');

queryClient.setQueryData(
QUERY_KEYS.NOTIFY.playlist(playlistId, user?.id),
true,
);

queryClient.setQueryData<UserAddedPlaylistsResponse[]>(
[QUERY_KEYS.USER_NOTIFIED_PLAYLISTS],
oldData =>
oldData?.filter(item => item.playlistId !== playlistId) ?? [],
);
},
});
};
5 changes: 4 additions & 1 deletion app/features/commons/hooks/usePlaylist.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import {useQuery} from '@tanstack/react-query';
import {getPlaylist} from '../../../services/playlist';
import {QUERY_KEYS} from '@app/lib/queryKeys';

interface Props {
playlistId: string;
}

export const usePlaylist = ({playlistId}: Props) => {
const playlistReq = useQuery({
queryKey: ['playlist', playlistId],
queryKey: [QUERY_KEYS.PLAYLIST_DETAIL, playlistId],
queryFn: () => getPlaylist(playlistId),
retry: 3,
staleTime: 5 * 60 * 1000, // 5 minutos
cacheTime: 30 * 60 * 1000, // 30 minutos
});

return playlistReq;
Expand Down
32 changes: 19 additions & 13 deletions app/features/commons/hooks/usePlaylistAllTracks.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import {useInfiniteQuery} from '@tanstack/react-query';
import {getPlaylistTracks} from '../../../services/playlist';

export const usePlaylistAllTracks = (playlistId: string) => {
const fetchTracks = async ({pageParam = ''}) => {
const res = await getPlaylistTracks(playlistId, pageParam);
return res;
};
import React, {useEffect} from 'react';
import {QUERY_KEYS} from '@app/lib/queryKeys';

export const usePlaylistAllTracks = (playlistId: string) => {
const {
data,
fetchNextPage,
Expand All @@ -16,18 +14,26 @@ export const usePlaylistAllTracks = (playlistId: string) => {
error,
refetch,
} = useInfiniteQuery({
queryKey: ['playlistAllTracks', playlistId],
queryFn: fetchTracks,
getNextPageParam: (lastPage, allPages) => {
return lastPage?.next;
},
queryKey: QUERY_KEYS.playlistTracks(playlistId),
queryFn: ({pageParam = ''}) => getPlaylistTracks(playlistId, pageParam),
getNextPageParam: lastPage => lastPage?.next || undefined,
keepPreviousData: true,
staleTime: 5 * 60 * 1000, // 5 minutos
cacheTime: 30 * 60 * 1000, // 30 minutos
});

if (hasNextPage) fetchNextPage();
// Memoizar el flatMap para evitar recálculos innecesarios
const tracks = React.useMemo(
() => data?.pages.flatMap(page => page?.items ?? []) ?? [],
[data?.pages],
);

//flatMapping data for getting only tracks items
const tracks = data?.pages.flatMap(page => page?.items ?? []) ?? [];
// Cargar siguiente página solo si hay más y no estamos ya cargando
useEffect(() => {
if (hasNextPage && !isFetching) {
fetchNextPage();
}
}, [hasNextPage, isFetching, fetchNextPage]);

return {
tracks,
Expand Down
Loading
Loading