diff --git a/src/app.js b/src/app.js
new file mode 100644
index 0000000..3d5a874
--- /dev/null
+++ b/src/app.js
@@ -0,0 +1,150 @@
+// Global variables
+let archiveData = [];
+let sortedData = [];
+let currentSort = { column: 'created_at', order: 'desc' };
+const rowsPerPage = 100;
+let currentPage = 1;
+
+// Event Listeners
+document.addEventListener('DOMContentLoaded', function() {
+ document.getElementById('fileInput').addEventListener('change', handleFileLoad);
+
+ // Add click listeners to sortable headers
+ document.querySelectorAll('#dataTable th[data-sort]').forEach(th => {
+ th.addEventListener('click', () => {
+ sortTable(th.dataset.sort);
+ });
+ });
+});
+
+// File handling
+function handleFileLoad(event) {
+ const file = event.target.files[0];
+ if (!file) return;
+
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ try {
+ const data = JSON.parse(e.target.result);
+ if (!Array.isArray(data)) throw new Error('Invalid archive format: Expected an array.');
+ archiveData = data;
+ sortedData = [...archiveData];
+ updateStats();
+ sortTable(currentSort.column); // Initial sort
+ } catch (err) {
+ alert(`Failed to load archive: ${err.message}`);
+ }
+ };
+ reader.readAsText(file);
+}
+
+// Statistics
+function updateStats() {
+ const statsDiv = document.getElementById('stats');
+ const kindCounts = archiveData.reduce((acc, event) => {
+ acc[event.kind] = (acc[event.kind] || 0) + 1;
+ return acc;
+ }, {});
+
+ const totalEvents = archiveData.length;
+ let statsText = `Total Events: ${totalEvents}
`;
+ statsText += 'Most Common Types: ';
+
+ const topKinds = Object.entries(kindCounts)
+ .sort(([,a], [,b]) => b - a)
+ .slice(0, 3)
+ .map(([kind, count]) => `${getKindLabel(parseInt(kind))}: ${count}`);
+
+ statsText += topKinds.join(' | ');
+
+ statsDiv.innerHTML = statsText;
+}
+
+// Table rendering
+function renderTable() {
+ const tbody = document.querySelector('#dataTable tbody');
+ tbody.innerHTML = '';
+
+ const start = (currentPage - 1) * rowsPerPage;
+ const end = start + rowsPerPage;
+ const pageData = sortedData.slice(start, end);
+
+ if (pageData.length === 0) {
+ tbody.innerHTML = `
+ No data loaded. Please load a JSON archive file. |
+
`;
+ return;
+ }
+
+ pageData.forEach((event, index) => {
+ const row = document.createElement('tr');
+ row.innerHTML = `
+ ${start + index + 1} |
+ ${new Date(event.created_at * 1000).toLocaleString()} |
+ ${getKindLabel(event.kind)} |
+ ${getContentDisplay(event)} |
+ `;
+ tbody.appendChild(row);
+ });
+
+ updatePagination(end);
+}
+
+// Pagination
+function updatePagination(end) {
+ document.getElementById('prevPage').disabled = currentPage === 1;
+ document.getElementById('nextPage').disabled = end >= sortedData.length;
+ document.getElementById('pageInfo').innerText = `Page ${currentPage} of ${Math.ceil(sortedData.length / rowsPerPage)}`;
+}
+
+function changePage(offset) {
+ currentPage += offset;
+ renderTable();
+}
+
+// Sorting
+function sortTable(column) {
+ if (currentSort.column === column) {
+ currentSort.order = currentSort.order === 'asc' ? 'desc' : 'asc';
+ } else {
+ currentSort.column = column;
+ currentSort.order = 'desc';
+ }
+
+ const order = currentSort.order === 'asc' ? 1 : -1;
+ sortedData.sort((a, b) => {
+ if (a[column] < b[column]) return -1 * order;
+ if (a[column] > b[column]) return 1 * order;
+ return 0;
+ });
+
+ currentPage = 1;
+ renderTable();
+}
+
+// Helper functions
+function getKindLabel(kind) {
+ const kinds = {
+ 0: 'Profile Metadata',
+ 1: 'Short Text Note',
+ 2: 'Recommend Relay',
+ 3: 'Contacts',
+ 4: 'Encrypted DM',
+ 6: 'Repost',
+ 7: 'Reaction',
+ 40: 'Channel Creation',
+ 41: 'Channel Metadata',
+ 42: 'Channel Message',
+ 30023: 'Long-form Content',
+ // Add more as needed
+ };
+ return `${kind} (${kinds[kind] || 'Unknown'})`;
+}
+
+function getContentDisplay(event) {
+ if (event.kind === 4) {
+ const recipient = event.tags.find(tag => tag[0] === 'p');
+ return `Encrypted Message: ${event.content || 'N/A'}
Recipient: ${recipient ? recipient[1] : 'Unknown'}`;
+ }
+ return event.content || 'No content available';
+}
\ No newline at end of file
diff --git a/src/script.js b/src/script.js
index caf8c93..dc0fce3 100644
--- a/src/script.js
+++ b/src/script.js
@@ -52,11 +52,10 @@ document.addEventListener('DOMContentLoaded', () => {
// Update the filter to include additional kinds
const filter = {
- kinds: [0, 1, 2, 3, 4, 6, 7, 10002, 30023], // Include multiple event kinds
- authors: [pubkey],
- limit: 1000,
+ kinds: [0, 1, 2, 3, 4, 6, 7, 10002, 30023, 10509], // Include all relevant kinds
+ authors: [pubkey], // Fetch events from the specified pubkey
};
-
+
relayUrls.forEach((url) => {
try {
const sub = pool.sub([url], [filter]);
@@ -78,12 +77,16 @@ document.addEventListener('DOMContentLoaded', () => {
case 4:
console.log('Encrypted DM captured:', event);
break;
+ case 10509:
+ console.log('Ephemeral DM captured:', event);
+ break;
case 30023:
console.log('Long-Form Content captured:', event);
break;
default:
console.log('Other event captured:', event);
}
+
collectedEvents.push(event); // Store the raw event data
eventIds.add(event.id);
}
diff --git a/src/styles.css b/src/styles.css
index d19d9db..a4aeef2 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -1,50 +1,110 @@
+/* Base styles */
body {
font-family: Arial, sans-serif;
- background-color: #f4f4f4;
margin: 0;
padding: 0;
+ background-color: #f9f9f9;
}
.container {
- width: 90%;
- max-width: 600px;
- margin: 50px auto;
- background: #fff;
+ max-width: 1200px;
+ margin: 20px auto;
padding: 20px;
+ background: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
+/* Header and Stats */
h1 {
text-align: center;
+ margin-bottom: 10px;
}
-label {
+.stats {
+ text-align: center;
+ color: #666;
+ margin-bottom: 20px;
+ font-size: 1.1em;
+}
+
+input[type="file"] {
display: block;
- margin-top: 15px;
+ margin: 20px auto;
}
-input[type="text"],
-textarea {
+/* Table styles */
+table {
width: 100%;
+ border-collapse: collapse;
+ margin-top: 20px;
+}
+
+th, td {
padding: 10px;
- margin-top: 5px;
- box-sizing: border-box;
+ border: 1px solid #ddd;
+ text-align: left;
+ vertical-align: top;
}
-button {
- margin-top: 20px;
- padding: 10px 15px;
- background-color: #007BFF;
- border: none;
- color: #fff;
+th {
cursor: pointer;
+ background: #f4f4f4;
+ position: relative;
}
-button:hover {
- background-color: #0056b3;
+th:hover {
+ background: #e0e0e0;
}
-#status {
+.empty-message {
+ text-align: center;
+ color: #888;
margin-top: 20px;
- min-height: 20px;
}
+
+.content-cell {
+ max-width: 600px;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
+/* Sort indicators */
+.sort-indicator::after {
+ content: '⬍';
+ margin-left: 5px;
+}
+
+.sort-asc::after {
+ content: '↑';
+}
+
+.sort-desc::after {
+ content: '↓';
+}
+
+.number-column {
+ width: 50px;
+ text-align: right;
+ color: #666;
+}
+
+/* Pagination */
+.pagination {
+ text-align: center;
+ margin: 20px 0;
+}
+
+.pagination button {
+ margin: 0 5px;
+ padding: 5px 10px;
+ background: #007BFF;
+ color: white;
+ border: none;
+ cursor: pointer;
+ border-radius: 3px;
+}
+
+.pagination button:disabled {
+ background: #ccc;
+ cursor: not-allowed;
+}
\ No newline at end of file
diff --git a/src/view-archive.html b/src/view-archive.html
index 4e2a8f4..c894c16 100644
--- a/src/view-archive.html
+++ b/src/view-archive.html
@@ -4,79 +4,7 @@
Nostr Archive Viewer
-
+
@@ -98,198 +26,12 @@
+
-
-
+