From 3ea8b8cf69e0261fd5e532db7259ebbf66e2eec1 Mon Sep 17 00:00:00 2001 From: WaDadidou Date: Mon, 16 Sep 2024 20:06:22 -0400 Subject: [PATCH] fix: map video and audio render, fix map location in url, better typing, remove legacy stuff --- .../components/mediaPlayer/TimerSliderAlt.tsx | 1 + packages/components/music/TrackCard.tsx | 4 +- .../music/UploadMusicModal/UploadTrack.tsx | 20 +++-- packages/components/socialFeed/Map/Map.tsx | 6 +- .../components/socialFeed/Map/Map.types.ts | 9 +- .../components/socialFeed/Map/Map.web.tsx | 81 +++++++----------- .../socialFeed/Map/MapPosts/MusicMapPost.tsx | 33 +++++-- .../socialFeed/Map/MapPosts/VideoMapPost.tsx | 85 ++++++++++++------- .../socialFeed/NewsFeed/NewsFeedInput.tsx | 11 ++- .../SocialCard/SocialCardHeader.tsx | 7 +- .../SocialCard/cards/SocialThreadCard.tsx | 7 +- .../modals/MapModal/AddressSearch.tsx | 10 ++- .../socialFeed/modals/MapModal/MapModal.tsx | 23 +++-- .../components/video/UploadVideoModal.tsx | 25 ++++-- packages/components/video/VideoCard.tsx | 4 +- packages/screens/Feed/FeedScreen.tsx | 9 +- packages/screens/Feed/components/MapFeed.tsx | 4 +- .../FeedNewArticle/FeedNewArticleScreen.tsx | 37 +++++--- packages/utils/feed/map.ts | 68 ++++++++++++--- packages/utils/feed/queries.ts | 5 +- packages/utils/navigation.ts | 3 +- packages/utils/types/feed.ts | 4 +- 22 files changed, 282 insertions(+), 174 deletions(-) diff --git a/packages/components/mediaPlayer/TimerSliderAlt.tsx b/packages/components/mediaPlayer/TimerSliderAlt.tsx index e7c7309d9e..2cb229d830 100644 --- a/packages/components/mediaPlayer/TimerSliderAlt.tsx +++ b/packages/components/mediaPlayer/TimerSliderAlt.tsx @@ -51,6 +51,7 @@ export const TimerSliderAlt: FC<{ setIsSliding(true)} onSlidingComplete={(value: number) => { + // FIXME: onChangeTimerPosition doesn't work here onChangeTimerPosition(value); setIsSliding(false); }} diff --git a/packages/components/music/TrackCard.tsx b/packages/components/music/TrackCard.tsx index 85dda09598..9dc59ed3b2 100644 --- a/packages/components/music/TrackCard.tsx +++ b/packages/components/music/TrackCard.tsx @@ -23,6 +23,7 @@ import NormalPlay from "@/assets/icons/music/normal-play.svg"; import { LocationButton } from "@/components/socialFeed/NewsFeed/LocationButton"; import { useMediaPlayer } from "@/context/MediaPlayerProvider"; import { useAppNavigation } from "@/hooks/navigation/useAppNavigation"; +import { locationToString } from "@/utils/feed/map"; import { zodTryParseJSON } from "@/utils/sanitize"; import { neutral17, @@ -113,9 +114,10 @@ export const TrackCard: React.FC<{ + track.location && navigation.navigate("Feed", { tab: "map", - locationToCenter: track.location, + center: locationToString(track.location), }) } stroke={neutralFF} diff --git a/packages/components/music/UploadMusicModal/UploadTrack.tsx b/packages/components/music/UploadMusicModal/UploadTrack.tsx index 19f1123763..d8ee062a3f 100644 --- a/packages/components/music/UploadMusicModal/UploadTrack.tsx +++ b/packages/components/music/UploadMusicModal/UploadTrack.tsx @@ -6,7 +6,6 @@ import { View, ViewStyle, } from "react-native"; -import { LatLng } from "react-native-leaflet-view"; import { useSelector } from "react-redux"; import AudioSVG from "../../../../assets/icons/audio.svg"; @@ -30,6 +29,7 @@ import { import { fontSemibold14 } from "../../../utils/style/fonts"; import { layout } from "../../../utils/style/layout"; import { + CustomLatLngExpression, PostCategory, SocialFeedTrackMetadata, } from "../../../utils/types/feed"; @@ -58,9 +58,11 @@ const UPLOAD_ALBUM_MODAL_WIDTH = 564; export const UploadTrack: React.FC = ({ onUploadDone }) => { const [developerMode] = useDeveloperMode(); const [isMapShown, setIsMapShown] = useState(false); - const [location, setLocation] = useState(); + const [location, setLocation] = useState< + CustomLatLngExpression | undefined + >(); - const { setToastError } = useFeedbacks(); + const { setToast } = useFeedbacks(); const selectedNetwork = useSelectedNetworkInfo(); const selectedWallet = useSelectedWallet(); const userId = selectedWallet?.userId; @@ -108,9 +110,11 @@ export const UploadTrack: React.FC = ({ onUploadDone }) => { console.error("post submit err", err); setIsUploadLoading(false); setIsProgressBarShown(false); - setToastError({ + setToast({ title: "Post creation failed", message: err instanceof Error ? err.message : `${err}`, + type: "error", + mode: "normal", }); } }; @@ -145,9 +149,11 @@ export const UploadTrack: React.FC = ({ onUploadDone }) => { userIPFSKey || (await generateIpfsKey(selectedNetwork?.id || "", userId)); if (!pinataJWTKey) { console.error("upload file err : No Pinata JWT"); - setToastError({ + setToast({ title: "File upload failed", message: "No Pinata JWT", + type: "error", + mode: "normal", }); setIsUploadLoading(false); return; @@ -160,9 +166,11 @@ export const UploadTrack: React.FC = ({ onUploadDone }) => { }); if (!uploadedFiles.find((file) => file.url)) { console.error("upload file err : Fail to pin to IPFS"); - setToastError({ + setToast({ title: "File upload failed", message: "Fail to pin to IPFS, please try to Publish again", + type: "error", + mode: "normal", }); setIsUploadLoading(false); return; diff --git a/packages/components/socialFeed/Map/Map.tsx b/packages/components/socialFeed/Map/Map.tsx index a0a8406551..46770d4d83 100644 --- a/packages/components/socialFeed/Map/Map.tsx +++ b/packages/components/socialFeed/Map/Map.tsx @@ -5,14 +5,14 @@ import { MapProps } from "@/components/socialFeed/Map/Map.types"; import { DEFAULT_MAP_POSITION, MAP_LAYER_URL } from "@/utils/feed/map"; export const Map: FC = ({ - locationSelected, + creatingPostLocation, locationToCenter = DEFAULT_MAP_POSITION, }) => { return ( <>} zoom={12} - mapCenterPosition={locationSelected || locationToCenter} + mapCenterPosition={creatingPostLocation || locationToCenter} mapLayers={[ { url: MAP_LAYER_URL, @@ -20,7 +20,7 @@ export const Map: FC = ({ ]} mapMarkers={[ { - position: locationSelected || locationToCenter, + position: creatingPostLocation || locationToCenter, icon: "https://i.ibb.co/gSnJ70P/location.png", //load image from web; not work with local image size: [32, 32], }, diff --git a/packages/components/socialFeed/Map/Map.types.ts b/packages/components/socialFeed/Map/Map.types.ts index a1e9efb197..1fe3831f3e 100644 --- a/packages/components/socialFeed/Map/Map.types.ts +++ b/packages/components/socialFeed/Map/Map.types.ts @@ -1,11 +1,10 @@ import { StyleProp, ViewStyle } from "react-native"; -import { LatLng } from "react-native-leaflet-view"; -import { PostCategory } from "@/utils/types/feed"; +import { CustomLatLngExpression, PostCategory } from "@/utils/types/feed"; export interface MapProps { - locationSelected?: LatLng; // When the user is adding a location to a post he's creating - locationToCenter?: LatLng; // When the user want to see a post's position on the Map (From Jungle Feed for example) - postCategory?: PostCategory; + creatingPostLocation?: CustomLatLngExpression; // When the user is adding a location to a post he's creating + creatingPostCategory?: PostCategory; // When the user is adding a location to a post he's creating + locationToCenter?: CustomLatLngExpression; // When the user want to see a post's position on the Map (From Jungle Feed for example) style?: StyleProp; } diff --git a/packages/components/socialFeed/Map/Map.web.tsx b/packages/components/socialFeed/Map/Map.web.tsx index 1d2f5d5cb1..6c3a9f043b 100644 --- a/packages/components/socialFeed/Map/Map.web.tsx +++ b/packages/components/socialFeed/Map/Map.web.tsx @@ -1,12 +1,6 @@ import "./styles.css"; import "leaflet/dist/leaflet.css"; -import { - DivIcon, - LatLngBounds, - LatLngExpression, - point, - PointExpression, -} from "leaflet"; +import { DivIcon, LatLngBounds, point, PointExpression } from "leaflet"; import { FC, useEffect, useMemo, useState } from "react"; import { MapContainer, @@ -19,7 +13,6 @@ import { import MarkerClusterGroup from "react-leaflet-cluster"; import { HeatmapLayer } from "react-leaflet-heatmap-layer-v3/lib"; import { View } from "react-native"; -import { LatLng } from "react-native-leaflet-view"; import { Post } from "@/api/feed/v1/feed"; import { MapProps } from "@/components/socialFeed/Map/Map.types"; @@ -33,19 +26,24 @@ import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; import { DEFAULT_MAP_POSITION, getMapPostIconColorRgba, - getMapPostIconSVGString, MAP_LAYER_URL + getMapPostIconSVGString, + MAP_LAYER_URL, } from "@/utils/feed/map"; import { zodTryParseJSON } from "@/utils/sanitize"; -import { PostCategory, ZodSocialFeedPostMetadata } from "@/utils/types/feed"; +import { + CustomLatLngExpression, + PostCategory, + ZodSocialFeedPostMetadata, +} from "@/utils/types/feed"; interface MarkerPopup { - position: LatLngExpression; + position: CustomLatLngExpression; post: Post; fileURL?: string; } // Custom map post icon -const getIcon = (postCategory: PostCategory) => { +const postIcon = (postCategory: PostCategory) => { const size = 32; const borderWidth = 1; const sizeWithBorders = 32 + borderWidth * 2; @@ -58,8 +56,8 @@ const getIcon = (postCategory: PostCategory) => { }); }; -// Custom cluster icon TODO: Make better style respecting the Figma -const createClusterCustomIcon = function (cluster: any): DivIcon { +// Custom cluster icon +const clusterIcon = function (cluster: any) { return new DivIcon({ html: `
${cluster.getChildCount()}
`, className: "custom-marker-cluster", @@ -68,33 +66,21 @@ const createClusterCustomIcon = function (cluster: any): DivIcon { }; export const Map: FC = ({ - // FIXME: locationSelected not updated when selecting an address from AddressSearch - locationSelected, locationToCenter = DEFAULT_MAP_POSITION, style, - postCategory = -1, + creatingPostLocation, + creatingPostCategory = -1, }) => { const selectedNetworkId = useSelectedNetworkId(); const [bounds, setBounds] = useState(null); // Prevent infinite rendering after locationSelected update - const [localLocationSelected, setLocalLocationSelected] = - useState(locationSelected); + const [localLocationSelected, setLocalLocationSelected] = useState< + CustomLatLngExpression | undefined + >(creatingPostLocation); useEffect(() => { - setLocalLocationSelected(locationSelected); - }, [locationSelected]); - - const customIcon = useMemo( - () => - new DivIcon({ - html: `
${getMapPostIconSVGString(postCategory)}
`, - className: "", - iconSize: [34, 34], - }), - [postCategory], - ); + setLocalLocationSelected(creatingPostLocation); + }, [creatingPostLocation]); // ---- Fetch existing posts that have a location and display them as markers const { data } = useFetchFeedLocation({ @@ -174,21 +160,12 @@ export const Map: FC = ({ ]} > {/*----Loads and displays tiles on the map*/} - - - {/*---- When the user creates a post and want to add a location to it*/} - {locationSelected && ( - - )} + {/*---- Heatmap displayed when dezoom*/} = ({ /> {/*---- Existing posts that have a location*/} - - {/* Mapping through the markers */} + + {/* When the user is creating a post*/} + {creatingPostLocation && ( + + )} + {/* Mapping through the markers (Fetched posts) */} {markers?.map((marker, index) => ( {marker.post.category === PostCategory.Normal ? ( diff --git a/packages/components/socialFeed/Map/MapPosts/MusicMapPost.tsx b/packages/components/socialFeed/Map/MapPosts/MusicMapPost.tsx index b28ffd0ea7..bcc6ab8179 100644 --- a/packages/components/socialFeed/Map/MapPosts/MusicMapPost.tsx +++ b/packages/components/socialFeed/Map/MapPosts/MusicMapPost.tsx @@ -13,6 +13,7 @@ import { errorColor, neutralFF, withAlpha } from "@/utils/style/colors"; import { fontSemibold10 } from "@/utils/style/fonts"; import { zodSocialFeedCommonMetadata, + ZodSocialFeedPostMetadata, ZodSocialFeedTrackMetadata, } from "@/utils/types/feed"; import { Media } from "@/utils/types/mediaPlayer"; @@ -21,20 +22,36 @@ export const MusicMapPost: FC<{ post: Post; }> = ({ post }) => { const { current: id } = useRef(uuidv4()); - const track = zodTryParseJSON(ZodSocialFeedTrackMetadata, post.metadata); - + const musicAudioNotePostMetadata = zodTryParseJSON( + ZodSocialFeedTrackMetadata, + post.metadata, + ); + const musicPostMetadata = zodTryParseJSON( + ZodSocialFeedPostMetadata, + post.metadata, + ); const baseMetadata = zodTryParseJSON( zodSocialFeedCommonMetadata, post.metadata, ); const title = baseMetadata?.title || "Music from Social Feed"; - const mediaToPlay: Media | undefined = track && { - id, - fileUrl: track.audioFile.url, - duration: track.audioFile.audioMetadata?.duration, - postId: post.id, - }; + // MusicAudio and Music have different metadata but have the same render on the map, so we handle these 2 cases + const mediaToPlay: Media | undefined = musicAudioNotePostMetadata + ? { + id, + fileUrl: musicAudioNotePostMetadata.audioFile.url, + duration: musicAudioNotePostMetadata.audioFile.audioMetadata?.duration, + postId: post.id, + } + : musicPostMetadata?.files + ? { + id, + fileUrl: musicPostMetadata.files[0].url, + duration: musicPostMetadata.files[0].audioMetadata?.duration, + postId: post.id, + } + : undefined; return ( diff --git a/packages/components/socialFeed/Map/MapPosts/VideoMapPost.tsx b/packages/components/socialFeed/Map/MapPosts/VideoMapPost.tsx index 8526f7e444..a32e336945 100644 --- a/packages/components/socialFeed/Map/MapPosts/VideoMapPost.tsx +++ b/packages/components/socialFeed/Map/MapPosts/VideoMapPost.tsx @@ -15,7 +15,9 @@ import { zodTryParseJSON } from "@/utils/sanitize"; import { errorColor, neutralFF, withAlpha } from "@/utils/style/colors"; import { fontSemibold10 } from "@/utils/style/fonts"; import { + SocialFeedVideoMetadata, zodSocialFeedCommonMetadata, + ZodSocialFeedPostMetadata, ZodSocialFeedVideoMetadata, } from "@/utils/types/feed"; import { Media } from "@/utils/types/mediaPlayer"; @@ -25,19 +27,47 @@ export const VideoMapPost: FC<{ }> = ({ post }) => { const { media } = useMediaPlayer(); const { current: id } = useRef(uuidv4()); - const video = zodTryParseJSON(ZodSocialFeedVideoMetadata, post.metadata); + const videoPostMetadata = zodTryParseJSON( + ZodSocialFeedVideoMetadata, + post.metadata, + ); + const videoNotePostMetadata = zodTryParseJSON( + ZodSocialFeedPostMetadata, + post.metadata, + ); const baseMetadata = zodTryParseJSON( zodSocialFeedCommonMetadata, post.metadata, ); const title = baseMetadata?.title || "Music from Social Feed"; - const mediaToPlay: Media | undefined = video && { - id, - fileUrl: video.videoFile.url, - duration: video.videoFile.videoMetadata?.duration, // FIXME: Known issue: Always 0. So, for videos, duration is set by playbackStatus, so it's 0 on the timer since the video is not started once - postId: post.id, - }; + // Video and VideoNote have different metadata but have the same render on the map, so we handle these 2 cases + const mediaToPlay: Media | undefined = videoPostMetadata + ? { + id, + fileUrl: videoPostMetadata.videoFile.url, + duration: videoPostMetadata.videoFile.videoMetadata?.duration, // FIXME: Known issue: Always 0. So, for videos, duration is set by playbackStatus, so it's 0 on the timer since the video is not started once + postId: post.id, + } + : videoNotePostMetadata?.files + ? { + id, + fileUrl: videoNotePostMetadata.files[0].url, + duration: videoNotePostMetadata.files[0].videoMetadata?.duration, + postId: post.id, + } + : undefined; + const videoMetadata: SocialFeedVideoMetadata | undefined = videoPostMetadata + ? { + title: "Video from Social Feed", + videoFile: videoPostMetadata.videoFile, + } + : videoNotePostMetadata?.files + ? { + title: "Video from Social Feed", + videoFile: videoNotePostMetadata.files[0], + } + : undefined; return ( @@ -48,34 +78,29 @@ export const VideoMapPost: FC<{ - {mediaToPlay ? ( - + {mediaToPlay && videoMetadata ? ( + <> + + + + ) : ( No media to play )} - - - {!video?.videoFile ? ( - - Video not found - - ) : ( - - )}
); diff --git a/packages/components/socialFeed/NewsFeed/NewsFeedInput.tsx b/packages/components/socialFeed/NewsFeed/NewsFeedInput.tsx index eedbab0e70..058fd19cd7 100644 --- a/packages/components/socialFeed/NewsFeed/NewsFeedInput.tsx +++ b/packages/components/socialFeed/NewsFeed/NewsFeedInput.tsx @@ -7,7 +7,6 @@ import { ViewStyle, useWindowDimensions, } from "react-native"; -import { LatLng } from "react-native-leaflet-view"; import Animated, { useSharedValue } from "react-native-reanimated"; import { useSelector } from "react-redux"; @@ -80,7 +79,11 @@ import { } from "@/utils/style/fonts"; import { RESPONSIVE_BREAKPOINT_S, layout } from "@/utils/style/layout"; import { replaceBetweenString } from "@/utils/text"; -import { NewPostFormValues, ReplyToType } from "@/utils/types/feed"; +import { + CustomLatLngExpression, + NewPostFormValues, + ReplyToType, +} from "@/utils/types/feed"; import { LocalFileData, RemoteFileData } from "@/utils/types/files"; interface NewsFeedInputProps { @@ -146,7 +149,9 @@ export const NewsFeedInput = React.forwardRef< const [isProgressBarShown, setIsProgressBarShown] = useState(false); const [premium, setPremium] = useState(false); const [isMapShown, setIsMapShown] = useState(false); - const [location, setLocation] = useState(); + const [location, setLocation] = useState< + CustomLatLngExpression | undefined + >(); const [developerMode] = useDeveloperMode(); const { setValue, handleSubmit, reset, watch } = useForm( diff --git a/packages/components/socialFeed/SocialCard/SocialCardHeader.tsx b/packages/components/socialFeed/SocialCard/SocialCardHeader.tsx index b5a381b1e1..97418b296f 100644 --- a/packages/components/socialFeed/SocialCard/SocialCardHeader.tsx +++ b/packages/components/socialFeed/SocialCard/SocialCardHeader.tsx @@ -1,6 +1,5 @@ import React, { FC } from "react"; import { useWindowDimensions, View } from "react-native"; -import { LatLng } from "react-native-leaflet-view"; import { DateTime } from "./DateTime"; import { OmniLink } from "../../OmniLink"; @@ -12,17 +11,19 @@ import { SpacerRow } from "../../spacer"; import { LocationButton } from "@/components/socialFeed/NewsFeed/LocationButton"; import { UserDisplayName } from "@/components/user/UserDisplayName"; import { Username } from "@/components/user/Username"; +import { locationToString } from "@/utils/feed/map"; import { useAppNavigation } from "@/utils/navigation"; import { neutral77, neutralFF } from "@/utils/style/colors"; import { fontSemibold14 } from "@/utils/style/fonts"; import { layout, RESPONSIVE_BREAKPOINT_S } from "@/utils/style/layout"; +import { CustomLatLngExpression } from "@/utils/types/feed"; // ====== Handle author image and username, date export const SocialCardHeader: FC<{ authorId: string; createdAt?: number; isWrapped?: boolean; - postLocation?: LatLng; + postLocation?: CustomLatLngExpression; }> = ({ authorId, createdAt, isWrapped, postLocation }) => { const { width } = useWindowDimensions(); const navigation = useAppNavigation(); @@ -106,7 +107,7 @@ export const SocialCardHeader: FC<{ onPress={() => navigation.navigate("Feed", { tab: "map", - locationToCenter: postLocation, + center: locationToString(postLocation), }) } stroke={neutralFF} diff --git a/packages/components/socialFeed/SocialCard/cards/SocialThreadCard.tsx b/packages/components/socialFeed/SocialCard/cards/SocialThreadCard.tsx index c4c4496c55..d47b538e41 100644 --- a/packages/components/socialFeed/SocialCard/cards/SocialThreadCard.tsx +++ b/packages/components/socialFeed/SocialCard/cards/SocialThreadCard.tsx @@ -1,6 +1,5 @@ import React, { memo, useEffect, useState } from "react"; import { StyleProp, ViewStyle } from "react-native"; -import { LatLng } from "react-native-leaflet-view"; import FlexRow from "../../../FlexRow"; import { CustomPressable } from "../../../buttons/CustomPressable"; @@ -58,10 +57,10 @@ export const SocialThreadCard: React.FC<{ const [, authorAddress] = parseUserId(localPost.authorId); const navigation = useAppNavigation(); const replyTo = authorNSInfo?.metadata?.tokenId || authorAddress; - const postLocation: LatLng = zodTryParseJSON( + const postMetadata = zodTryParseJSON( ZodSocialFeedPostMetadata, post.metadata, - )?.location; + ); const handleReply = () => onPressReply?.({ @@ -104,7 +103,7 @@ export const SocialThreadCard: React.FC<{ diff --git a/packages/components/socialFeed/modals/MapModal/AddressSearch.tsx b/packages/components/socialFeed/modals/MapModal/AddressSearch.tsx index 04aaa52ab6..41fc1ad4c9 100644 --- a/packages/components/socialFeed/modals/MapModal/AddressSearch.tsx +++ b/packages/components/socialFeed/modals/MapModal/AddressSearch.tsx @@ -1,5 +1,5 @@ import { useQuery } from "@tanstack/react-query"; -import { Dispatch, SetStateAction, useMemo } from "react"; +import { Dispatch, FC, SetStateAction, useMemo } from "react"; import { ActivityIndicator, ScrollView, @@ -7,7 +7,6 @@ import { View, ViewStyle, } from "react-native"; -import { LatLng } from "react-native-leaflet-view"; import { z } from "zod"; import { BrandText } from "../../../BrandText"; @@ -20,6 +19,7 @@ import { useDebounce } from "@/hooks/useDebounce"; import { neutral77, neutralFF, secondaryColor } from "@/utils/style/colors"; import { fontSemibold13 } from "@/utils/style/fonts"; import { layout } from "@/utils/style/layout"; +import { CustomLatLngExpression } from "@/utils/types/feed"; const zodAddressSearchResult = z.array( z.object({ @@ -33,11 +33,13 @@ interface AddressSearchProps { addressPlaceHolder: string; address: string; setAddress: Dispatch>; - setLocationSelected: Dispatch>; + setLocationSelected: Dispatch< + SetStateAction + >; setAddressPlaceHolder: Dispatch>; } -export const AddressSearch: React.FC = ({ +export const AddressSearch: FC = ({ addressPlaceHolder, address, setAddress, diff --git a/packages/components/socialFeed/modals/MapModal/MapModal.tsx b/packages/components/socialFeed/modals/MapModal/MapModal.tsx index 816c6359eb..140dd0f56d 100644 --- a/packages/components/socialFeed/modals/MapModal/MapModal.tsx +++ b/packages/components/socialFeed/modals/MapModal/MapModal.tsx @@ -1,6 +1,5 @@ import React, { Suspense, useState, Dispatch, SetStateAction } from "react"; import { View } from "react-native"; -import { LatLng } from "react-native-leaflet-view"; import { PrimaryButton } from "../../../buttons/PrimaryButton"; import { SecondaryButtonOutline } from "../../../buttons/SecondaryButtonOutline"; @@ -16,16 +15,16 @@ import { transparentColor, } from "@/utils/style/colors"; import { layout } from "@/utils/style/layout"; -import { PostCategory } from "@/utils/types/feed"; +import { CustomLatLngExpression, PostCategory } from "@/utils/types/feed"; interface TMapModalProps { visible: boolean; onClose: () => void; - setLocation: Dispatch>; + setLocation: Dispatch>; // TODO: Description ? // description: string; // setDescription: (newDescription: string) => void; - location?: LatLng; + location?: CustomLatLngExpression; postCategory?: PostCategory; } @@ -38,9 +37,9 @@ export const MapModal: React.FC = ({ }) => { const [addressPlaceHolder, setAddressPlaceHolder] = useState("Address"); const [address, setAddress] = useState(""); - const [locationSelected, setLocationSelected] = useState( - location, - ); + const [creatingPostLocation, setCreatingPostLocation] = useState< + CustomLatLngExpression | undefined + >(location); return ( = ({ setAddressPlaceHolder={setAddressPlaceHolder} address={address} setAddress={setAddress} - setLocationSelected={setLocationSelected} + setLocationSelected={setCreatingPostLocation} /> @@ -88,8 +87,8 @@ export const MapModal: React.FC = ({ > }> @@ -118,7 +117,7 @@ export const MapModal: React.FC = ({ borderColor={transparentColor} text="Remove Location" onPress={() => { - setLocationSelected(undefined); + setCreatingPostLocation(undefined); setLocation(undefined); onClose(); }} @@ -134,7 +133,7 @@ export const MapModal: React.FC = ({ size="M" text={location ? "Update Location" : "Add Location"} onPress={() => { - setLocation(locationSelected); + setLocation(creatingPostLocation); onClose(); }} /> diff --git a/packages/components/video/UploadVideoModal.tsx b/packages/components/video/UploadVideoModal.tsx index ce3df717fd..028502b5be 100644 --- a/packages/components/video/UploadVideoModal.tsx +++ b/packages/components/video/UploadVideoModal.tsx @@ -9,7 +9,6 @@ import { ViewStyle, } from "react-native"; import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scrollview"; -import { LatLng } from "react-native-leaflet-view"; import { useSelector } from "react-redux"; import Img from "../../../assets/icons/img.svg"; @@ -33,7 +32,11 @@ import { } from "../../utils/style/colors"; import { fontSemibold14 } from "../../utils/style/fonts"; import { layout } from "../../utils/style/layout"; -import { PostCategory, SocialFeedVideoMetadata } from "../../utils/types/feed"; +import { + CustomLatLngExpression, + PostCategory, + SocialFeedVideoMetadata, +} from "../../utils/types/feed"; import { LocalFileData } from "../../utils/types/files"; import { BrandText } from "../BrandText"; import { DeleteButton } from "../FilePreview/DeleteButton"; @@ -64,9 +67,11 @@ export const UploadVideoModal: FC<{ }> = ({ onClose, isVisible }) => { const [developerMode] = useDeveloperMode(); const [isMapShown, setIsMapShown] = useState(false); - const [location, setLocation] = useState(); + const [location, setLocation] = useState< + CustomLatLngExpression | undefined + >(); - const { setToastError } = useFeedbacks(); + const { setToast } = useFeedbacks(); const selectedNetwork = useSelectedNetworkInfo(); const selectedWallet = useSelectedWallet(); const userId = selectedWallet?.userId || ""; @@ -119,9 +124,11 @@ export const UploadVideoModal: FC<{ console.error("post submit err", err); setIsUploadLoading(false); setIsProgressBarShown(false); - setToastError({ + setToast({ title: "Post creation failed", message: err instanceof Error ? err.message : `${err}`, + mode: "normal", + type: "success", }); } }; @@ -156,9 +163,11 @@ export const UploadVideoModal: FC<{ userIPFSKey || (await generateIpfsKey(selectedNetwork?.id || "", userId)); if (!pinataJWTKey) { console.error("upload file err : No Pinata JWT"); - setToastError({ + setToast({ title: "File upload failed", message: "No Pinata JWT", + mode: "normal", + type: "success", }); setIsUploadLoading(false); return; @@ -177,9 +186,11 @@ export const UploadVideoModal: FC<{ }); if (!uploadedFiles.find((file) => file.url)) { console.error("upload file err : Fail to pin to IPFS"); - setToastError({ + setToast({ title: "File upload failed", message: "Fail to pin to IPFS, please try to Publish again", + mode: "normal", + type: "success", }); setIsUploadLoading(false); return; diff --git a/packages/components/video/VideoCard.tsx b/packages/components/video/VideoCard.tsx index 06556d100a..0f119fde75 100644 --- a/packages/components/video/VideoCard.tsx +++ b/packages/components/video/VideoCard.tsx @@ -41,6 +41,7 @@ import { SpacerColumn, SpacerRow } from "../spacer"; import { LocationButton } from "@/components/socialFeed/NewsFeed/LocationButton"; import { useAppNavigation } from "@/hooks/navigation/useAppNavigation"; +import { locationToString } from "@/utils/feed/map"; const IMAGE_HEIGHT = 173; const VIDEO_CARD_WIDTH = 261; @@ -126,9 +127,10 @@ export const VideoCard: React.FC<{ + video?.location && navigation.navigate("Feed", { tab: "map", - locationToCenter: video.location, + center: locationToString(video.location), }) } stroke={neutralFF} diff --git a/packages/screens/Feed/FeedScreen.tsx b/packages/screens/Feed/FeedScreen.tsx index 1ddf1e9985..8f91345896 100644 --- a/packages/screens/Feed/FeedScreen.tsx +++ b/packages/screens/Feed/FeedScreen.tsx @@ -18,6 +18,7 @@ import { useForceNetworkSelection } from "@/hooks/useForceNetworkSelection"; import { useIsMobile } from "@/hooks/useIsMobile"; import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; import { NetworkFeature } from "@/networks"; +import { stringToLocation } from "@/utils/feed/map"; import { ScreenFC } from "@/utils/navigation"; export const FeedScreen: ScreenFC<"Feed"> = ({ route: { params } }) => { @@ -36,7 +37,11 @@ export const FeedScreen: ScreenFC<"Feed"> = ({ route: { params } }) => { return ; case "map": return developerMode ? ( - + ) : null; case "pics": return ; @@ -64,7 +69,7 @@ export const FeedScreen: ScreenFC<"Feed"> = ({ route: { params } }) => { } }, [ params?.tab, - params?.locationToCenter, + params?.center, isMobile, developerMode, defaultFeedRequest, diff --git a/packages/screens/Feed/components/MapFeed.tsx b/packages/screens/Feed/components/MapFeed.tsx index a354e52d79..f697062172 100644 --- a/packages/screens/Feed/components/MapFeed.tsx +++ b/packages/screens/Feed/components/MapFeed.tsx @@ -1,6 +1,5 @@ import React, { FC, Suspense } from "react"; import { ScrollView, useWindowDimensions } from "react-native"; -import { LatLng } from "react-native-leaflet-view"; import { FeedHeader } from "./FeedHeader"; @@ -13,9 +12,10 @@ import { RESPONSIVE_BREAKPOINT_S, screenContentMaxWidth, } from "@/utils/style/layout"; +import { CustomLatLngExpression } from "@/utils/types/feed"; export const MapFeed: FC<{ - locationToCenter?: LatLng; + locationToCenter?: CustomLatLngExpression; }> = ({ locationToCenter }) => { const { height: windowHeight, width: windowWidth } = useWindowDimensions(); const { width, height } = useMaxResolution(); diff --git a/packages/screens/FeedNewArticle/FeedNewArticleScreen.tsx b/packages/screens/FeedNewArticle/FeedNewArticleScreen.tsx index 002089352d..df55712b6f 100644 --- a/packages/screens/FeedNewArticle/FeedNewArticleScreen.tsx +++ b/packages/screens/FeedNewArticle/FeedNewArticleScreen.tsx @@ -1,7 +1,6 @@ import React, { useEffect, useRef, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { ScrollView, View } from "react-native"; -import { LatLng } from "react-native-leaflet-view"; import { useSelector } from "react-redux"; import priceSVG from "../../../assets/icons/price.svg"; @@ -11,7 +10,7 @@ import { BrandText } from "@/components/BrandText"; import { SVG } from "@/components/SVG"; import { ScreenContainer } from "@/components/ScreenContainer"; import { WalletStatusBox } from "@/components/WalletStatusBox"; -import { LegacyTertiaryBox } from "@/components/boxes/LegacyTertiaryBox"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; import { FileUploader } from "@/components/fileUploader"; import { Label, TextInputCustom } from "@/components/inputs/TextInputCustom"; import { FeedPostingProgressBar } from "@/components/loaders/FeedPostingProgressBar"; @@ -47,7 +46,11 @@ import { import { fontSemibold13, fontSemibold20 } from "@/utils/style/fonts"; import { layout, screenContentMaxWidth } from "@/utils/style/layout"; import { pluralOrNot } from "@/utils/text"; -import { NewArticleFormValues, PostCategory } from "@/utils/types/feed"; +import { + CustomLatLngExpression, + NewArticleFormValues, + PostCategory, +} from "@/utils/types/feed"; import { RemoteFileData } from "@/utils/types/files"; //TODO: In mobile : Make ActionsContainer accessible (floating button ?) @@ -76,7 +79,12 @@ export const FeedNewArticleScreen: ScreenFC<"FeedNewArticle"> = () => { setTimeout(() => { setIsUploadLoading(false); setIsProgressBarShown(false); - setToastSuccess({ title: "Post submitted successfully.", message: "" }); + setToast({ + mode: "normal", + type: "success", + title: "Post submitted successfully.", + message: "", + }); navigateBack(); reset(); }, 1000); @@ -85,10 +93,12 @@ export const FeedNewArticleScreen: ScreenFC<"FeedNewArticle"> = () => { const { showNotEnoughFundsModal, showConnectWalletModal } = useWalletControl(); const isLoading = isUploadLoading || isProcessing; - const { setToastSuccess, setToastError } = useFeedbacks(); + const { setToast } = useFeedbacks(); const navigation = useAppNavigation(); const scrollViewRef = useRef(null); - const [location, setLocation] = useState(); + const [location, setLocation] = useState< + CustomLatLngExpression | undefined + >(); const [isMapShown, setIsMapShown] = useState(false); const { control, @@ -170,7 +180,9 @@ export const FeedNewArticleScreen: ScreenFC<"FeedNewArticle"> = () => { // If the user uploaded files, but they are not pinned to IPFS, it returns files with empty url, so this is an error. if (formValues.files?.length && !remoteFiles.find((file) => file.url)) { console.error("upload file err : Fail to pin to IPFS"); - setToastError({ + setToast({ + mode: "normal", + type: "error", title: "File upload failed", message: "Fail to pin to IPFS, please try to Publish again", }); @@ -207,7 +219,9 @@ export const FeedNewArticleScreen: ScreenFC<"FeedNewArticle"> = () => { console.error("post submit error", err); setIsUploadLoading(false); setIsProgressBarShown(false); - setToastError({ + setToast({ + mode: "normal", + type: "error", title: "Something went wrong.", message: err instanceof Error ? err.message : `${err}`, }); @@ -255,9 +269,8 @@ export const FeedNewArticleScreen: ScreenFC<"FeedNewArticle"> = () => { - = () => { )} left` : `The cost for this Article is ${prettyPublishingFee}`} - + as string from svg files (These svg strings are used in FeedMapList.web.tsx) export const MAP_LAYER_URL = `https://{s}.tile.jawg.io/jawg-dark/{z}/{x}/{y}{r}.png?access-token=${process.env.EXPO_PUBLIC_LEAFLET_MAP_TOKEN}`; // Paris baguette -export const DEFAULT_MAP_POSITION = [48.8566, 2.3522]; +export const DEFAULT_MAP_POSITION: CustomLatLngExpression = [48.8566, 2.3522]; const musicPostSvgString = ` @@ -173,11 +178,13 @@ export const getMapPostIconSVG = ( postCategory: PostCategory, ): FunctionComponent => { switch (postCategory) { - case PostCategory.Audio || PostCategory.MusicAudio: + case PostCategory.Audio: + case PostCategory.MusicAudio: return musicPostSvg; case PostCategory.Picture: return picturePostSvg; - case PostCategory.Video || PostCategory.VideoNote: + case PostCategory.Video: + case PostCategory.VideoNote: return videoPostSvg; case PostCategory.Article: return articlePostSvg; @@ -190,11 +197,13 @@ export const getMapPostIconSVG = ( export const getMapPostIconSVGString = (postCategory: PostCategory) => { switch (postCategory) { - case PostCategory.Audio || PostCategory.MusicAudio: + case PostCategory.Audio: + case PostCategory.MusicAudio: return musicPostSvgString; case PostCategory.Picture: return picturePostSvgString; - case PostCategory.Video || PostCategory.VideoNote: + case PostCategory.Video: + case PostCategory.VideoNote: return videoPostSvgString; case PostCategory.Article: return articlePostSvgString; @@ -207,11 +216,13 @@ export const getMapPostIconSVGString = (postCategory: PostCategory) => { export const getMapPostIconColorRgba = (postCategory: PostCategory) => { switch (postCategory) { - case PostCategory.Audio || PostCategory.MusicAudio: + case PostCategory.Audio: + case PostCategory.MusicAudio: return "255,147,147,.40"; case PostCategory.Picture: return "136,147,255,.40"; - case PostCategory.Video || PostCategory.VideoNote: + case PostCategory.Video: + case PostCategory.VideoNote: return "198,171,255,.40"; case PostCategory.Article: return "255,252,207,.40"; @@ -224,11 +235,13 @@ export const getMapPostIconColorRgba = (postCategory: PostCategory) => { export const getMapPostTextGradientType = (postCategory: PostCategory) => { switch (postCategory) { - case PostCategory.Audio || PostCategory.MusicAudio: + case PostCategory.Audio: + case PostCategory.MusicAudio: return "feed-map-music-post"; case PostCategory.Picture: return "feed-map-picture-post"; - case PostCategory.Video || PostCategory.VideoNote: + case PostCategory.Video: + case PostCategory.VideoNote: return "feed-map-video-post"; case PostCategory.Article: return "feed-map-article-post"; @@ -246,13 +259,15 @@ export const getMapPostTextGradient = (postCategory: PostCategory) => { end: { x: 0, y: 0 }, }; switch (postCategory) { - case PostCategory.Audio || PostCategory.MusicAudio: + case PostCategory.Audio: + case PostCategory.MusicAudio: gradientProps.colors = ["#FF9393", "#FF5C5C"]; break; case PostCategory.Picture: gradientProps.colors = ["#88DCFF", "#16BBFF"]; break; - case PostCategory.Video || PostCategory.VideoNote: + case PostCategory.Video: + case PostCategory.VideoNote: gradientProps.colors = ["#C6ABFF", "#A57AFF"]; break; case PostCategory.Article: @@ -270,11 +285,13 @@ export const getMapPostTextGradient = (postCategory: PostCategory) => { export const getMapPostTextGradientString = (postCategory: PostCategory) => { switch (postCategory) { - case PostCategory.Audio || PostCategory.MusicAudio: + case PostCategory.Audio: + case PostCategory.MusicAudio: return `180deg, #FF9393 100%, #FF5C5C 100%`; case PostCategory.Picture: return `180deg, #88DCFF 100%, #16BBFF 100%`; - case PostCategory.Video || PostCategory.VideoNote: + case PostCategory.Video: + case PostCategory.VideoNote: return `180deg, #C6ABFF 100%, #A57AFF 100%`; case PostCategory.Article: return `180deg, #FFFC6B 100%, #E5E13B 100%`; @@ -284,3 +301,26 @@ export const getMapPostTextGradientString = (postCategory: PostCategory) => { return `180deg, #FFFFFF 100%, #AAAAAA 100%`; } }; + +const STRING_LOCATION_SEPARATOR = "-"; +export const locationToString: ( + location: CustomLatLngExpression, +) => string | undefined = (location: CustomLatLngExpression) => { + const zodLatLngLiteralParseResult = ZodLatLngLiteral.safeParse(location); + const zodLatLngTupleParseResult = ZodLatLngTuple.safeParse(location); + + if (zodLatLngLiteralParseResult.success) { + return `${zodLatLngLiteralParseResult.data.lat}${STRING_LOCATION_SEPARATOR}${zodLatLngLiteralParseResult.data.lng}`; + } + if (zodLatLngTupleParseResult.success) { + return `${zodLatLngTupleParseResult.data[0]}${STRING_LOCATION_SEPARATOR}${zodLatLngTupleParseResult.data[1]}`; + } +}; +export const stringToLocation: ( + locationStr: string, +) => CustomLatLngExpression | undefined = (locationStr: string) => { + const parts = locationStr.split(STRING_LOCATION_SEPARATOR).map(Number); + if ((parts.length === 2 || parts.length === 3) && !parts.some(isNaN)) { + return [parts[0], parts[1]]; + } +}; diff --git a/packages/utils/feed/queries.ts b/packages/utils/feed/queries.ts index acd5042fa0..2bd75f9eb8 100644 --- a/packages/utils/feed/queries.ts +++ b/packages/utils/feed/queries.ts @@ -1,5 +1,3 @@ -import { LatLng } from "react-native-leaflet-view"; - import { nonSigningSocialFeedClient, signingSocialFeedClient, @@ -12,6 +10,7 @@ import { parseUserId, } from "../../networks"; import { + CustomLatLngExpression, NewArticleFormValues, NewPostFormValues, PostCategory, @@ -121,7 +120,7 @@ export const getPostCategory = ({ interface GeneratePostMetadataParams extends Omit { files: RemoteFileData[]; premium: boolean; - location?: LatLng; + location?: CustomLatLngExpression; } interface GenerateArticleMetadataParams diff --git a/packages/utils/navigation.ts b/packages/utils/navigation.ts index f9f1b25ded..fb922d1930 100644 --- a/packages/utils/navigation.ts +++ b/packages/utils/navigation.ts @@ -1,7 +1,6 @@ import { RouteProp, useNavigation } from "@react-navigation/native"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import React from "react"; -import { LatLng } from "react-native-leaflet-view"; import { feedsTabItems } from "./social-feed"; import { AppMode } from "./types/app-mode"; @@ -52,7 +51,7 @@ export type RootStackParamList = { Feed?: { tab: keyof typeof feedsTabItems; network?: string; - locationToCenter?: LatLng; + center?: string; }; FeedNewArticle: | (NewPostFormValues & { diff --git a/packages/utils/types/feed.ts b/packages/utils/types/feed.ts index 26bf67eac4..b80f2d679b 100644 --- a/packages/utils/types/feed.ts +++ b/packages/utils/types/feed.ts @@ -32,7 +32,7 @@ export interface NewArticleFormValues { thumbnailImage?: LocalFileData; coverImage?: LocalFileData; shortDescription?: string; - location?: LatLngExpression; + location?: CustomLatLngExpression; } export interface NewPostFormValues { @@ -73,7 +73,7 @@ export const ZodLatLngTuple = z.tuple([ ]); export const ZodLatLngExpression = z.union([ZodLatLngLiteral, ZodLatLngTuple]); -export type LatLngExpression = z.infer; +export type CustomLatLngExpression = z.infer; export const zodSocialFeedCommonMetadata = z.object({ title: z.string(),