Skip to content

Commit a34ac0b

Browse files
committed
Persist recap list oder in local storage
1 parent c4cb1e6 commit a34ac0b

File tree

5 files changed

+80
-15
lines changed

5 files changed

+80
-15
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "koshelf"
3-
version = "1.3.0"
3+
version = "1.4.0"
44
description = "Transform your KOReader library into a beautiful reading dashboard with statistics."
55
repository = "https://github.com/paviro/KOShelf"
66
license = "EUPL-1.2 license"

assets/recap.js

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { StorageManager } from './storage-manager.js';
2+
13
// Recap interactions: year dropdown + navigation
24
document.addEventListener('DOMContentLoaded', () => {
35
const wrapper = document.getElementById('yearSelectorWrapper');
@@ -37,7 +39,8 @@ document.addEventListener('DOMContentLoaded', () => {
3739
const Timeline = document.getElementById('recapTimeline');
3840

3941
if (sortToggle && Timeline) {
40-
let isNewestFirst = true; // Default matches backend
42+
// Read from storage, default to true (Newest First)
43+
let isNewestFirst = StorageManager.get(StorageManager.KEYS.RECAP_SORT_ORDER, true);
4144

4245
// Icons
4346
// Newest First (Sort Descending): Lines + Arrow Down
@@ -50,40 +53,47 @@ document.addEventListener('DOMContentLoaded', () => {
5053
// Arrow: M18 18V6M15 9l3-3 3 3
5154
const iconOldest = `<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7h5M4 12h8M4 17h8M18 18V6M15 9l3-3 3 3"></path>`;
5255

53-
sortToggle.addEventListener('click', () => {
54-
isNewestFirst = !isNewestFirst;
55-
56-
// Update SVG icon
56+
// Function to update the button UI
57+
const updateUI = () => {
5758
const svg = sortToggle.querySelector('svg');
5859
if (svg) {
5960
svg.innerHTML = isNewestFirst ? iconNewest : iconOldest;
6061
}
61-
62-
// Update title
6362
sortToggle.title = isNewestFirst ? "Current: Newest First" : "Current: Oldest First";
63+
};
6464

65+
// Function to flip the DOM order
66+
const flipOrder = () => {
6567
// 1. Reorder Month Groups
6668
const months = Array.from(Timeline.querySelectorAll('.month-group'));
6769
months.reverse().forEach(month => Timeline.appendChild(month));
6870

6971
// 2. Reorder Items within each Month Group (keep header at top)
7072
months.forEach(month => {
71-
// The first child is the "month header" (.recap-event with month label)
72-
// subsequent children are book items
73-
// We only want to reverse the book items, keeping the header first.
7473
const children = Array.from(month.children);
7574
if (children.length > 1) {
7675
const header = children[0]; // first element is month title
7776
const items = children.slice(1); // the rest are books
7877

79-
// Clear content
8078
month.innerHTML = '';
81-
// Apppend header back
8279
month.appendChild(header);
83-
// Append reversed items
8480
items.reverse().forEach(item => month.appendChild(item));
8581
}
8682
});
83+
};
84+
85+
// Apply initial state if different from default (Newest First)
86+
if (!isNewestFirst) {
87+
updateUI();
88+
flipOrder();
89+
}
90+
91+
sortToggle.addEventListener('click', () => {
92+
isNewestFirst = !isNewestFirst;
93+
StorageManager.set(StorageManager.KEYS.RECAP_SORT_ORDER, isNewestFirst);
94+
95+
updateUI();
96+
flipOrder();
8797
});
8898
}
8999
});

assets/storage-manager.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Central LocalStorage Manager
3+
* Handles namespaced storage to avoid collisions and provides type safety helper.
4+
*/
5+
export class StorageManager {
6+
static PREFIX = 'koshelf_';
7+
8+
// Centralized keys configuration
9+
static KEYS = {
10+
RECAP_SORT_ORDER: 'recap_sort_newest_first',
11+
// Add future keys here
12+
};
13+
14+
/**
15+
* Get a value from local storage
16+
* @param {string} key - The key to retrieve (value from StorageManager.KEYS)
17+
* @param {any} defaultValue - Default value if key doesn't exist
18+
* @returns {any} - The stored value or default
19+
*/
20+
static get(key, defaultValue = null) {
21+
try {
22+
const item = localStorage.getItem(this.PREFIX + key);
23+
if (item === null) return defaultValue;
24+
return JSON.parse(item);
25+
} catch (e) {
26+
console.warn('StorageManager: Failed to parse item', e);
27+
return defaultValue;
28+
}
29+
}
30+
31+
/**
32+
* Set a value in local storage
33+
* @param {string} key - The key to set (value from StorageManager.KEYS)
34+
* @param {any} value - The value to store (will be JSON stringified)
35+
*/
36+
static set(key, value) {
37+
try {
38+
localStorage.setItem(this.PREFIX + key, JSON.stringify(value));
39+
} catch (e) {
40+
console.warn('StorageManager: Failed to save item', e);
41+
}
42+
}
43+
44+
/**
45+
* Remove a value from local storage
46+
* @param {string} key - The key to remove (value from StorageManager.KEYS)
47+
*/
48+
static remove(key) {
49+
localStorage.removeItem(this.PREFIX + key);
50+
}
51+
}

src/site_generator.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,10 @@ impl SiteGenerator {
258258
let calendar_init_js_content = include_str!("../assets/calendar.js");
259259
fs::write(self.js_dir().join("calendar.js"), calendar_init_js_content)?;
260260

261+
// Storage utility
262+
let storage_js_content = include_str!("../assets/storage-manager.js");
263+
fs::write(self.js_dir().join("storage-manager.js"), storage_js_content)?;
264+
261265
// Recap small UI logic
262266
let recap_js_content = include_str!("../assets/recap.js");
263267
fs::write(self.js_dir().join("recap.js"), recap_js_content)?;

0 commit comments

Comments
 (0)