@blockfact/react-facti 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +185 -0
  2. package/index.d.ts +35 -0
  3. package/index.js +138 -0
  4. package/package.json +32 -0
package/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # @blockfact/react-facti
2
+
3
+ React component for displaying and verifying .facti files with blockchain verification.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @blockfact/react-facti
9
+ # or
10
+ yarn add @blockfact/react-facti
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Basic Display
16
+
17
+ ```jsx
18
+ import FactiImage from '@blockfact/react-facti';
19
+
20
+ function App() {
21
+ return (
22
+ <FactiImage
23
+ factiUrl="https://gateway.pinata.cloud/ipfs/QmXXX"
24
+ alt="Verified Content"
25
+ />
26
+ );
27
+ }
28
+ ```
29
+
30
+ ### With Metadata
31
+
32
+ ```jsx
33
+ <FactiImage
34
+ factiUrl="https://gateway.pinata.cloud/ipfs/QmXXX"
35
+ showMetadata={true}
36
+ onMetadataLoad={(metadata) => {
37
+ console.log('Transaction:', metadata.tx_hash);
38
+ }}
39
+ />
40
+ ```
41
+
42
+ ### Custom Styling
43
+
44
+ ```jsx
45
+ <FactiImage
46
+ factiUrl="https://gateway.pinata.cloud/ipfs/QmXXX"
47
+ className="my-image"
48
+ style={{ maxWidth: '500px', margin: '20px auto' }}
49
+ />
50
+ ```
51
+
52
+ ### Parse Manually
53
+
54
+ ```jsx
55
+ import { parseFacTi } from '@blockfact/react-facti';
56
+
57
+ function CustomComponent() {
58
+ const [data, setData] = useState(null);
59
+
60
+ useEffect(() => {
61
+ parseFacTi('https://gateway.pinata.cloud/ipfs/QmXXX')
62
+ .then(setData);
63
+ }, []);
64
+
65
+ if (!data) return <div>Loading...</div>;
66
+
67
+ return (
68
+ <div>
69
+ <img src={data.imageUrl} alt="Content" />
70
+ <p>Owner: {data.metadata.wallet}</p>
71
+ <p>Location: {data.metadata.latitude}, {data.metadata.longitude}</p>
72
+ </div>
73
+ );
74
+ }
75
+ ```
76
+
77
+ ## Props
78
+
79
+ | Prop | Type | Default | Description |
80
+ |------|------|---------|-------------|
81
+ | `factiUrl` | string | required | URL to .facti file |
82
+ | `alt` | string | `'BlockFact Content'` | Image alt text |
83
+ | `className` | string | `''` | CSS class name |
84
+ | `style` | object | `{}` | Inline styles |
85
+ | `showMetadata` | boolean | `false` | Show metadata below image |
86
+ | `onMetadataLoad` | function | `null` | Callback when metadata loads |
87
+ | `onError` | function | `null` | Callback on error |
88
+
89
+ ## API
90
+
91
+ ### `parseFacTi(factiUrl)`
92
+
93
+ Parse a .facti file and extract image + metadata.
94
+
95
+ **Returns:**
96
+ ```javascript
97
+ {
98
+ imageUrl: "blob:...", // Blob URL for image
99
+ metadata: {
100
+ tx_hash: "0x...",
101
+ wallet: "0x...",
102
+ timestamp: "2026-02-26T13:20:00Z",
103
+ latitude: 40.7128,
104
+ longitude: -74.0060,
105
+ // ... other fields
106
+ }
107
+ }
108
+ ```
109
+
110
+ ## TypeScript
111
+
112
+ Full TypeScript support included:
113
+
114
+ ```tsx
115
+ import FactiImage, { FactiMetadata, parseFacTi } from '@blockfact/react-facti';
116
+
117
+ function App() {
118
+ const handleLoad = (metadata: FactiMetadata) => {
119
+ console.log(metadata.tx_hash);
120
+ };
121
+
122
+ return <FactiImage factiUrl="..." onMetadataLoad={handleLoad} />;
123
+ }
124
+ ```
125
+
126
+ ## Features
127
+
128
+ - ✅ Zero dependencies (only React peer dependency)
129
+ - ✅ TypeScript support
130
+ - ✅ Automatic image extraction from .facti files
131
+ - ✅ Blockchain verification links
132
+ - ✅ GPS coordinates with Google Maps links
133
+ - ✅ Loading and error states
134
+ - ✅ Memory cleanup (revokes blob URLs)
135
+
136
+ ## Examples
137
+
138
+ ### Gallery
139
+
140
+ ```jsx
141
+ const images = [
142
+ 'https://gateway.pinata.cloud/ipfs/QmAAA',
143
+ 'https://gateway.pinata.cloud/ipfs/QmBBB',
144
+ 'https://gateway.pinata.cloud/ipfs/QmCCC',
145
+ ];
146
+
147
+ function Gallery() {
148
+ return (
149
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '20px' }}>
150
+ {images.map(url => (
151
+ <FactiImage key={url} factiUrl={url} showMetadata={true} />
152
+ ))}
153
+ </div>
154
+ );
155
+ }
156
+ ```
157
+
158
+ ### With Error Handling
159
+
160
+ ```jsx
161
+ function SafeFactiImage({ factiUrl }) {
162
+ const [error, setError] = useState(null);
163
+
164
+ if (error) {
165
+ return <div>Failed to load: {error.message}</div>;
166
+ }
167
+
168
+ return (
169
+ <FactiImage
170
+ factiUrl={factiUrl}
171
+ onError={setError}
172
+ />
173
+ );
174
+ }
175
+ ```
176
+
177
+ ## License
178
+
179
+ MIT
180
+
181
+ ## Links
182
+
183
+ - [npm](https://www.npmjs.com/package/@blockfact/react-facti)
184
+ - [GitHub](https://github.com/blockfact/react-facti)
185
+ - [BlockFact](https://blockfact.io)
package/index.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+
3
+ export interface FactiMetadata {
4
+ tx_hash: string;
5
+ wallet: string;
6
+ timestamp: string;
7
+ latitude: number;
8
+ longitude: number;
9
+ session_id: string;
10
+ poseidon_hash: string;
11
+ owner_address: string;
12
+ created_at: string;
13
+ finalized_at: string;
14
+ mime: string;
15
+ version: string;
16
+ }
17
+
18
+ export interface ParsedFacti {
19
+ imageUrl: string;
20
+ metadata: FactiMetadata;
21
+ }
22
+
23
+ export interface FactiImageProps {
24
+ factiUrl: string;
25
+ alt?: string;
26
+ className?: string;
27
+ style?: React.CSSProperties;
28
+ showMetadata?: boolean;
29
+ onMetadataLoad?: (metadata: FactiMetadata) => void;
30
+ onError?: (error: Error) => void;
31
+ }
32
+
33
+ export function parseFacTi(factiUrl: string): Promise<ParsedFacti>;
34
+
35
+ export default function FactiImage(props: FactiImageProps): JSX.Element;
package/index.js ADDED
@@ -0,0 +1,138 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+
3
+ /**
4
+ * Parse .facti file from URL
5
+ * @param {string} factiUrl - URL to .facti file
6
+ * @returns {Promise<{imageUrl: string, metadata: object}>}
7
+ */
8
+ export async function parseFacTi(factiUrl) {
9
+ const response = await fetch(factiUrl);
10
+ const arrayBuffer = await response.arrayBuffer();
11
+ const data = new Uint8Array(arrayBuffer);
12
+
13
+ if (data[0] !== 0xFA || data[1] !== 0x49 || data[2] !== 0x01 || data[3] !== 0x00) {
14
+ throw new Error('Invalid .facti file format');
15
+ }
16
+
17
+ const metadataLength = new DataView(arrayBuffer, 36, 4).getUint32(0);
18
+ const metadataBytes = data.slice(40, 40 + metadataLength);
19
+ const metadata = JSON.parse(new TextDecoder().decode(metadataBytes));
20
+
21
+ const imageData = data.slice(40 + metadataLength);
22
+ const blob = new Blob([imageData], { type: metadata.mime || 'image/png' });
23
+ const imageUrl = URL.createObjectURL(blob);
24
+
25
+ return { imageUrl, metadata };
26
+ }
27
+
28
+ /**
29
+ * FactiImage Component
30
+ * Displays .facti file with blockchain verification
31
+ */
32
+ export default function FactiImage({
33
+ factiUrl,
34
+ alt = 'BlockFact Content',
35
+ className = '',
36
+ style = {},
37
+ showMetadata = false,
38
+ onMetadataLoad,
39
+ onError
40
+ }) {
41
+ const [imageUrl, setImageUrl] = useState(null);
42
+ const [metadata, setMetadata] = useState(null);
43
+ const [loading, setLoading] = useState(true);
44
+ const [error, setError] = useState(null);
45
+
46
+ useEffect(() => {
47
+ let mounted = true;
48
+
49
+ async function load() {
50
+ try {
51
+ setLoading(true);
52
+ const { imageUrl, metadata } = await parseFacTi(factiUrl);
53
+
54
+ if (mounted) {
55
+ setImageUrl(imageUrl);
56
+ setMetadata(metadata);
57
+ if (onMetadataLoad) onMetadataLoad(metadata);
58
+ }
59
+ } catch (err) {
60
+ if (mounted) {
61
+ setError(err.message);
62
+ if (onError) onError(err);
63
+ }
64
+ } finally {
65
+ if (mounted) setLoading(false);
66
+ }
67
+ }
68
+
69
+ load();
70
+
71
+ return () => {
72
+ mounted = false;
73
+ if (imageUrl) URL.revokeObjectURL(imageUrl);
74
+ };
75
+ }, [factiUrl]);
76
+
77
+ if (loading) {
78
+ return <div className={className} style={style}>Loading...</div>;
79
+ }
80
+
81
+ if (error) {
82
+ return <div className={className} style={{ ...style, color: '#f44336' }}>Error: {error}</div>;
83
+ }
84
+
85
+ return (
86
+ <div className={className} style={style}>
87
+ <img
88
+ src={imageUrl}
89
+ alt={alt}
90
+ style={{
91
+ maxWidth: '100%',
92
+ border: '2px solid #4CAF50',
93
+ borderRadius: '4px'
94
+ }}
95
+ />
96
+
97
+ {showMetadata && metadata && (
98
+ <div style={{
99
+ background: '#f5f5f5',
100
+ padding: '15px',
101
+ borderRadius: '5px',
102
+ marginTop: '10px',
103
+ fontSize: '13px'
104
+ }}>
105
+ <div style={{ fontWeight: 'bold', marginBottom: '10px' }}>📋 Metadata</div>
106
+ <div style={{ margin: '5px 0' }}>
107
+ <strong>Transaction:</strong>{' '}
108
+ <a
109
+ href={`https://sepolia.voyager.online/tx/${metadata.tx_hash}`}
110
+ target="_blank"
111
+ rel="noopener noreferrer"
112
+ style={{ color: '#4CAF50' }}
113
+ >
114
+ {metadata.tx_hash?.slice(0, 20)}...
115
+ </a>
116
+ </div>
117
+ <div style={{ margin: '5px 0' }}>
118
+ <strong>Owner:</strong> {metadata.wallet?.slice(0, 20)}...
119
+ </div>
120
+ <div style={{ margin: '5px 0' }}>
121
+ <strong>Timestamp:</strong> {metadata.timestamp}
122
+ </div>
123
+ <div style={{ margin: '5px 0' }}>
124
+ <strong>Location:</strong>{' '}
125
+ <a
126
+ href={`https://www.google.com/maps?q=${metadata.latitude},${metadata.longitude}`}
127
+ target="_blank"
128
+ rel="noopener noreferrer"
129
+ style={{ color: '#4CAF50' }}
130
+ >
131
+ {metadata.latitude}, {metadata.longitude}
132
+ </a>
133
+ </div>
134
+ </div>
135
+ )}
136
+ </div>
137
+ );
138
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@blockfact/react-facti",
3
+ "version": "1.0.0",
4
+ "description": "React component for displaying and verifying .facti files",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "scripts": {
8
+ "test": "echo \"No tests yet\""
9
+ },
10
+ "keywords": [
11
+ "blockfact",
12
+ "facti",
13
+ "blockchain",
14
+ "verification",
15
+ "react",
16
+ "image"
17
+ ],
18
+ "author": "BlockFact",
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/blockfact/react-facti.git"
23
+ },
24
+ "peerDependencies": {
25
+ "react": ">=16.8.0"
26
+ },
27
+ "files": [
28
+ "index.js",
29
+ "index.d.ts",
30
+ "README.md"
31
+ ]
32
+ }