experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

add default untagged group

+80 -6
+47 -6
app/groups/home.js
··· 15 15 const VIEW_GROUPS = 'groups'; 16 16 const VIEW_ADDRESSES = 'addresses'; 17 17 18 + // Special pseudo-tag for untagged addresses 19 + const UNTAGGED_GROUP = { 20 + id: '__untagged__', 21 + name: 'Untagged', 22 + color: '#666666', 23 + frequency: 0, 24 + isSpecial: true 25 + }; 26 + 18 27 let state = { 19 28 view: VIEW_GROUPS, 20 29 tags: [], 21 30 currentTag: null, 22 - addresses: [] 31 + addresses: [], 32 + untaggedCount: 0 23 33 }; 24 34 25 35 // Handle ESC - go back to groups view ··· 46 56 }; 47 57 48 58 /** 49 - * Load all tags sorted by frecency 59 + * Load all tags sorted by frecency, and count untagged addresses 50 60 */ 51 61 const loadTags = async () => { 52 62 const result = await api.datastore.getTagsByFrecency(); ··· 57 67 console.error('Failed to load tags:', result.error); 58 68 state.tags = []; 59 69 } 70 + 71 + // Get count of untagged addresses 72 + const untaggedResult = await api.datastore.getUntaggedAddresses(); 73 + if (untaggedResult.success) { 74 + state.untaggedCount = untaggedResult.data.length; 75 + debug && console.log('Untagged addresses:', state.untaggedCount); 76 + } else { 77 + state.untaggedCount = 0; 78 + } 60 79 }; 61 80 62 81 /** ··· 109 128 const container = document.querySelector('.cards'); 110 129 container.innerHTML = ''; 111 130 112 - if (state.tags.length === 0) { 131 + // Always show Untagged group first if there are untagged addresses 132 + if (state.untaggedCount > 0) { 133 + const untaggedCard = createGroupCard({ ...UNTAGGED_GROUP, frequency: state.untaggedCount }); 134 + container.appendChild(untaggedCard); 135 + } 136 + 137 + if (state.tags.length === 0 && state.untaggedCount === 0) { 113 138 container.innerHTML = '<div class="empty-state">No groups yet. Create one to get started.</div>'; 114 139 return; 115 140 } ··· 127 152 state.view = VIEW_ADDRESSES; 128 153 state.currentTag = tag; 129 154 130 - // Load addresses for this tag 131 - await loadAddressesForTag(tag.id); 155 + // Load addresses - handle special untagged group 156 + if (tag.isSpecial && tag.id === '__untagged__') { 157 + const result = await api.datastore.getUntaggedAddresses(); 158 + if (result.success) { 159 + state.addresses = result.data; 160 + } else { 161 + state.addresses = []; 162 + } 163 + } else { 164 + await loadAddressesForTag(tag.id); 165 + } 132 166 133 167 // Update UI 134 168 document.querySelector('.header-title').textContent = tag.name; ··· 156 190 const createGroupCard = (tag) => { 157 191 const card = document.createElement('div'); 158 192 card.className = 'card group-card'; 193 + if (tag.isSpecial) { 194 + card.classList.add('special-group'); 195 + } 159 196 card.dataset.tagId = tag.id; 160 197 161 198 const colorDot = document.createElement('div'); ··· 171 208 172 209 const meta = document.createElement('div'); 173 210 meta.className = 'card-meta'; 174 - meta.textContent = `Used ${tag.frequency || 0} times`; 211 + if (tag.isSpecial) { 212 + meta.textContent = `${tag.frequency || 0} addresses`; 213 + } else { 214 + meta.textContent = `Used ${tag.frequency || 0} times`; 215 + } 175 216 176 217 content.appendChild(title); 177 218 content.appendChild(meta);
+30
index.js
··· 1718 1718 } 1719 1719 }); 1720 1720 1721 + // Get addresses that have no tags 1722 + ipcMain.handle('datastore-get-untagged-addresses', async (ev, data) => { 1723 + try { 1724 + const addressTagsTable = datastoreStore.getTable('address_tags'); 1725 + const addressesTable = datastoreStore.getTable('addresses'); 1726 + 1727 + // Get all address IDs that have at least one tag 1728 + const taggedAddressIds = new Set(); 1729 + for (const [, link] of Object.entries(addressTagsTable)) { 1730 + taggedAddressIds.add(link.addressId); 1731 + } 1732 + 1733 + // Get addresses that are NOT in the tagged set 1734 + const untaggedAddresses = []; 1735 + for (const [id, addr] of Object.entries(addressesTable)) { 1736 + if (!taggedAddressIds.has(id)) { 1737 + untaggedAddresses.push({ id, ...addr }); 1738 + } 1739 + } 1740 + 1741 + // Sort by visitCount descending 1742 + untaggedAddresses.sort((a, b) => (b.visitCount || 0) - (a.visitCount || 0)); 1743 + 1744 + return { success: true, data: untaggedAddresses }; 1745 + } catch (error) { 1746 + console.error('datastore-get-untagged-addresses error:', error); 1747 + return { success: false, error: error.message }; 1748 + } 1749 + }); 1750 + 1721 1751 const modWindow = (bw, params) => { 1722 1752 if (params.action == 'close') { 1723 1753 bw.close();
+3
preload.js
··· 273 273 }, 274 274 getAddressesByTag: (tagId) => { 275 275 return ipcRenderer.invoke('datastore-get-addresses-by-tag', { tagId }); 276 + }, 277 + getUntaggedAddresses: () => { 278 + return ipcRenderer.invoke('datastore-get-untagged-addresses', {}); 276 279 } 277 280 }; 278 281