/
explore.js
133 lines (121 loc) · 4.29 KB
/
explore.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { CID } from 'multiformats/cid'
import { createAsyncResourceBundle, createSelector } from 'redux-bundler'
import { ensureLeadingSlash } from '../lib/helpers'
import { importCar } from '../lib/import-car'
import parseIpldPath from '../lib/parse-ipld-path'
import resolveIpldPath from '../lib/resolve-ipld-path'
const getCidFromCidOrFqdn = (cidOrFqdn) => {
if (cidOrFqdn.startsWith('/ipfs/')) {
return cidOrFqdn.slice('/ipfs/'.length)
}
if (cidOrFqdn.startsWith('/ipns/')) {
// TODO: handle ipns
throw new Error('ipns not supported yet')
}
if (cidOrFqdn.startsWith('/')) {
return cidOrFqdn.slice(1)
}
return cidOrFqdn
}
// Find all the nodes and path boundaries traversed along a given path
const makeBundle = () => {
const bundle = createAsyncResourceBundle({
name: 'explore',
actionBaseType: 'EXPLORE',
getPromise: async ({ store }) => {
const path = store.selectExplorePathFromHash()
if (!path) return null
const pathParts = parseIpldPath(path)
if (!pathParts) return null
const { cidOrFqdn, rest } = pathParts
try {
// TODO: handle ipns, which would give us a fqdn in the cid position.
const cid = CID.parse(getCidFromCidOrFqdn(cidOrFqdn))
const {
targetNode,
canonicalPath,
localPath,
nodes,
pathBoundaries
} = await resolveIpldPath(store.selectHelia(), cid, rest)
return {
path,
targetNode,
canonicalPath,
localPath,
nodes,
pathBoundaries
}
} catch (error) {
console.warn('Failed to resolve path', path, error)
return { path, error }
}
},
staleAfter: Infinity,
checkIfOnline: false
})
bundle.selectExplorePathFromHash = createSelector(
'selectRouteInfo',
(routeInfo) => {
if (!routeInfo.url.startsWith('/explore')) return
const path = routeInfo.url.slice('/explore'.length)
return decodeURIComponent(path)
}
)
/**
* Fetch the explore data when the address in the url hash changes.
* Note that this is called automatically by redux-bundler
*
* @see https://reduxbundler.com/api-reference/bundle#bundle.reactx
*/
bundle.reactExploreFetch = createSelector(
'selectHeliaReady',
'selectExploreIsLoading',
'selectExploreIsWaitingToRetry',
'selectExplorePathFromHash',
'selectExplore',
(ipfsReady, isLoading, isWaitingToRetry, explorePathFromHash, obj) => {
// Wait for ipfs or the pending request to complete
if (!ipfsReady || isLoading || isWaitingToRetry) return false
// Theres no url path and no data so nothing to do.
if (!explorePathFromHash && !obj) return false
// We already have the data for the path.
if (obj && explorePathFromHash === obj.path) return false
return { actionCreator: 'doFetchExplore' }
}
)
// Unpack append a dag link target to the current path and update the url hash
bundle.doExploreLink = (link) => ({ store }) => {
const { nodes, pathBoundaries } = store.selectExplore()
const cid = nodes[0].cid
const pathParts = pathBoundaries.map(p => p.path)
// add the extra path step from the link to the end
if (link && link.path) {
pathParts.push(link.path)
}
// add the root cid to the start
pathParts.unshift(cid)
const path = pathParts.map((part) => encodeURIComponent(part)).join('/')
const hash = `#/explore/${path}`
store.doUpdateHash(hash)
}
// validate user submitted path and put it in url hash fragment
bundle.doExploreUserProvidedPath = (path) => ({ store }) => {
const hash = path ? `#/explore${ensureLeadingSlash(path)}` : '#/explore'
store.doUpdateHash(hash)
}
bundle.doUploadUserProvidedCar = (file, uploadImage) => async ({ store }) => {
try {
const rootCid = await importCar(file, await store.selectHelia())
const hash = rootCid.toString() ? `#/explore${ensureLeadingSlash(rootCid.toString())}` : '#/explore'
store.doUpdateHash(hash)
// Grab the car loader image so we can change it's state
const imageFileLoader = document.getElementById('car-loader-image')
imageFileLoader.src = uploadImage
} catch (err) {
console.error('Could not import car file', err)
}
}
return bundle
}
export default makeBundle