diff --git a/packages/app/components/trip/TripForm.tsx b/packages/app/components/trip/TripForm.tsx index e18030411..5c789e63f 100644 --- a/packages/app/components/trip/TripForm.tsx +++ b/packages/app/components/trip/TripForm.tsx @@ -5,6 +5,7 @@ import { FormSelect as OriginalFormSelect, YStack, SubmitButton, + FormSwitch, } from '@packrat/ui'; import { useRouter } from 'app/hooks/router'; import { useAddTrip, useEditTrips } from 'app/hooks/trips'; @@ -27,11 +28,6 @@ interface TripFormProps { tripId?: string; } -const isPublicOptions = ['For me only', 'Public'].map((key, index) => ({ - label: key, - value: String(index), -})); - export const TripForm = ({ tripStore, dateRange, @@ -63,7 +59,7 @@ export const TripForm = ({ defaultValues={{ name: initialState?.name, description: initialState?.description, - is_public: initialState?.is_public ? '1' : '0', + is_public: initialState?.is_public, activity: initialState?.activity || ActivityOptions[0].value, }} > @@ -78,13 +74,7 @@ export const TripForm = ({ fullWidth name="activity" /> - + Save Trip diff --git a/packages/app/hooks/trips/useSetTripVisibility.ts b/packages/app/hooks/trips/useSetTripVisibility.ts new file mode 100644 index 000000000..068792e84 --- /dev/null +++ b/packages/app/hooks/trips/useSetTripVisibility.ts @@ -0,0 +1,26 @@ +import { queryTrpc } from '../../trpc'; + +export const useSetTripVisibility = () => { + const utils = queryTrpc.useUtils(); + const mutation = queryTrpc.setTripVisibility.useMutation(); + + const setTripVisibility = ( + updatedTrip, + options?: { onSuccess?: (queryUtils: typeof utils) => void }, + ) => { + mutation.mutate(updatedTrip, { + onSuccess: () => { + utils.getTrips.invalidate(); + utils.getUserTripsFeed.invalidate(); + utils.getSingleTrip.invalidate(); + utils.getUserTrips.invalidate(); + options?.onSuccess?.(utils); + }, + }); + }; + + return { + setTripVisibility, + ...mutation, + }; +}; diff --git a/packages/app/modules/feed/hooks/useFetchUserFavorites.ts b/packages/app/modules/feed/hooks/useFetchUserFavorites.ts index 121b915fc..8588db6d2 100644 --- a/packages/app/modules/feed/hooks/useFetchUserFavorites.ts +++ b/packages/app/modules/feed/hooks/useFetchUserFavorites.ts @@ -12,8 +12,19 @@ import { export const useFetchUserFavorites = ( userId: string, - { queryEnabled = true, isPreview = false, searchTerm = '', isPublic } = {}, + options: { + queryEnabled: boolean; + isPreview: boolean; + searchTerm: string; + isPublic: boolean; + }, ) => { + const { + queryEnabled = true, + isPreview = false, + searchTerm = '', + isPublic, + } = options || {}; const enabled = !!userId && queryEnabled; const [pagination, setPagination] = useState( getPaginationInitialParams(), @@ -68,13 +79,15 @@ export const useFetchUserFavoritesWithPreview = ( ): FetchUserFavoritesReturn => { const { isAllQueryEnabled, ...previewResourceState } = usePreviewResourceState(); - const { data: previewData, isLoading: isPreviewLoading } = - useFetchUserFavorites(userId, { isPreview: true, isPublic }); + const { + data: previewData, + isLoading: isPreviewLoading, + totalCount, + } = useFetchUserFavorites(userId, { isPreview: true, isPublic }); const { data: allQueryData, isLoading: isAllQueryLoading, - totalCount, fetchPrevPage, fetchNextPage, totalPages, @@ -84,6 +97,7 @@ export const useFetchUserFavoritesWithPreview = ( } = useFetchUserFavorites(userId, { queryEnabled: isAllQueryEnabled, searchTerm, + isPublic, }); return { diff --git a/packages/app/modules/pack/hooks/useEditPack.ts b/packages/app/modules/pack/hooks/useEditPack.ts index 3f8eb63eb..51cdb1335 100644 --- a/packages/app/modules/pack/hooks/useEditPack.ts +++ b/packages/app/modules/pack/hooks/useEditPack.ts @@ -12,6 +12,7 @@ export const useEditPack = () => { mutation.mutate(updatedPack, { onSuccess: () => { utils.getPacks.invalidate(); + utils.getUserPacksFeed.invalidate(); options?.onSuccess?.(utils); }, }); diff --git a/packages/app/modules/pack/hooks/useUserPacks.ts b/packages/app/modules/pack/hooks/useUserPacks.ts index 5ed78a401..4d330457b 100644 --- a/packages/app/modules/pack/hooks/useUserPacks.ts +++ b/packages/app/modules/pack/hooks/useUserPacks.ts @@ -97,19 +97,17 @@ export const useUserPacksWithPreview = ( ): FetchUserPacksPreviewReturn => { const { isAllQueryEnabled, ...previewResourceState } = usePreviewResourceState(); - const { data: previewData, isLoading: isPreviewLoading } = useUserPacks( - userId, - { isPreview: true, isPublic }, - 'Most Recent', - true, - ); + const { + data: previewData, + isLoading: isPreviewLoading, + totalCount, + } = useUserPacks(userId, { isPreview: true, isPublic }, 'Most Recent', true); const { data: allQueryData, isLoading: isAllQueryLoading, fetchPrevPage, fetchNextPage, - totalCount, hasPrevPage, hasNextPage, currentPage, diff --git a/packages/app/modules/pack/useSetPackVisibility.ts b/packages/app/modules/pack/useSetPackVisibility.ts new file mode 100644 index 000000000..b86150ed6 --- /dev/null +++ b/packages/app/modules/pack/useSetPackVisibility.ts @@ -0,0 +1,25 @@ +import { queryTrpc } from '../../trpc'; + +export const useSetPackVisibility = () => { + const utils = queryTrpc.useUtils(); + const mutation = queryTrpc.editPack.useMutation(); + + const setPackVisibility = ( + updatePack: { id: string; is_public: boolean; name: string }, + options?: { onSuccess?: (queryUtils: typeof utils) => void }, + ) => { + mutation.mutate(updatePack, { + onSuccess: () => { + utils.getUserFavorites.invalidate(); + utils.getUserPacksFeed.invalidate(); + utils.getPublicFeed.invalidate(); + options?.onSuccess?.(utils); + }, + }); + }; + + return { + setPackVisibility, + ...mutation, + }; +}; diff --git a/packages/app/modules/trip/hooks/useUserTrips.ts b/packages/app/modules/trip/hooks/useUserTrips.ts index c1ff57a39..158f33447 100644 --- a/packages/app/modules/trip/hooks/useUserTrips.ts +++ b/packages/app/modules/trip/hooks/useUserTrips.ts @@ -82,16 +82,15 @@ export const useUserTripsWithPreview = ( ): FetchUserTripsPreviewReturn => { const { isAllQueryEnabled, ...previewResourceState } = usePreviewResourceState(); - const { data: previewData, isLoading: isPreviewLoading } = useUserTrips( - userId, - { isPublic, isPreview: true, searchTerm }, - true, - ); + const { + data: previewData, + isLoading: isPreviewLoading, + totalCount, + } = useUserTrips(userId, { isPublic, isPreview: true, searchTerm }, true); const { data: allQueryData, isLoading: isAllQueryLoading, - totalCount, fetchPrevPage, fetchNextPage, currentPage, diff --git a/packages/app/modules/trip/utils.ts b/packages/app/modules/trip/utils.ts new file mode 100644 index 000000000..9de98c5b5 --- /dev/null +++ b/packages/app/modules/trip/utils.ts @@ -0,0 +1,5 @@ +import moment from 'moment'; + +export const formatTripDate = (date: string) => { + return moment(date, 'MM/DD/YYYY'); +}; diff --git a/packages/app/modules/user/components/UserDataCard.tsx b/packages/app/modules/user/components/UserDataCard.tsx index 33ab2a66e..2c8b9e629 100644 --- a/packages/app/modules/user/components/UserDataCard.tsx +++ b/packages/app/modules/user/components/UserDataCard.tsx @@ -19,6 +19,7 @@ const cardComponentsByType = { interface UserDataCardProps { feedType: UserDataResource; + isAuthUserProfile?: boolean; cardType: CardType; item: UserData; } @@ -27,6 +28,7 @@ export const UserDataCard: FC = ({ item, cardType, feedType, + isAuthUserProfile, }) => { const { addFavorite } = useAddFavorite(); const user = useAuthUser(); @@ -56,6 +58,7 @@ export const UserDataCard: FC = ({ {...cardProps} cardType={cardType} toggleFavorite={handleAddToFavorite} + isAuthUserProfile={isAuthUserProfile} /> ); }; diff --git a/packages/app/modules/user/components/UserPackCard.tsx b/packages/app/modules/user/components/UserPackCard.tsx index 397f64c0b..1448efc72 100644 --- a/packages/app/modules/user/components/UserPackCard.tsx +++ b/packages/app/modules/user/components/UserPackCard.tsx @@ -1,45 +1,28 @@ -import { Card, RButton, RStack, RSwitch, RText } from '@packrat/ui'; +import { Card, RButton, RStack, RSwitch, RText, Switch } from '@packrat/ui'; import React, { useEffect, useState, type FC } from 'react'; import { PackImage } from 'app/modules/pack/components/PackCard/PackImage'; -import { - FavoriteButton, - useFetchUserFavorites, - type FeedCardProps, -} from 'app/modules/feed'; +import { FavoriteButton } from 'app/modules/feed'; import { type PackDetails } from 'app/modules/pack/model'; -import { useEditPack } from 'app/modules/pack/hooks'; import useTheme from 'app/hooks/useTheme'; import { useUserPacks } from 'app/modules/pack/hooks'; import { ScoreLabel } from 'app/components/ScoreLabel'; +import { Eye, EyeOff } from '@tamagui/lucide-icons'; +import { type UserDataCardProps } from './model'; +import { useSetPackVisibility } from 'app/modules/pack/useSetPackVisibility'; -interface PackCardProps extends FeedCardProps {} +interface PackCardProps extends UserDataCardProps {} export const UserPackCard: FC = (props) => { - const { editPack } = useEditPack(); - const [isPublic, setIsPublic] = useState(props.is_public); + const { setPackVisibility, isLoading } = useSetPackVisibility(); const { currentTheme } = useTheme(); const { refetch } = useUserPacks(props.ownerId, {}, '', true); - const { refetch: refetchFavorites } = useFetchUserFavorites(props.ownerId); const updateIsPublic = (value) => { - setIsPublic(value); - editPack( - { id: props.id, name: props.title, is_public: value }, - { - onSuccess: () => { - refetch(); - refetchFavorites(); - }, - }, - ); + setPackVisibility({ id: props.id, name: props.title, is_public: value }); }; - useEffect(() => { - setIsPublic(props.is_public); - }, [props.is_public]); - return ( = (props) => { /> } actions={ - + + {props.isAuthUserProfile && ( + updateIsPublic(!props.isPublic)} + onClick={(e) => { + e.stopPropagation(); + e.preventDefault(); + }} + > + {props.isPublic ? ( + + ) : ( + + )} + + )} = (props) => { refetch(); }} /> - { - e.stopPropagation(); - e.preventDefault(); - }} - > - - } type={props.cardType} style={{ - borderColor: isPublic + borderColor: props.isPublic ? currentTheme.colors.secondaryBlue : currentTheme.colors.background, borderWidth: 2, diff --git a/packages/app/modules/user/components/UserTripCard.tsx b/packages/app/modules/user/components/UserTripCard.tsx index e242716fa..450c097e0 100644 --- a/packages/app/modules/user/components/UserTripCard.tsx +++ b/packages/app/modules/user/components/UserTripCard.tsx @@ -1,21 +1,21 @@ -import { Card, RButton, RStack, RSwitch } from '@packrat/ui'; +import { Card, RButton, RStack } from '@packrat/ui'; import React, { useState, type FC } from 'react'; import { TripImage } from 'app/modules/trip/components/TripCard/TripImage'; -import { type FeedCardProps } from 'app/modules/feed'; import { LocationLabel } from 'app/modules/trip/components/LocationLabel/LocationLabel'; import useTheme from 'app/hooks/useTheme'; import { type TripDetails } from 'modules/trip/model'; -import { useEditTrips } from 'app/hooks/trips'; +import { useSetTripVisibility } from 'app/hooks/trips/useSetTripVisibility'; +import { Eye, EyeOff } from '@tamagui/lucide-icons'; +import { type UserDataCardProps } from './model'; -interface TripCardProps extends FeedCardProps {} +interface TripCardProps extends UserDataCardProps {} export const UserTripCard: FC = (props) => { - const { editTrips, isLoading, isError } = useEditTrips(); - const [isPublic, setIsPublic] = useState(props.is_public); + const { setTripVisibility, isLoading, isError } = useSetTripVisibility(); const updateIsPublic = (value) => { - setIsPublic(value); - editTrips({ id: props.id, name: props.title, is_public: value }); + setTripVisibility({ tripId: props.id, is_public: value }); }; + const isPublic = props?.isPublic; const { currentTheme } = useTheme(); return ( @@ -25,25 +25,30 @@ export const UserTripCard: FC = (props) => { image={} subtitle={} actions={ - - { - e.stopPropagation(); - e.preventDefault(); - }} - > - - + + {props.isAuthUserProfile && ( + updateIsPublic(!isPublic)} + onClick={(e) => { + e.stopPropagation(); + e.preventDefault(); + }} + > + {isPublic ? ( + + ) : ( + + )} + + )} } type={props.cardType} diff --git a/packages/app/modules/user/components/model.ts b/packages/app/modules/user/components/model.ts index a3aeae3cc..247a76838 100644 --- a/packages/app/modules/user/components/model.ts +++ b/packages/app/modules/user/components/model.ts @@ -56,5 +56,7 @@ export interface UserDataCardProps
{ ownerId: string; favoriteCount: number; isUserFavorite?: boolean; + isPublic?: boolean; + isAuthUserProfile?: boolean; toggleFavorite?: () => void; } diff --git a/packages/app/modules/user/components/utils.ts b/packages/app/modules/user/components/utils.ts index 2378796c2..bf17b9cdd 100644 --- a/packages/app/modules/user/components/utils.ts +++ b/packages/app/modules/user/components/utils.ts @@ -40,7 +40,7 @@ export const UserDataPackCardConverter: Converter< (item) => item === currentUserId || item?.userId === currentUserId, ), favoriteCount: input.favorites_count, - is_public: input.is_public, + isPublic: input.is_public, }; }; @@ -70,5 +70,6 @@ export const UserDataTripCardConverter: Converter< activity: input.activity, }, favoriteCount: input.favorites_count, + isPublic: input.is_public, }; }; diff --git a/packages/app/modules/user/hooks/useProfile.ts b/packages/app/modules/user/hooks/useProfile.ts index 84572e9de..932cc432b 100644 --- a/packages/app/modules/user/hooks/useProfile.ts +++ b/packages/app/modules/user/hooks/useProfile.ts @@ -9,7 +9,6 @@ import type { PreviewListType } from '../model'; export const useProfile = (id = null) => { const authUser = useAuthUser(); const isAuthUserProfile = !id || id === authUser?.id; - console.log('isAuthUserProfile', isAuthUserProfile); const userId = id ?? authUser?.id; const [searchTerms, setSearchTerms] = useState< Record @@ -20,6 +19,7 @@ export const useProfile = (id = null) => { const favoritesQuery = useFetchUserFavoritesWithPreview( userId as string, searchTerms.favorites, + !isAuthUserProfile ? true : undefined, ); const userPacksQuery = useUserPacksWithPreview( userId as string, @@ -58,6 +58,7 @@ export const useProfile = (id = null) => { favoritesQuery, userPacksQuery, userTripsQuery, + isAuthUserProfile, tripsCount: userTripsQuery.totalCount, packsCount: userPacksQuery.totalCount, favoritesCount: favoritesQuery.totalCount, diff --git a/packages/app/modules/user/widgets/ProfileContainer.tsx b/packages/app/modules/user/widgets/ProfileContainer.tsx index 1e45eb636..512c58672 100644 --- a/packages/app/modules/user/widgets/ProfileContainer.tsx +++ b/packages/app/modules/user/widgets/ProfileContainer.tsx @@ -168,6 +168,7 @@ export function ProfileContainer({ id = null }) { favoritesCount, isLoading, isCurrentUser, + isAuthUserProfile, error, } = useProfile(id); @@ -196,6 +197,7 @@ export function ProfileContainer({ id = null }) { resource={favoritesQuery} type="favorites" userId={user?.id} + isAuthUserProfile={isAuthUserProfile} isLoading={isLoading} searchTerm={searchTerms.favorites} onSearchChange={onSearchChange} @@ -206,6 +208,7 @@ export function ProfileContainer({ id = null }) { resource={userPacksQuery} type="packs" userId={user?.id} + isAuthUserProfile={isAuthUserProfile} searchTerm={searchTerms.packs} onSearchChange={onSearchChange} /> @@ -215,6 +218,7 @@ export function ProfileContainer({ id = null }) { resource={userTripsQuery} type="trips" userId={user?.id} + isAuthUserProfile={isAuthUserProfile} searchTerm={searchTerms.packs} onSearchChange={onSearchChange} /> diff --git a/packages/app/modules/user/widgets/UserDataContainer.tsx b/packages/app/modules/user/widgets/UserDataContainer.tsx index 8d957f2ec..1f1ccd0e2 100644 --- a/packages/app/modules/user/widgets/UserDataContainer.tsx +++ b/packages/app/modules/user/widgets/UserDataContainer.tsx @@ -31,6 +31,7 @@ interface UserDataContainerProps { userId?: string; isLoading?: boolean; SkeletonComponent?: React.ReactElement; + isAuthUserProfile?: boolean; searchTerm: string; onSearchChange: (search: string, type: PreviewListType) => void; } @@ -41,6 +42,7 @@ export const UserDataContainer = memo(function UserDataContainer({ userId, isLoading, SkeletonComponent, + isAuthUserProfile, searchTerm, onSearchChange, }: UserDataContainerProps) { @@ -54,7 +56,14 @@ export const UserDataContainer = memo(function UserDataContainer({ const differentUser = userId && currentUser && userId !== currentUser.id; const Card = ({ item }) => { - return ; + return ( + + ); }; // Map function to render multiple skeleton cards diff --git a/packages/app/screens/trip/editTrip.tsx b/packages/app/screens/trip/editTrip.tsx index 601c32170..07987f88e 100644 --- a/packages/app/screens/trip/editTrip.tsx +++ b/packages/app/screens/trip/editTrip.tsx @@ -7,11 +7,11 @@ import { useFetchSingleTrip } from 'app/hooks/singletrips'; import { type addTripKey } from './createTripStore/store'; import { useOSM } from 'app/hooks/geojson'; import { useTripId } from 'app/hooks/trips'; +import { formatTripDate } from 'app/modules/trip/utils'; export default function EditTripScreen() { const [tripId] = useTripId(); const { isLoading, isError, data } = useFetchSingleTrip(tripId); - return ( { + style?: StyleProp; +} + +// TODO change the name to "Input" after handling tamagui all components export, +// which is cause of a name collision +export const FormSwitch = (props: Props) => { + const { style, ...rest } = props; + return ; +}; diff --git a/packages/ui/src/form/components/Select.tsx b/packages/ui/src/form/components/Select.tsx index 9c6f4e6c4..5515cccc7 100644 --- a/packages/ui/src/form/components/Select.tsx +++ b/packages/ui/src/form/components/Select.tsx @@ -2,7 +2,8 @@ import { LmSelectRhf, type LmSelectRhfProps } from '../lib'; import { StyleProp, ViewStyle } from 'react-native'; export { LmSelect as Select } from '../lib'; -interface Props extends LmSelectRhfProps<{ label: string | number; value: any }> { +interface Props + extends LmSelectRhfProps<{ label: string | number; value: any }> { style?: StyleProp; } @@ -11,4 +12,4 @@ interface Props extends LmSelectRhfProps<{ label: string | number; value: any }> export const FormSelect = (props: Props) => { const { style, ...rest } = props; return ; -}; \ No newline at end of file +}; diff --git a/packages/ui/src/form/index.ts b/packages/ui/src/form/index.ts index 760637720..4a20c56ee 100644 --- a/packages/ui/src/form/index.ts +++ b/packages/ui/src/form/index.ts @@ -3,6 +3,7 @@ export * from './components/Input'; export * from './components/InputWithIcon'; export * from './components/SubmitButton'; export * from './components/Select'; +export * from './components/FormSwitch'; export * from './components/RadioGroup'; export * from './components/ImageUpload'; export * from './useFormSubmitTrigger'; diff --git a/server/src/services/trip/editTripService.ts b/server/src/services/trip/editTripService.ts index 900e169c3..c9f1d669a 100644 --- a/server/src/services/trip/editTripService.ts +++ b/server/src/services/trip/editTripService.ts @@ -20,7 +20,10 @@ export const editTripService = async ( start_date: tripData.start_date || selectedTrip.start_date, end_date: tripData.end_date || selectedTrip.end_date, activity: tripData.activity || selectedTrip.activity, - is_public: tripData.is_public || selectedTrip.is_public, + is_public: + typeof tripData.is_public === 'boolean' + ? tripData.is_public + : selectedTrip.is_public, pack_id: tripData.pack_id || selectedTrip.pack_id, trails: tripData.trails ? JSON.parse(tripData.trails) @@ -73,7 +76,7 @@ export const setTripVisibilityService = async ( ) => { try { const tripClass = new Trip(); - const selectedTrip = await tripClass.findById(tripData.id); + const selectedTrip = await tripClass.findById(tripData.tripId); const updatedTrip = await tripClass.update({ ...selectedTrip, id: selectedTrip.id,