Skip to content

Commit e9635eb

Browse files
authored
history: include 'count' for ranges (#1541)
* history: include 'count' for ranges * linting * re-fetch after service calls * cleanup * cleanup * animate
1 parent ec112e8 commit e9635eb

File tree

15 files changed

+254
-146
lines changed

15 files changed

+254
-146
lines changed

special-pages/pages/history/app/components/App.module.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ body {
77
font-weight: var(--body-font-weight);
88
line-height: var(--body-line-height);
99
background-color: var(--history-background-color);
10+
color: var(--history-text-normal);
1011
}
1112

1213
.layout {

special-pages/pages/history/app/components/Item.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,17 @@ export const Item = memo(
3131
const hasTitle = kind === TITLE_KIND || kind === BOTH_KIND;
3232
return (
3333
<Fragment>
34-
{hasTitle && <div className={cn(styles.title)}>{dateRelativeDay}</div>}
34+
{hasTitle && (
35+
<div class={cn(styles.title)} style={{ viewTransitionName: `item-title-${props.id}` }}>
36+
{dateRelativeDay}
37+
</div>
38+
)}
3539
<div
3640
class={cn(styles.row, styles.hover, hasFooterGap && styles.last)}
3741
data-history-entry={props.id}
3842
data-index={index}
3943
aria-selected={selected}
44+
style={{ viewTransitionName: `item-${props.id}` }}
4045
>
4146
<div class={styles.favicon}>
4247
<FaviconWithState

special-pages/pages/history/app/components/Sidebar.js

Lines changed: 30 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import { useTypedTranslation } from '../types.js';
66
import { Trash } from '../icons/Trash.js';
77
import { useTypedTranslationWith } from '../../../new-tab/app/types.js';
88
import { useQueryContext, useQueryDispatch } from '../global/Providers/QueryProvider.js';
9-
import { useHistoryServiceDispatch, useResultsData } from '../global/Providers/HistoryServiceProvider.js';
9+
import { useHistoryServiceDispatch } from '../global/Providers/HistoryServiceProvider.js';
1010

1111
/**
1212
* @import json from "../strings.json"
1313
* @typedef {import('../../types/history.js').Range} Range
14+
* @typedef {import('../../types/history.js').RangeId} RangeId
1415
*/
1516

16-
/** @type {Record<Range, string>} */
17+
/** @type {Record<RangeId, string>} */
1718
const iconMap = {
1819
all: 'icons/all.svg',
1920
today: 'icons/today.svg',
@@ -28,7 +29,7 @@ const iconMap = {
2829
older: 'icons/older.svg',
2930
};
3031

31-
/** @type {Record<Range, (t: (s: keyof json) => string) => string>} */
32+
/** @type {Record<RangeId, (t: (s: keyof json) => string) => string>} */
3233
const titleMap = {
3334
all: (t) => t('range_all'),
3435
today: (t) => t('range_today'),
@@ -53,13 +54,11 @@ export function Sidebar({ ranges }) {
5354
const { t } = useTypedTranslation();
5455
const search = useQueryContext();
5556
const current = useComputed(() => search.value.range);
56-
const results = useResultsData();
57-
const count = useComputed(() => results.value.items.length);
5857
const dispatch = useQueryDispatch();
5958
const historyServiceDispatch = useHistoryServiceDispatch();
6059

6160
/**
62-
* @param {Range} range
61+
* @param {RangeId} range
6362
*/
6463
function onClick(range) {
6564
if (range === 'all') {
@@ -69,7 +68,7 @@ export function Sidebar({ ranges }) {
6968
}
7069
}
7170
/**
72-
* @param {Range} range
71+
* @param {RangeId} range
7372
*/
7473
function onDelete(range) {
7574
historyServiceDispatch({ kind: 'delete-range', value: range });
@@ -80,7 +79,18 @@ export function Sidebar({ ranges }) {
8079
<h1 class={styles.pageTitle}>{t('page_title')}</h1>
8180
<nav class={styles.nav}>
8281
{ranges.value.map((range) => {
83-
return <Item onClick={onClick} onDelete={onDelete} current={current} range={range} ranges={ranges} count={count} />;
82+
console.log(range.id, range.count);
83+
return (
84+
<Item
85+
key={range.id}
86+
onClick={onClick}
87+
onDelete={onDelete}
88+
current={current}
89+
range={range.id}
90+
count={range.count}
91+
ranges={ranges}
92+
/>
93+
);
8494
})}
8595
</nav>
8696
</div>
@@ -91,12 +101,12 @@ export function Sidebar({ ranges }) {
91101
* A component that renders a list item with optional delete actions and a link.
92102
*
93103
* @param {Object} props
94-
* @param {import('@preact/signals').ReadonlySignal<Range|null>} props.current The current selection with a value property.
95-
* @param {Range} props.range The range represented by this item.
96-
* @param {(range: Range) => void} props.onClick Callback function triggered when the range is clicked.
97-
* @param {(range: Range) => void} props.onDelete Callback function triggered when the delete action is clicked.
104+
* @param {import('@preact/signals').ReadonlySignal<RangeId|null>} props.current The current selection with a value property.
105+
* @param {RangeId} props.range The range represented by this item.
106+
* @param {(range: RangeId) => void} props.onClick Callback function triggered when the range is clicked.
107+
* @param {(range: RangeId) => void} props.onDelete Callback function triggered when the delete action is clicked.
98108
* @param {import("@preact/signals").Signal<Range[]>} props.ranges
99-
* @param {import('@preact/signals').ReadonlySignal<number>} props.count The count value associated with the ranges.
109+
* @param {number} props.count The count value associated with the ranges.
100110
*/
101111
function Item({ current, range, onClick, onDelete, ranges, count }) {
102112
const { t } = useTypedTranslation();
@@ -124,46 +134,27 @@ function Item({ current, range, onClick, onDelete, ranges, count }) {
124134
</span>
125135
{titleMap[range](t)}
126136
</button>
127-
{range === 'all' && <DeleteAllButton onClick={onDelete} ariaLabel={buttonLabel} range={range} ranges={ranges} count={count} />}
128-
{range !== 'all' && <DeleteButton onClick={() => onDelete(range)} label={buttonLabel} range={range} />}
137+
<DeleteAllButton onClick={onDelete} ariaLabel={buttonLabel} range={range} ranges={ranges} count={count} />
129138
</div>
130139
);
131140
}
132141

133-
/**
134-
* @param {Object} props
135-
* @param {Range} props.range The range value used for filtering and identification.
136-
* @param {string} props.label The title or label of the item.
137-
* @param {(range: MouseEvent)=>void} props.onClick
138-
*/
139-
function DeleteButton({ range, onClick, label }) {
140-
return (
141-
<button class={styles.delete} onClick={onClick} aria-label={label} tabindex={0} value={range}>
142-
<Trash />
143-
</button>
144-
);
145-
}
146-
147142
/**
148143
* The 'Delete' button for the 'all' range. This is a separate component because it contains
149144
* logic that's only relevant to this row item.
150145
*
151146
* @param {Object} props - The properties passed to the component.
152-
* @param {Range} props.range - The range value used for filtering and identification.
147+
* @param {RangeId} props.range - The range value used for filtering and identification.
153148
* @param {import('@preact/signals').Signal<Range[]>} props.ranges - A signal containing an array of range values used for navigation.
154149
* @param {string} props.ariaLabel - The accessible label for the delete button.
155-
* @param {(evt: Range) => void} props.onClick - The callback function triggered on button click.
156-
* @param {import('@preact/signals').Signal<number>} props.count - A signal representing the count of items in the range.
150+
* @param {(evt: RangeId) => void} props.onClick - The callback function triggered on button click.
151+
* @param {number} props.count - A signal representing the count of items in the range.
157152
*/
158153
function DeleteAllButton({ range, ranges, onClick, ariaLabel, count }) {
159154
const { t } = useTypedTranslationWith(/** @type {json} */ ({}));
160155

161-
const ariaDisabled = useComputed(() => {
162-
return count.value === 0 && ranges.value.length === 1 ? 'true' : 'false';
163-
});
164-
const buttonTitle = useComputed(() => {
165-
return count.value === 0 && ranges.value.length === 1 ? t('delete_none') : '';
166-
});
156+
const ariaDisabled = count === 0 ? 'true' : 'false';
157+
const buttonTitle = count === 0 ? t('delete_none') : '';
167158

168159
return (
169160
<button
@@ -186,7 +177,7 @@ function DeleteAllButton({ range, ranges, onClick, ariaLabel, count }) {
186177
}
187178

188179
/**
189-
* @param {Range} range
180+
* @param {RangeId} range
190181
* @return {{linkLabel: string, buttonLabel: string}}
191182
*/
192183
function labels(range, t) {

special-pages/pages/history/app/global/Providers/HistoryServiceProvider.js

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,9 @@ export function HistoryServiceProvider({ service, children, initial }) {
9191
service
9292
.deleteRange(range)
9393
.then((resp) => {
94-
if (resp.kind === 'range-deleted') {
94+
if (resp.kind === 'delete') {
9595
queryDispatch({ kind: 'reset' });
96+
service.refreshRanges();
9697
}
9798
})
9899
.catch(console.error);
@@ -103,27 +104,43 @@ export function HistoryServiceProvider({ service, children, initial }) {
103104
service
104105
.deleteDomain(action.domain)
105106
.then((resp) => {
106-
if (resp.kind === 'domain-deleted') {
107+
if (resp.kind === 'delete') {
107108
queryDispatch({ kind: 'reset' });
109+
service.refreshRanges();
108110
}
109111
})
110112
.catch(console.error);
111113
break;
112114
}
113115
case 'delete-entries-by-index': {
114-
service.entriesDelete(action.value).catch(console.error);
116+
service
117+
.entriesDelete(action.value)
118+
.then((resp) => {
119+
if (resp.kind === 'delete') {
120+
service.refreshRanges();
121+
}
122+
})
123+
.catch(console.error);
115124
break;
116125
}
117126
case 'delete-all': {
118-
service.deleteRange('all').catch(console.error);
127+
service
128+
.deleteRange('all')
129+
.then((x) => {
130+
if (x.kind === 'delete') {
131+
service.refreshRanges();
132+
}
133+
})
134+
.catch(console.error);
119135
break;
120136
}
121137
case 'delete-term': {
122138
service
123139
.deleteTerm(action.term)
124140
.then((resp) => {
125-
if (resp.kind === 'term-deleted') {
141+
if (resp.kind === 'delete') {
126142
queryDispatch({ kind: 'reset' });
143+
service.refreshRanges();
127144
}
128145
})
129146
.catch(console.error);
@@ -137,8 +154,10 @@ export function HistoryServiceProvider({ service, children, initial }) {
137154
service
138155
.entriesMenu(action.indexes)
139156
.then((resp) => {
140-
if (resp.kind === 'domain-search') {
157+
if (resp.kind === 'domain-search' && 'value' in resp) {
141158
queryDispatch({ kind: 'search-by-domain', value: resp.value });
159+
} else if (resp.kind === 'delete') {
160+
service.refreshRanges();
142161
}
143162
})
144163
.catch(console.error);

special-pages/pages/history/app/global/Providers/QueryProvider.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import { signal, useSignal } from '@preact/signals';
44

55
/**
66
* @typedef {import('../../../types/history.ts').Range} Range
7+
* @typedef {import('../../../types/history.ts').RangeId} RangeId
78
* @typedef {{
89
* term: string | null,
9-
* range: Range | null,
10+
* range: RangeId | null,
1011
* domain: string | null,
1112
* }} QueryState - this is the value the entire application can read/observe
1213
*/
@@ -22,7 +23,7 @@ const QueryContext = createContext(
2223
/** @type {import('@preact/signals').ReadonlySignal<QueryState>} */ (
2324
signal({
2425
term: /** @type {string|null} */ (null),
25-
range: /** @type {import('../../../types/history.ts').Range|null} */ (null),
26+
range: /** @type {RangeId|null} */ (null),
2627
domain: /** @type {string|null} */ (null),
2728
})
2829
),
@@ -65,7 +66,7 @@ export function QueryProvider({ children, query = { term: '' } }) {
6566
return { term: null, domain: action.value, range: null };
6667
}
6768
case 'search-by-range': {
68-
return { term: null, domain: null, range: /** @type {Range} */ (action.value) };
69+
return { term: null, domain: null, range: /** @type {RangeId} */ (action.value) };
6970
}
7071
case 'search-by-term': {
7172
return { term: action.value, domain: null, range: null };

special-pages/pages/history/app/history.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,24 @@ Retrieves available time ranges for history filtering.
3434

3535
```json
3636
{
37-
"ranges": ["today", "yesterday", "monday", "recentlyOpened"]
37+
"ranges": [
38+
{
39+
"id": "today",
40+
"count": 13
41+
},
42+
{
43+
"id": "yesterday",
44+
"count": 10
45+
},
46+
{
47+
"id": "monday",
48+
"count": 2
49+
},
50+
{
51+
"id": "older",
52+
"count": 120
53+
}
54+
]
3855
}
3956
```
4057

0 commit comments

Comments
 (0)