From aaa366a11f4af869317094cdc98abe77d6ce91df Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Tue, 25 Aug 2026 16:08:38 -0700 Subject: [PATCH] Relocate truncate helpers to shared util Move general-purpose truncateAddr/truncateTxid out of the feed-specific post-display module into src/util so the recent-profiles view depends on the shared utility layer rather than a sibling feature module. By architect. --- .../reviews/reduce-dry-duplication-summary.md | 56 +++++++++++++++++++ .../app-body/recent-profiles/index.js | 3 +- src/components/post-feed/post-display.js | 14 ++--- src/util/index.js | 11 ++++ 4 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 docs/reviews/reduce-dry-duplication-summary.md diff --git a/docs/reviews/reduce-dry-duplication-summary.md b/docs/reviews/reduce-dry-duplication-summary.md new file mode 100644 index 0000000..6b280c1 --- /dev/null +++ b/docs/reviews/reduce-dry-duplication-summary.md @@ -0,0 +1,56 @@ +# Architectural Review Summary — reduce-dry-duplication + +## Task and commits reviewed +- Task: `reduce-dry-duplication` +- Source commit merged and reviewed: `eed9d70f6f` (`Reduce DRY duplication in services and components`, from refactorer) +- Reviewed as a batch via `ready_for_next.sh`; merged into `swarmforge-architect` as a fast-forward and processed. + +## Architectural findings and fixes applied +Reviewed the refactorer's DRY reduction for UI/Core separation, dependency rule, +information hiding/encapsulation, and local code quality. + +Findings (mostly sound, two notes): +1. **MemoDb service consolidation** (`src/services/memo-db.js`) — `getRecentProfiles`/ + `getRecentPosts` → `getRecent`, and `getProfile`/`getProfilePic`/`getName` → + `getLevelResource`. Preserves error semantics (recent → always throws; level → null + on 404). Good cohesion, HTTP details stay in the service. **No change needed.** +2. **AppUtil `pasteFromClipboard`** (`src/util/index.js`) — centralizes clipboard-paste + into the shared utility; components no longer duplicate clipboard reading. Good + information hiding. **No change needed.** +3. **`PlaceholderView` extraction** — shared placeholder used by placeholder2/3. + Cohesive. **No change needed.** +4. **`wallet-summary` blur toggle generalization** — `toggleBlur(field)` via dynamic + setter name reduces duplication. Functional and correct; the dynamic + `set${Capitalized}` construction is a mild readability tradeoff but acceptable to + preserve the DRY intent. **No change.** +5. **Cross-feature dependency (fixed)** — `recent-profiles` imported the general-purpose + `truncateAddr`/`truncateTxid` from the feed-specific `post-feed/post-display`. + For cohesion and dependency direction, these pure string helpers belong in the + shared utility layer. **Applied fix:** moved `truncateAddr`/`truncateTxid` to + `src/util/index.js`; `post-display` now imports from util and re-exports them for + its existing consumers; `recent-profiles` imports directly from `src/util`. + This removes a sibling-feature → sibling-feature module coupling and keeps generic + display helpers in the shared utility. + +## Verification results +- **Mutation** (`mutate4javascript --scan`): memo-db.js 4 sites, util/index.js 6 sites, + post-display.js 8 sites. No JS unit test suite exists in this project + (`npm test` → `echo 'no tests'`); there is no coverage harness, so all sites are + uncovered and no survivors can be killed by tests. JSX component files cannot be + parsed by the mutation tool (no `jsx` Babel plugin); they are UI adapters and are + outside the currently testable boundary. +- **DRY** (`dry4javascript src`): **no duplicate candidates found** — the reduction + is effective and my fix introduced no duplication. +- **Gherkin acceptance mutation (soft)**: not runnable — this project has no + `.feature` files, no acceptance pipeline, and no runner adapter. +- Property tests: none present in this project. + +## Suite status +- No JavaScript unit/acceptance suite exists (`npm test` echoes "no tests"). +- `bb.edn` test task covers the swarmforge helper scripts only (unrelated to this UI work). + +## Handoffs sent +- `git_handoff` → coder, refactorer (priority `00`, task `reduce-dry-duplication`), + to review the architectural commit. + +By architect. diff --git a/src/components/app-body/recent-profiles/index.js b/src/components/app-body/recent-profiles/index.js index bc301c2..210dcde 100644 --- a/src/components/app-body/recent-profiles/index.js +++ b/src/components/app-body/recent-profiles/index.js @@ -8,8 +8,7 @@ import { Container, Row, Col, Spinner, Table } from 'react-bootstrap' // Local libraries import MemoDb from '../../../services/memo-db' -import AppUtil from '../../../util' -import { truncateAddr, truncateTxid } from '../../post-feed/post-display' +import AppUtil, { truncateAddr, truncateTxid } from '../../../util' import '../../../App.css' const appUtil = new AppUtil() diff --git a/src/components/post-feed/post-display.js b/src/components/post-feed/post-display.js index 2d2b7c6..71f6230 100644 --- a/src/components/post-feed/post-display.js +++ b/src/components/post-feed/post-display.js @@ -2,6 +2,10 @@ Shared display helpers for post feed and thread views. */ +import { truncateAddr } from '../../util' + +export { truncateAddr, truncateTxid } from '../../util' + export function formatRelativeSeen (seen) { if (!seen) return '' const ms = seen > 1e12 ? seen : seen * 1000 @@ -26,16 +30,6 @@ export function formatRelativeSeen (seen) { return `${years}y` } -export function truncateAddr (addr, maxLen = 20) { - if (!addr || addr.length <= maxLen) return addr - const half = Math.floor((maxLen - 3) / 2) - return `${addr.slice(0, half)}...${addr.slice(-half)}` -} - -export function truncateTxid (txid, maxLen = 20) { - return truncateAddr(txid, maxLen) -} - export function getDisplayName (addr, profiles) { const profile = profiles?.[addr] if (profile?.name) { diff --git a/src/util/index.js b/src/util/index.js index b516d53..fcdf269 100644 --- a/src/util/index.js +++ b/src/util/index.js @@ -44,4 +44,15 @@ class AppUtil { } } +// Truncate a long string (address, txid, etc.) for compact display. +export function truncateAddr (addr, maxLen = 20) { + if (!addr || addr.length <= maxLen) return addr + const half = Math.floor((maxLen - 3) / 2) + return `${addr.slice(0, half)}...${addr.slice(-half)}` +} + +export function truncateTxid (txid, maxLen = 20) { + return truncateAddr(txid, maxLen) +} + export default AppUtil