Skip to content
Draft
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
8 changes: 8 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,10 @@ PODS:
- RCTTypeSafety
- React
- ReactCommon/turbomodule/core
- react-native-skia (0.1.193):
- React
- React-callinvoker
- React-Core
- react-native-static-server (0.5.0):
- GCDWebServer (~> 3.0)
- React
Expand Down Expand Up @@ -729,6 +733,7 @@ DEPENDENCIES:
- react-native-quick-crypto (from `../node_modules/react-native-quick-crypto`)
- react-native-randombytes (from `../node_modules/react-native-randombytes`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- "react-native-skia (from `../node_modules/@shopify/react-native-skia`)"
- react-native-static-server (from `../node_modules/react-native-static-server`)
- react-native-udp (from `../node_modules/react-native-udp`)
- react-native-webview (from `../node_modules/react-native-webview`)
Expand Down Expand Up @@ -900,6 +905,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-randombytes"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-skia:
:path: "../node_modules/@shopify/react-native-skia"
react-native-static-server:
:path: "../node_modules/react-native-static-server"
react-native-udp:
Expand Down Expand Up @@ -1055,6 +1062,7 @@ SPEC CHECKSUMS:
react-native-quick-crypto: 40402e0c2f3c2d7eaab42dc989c48cdc812b7abf
react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846
react-native-safe-area-context: f98b0b16d1546d208fc293b4661e3f81a895afd9
react-native-skia: e92aaf74c38d71feb315deed516a91d211f90553
react-native-static-server: 201b2a945a35096be3ae7f43e367c65bcbd61343
react-native-udp: 8864b1211857e9d8224ae5cbaf8580970fb99de1
react-native-webview: 203b6a1c7507737f777c91d7e10c30e7e67c1a17
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@react-navigation/native-stack": "^6.0.2",
"@reduxjs/toolkit": "^1.9.3",
"@sentry/react-native": "^4.12.0",
"@shopify/react-native-skia": "^0.1.193",
"@sinonjs/text-encoding": "^0.7.2",
"@tradle/react-native-http": "^2.0.1",
"@veramo/core": "^5.2.0",
Expand All @@ -66,8 +67,8 @@
"@verida/types": "^2.3.1",
"@verida/vda-did-resolver": "^2.3.5",
"@verida/vda-name-client": "^2.3.4",
"@verida/verifiable-credentials": "^2.3.4",
"@verida/vda-sbt-client": "^2.2.1",
"@verida/verifiable-credentials": "^2.3.4",
"@verida/wallet-utils": "^1.7.4",
"@walletconnect/client": "^1.7.7",
"@walletconnect/sign-client": "2.0.0-rc.3",
Expand Down Expand Up @@ -124,7 +125,7 @@
"promise": "^8.3.0",
"punycode": "^1.4.1",
"querystring-es3": "^0.2.1",
"react": "17.0.2",
"react": "^18.2.0",
"react-dom": "17.0.2",
"react-native": "0.68.5",
"react-native-animated-pagination-dots": "^0.1.73",
Expand Down Expand Up @@ -316,4 +317,4 @@
"vm": "vm-browserify",
"tls": false
}
}
}
289 changes: 256 additions & 33 deletions src/pages/Assets/Badges.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,278 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { useNavigation } from '@react-navigation/native'
import * as sentry from '@sentry/react-native'
import { useTheme } from 'contexts/ThemeContext'
import { getNFTImageUri } from 'helpers/nft'
import React, { useCallback, useEffect } from 'react'
import {
ListRenderItem,
RefreshControl,
StyleSheet,
TouchableOpacity,
View,
} from 'react-native'
import FastImage from 'react-native-fast-image'
import { useDispatch, useSelector } from 'react-redux'
import { VeridaWallet } from 'types/wallet'

import { NFT, NFTCollection, NFTMetadata } from 'api/types'
import NFTPlaceholder from 'assets/stubs/nft_placeholder.svg'
import { NftItem } from 'components/Assets/NftItem'
import Button from 'components/Button'
import GridView from 'components/Grids/GridView'
Canvas,
CornerPathEffect,
DashPathEffect,
Group,
Image as SkiaImage,
Path,
RadialGradient,
Rect,
Skia,
useImage,
vec,
} from '@shopify/react-native-skia'
import { useTheme } from 'contexts/ThemeContext'
import React from 'react'
import { ScrollView, StyleSheet, useWindowDimensions, View } from 'react-native'
import { useDispatch } from 'react-redux'

import { Line } from 'components/Line'
import LoadingIndicator from 'components/LoadingIndicator'
import { Tag } from 'components/Tag'
import { Title } from 'components/Typography/Title'
import { useReduxState } from 'hooks/useReduxState'
import { useThemeAwareStyle } from 'hooks/useThemeAwareStyle'
import { useGetWalletNFTCollectionsQuery } from 'reduxStore/assets/api'
import {
allWalletsSelector,
getUniqueWalletAddresses,
selectedWalletSelector,
} from 'reduxStore/wallet/selectors'
import { Theme } from 'styles/types'

import { IMAGE_WIDTH, NUMBER_OF_COLUMNS } from './constants'
import { IMAGE_WIDTH } from './constants'

const imageSize = 160
function makeHexagonPath(size, offset) {
const path = Skia.Path.Make()
const [xOffset, yOffset] = offset || [0, 0]
if (!size) size = 10

// https://www.quora.com/How-can-you-find-the-coordinates-in-a-hexagon
const halfed = size / 2
const sqrt = (Math.sqrt(3) * size) / 2
const points = [
[size, 0], //a
[halfed, sqrt], //b
[-halfed, sqrt], //c
[-size, 0], //d
[-halfed, -sqrt], //e
[halfed, -sqrt], //f
].map(([x, y]) => [x + xOffset, y + yOffset])
// console.log(points)
path.moveTo(...points[0])

points.slice(1, 6).forEach((point) => path.lineTo(...point))
path.close()
return path
}

export function ClipImage({
imageSrc,
path,
innerPath,
color,
offsetX,
offsetY,
}) {
const image = useImage(imageSrc)
return (
<Group clip={image ? path : null}>
<Path
path={path}
color={color}
style='stroke'
strokeJoin='round'
strokeWidth={0.5}>
<CornerPathEffect r={14} />
</Path>
<Path
path={innerPath}
color={'blue'}
style='stroke'
strokeJoin='round'
strokeWidth={1}>
<DashPathEffect intervals={[8, 8]} />
<CornerPathEffect r={14} />
</Path>
{image && (
<SkiaImage
image={image}
fit='cover'
x={offsetX}
y={offsetY}
width={imageSize + 25}
height={imageSize + 25}
/>
)}
</Group>
)
}

const Badges = () => {
const dispatch = useDispatch()
const navigation = useNavigation()
const styles = useThemeAwareStyle(createStyles)
const { theme } = useTheme()
const { width, height } = useWindowDimensions()
// const center = vec(width / 2, height / 2)
const size = 160
// const r = size * 0.33

const drawHexagonGrid = () => {
const hexagons = []
const numberOfCols = Math.floor(width / size) + 2
const numberOfRows = Math.floor(height / size) + 6
const hexagonSize = size / 2
let colOffset = -size / 2 - 20
let rowOffset = -size * 3 + size / 3

console.log(
'Grid',
JSON.stringify({ numberOfCols, numberOfRows, colOffset, rowOffset })
)

const badgesData = {
'1-2': {
name: 'Verida Identity',
image:
'https://ipfs.moralis.io:2053/ipfs/QmbPHjLsp48QuSoCtDHvXRnfzAqRDYsX4udQXRQ5gB2w87/Gen0/verida-identity.png',
},
'2-2': {
name: 'Discord Account',
image:
'https://ipfs.moralis.io:2053/ipfs/QmQsghP7Y1dbZHgoo48hQz8SHdFk2mjmPikhkHg69HHhWb/Gen0/discord-account.png',
},
'1-3': {
name: 'Facebook Account',
image:
'https://ipfs.moralis.io:2053/ipfs/QmT1hqphvztijTtEKd1xdUMtPXFfKqrx7qRby3mpeLeiMQ/Gen0/facebook-account.png',
},
'2-3': {
name: 'Twitter Account',
image:
'https://ipfs.moralis.io:2053/ipfs/QmVZYkMfNC26LcwRJPdZmfUVGs5bmGFxz6AFpwwrAcVuuz/Gen0/twitter-account.png',
},
'1-4': {
name: 'LinkedIn Account',
image:
'https://ipfs.moralis.io:2053/ipfs/QmeAJDpYUzfjguoEqsvg7taZzoUryLohDLGCo5NREgQ2zX/Gen0/linkedin-account.png',
},
'2-4': {
name: 'Github Account',
image:
'https://ipfs.moralis.io:2053/ipfs/QmZ9wDPdjHAzC8WwqvbsqLPKkfiQNgwXT8hSkwKWrCMKY2/Gen0/github-account.png',
},
'1-5': {
name: 'Test Account',
// image:
// 'https://ipfs.moralis.io:2053/ipfs/QmeAJDpYUzfjguoEqsvg7taZzoUryLohDLGCo5NREgQ2zX/Gen0/linkedin-account.png',
},
'2-5': {
name: 'Test Account 2',
// image:
// 'https://ipfs.moralis.io:2053/ipfs/QmZ9wDPdjHAzC8WwqvbsqLPKkfiQNgwXT8hSkwKWrCMKY2/Gen0/github-account.png',
},
'1-6': {
name: 'Test Account 3',
// image:
// 'https://ipfs.moralis.io:2053/ipfs/QmeAJDpYUzfjguoEqsvg7taZzoUryLohDLGCo5NREgQ2zX/Gen0/linkedin-account.png',
},
'2-6': {
name: 'Test Account 4',
// image:
// 'https://ipfs.moralis.io:2053/ipfs/QmZ9wDPdjHAzC8WwqvbsqLPKkfiQNgwXT8hSkwKWrCMKY2/Gen0/github-account.png',
},
}

for (let i = 0; i < numberOfCols; i++) {
console.log('Enter col:', i)
rowOffset = -size * 3 + size / 2 + 16
const extraRowOffset = (i * size) / 2 // - size / 4
for (let j = 0; j < numberOfRows; j++) {
console.log('Enter row:', j)
const path = makeHexagonPath(hexagonSize + 12, [
colOffset + hexagonSize,
rowOffset + hexagonSize + extraRowOffset,
])
rowOffset += size
const badgePosition = `${i}-${j}` as any
if (badgesData[badgePosition]) {
const innePath = makeHexagonPath(hexagonSize + 6, [
colOffset + hexagonSize,
rowOffset - size + hexagonSize + extraRowOffset,
])

hexagons.push(
<ClipImage
key={'hex' + `${i}-${j}`}
imageSrc={badgesData[badgePosition].image}
path={path}
innerPath={innePath}
color={'#73b4be'}
offsetX={(size + 1) * (i - 1) + (size / 5 - 7.5) * (2 - i)}
offsetY={rowOffset + extraRowOffset - size - 12}
/>
)
continue
}

hexagons.push(
<Path
key={'hex' + `${i}-${j}`}
path={path}
color={'#73b4be'}
style='stroke'
strokeJoin='round'
strokeWidth={0.5}>
<CornerPathEffect r={14} />
</Path>
)
}
colOffset += size - size / 7
}

return hexagons
}

const checkIfWithinShape = (locationX, locationY, shapeProps) => {
if (shapeProps.type === 'circle') {
const { cx, cy, r } = shapeProps
const distanceSquared = (locationX - cx) ** 2 + (locationY - cy) ** 2
return distanceSquared <= r ** 2
}

if (shapeProps.type === 'rect') {
const { x, y, width, height } = shapeProps
return (
locationX >= x &&
locationX <= x + width &&
locationY >= y &&
locationY <= y + height
)
}

// Add support for other shapes if needed
return false
}

// TODO: Fix touch event
// Shape properties
const mainCircle = { type: 'circle', cx: 0, cy: 0, r: 200 }
const handleTouchEnd = (e) => {
const { locationX, locationY } = e.nativeEvent
// Check if the touch coordinates fall within the bounds of the shapes
if (checkIfWithinShape(locationX, locationY, mainCircle)) {
navigation.navigate('ClaimableBadges')
}
}

return (
<View style={styles.container}>
<Line style={{ marginTop: theme.spacing.s }} />
<Button

<ScrollView>
<Canvas
style={{ flex: 1, minHeight: 8 * size }}
onTouchEnd={handleTouchEnd}>
<Rect x={0} y={0} width={width} height={8 * size}>
<RadialGradient
c={vec(width, 0)}
r={1283}
colors={['rgba(55, 213, 199, 0.06)', 'rgba(174, 71, 255, 0.1)']}
positions={[0, 0.5]}
/>
</Rect>

{drawHexagonGrid()}
</Canvas>
</ScrollView>
{/* <Button
style={{ margin: theme.spacing.m }}
onPress={() => navigation.navigate('ClaimableBadges')}>
Claim Badges
</Button>
</Button> */}
</View>
)
}
Expand Down
Loading