Skip to content

Persist story order in URL hash fragment#228

Merged
simonw merged 1 commit intomainfrom
claude/persist-story-order-url-AoJoE
Feb 17, 2026
Merged

Persist story order in URL hash fragment#228
simonw merged 1 commit intomainfrom
claude/persist-story-order-url-AoJoE

Conversation

@simonw
Copy link
Owner

@simonw simonw commented Feb 17, 2026

blog-to-newsletter.html Make it so when I rearrange the order of the stories the new order is persisted in a #order=123,341,555 fragment in the URL bar using their story IDs, and when the page loads that order is reflected - if any of the IDs in that fragment are no longer available on the page those invalid IDs are ignored, if there are three stories but only one is. a valid ID that one comes first

Summary

Added functionality to persist and restore the story order in the newsletter generator by encoding the order in the URL hash fragment. This allows users to share links with a specific story arrangement and have that order automatically applied when the page loads.

Key Changes

  • Added updateUrlHash() function to encode the current story order as a comma-separated list of story IDs in the URL hash (e.g., #order=1,3,2)
  • Added applyOrderFromHash() function to read the story order from the URL hash on page load and reorder stories accordingly, with graceful fallback to default order for any missing or invalid IDs
  • Integrated applyOrderFromHash() call after initializing the default story order from database entries
  • Integrated updateUrlHash() call after drag-and-drop reordering to persist the new order to the URL

Implementation Details

  • The story order is encoded using story IDs extracted from the storyOrder array format ({id}: {title})
  • Hash parsing includes validation to filter out invalid IDs and only apply orders for stories that exist in the current dataset
  • Stories not mentioned in the hash are appended in their default order, ensuring all stories remain visible
  • Uses history.replaceState() to update the URL without creating browser history entries

https://claude.ai/code/session_01C19UgCaTpUpdknig1mnMfj

When stories are reordered via drag-and-drop, the new order is saved
to the URL as #order=id1,id2,id3 using entry IDs. On page load, the
hash is read and applied - invalid IDs (no longer on the page) are
ignored, and valid IDs from the hash are placed first with remaining
stories appended in their default order.

https://claude.ai/code/session_01C19UgCaTpUpdknig1mnMfj
@claude
Copy link
Contributor

claude bot commented Feb 17, 2026

Claude finished @simonw's task —— View job


Code Review Complete ✅

I've completed a comprehensive review of the URL hash persistence feature for story ordering. Here's my detailed analysis:

Review Checklist:

  • Read and analyze the code changes
  • Check for code quality and best practices
  • Identify potential bugs or edge cases
  • Review performance considerations
  • Examine security concerns
  • Assess test coverage needs
  • Provide comprehensive feedback

📋 Summary

The implementation adds URL hash persistence for story ordering (lines 427-455) with two key functions:

  • updateUrlHash() - Encodes story order as comma-separated IDs in hash
  • applyOrderFromHash() - Restores order from URL hash with validation

✅ Strengths

  1. Clean API Design: The functions have clear, descriptive names and focused responsibilities
  2. Graceful Error Handling: Invalid IDs are filtered out, missing stories are appended in default order
  3. Non-Intrusive URL Updates: Uses history.replaceState() to avoid browser history pollution
  4. Robust Integration: Properly integrated with existing drag-and-drop functionality

⚠️ Areas for Improvement

Bug: Potential Race Condition (Line 757)

storyOrder = entries.map(e => `${e.id}: ${e.title}`).reverse();
applyOrderFromHash(); // Called immediately after initialization

If URL hash contains IDs not in current dataset (e.g., different date range), those stories will be lost silently. Consider adding user feedback when hash stories are missing.

Code Quality Issues

String Parsing Pattern (Lines 428, 444, 959):

const ids = storyOrder.map(s => parseInt(s.split(':')[0], 10));

This pattern is repeated 3 times. Consider extracting to a helper function:

function extractStoryId(storyString) {
  return parseInt(storyString.split(':')[0], 10);
}

Magic String (Lines 435, 437):
The #order= prefix is hardcoded in multiple places. Consider defining as a constant:

const ORDER_HASH_PREFIX = '#order=';

Edge Cases

  1. Empty Hash Values: #order=,1,2 would create NaN values that get filtered out (handled correctly)
  2. Malformed Hash: #order=abc,def gets filtered out (handled correctly)
  3. Very Long URLs: Many IDs could create very long URLs, but browsers typically handle 2048+ characters

Performance Considerations

  1. Array Operations: The filter and map operations in applyOrderFromHash() are O(n²) but acceptable for typical story counts
  2. URL Updates: history.replaceState() calls are lightweight
  3. No Unnecessary Re-renders: Updates only occur when actually needed

🔒 Security Assessment

No security concerns identified:

  • Only numeric IDs are processed (parseInt with base 10)
  • No XSS risks - IDs aren't rendered as HTML
  • No external API calls with user input
  • Uses standard browser APIs safely

🧪 Test Coverage Recommendations

Consider adding tests for:

  1. Hash parsing with various malformed inputs (#order=, #order=abc, #order=1,2,invalid)
  2. Story reordering with partial hash matches
  3. URL updates after drag-and-drop operations
  4. Edge case: empty story lists

📊 Performance Profile

  • Time Complexity: O(n²) for hash processing, O(n log n) for sorting - acceptable for expected data size
  • Space Complexity: O(n) for temporary arrays - minimal overhead
  • Browser Compatibility: Uses standard ES6+ features, good modern browser support

🎯 Overall Assessment

Rating: Good ✅

This is a well-implemented feature that adds valuable functionality without compromising existing behavior. The code follows the project's patterns and includes appropriate error handling. The minor issues identified are primarily code quality improvements rather than functional problems.

Recommendation: Approve with suggested refactoring for the repeated string parsing pattern.

@cloudflare-workers-and-pages
Copy link

Deploying tools with  Cloudflare Pages  Cloudflare Pages

Latest commit: 02401fe
Status: ✅  Deploy successful!
Preview URL: https://514b63b2.tools-b1q.pages.dev
Branch Preview URL: https://claude-persist-story-order-u.tools-b1q.pages.dev

View logs

@simonw simonw merged commit 07da623 into main Feb 17, 2026
2 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants