-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
lightbox mobile #2460
lightbox mobile #2460
Changes from 6 commits
94b8b42
78bf03f
e5a6ebc
040c2a2
8cfde56
b66c6ed
582c660
2d2e0f5
0c777ba
7dc7cfe
de569b9
e71a0bd
20880ea
19717a5
61d3832
8cd4737
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import Svg, { Path, SvgProps } from 'react-native-svg'; | ||
|
||
export function CloseIcon({ ...props }: SvgProps) { | ||
return ( | ||
<Svg width="18" height="18" fill="none" viewBox="0 0 16 16" {...props}> | ||
<Path d="M12.6663 3.33398L3.33301 12.6673" stroke="#F9F9F9" stroke-miterlimit="10" /> | ||
<Path d="M3.33301 3.33398L12.6663 12.6673" stroke="#F9F9F9" stroke-miterlimit="10" /> | ||
</Svg> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { useColorScheme } from 'nativewind'; | ||
import React from 'react'; | ||
import Svg, { Path, SvgProps } from 'react-native-svg'; | ||
|
||
import colors from '~/shared/theme/colors'; | ||
|
||
export function MaximizeIcon({ ...props }: SvgProps) { | ||
const { colorScheme } = useColorScheme(); | ||
const strokeColor = colorScheme === 'dark' ? colors.white : colors.black['800']; | ||
|
||
return ( | ||
<Svg width="16" height="16" viewBox="0 0 16 16" fill="none" {...props}> | ||
<Path | ||
d="M13.9997 6.66667V2H9.33301" | ||
stroke={strokeColor} | ||
stroke-width="0.666667" | ||
stroke-miterlimit="10" | ||
/> | ||
<Path | ||
d="M13.9997 2L9.33301 6.66667" | ||
stroke={strokeColor} | ||
stroke-width="0.666667" | ||
stroke-miterlimit="10" | ||
/> | ||
<Path | ||
d="M2 9.33398V14.0007H6.66667" | ||
stroke={strokeColor} | ||
stroke-width="0.666667" | ||
stroke-miterlimit="10" | ||
/> | ||
<Path | ||
d="M2 13.9993L6.33333 9.66602" | ||
stroke={strokeColor} | ||
stroke-width="0.666667" | ||
stroke-miterlimit="10" | ||
/> | ||
</Svg> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,18 @@ | ||
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; | ||
import { useColorScheme } from 'nativewind'; | ||
import { useCallback, useMemo } from 'react'; | ||
import { ScrollView, View } from 'react-native'; | ||
import { useCallback, useMemo, useRef, useState } from 'react'; | ||
import { Dimensions, ScrollView, View } from 'react-native'; | ||
import FastImage from 'react-native-fast-image'; | ||
import Lightbox from 'react-native-lightbox-v2'; | ||
import { graphql, useFragment } from 'react-relay'; | ||
import { useNavigateToCommunityScreen } from 'src/hooks/useNavigateToCommunityScreen'; | ||
import { useToggleTokenAdmire } from 'src/hooks/useToggleTokenAdmire'; | ||
import { BookmarkIcon } from 'src/icons/BookmarkIcon'; | ||
import { CloseIcon } from 'src/icons/CloseIcon'; | ||
import { MaximizeIcon } from 'src/icons/MaximizeIcon'; | ||
import { PoapIcon } from 'src/icons/PoapIcon'; | ||
import { ShareIcon } from 'src/icons/ShareIcon'; | ||
import Zoom from 'react-native-zoom-reanimated'; | ||
|
||
import { BackButton } from '~/components/BackButton'; | ||
import { TokenFailureBoundary } from '~/components/Boundaries/TokenFailureBoundary/TokenFailureBoundary'; | ||
|
@@ -22,6 +26,7 @@ import { | |
CreatorProfilePictureAndUsernameOrAddress, | ||
OwnerProfilePictureAndUsername, | ||
} from '~/components/ProfilePicture/ProfilePictureAndUserOrAddress'; | ||
import { useSafeAreaPadding } from '~/components/SafeAreaViewWithPadding'; | ||
import { Typography } from '~/components/Typography'; | ||
import { NftDetailSectionQueryFragment$key } from '~/generated/NftDetailSectionQueryFragment.graphql'; | ||
import { PostIcon } from '~/navigation/MainTabNavigator/PostIcon'; | ||
|
@@ -41,6 +46,8 @@ type Props = { | |
queryRef: NftDetailSectionQueryFragment$key; | ||
}; | ||
|
||
const { width } = Dimensions.get('window'); | ||
|
||
export function NftDetailSection({ onShare, queryRef }: Props) { | ||
const route = useRoute<RouteProp<MainTabStackNavigatorParamList, 'NftDetail'>>(); | ||
|
||
|
@@ -96,6 +103,7 @@ export function NftDetailSection({ onShare, queryRef }: Props) { | |
); | ||
|
||
const { colorScheme } = useColorScheme(); | ||
const [isLightboxOpen, setIsLightboxOpen] = useState(false); | ||
|
||
const token = query.tokenById; | ||
const ownerWalletAddress = | ||
|
@@ -120,6 +128,8 @@ export function NftDetailSection({ onShare, queryRef }: Props) { | |
} | ||
}, [navigateToCommunity, tokenDefinition.community]); | ||
|
||
const { top } = useSafeAreaPadding(); | ||
|
||
const handleCreatePost = useCallback(() => { | ||
if (token.dbid) { | ||
navigation.navigate('PostComposer', { | ||
|
@@ -162,13 +172,70 @@ export function NftDetailSection({ onShare, queryRef }: Props) { | |
queryRef: query, | ||
}); | ||
|
||
const customHeader = useCallback( | ||
(close: () => void) => { | ||
return ( | ||
<View | ||
className="flex-row justify-end items-center px-3 bg-black-800" | ||
style={{ | ||
paddingTop: top, | ||
}} | ||
> | ||
<IconContainer | ||
color="faint" | ||
icon={<CloseIcon />} | ||
onPress={close} | ||
eventElementId={null} | ||
eventName={null} | ||
eventContext={null} | ||
/> | ||
</View> | ||
); | ||
}, | ||
[top] | ||
); | ||
|
||
const { contractName } = extractRelevantMetadataFromToken(token); | ||
|
||
const blueToDisplay = useMemo( | ||
() => (colorScheme === 'dark' ? 'darkModeBlue' : 'activeBlue'), | ||
[colorScheme] | ||
); | ||
|
||
const handleMaximizeToggle = () => { | ||
setIsLightboxOpen((currIsLightboxOpen) => !currIsLightboxOpen); | ||
}; | ||
|
||
const thumbnailRef = useRef<View | null>(null); | ||
const [thumbnailPosition, setThumbnailPosition] = useState({ | ||
width: width, | ||
height: width, | ||
x: 0, | ||
y: 0, | ||
}); | ||
|
||
const updateThumbnailPosition = () => { | ||
if (thumbnailRef.current) { | ||
thumbnailRef.current.measure((x, y, w, h, pageX, pageY) => { | ||
setThumbnailPosition({ | ||
width: w, | ||
height: h, | ||
x: pageX, | ||
y: pageY, | ||
}); | ||
}); | ||
} | ||
}; | ||
|
||
const handleOpenLightbox = () => { | ||
updateThumbnailPosition(); | ||
setIsLightboxOpen(true); | ||
}; | ||
|
||
const handleCloseLightbox = () => { | ||
setIsLightboxOpen(false); | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same for these |
||
|
||
return ( | ||
<ScrollView> | ||
<View className="flex flex-col space-y-3 px-4 pb-4"> | ||
|
@@ -188,14 +255,58 @@ export function NftDetailSection({ onShare, queryRef }: Props) { | |
/> | ||
</View> | ||
|
||
<View className="w-full mb-3"> | ||
<TokenFailureBoundary tokenRef={token} variant="large"> | ||
<NftDetailAssetCacheSwapper | ||
cachedPreviewAssetUrl={route.params.cachedPreviewAssetUrl} | ||
<View className="flex justify-between w-full mb-3"> | ||
<Lightbox | ||
isOpen={isLightboxOpen} | ||
onClose={handleCloseLightbox} | ||
onOpen={handleOpenLightbox} | ||
backgroundColor={colors.black['800']} | ||
swipeToDismiss={false} | ||
renderHeader={customHeader} | ||
doubleTapZoomEnabled={false} | ||
renderContent={() => ( | ||
<TokenFailureBoundary tokenRef={token} variant="large"> | ||
<NftDetailAssetCacheSwapper | ||
cachedPreviewAssetUrl={route.params.cachedPreviewAssetUrl} | ||
> | ||
<Zoom | ||
contentContainerStyle={{ | ||
display: 'flex', | ||
width: width, | ||
flexGrow: 1, | ||
backgroundColor: colors.black['800'], | ||
}} | ||
style={{ display: 'flex', flexGrow: 1 }} | ||
doubleTapConfig={{ | ||
minZoomScale: 1, | ||
}} | ||
> | ||
<NftDetailAsset tokenRef={token} /> | ||
</Zoom> | ||
</NftDetailAssetCacheSwapper> | ||
</TokenFailureBoundary> | ||
)} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's define this as a |
||
origin={{ | ||
x: thumbnailPosition.x, | ||
y: thumbnailPosition.y, | ||
width: thumbnailPosition.width, | ||
height: thumbnailPosition.height, | ||
}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
> | ||
<View | ||
ref={thumbnailRef} | ||
style={{ width: width * 0.92, minHeight: width * 0.92 }} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
onLayout={updateThumbnailPosition} | ||
> | ||
<NftDetailAsset tokenRef={token} /> | ||
</NftDetailAssetCacheSwapper> | ||
</TokenFailureBoundary> | ||
<TokenFailureBoundary tokenRef={token} variant="large"> | ||
<NftDetailAssetCacheSwapper | ||
cachedPreviewAssetUrl={route.params.cachedPreviewAssetUrl} | ||
> | ||
<NftDetailAsset tokenRef={token} /> | ||
</NftDetailAssetCacheSwapper> | ||
</TokenFailureBoundary> | ||
</View> | ||
</Lightbox> | ||
</View> | ||
</View> | ||
|
||
|
@@ -211,6 +322,14 @@ export function NftDetailSection({ onShare, queryRef }: Props) { | |
{tokenDefinition.name} | ||
</Typography> | ||
</View> | ||
<GalleryTouchableOpacity | ||
onPress={handleMaximizeToggle} | ||
eventElementId="NFT Detail Maximize Icon" | ||
eventName="NFT Detail Maximize Icon Pressed" | ||
eventContext={contexts['NFT Detail']} | ||
> | ||
<MaximizeIcon /> | ||
</GalleryTouchableOpacity> | ||
</View> | ||
<GalleryTouchableOpacity | ||
eventElementId="NFT Detail Contract Name Pill" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's make sure to wrap in
useCallback