| 
 | 1 | +import { LitElement, html, css } from 'https://cdn.jsdelivr.net/gh/lit/dist@3/all/lit-all.min.js';  | 
 | 2 | +import RPCCall from '/lib/jsonrpc.mjs';  | 
 | 3 | + | 
 | 4 | +class ContentPage extends LitElement {  | 
 | 5 | +    static properties = {  | 
 | 6 | +        searchCid: { type: String },  | 
 | 7 | +        results: { type: Array },  | 
 | 8 | +        loading: { type: Boolean },  | 
 | 9 | +        error: { type: String }  | 
 | 10 | +    };  | 
 | 11 | + | 
 | 12 | +    constructor() {  | 
 | 13 | +        super();  | 
 | 14 | +        this.searchCid = '';  | 
 | 15 | +        this.results = [];  | 
 | 16 | +        this.loading = false;  | 
 | 17 | +        this.error = '';  | 
 | 18 | +    }  | 
 | 19 | + | 
 | 20 | +    handleInput(e) {  | 
 | 21 | +        this.searchCid = e.target.value;  | 
 | 22 | +    }  | 
 | 23 | + | 
 | 24 | +    async handleFind() {  | 
 | 25 | +        if (!this.searchCid.trim()) {  | 
 | 26 | +            this.error = 'Please enter a CID';  | 
 | 27 | +            return;  | 
 | 28 | +        }  | 
 | 29 | + | 
 | 30 | +        this.loading = true;  | 
 | 31 | +        this.error = '';  | 
 | 32 | +        this.results = [];  | 
 | 33 | + | 
 | 34 | +        try {  | 
 | 35 | +            const results = await RPCCall('FindContentByCID', [this.searchCid.trim()]);  | 
 | 36 | +            this.results = results || [];  | 
 | 37 | +            if (this.results.length === 0) {  | 
 | 38 | +                this.error = 'No content found for this CID';  | 
 | 39 | +            }  | 
 | 40 | +        } catch (err) {  | 
 | 41 | +            console.error('Error finding content:', err);  | 
 | 42 | +            this.error = `Error: ${err.message || err}`;  | 
 | 43 | +        } finally {  | 
 | 44 | +            this.loading = false;  | 
 | 45 | +        }  | 
 | 46 | +    }  | 
 | 47 | + | 
 | 48 | +    render() {  | 
 | 49 | +        return html`  | 
 | 50 | +            <link  | 
 | 51 | +                href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"  | 
 | 52 | +                rel="stylesheet"  | 
 | 53 | +                integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"  | 
 | 54 | +                crossorigin="anonymous"  | 
 | 55 | +            />  | 
 | 56 | +            <link rel="stylesheet" href="/ux/main.css" onload="document.body.style.visibility = 'initial'">  | 
 | 57 | +
  | 
 | 58 | +            <div class="container">  | 
 | 59 | +                <h2>Find CID</h2>  | 
 | 60 | +                <div class="search-container">  | 
 | 61 | +                    <input  | 
 | 62 | +                        autofocus  | 
 | 63 | +                        type="text"  | 
 | 64 | +                        placeholder="Enter CID (baf...)"  | 
 | 65 | +                        .value="${this.searchCid}"  | 
 | 66 | +                        @input="${this.handleInput}"  | 
 | 67 | +                        @keypress="${(e) => e.key === 'Enter' && this.handleFind()}"  | 
 | 68 | +                    />  | 
 | 69 | +                    <button  | 
 | 70 | +                        class="btn btn-primary"  | 
 | 71 | +                        @click="${this.handleFind}"  | 
 | 72 | +                        ?disabled="${this.loading}"  | 
 | 73 | +                    >  | 
 | 74 | +                        ${this.loading ? 'Searching...' : 'Find'}  | 
 | 75 | +                    </button>  | 
 | 76 | +                </div>  | 
 | 77 | +
  | 
 | 78 | +                ${this.error ? html`  | 
 | 79 | +                    <div class="alert alert-danger">${this.error}</div>  | 
 | 80 | +                ` : ''}  | 
 | 81 | +
  | 
 | 82 | +                ${this.results.length > 0 ? html`  | 
 | 83 | +                    <h3>Results</h3>  | 
 | 84 | +                    <table class="table table-dark table-striped">  | 
 | 85 | +                        <thead>  | 
 | 86 | +                            <tr>  | 
 | 87 | +                                <th>Piece CID</th>  | 
 | 88 | +                                <th>Offset</th>  | 
 | 89 | +                                <th>Size</th>  | 
 | 90 | +                            </tr>  | 
 | 91 | +                        </thead>  | 
 | 92 | +                        <tbody>  | 
 | 93 | +                            ${this.results.map(item => html`  | 
 | 94 | +                                <tr>  | 
 | 95 | +                                    <td><a href="/pages/piece/?id=${item.piece_cid}">${item.piece_cid}</a></td>  | 
 | 96 | +                                    <td>${item.err ? html`<span class="text-danger">${item.err}</span>` : item.offset}</td>  | 
 | 97 | +                                    <td>${this.formatBytes(item.size)}</td>  | 
 | 98 | +                                </tr>  | 
 | 99 | +                            `)}  | 
 | 100 | +                        </tbody>  | 
 | 101 | +                    </table>  | 
 | 102 | +                ` : ''}  | 
 | 103 | +            </div>  | 
 | 104 | +        `;  | 
 | 105 | +    }  | 
 | 106 | + | 
 | 107 | +    formatBytes(bytes) {  | 
 | 108 | +        if (!bytes) return '0 Bytes';  | 
 | 109 | +        const k = 1024;  | 
 | 110 | +        const sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB'];  | 
 | 111 | +        const i = Math.floor(Math.log(bytes) / Math.log(k));  | 
 | 112 | +        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];  | 
 | 113 | +    }  | 
 | 114 | + | 
 | 115 | +    static styles = css`  | 
 | 116 | +        .search-container {  | 
 | 117 | +            display: grid;  | 
 | 118 | +            grid-template-columns: 1fr auto;  | 
 | 119 | +            grid-column-gap: 0.75rem;  | 
 | 120 | +            margin-bottom: 1rem;  | 
 | 121 | +        }  | 
 | 122 | +    `;  | 
 | 123 | +}  | 
 | 124 | + | 
 | 125 | +customElements.define('content-page', ContentPage);  | 
0 commit comments