Skip to content

Commit

Permalink
Ownables WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jasny committed Jun 19, 2023
1 parent e96c2ac commit 0b2abc6
Show file tree
Hide file tree
Showing 17 changed files with 793 additions and 383 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ LTO_EXPLORER_URL=https://explorer.testnet.lto.network
LTO_WALLET_URL=https://wallet.testnet.lto.network
CMC_API_KEY=
COMMUNITY_NODES_URL=
ANCHORING_DISABLED=false
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
"dependencies": {
"@expo/vector-icons": "^13.0.0",
"@ltonetwork/lto": "^0.8.7",
"@ltonetwork/lto": "^0.12.4",
"@react-native-async-storage/async-storage": "^1.18.1",
"@react-native-community/clipboard": "^1.5.1",
"@react-navigation/bottom-tabs": "^6.0.5",
Expand All @@ -30,7 +30,8 @@
"@types/jest": "^29.5.1",
"@types/styled-components-react-native": "^5.1.3",
"axios": "^1.4.0",
"base-64": "^1.0.0",
"blockstore-core": "^4.2.0",
"boolean": "^3.2.0",
"browserify": "^17.0.0",
"browserify-global": "^1.1.2",
"browserify-zlib": "^0.1.4",
Expand All @@ -50,6 +51,7 @@
"expo-system-ui": "~1.2.0",
"expo-web-browser": "~10.2.1",
"https-browserify": "^0.0.1",
"ipfs-unixfs-importer": "^15.1.5",
"path-browserify": "^0.0.0",
"punycode": "^1.4.1",
"react": "17.0.2",
Expand Down
23 changes: 18 additions & 5 deletions src/components/Ownable.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import React, {useEffect, useState} from 'react'
import { WebView } from 'react-native-webview'
import {WebViewErrorEvent, WebViewMessageEvent} from "react-native-webview/lib/WebViewTypes"
import {TypedOwnable} from "../interfaces/TypedOwnable"
import OwnableService from "../services/Ownable.service"
import {EventChain} from "@ltonetwork/lto"
import {TypedPackage} from "../interfaces/TypedPackage"
import {Card} from "react-native-paper"

export default function Ownable(props: {ownable: TypedOwnable}): JSX.Element {
const { ownable } = props
export default function Ownable(props: {chain: EventChain, package: TypedPackage}): JSX.Element {
const { chain, package: pkg } = props

let webView: WebView
const [js, setJs] = useState<string|undefined>()

useEffect(() => {
/*useEffect(() => {
OwnableService.getHTML(ownable.option.id).then(setJs)
}, [])
Expand Down Expand Up @@ -79,5 +80,17 @@ export default function Ownable(props: {ownable: TypedOwnable}): JSX.Element {
onMessage={handleMessage}
onError={handleError}
/>
)*/

return (
<Card style={{
aspectRatio: "1/1",
position: 'relative',
}}>
<Card.Title
title={pkg.title}
subtitle={chain.id}
/>
</Card>
)
}
11 changes: 11 additions & 0 deletions src/interfaces/TypedCosmWasmMsg.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
interface TypedCosmWasmMethod {
required: string[];
properties: {
[methodName: string]: {}
}
}

export interface TypedCosmWasmMsg {
title: string;
oneOf: Array<TypedCosmWasmMethod>;
}
7 changes: 0 additions & 7 deletions src/interfaces/TypedOwnable.d.ts

This file was deleted.

16 changes: 16 additions & 0 deletions src/interfaces/TypedPackage.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export interface TypedPackageCapabilities {
isDynamic: boolean;
hasMetadata: boolean;
hasWidgetState: boolean;
isConsumable: boolean;
isConsumer: boolean;
isTransferable: boolean;
}

export interface TypedPackage extends TypedPackageCapabilities {
cid: string;
title: string;
name: string;
description?: string;
versions: Array<{date: Date, cid: string}>;
}
25 changes: 14 additions & 11 deletions src/navigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ function RootNavigator(): any {
const [appFirstLaunch, setAppFirstLaunch] = useState<boolean | null>(null)
const [userAlias, setUserAlias] = useState<boolean | null>(null)

/*
useEffect(() => {
skipOnboarding()
}, [appFirstLaunch])
const skipOnboarding = (): void => {
setAppFirstLaunch(false)
/*StorageService.getData('@appFirstLaunch')
StorageService.getData('@appFirstLaunch')
.then((data) => {
if (data === null) {
setAppFirstLaunch(true)
Expand All @@ -70,22 +70,25 @@ function RootNavigator(): any {
})
.catch((error) => {
throw new Error(`Error retrieving data. ${error}`)
})*/
})
}
*/

StorageService.getItem('@userAlias')
.then((data) => {
setUserAlias(data !== null)
})
.catch((error) => {
throw new Error(`Error retrieving data. ${error}`)
})
useEffect(() => {
StorageService.getItem('@userAlias')
.then((data) => {
setUserAlias(data !== null)
})
.catch((error) => {
throw new Error(`Error retrieving data. ${error}`)
})
}, [])


return (
userAlias !== null && (
<Stack.Navigator
initialRouteName={appFirstLaunch ? 'OnBoarding' : userAlias ? 'SignIn' : 'SignUp'}
initialRouteName={userAlias ? 'SignIn' : 'SignUp'}
screenOptions={{
headerTitleStyle: { color: '#A017B7', fontWeight: '400', fontSize: 16 },
headerTintColor: '#A017B7',
Expand Down
55 changes: 8 additions & 47 deletions src/screens/OwnablesTabScreen/OwnablesTabScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
import React, { useState} from 'react'
import {ImageBackground, Text, useWindowDimensions, View} from 'react-native'
import {ImageBackground, useWindowDimensions} from 'react-native'
import { RootTabScreenProps } from '../../../types'
import OverviewHeader from '../../components/OverviewHeader'
import StatusBarIOS from '../../components/StatusBarIOS'
import { Container, MainTitle } from '../../components/styles/NextFunctionality.styles'
import { OWNABLES } from '../../constants/Text'
import { backgroundImage } from '../../utils/images'
import DocumentPicker, { isInProgress, types } from 'react-native-document-picker'
import OwnableService from '../../services/Ownable.service'
import {Modal, Portal, Button, Provider, List, Card} from 'react-native-paper'
import OwnableService from '../../services/Ownables/Ownable.service'
import {Button, Provider} from 'react-native-paper'
import {useFocusEffect} from "@react-navigation/native";
import ShortList from "../../components/ShortList";
import PageFAB from "../../components/PageFAB";
import WebView from "react-native-webview";
import Ownable from "../../components/Ownable";
import {EventChain} from "@ltonetwork/lto"
import {TypedPackage} from "../../interfaces/TypedPackage"

export default function OwnablesTabScreen({ navigation }: RootTabScreenProps<'Ownables'>) {

const { width, height } = useWindowDimensions()

const [showAddModal, setShowAddModal] = React.useState(false)
const [ownableOptions, setOwnableOptions] = useState<{id: string, name: string}[]>([])
const [ownables, setOwnables] = useState<{id: string, option: {id: string, name: string}}[]>([])
const [ownables, setOwnables] = useState<Array<{chain: EventChain, package: TypedPackage}>>([]);

useFocusEffect(
React.useCallback(() => {
loadOwnableOptions()
loadOwnables()
},[])
)
Expand All @@ -38,7 +33,6 @@ export default function OwnablesTabScreen({ navigation }: RootTabScreenProps<'Ow
copyTo: 'documentDirectory'
})
await OwnableService.import(pickerResult)
loadOwnableOptions()
} catch (err) {
if (DocumentPicker.isCancel(err)) {
console.warn('cancelled')
Expand All @@ -51,26 +45,10 @@ export default function OwnablesTabScreen({ navigation }: RootTabScreenProps<'Ow
}
}

const loadOwnableOptions = () => {
OwnableService.listOptions().then(setOwnableOptions)
}

const loadOwnables = () => {
OwnableService.list().then(setOwnables)
}

const renderOwnableOption = (option: {id: string, name: string}) =>
<List.Item
key={`ownable-option:${option.id}`}
onPress={async () => {
await OwnableService.issue(option.id)
loadOwnables()
setShowAddModal(false)
}}
title={`${option.name} (${option.id})`}
/>


return (
<>
<StatusBarIOS backgroundColor={'#ffffff'} />
Expand All @@ -83,28 +61,11 @@ export default function OwnablesTabScreen({ navigation }: RootTabScreenProps<'Ow
onQrPress={() => navigation.navigate('QrReader')}
input={<MainTitle>{OWNABLES.MAINTITLE}</MainTitle>} />

<Button onPress={() => { OwnableService.clear().then(loadOwnables) }}>Clear</Button>
{ ownables.map(ownable => <Ownable key={ownable.id} ownable={ownable} />) }
{ ownables.map(({chain, package: pkg}) => <Ownable chain={chain} package={pkg} />) }
</Container>

<Provider>
<Portal>
<Modal visible={showAddModal} onDismiss={() => setShowAddModal(false)} style={{margin: 20}}>
<Card>
<Card.Content>
<ShortList
data={ownableOptions}
renderItem={({item}) => renderOwnableOption(item)}
/>
</Card.Content>
<Card.Actions>
<Button onPress={importOwnable} style={{width: '100%'}}>Import</Button>
</Card.Actions>
</Card>
</Modal>
</Portal>

<PageFAB onPress={() => { setShowAddModal(true) }} />
<PageFAB onPress={() => importOwnable() } />
</Provider>
</>
)
Expand Down
35 changes: 34 additions & 1 deletion src/services/LTO.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Account, CancelLease, Lease, LTO, Transaction } from "@ltonetwork/lto"
import {Account, Binary, CancelLease, Lease, LTO, Transaction} from "@ltonetwork/lto"
import StorageService from "./Storage.service"
import { TypedTransaction } from "../interfaces/TypedTransaction"

Expand Down Expand Up @@ -168,11 +168,44 @@ export default class LTOService {
if (response.status >= 400) throw new Error('Broadcast transaction failed: ' + await response.text())
}

public static anchor = async (...anchors: Array<{key: Binary, value: Binary}>|Array<Binary>) => {
if (!this.account) throw new Error('Not logged in')

if (anchors[0] instanceof Uint8Array) {
await lto.anchor(this.account, ...anchors as Array<Binary>)
} else {
await lto.mappedAnchor(this.account, ...anchors as Array<{key: Binary, value: Binary}>)
}
}

public static verifyAnchors = async(...anchors: Array<{key: Binary, value: Binary}>|Array<Binary>) => {
const data = anchors[0] instanceof Uint8Array
? (anchors as Array<Binary>).map(anchor => anchor.hex)
: Object.fromEntries((anchors as Array<{key: Binary, value: Binary}>).map(({key, value}) => (
[key.hex, value.hex]
)))

const url = this.apiUrl('/index/hash/verify?encoding=hex')
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data),
})

return await response.json()
}

public static isValidAddress = (address: string): boolean => {
try {
return lto.isValidAddress(address)
} catch (e) {
return false
}
}

public static accountOf(publicKey: Binary|string): string {
return lto.account({publicKey: publicKey instanceof Binary ? publicKey.base58 : publicKey}).address
}
}
Loading

0 comments on commit 0b2abc6

Please sign in to comment.