Skip to content

Commit a4840a2

Browse files
committed
Merge branch 'main' into dbajpeyi/refactor/decouple-import-meta
2 parents 6f3d8d1 + 59fcdfe commit a4840a2

File tree

16 files changed

+149
-56
lines changed

16 files changed

+149
-56
lines changed

injected/src/content-feature.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class ConfigParser {
5050
/**
5151
* @param {import('./content-scope-features.js').LoadArgs} loadArgs
5252
*/
53-
initConfig(loadArgs) {
53+
initLoadArgs(loadArgs) {
5454
const { bundledConfig, site, platform } = loadArgs;
5555
this.#bundledConfig = bundledConfig;
5656
// If we have a bundled config, treat it as a regular config
@@ -352,7 +352,7 @@ export default class ContentFeature extends ConfigParser {
352352
const mark = this.monitor.mark(this.name + 'CallLoad');
353353
this.#args = args;
354354
this.platform = args.platform;
355-
this.initConfig(args);
355+
this.initLoadArgs(args);
356356
this.load(args);
357357
mark.end();
358358
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const Item = memo(
1313
*
1414
* @param {Object} props - An object containing the properties for the item.
1515
* @param {string} props.id - A unique identifier for the item.
16+
* @param {string} props.viewId - A unique identifier for the item, safe to use in CSS names
1617
* @param {string} props.title - The text to be displayed for the item.
1718
* @param {string} props.url - The text to be displayed for the item.
1819
* @param {string} props.domain - The text to be displayed for the domain
@@ -26,13 +27,14 @@ export const Item = memo(
2627
* @param {number} props.faviconMax
2728
*/
2829
function Item(props) {
29-
const { title, kind, etldPlusOne, faviconSrc, faviconMax, dateTimeOfDay, dateRelativeDay, index, selected } = props;
30+
const { viewId, title, kind, etldPlusOne, faviconSrc, faviconMax, dateTimeOfDay, dateRelativeDay, index, selected } = props;
3031
const hasFooterGap = kind === END_KIND || kind === BOTH_KIND;
3132
const hasTitle = kind === TITLE_KIND || kind === BOTH_KIND;
33+
3234
return (
3335
<Fragment>
3436
{hasTitle && (
35-
<div class={cn(styles.title)} style={{ viewTransitionName: `item-title-${props.id}` }}>
37+
<div class={cn(styles.title)} style={{ viewTransitionName: `Item-title-${viewId}` }}>
3638
{dateRelativeDay}
3739
</div>
3840
)}
@@ -41,7 +43,7 @@ export const Item = memo(
4143
data-history-entry={props.id}
4244
data-index={index}
4345
aria-selected={selected}
44-
style={{ viewTransitionName: `item-${props.id}` }}
46+
style={{ viewTransitionName: `Item-item-${viewId}` }}
4547
>
4648
<div class={styles.favicon}>
4749
<FaviconWithState

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ export function Results({ results, selected, onChange }) {
6565
const isSelected = selected.value.has(index);
6666
const faviconMax = item.favicon?.maxAvailableSize ?? DDG_DEFAULT_ICON_SIZE;
6767
const favoriteSrc = item.favicon?.src;
68+
const viewId = results.value.viewIds[index];
6869
return (
6970
<li key={item.id} data-id={item.id} class={cssClassName} style={style} data-is-selected={isSelected}>
7071
<Item
7172
id={item.id}
73+
viewId={viewId}
7274
kind={results.value.heights[index]}
7375
url={item.url}
7476
domain={item.domain}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export function Sidebar({ ranges }) {
6262
*/
6363
function onClick(range) {
6464
if (range === 'all') {
65-
dispatch({ kind: 'reset' });
65+
dispatch({ kind: 'search-by-term', value: '' });
6666
} else if (range) {
6767
dispatch({ kind: 'search-by-range', value: range });
6868
}

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import { paramsToQuery, toRange } from '../../history.service.js';
44
import { useCallback, useContext } from 'preact/hooks';
55
import { useQueryDispatch } from './QueryProvider.js';
66
import { signal, useSignal, useSignalEffect } from '@preact/signals';
7-
import { generateHeights } from '../../utils.js';
7+
import { generateHeights, generateViewIds } from '../../utils.js';
88

99
/**
10-
* @typedef {{kind: 'search-commit', params: URLSearchParams}
10+
* @typedef {import('../../../types/history.ts').HistoryQuery['source']} Source
11+
* @typedef {{kind: 'search-commit', params: URLSearchParams, source: Source}
1112
* | {kind: 'delete-range'; value: string }
1213
* | {kind: 'delete-all'; }
1314
* | {kind: 'delete-term'; term: string }
@@ -31,13 +32,14 @@ const HistoryServiceDispatchContext = createContext(defaultDispatch);
3132
* @typedef {object} Results
3233
* @property {import('../../../types/history.ts').HistoryItem[]} items
3334
* @property {number[]} heights
35+
* @property {string[]} viewIds
3436
*/
3537
/**
3638
* @typedef {import('../../../types/history.ts').Range} Range
3739
* @import { ReadonlySignal } from '@preact/signals'
3840
*/
3941

40-
const ResultsContext = createContext(/** @type {ReadonlySignal<Results>} */ (signal({ items: [], heights: [] })));
42+
const ResultsContext = createContext(/** @type {ReadonlySignal<Results>} */ (signal({ items: [], heights: [], viewIds: [] })));
4143
const RangesContext = createContext(/** @type {ReadonlySignal<Range[]>} */ (signal([])));
4244

4345
/**
@@ -55,13 +57,15 @@ export function HistoryServiceProvider({ service, children, initial }) {
5557
const results = useSignal({
5658
items: initial.query.results,
5759
heights: generateHeights(initial.query.results),
60+
viewIds: generateViewIds(initial.query.results),
5861
});
5962

6063
useSignalEffect(() => {
6164
const unsub = service.onResults((data) => {
6265
results.value = {
6366
items: data.results,
6467
heights: generateHeights(data.results),
68+
viewIds: generateViewIds(data.results),
6569
};
6670
});
6771

@@ -81,7 +85,7 @@ export function HistoryServiceProvider({ service, children, initial }) {
8185
function dispatch(action) {
8286
switch (action.kind) {
8387
case 'search-commit': {
84-
const asQuery = paramsToQuery(action.params);
88+
const asQuery = paramsToQuery(action.params, action.source);
8589
service.trigger(asQuery);
8690
break;
8791
}

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import { signal, useSignal } from '@preact/signals';
55
/**
66
* @typedef {import('../../../types/history.ts').Range} Range
77
* @typedef {import('../../../types/history.ts').RangeId} RangeId
8+
* @typedef {import('../../../types/history.ts').HistoryQuery['source']} Source
89
* @typedef {{
910
* term: string | null,
1011
* range: RangeId | null,
1112
* domain: string | null,
13+
* source: Source,
1214
* }} QueryState - this is the value the entire application can read/observe
1315
*/
1416

@@ -25,6 +27,7 @@ const QueryContext = createContext(
2527
term: /** @type {string|null} */ (null),
2628
range: /** @type {RangeId|null} */ (null),
2729
domain: /** @type {string|null} */ (null),
30+
source: /** @type {Source} */ ('initial'),
2831
})
2932
),
3033
);
@@ -49,6 +52,7 @@ export function QueryProvider({ children, query = { term: '' } }) {
4952
term: 'term' in query ? query.term : null,
5053
range: 'range' in query ? query.range : null,
5154
domain: 'domain' in query ? query.domain : null,
55+
source: /** @type {Source} */ ('initial'),
5256
};
5357
const queryState = useSignal(initial);
5458

@@ -60,19 +64,24 @@ export function QueryProvider({ children, query = { term: '' } }) {
6064
queryState.value = (() => {
6165
switch (action.kind) {
6266
case 'reset': {
63-
return { term: '', domain: null, range: null };
67+
return { term: '', domain: null, range: null, source: /** @type {const} */ ('auto') };
6468
}
6569
case 'search-by-domain': {
66-
return { term: null, domain: action.value, range: null };
70+
return { term: null, domain: action.value, range: null, source: /** @type {const} */ ('user') };
6771
}
6872
case 'search-by-range': {
69-
return { term: null, domain: null, range: /** @type {RangeId} */ (action.value) };
73+
return {
74+
term: null,
75+
domain: null,
76+
range: /** @type {RangeId} */ (action.value),
77+
source: /** @type {const} */ ('user'),
78+
};
7079
}
7180
case 'search-by-term': {
72-
return { term: action.value, domain: null, range: null };
81+
return { term: action.value, domain: null, range: null, source: /** @type {const} */ ('user') };
7382
}
7483
default:
75-
return { term: '', domain: null, range: null };
84+
return { term: '', domain: null, range: null, source: /** @type {const} */ ('auto') };
7685
}
7786
})();
7887
}

special-pages/pages/history/app/global/hooks/useSearchCommit.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,17 @@ export function useSearchCommit() {
2727
clearTimeout(timer);
2828
if (next.term !== null) {
2929
const term = next.term;
30+
const source = next.source;
3031
timer = setTimeout(() => {
3132
const params = new URLSearchParams();
3233
params.set('q', term);
33-
dispatch({ kind: 'search-commit', params });
34+
dispatch({ kind: 'search-commit', params, source });
3435
}, settings.typingDebounce);
3536
}
3637
if (next.domain !== null) {
3738
const params = new URLSearchParams();
3839
params.set('domain', next.domain);
39-
dispatch({ kind: 'search-commit', params });
40+
dispatch({ kind: 'search-commit', params, source: next.source });
4041
}
4142
return null;
4243
});
Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useHistoryServiceDispatch } from '../Providers/HistoryServiceProvider.js';
2-
import { useComputed, useSignalEffect } from '@preact/signals';
32
import { useQueryContext } from '../Providers/QueryProvider.js';
3+
import { useEffect } from 'preact/hooks';
44

55
/**
66
* Synchronizes the `derivedRange` signal with the browser's URL and issues a
@@ -10,14 +10,12 @@ import { useQueryContext } from '../Providers/QueryProvider.js';
1010
export function useSearchCommitForRange() {
1111
const dispatch = useHistoryServiceDispatch();
1212
const query = useQueryContext();
13-
const derivedRange = useComputed(() => {
14-
return query.value.range;
15-
});
1613

17-
useSignalEffect(() => {
14+
useEffect(() => {
1815
let timer;
1916
let counter = 0;
20-
const sub = derivedRange.subscribe((nextRange) => {
17+
const sub = query.subscribe((nextQuery) => {
18+
const { range } = nextQuery;
2119
if (counter === 0) {
2220
counter += 1;
2321
return;
@@ -27,16 +25,16 @@ export function useSearchCommitForRange() {
2725
url.searchParams.delete('q');
2826
url.searchParams.delete('range');
2927

30-
if (nextRange !== null) {
31-
url.searchParams.set('range', nextRange);
28+
if (range !== null) {
29+
url.searchParams.set('range', range);
3230
window.history.replaceState(null, '', url.toString());
33-
dispatch({ kind: 'search-commit', params: new URLSearchParams(url.searchParams) });
31+
dispatch({ kind: 'search-commit', params: new URLSearchParams(url.searchParams), source: 'user' });
3432
}
3533
});
3634

3735
return () => {
3836
sub();
3937
clearTimeout(timer);
4038
};
41-
});
39+
}, [query, dispatch]);
4240
}

special-pages/pages/history/app/history.service.js

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export class HistoryService {
8989
* concatenate results if this was a 'fetch more' request, or overwrite
9090
*/
9191
let valueToPublish;
92-
if (eq(old.info.query, next.info.query) && next.lastQueryParams?.offset > 0) {
92+
if (queryEq(old.info.query, next.info.query) && next.lastQueryParams?.offset > 0) {
9393
const results = old.results.concat(next.results);
9494
valueToPublish = { info: next.info, results, lastQueryParams: next.lastQueryParams };
9595
} else {
@@ -134,6 +134,7 @@ export class HistoryService {
134134
query: lastquery,
135135
limit: HistoryService.CHUNK_SIZE,
136136
offset: this.data.results.length,
137+
source: 'user',
137138
};
138139
this.internal.dispatchEvent(new CustomEvent(HistoryService.QUERY_EVENT, { detail: query }));
139140
}
@@ -156,7 +157,7 @@ export class HistoryService {
156157
* @param {HistoryQuery} query
157158
*/
158159
queryFetcher(query) {
159-
console.log(`🦻 [query] ${JSON.stringify(query.query)} offset: ${query.offset}, limit: ${query.limit}`);
160+
console.log(`🦻 [query] ${JSON.stringify(query.query)} offset: ${query.offset}, limit: ${query.limit} source: ${query.source}`);
160161
// eslint-disable-next-line promise/prefer-await-to-then
161162
return this.history.messaging.request('query', query).then((resp) => {
162163
return { info: resp.info, results: resp.value, lastQueryParams: query };
@@ -374,9 +375,10 @@ function deleteByIndexes(old, indexes) {
374375

375376
/**
376377
* @param {URLSearchParams} params
378+
* @param {HistoryQuery['source']} source
377379
* @return {HistoryQuery}
378380
*/
379-
export function paramsToQuery(params) {
381+
export function paramsToQuery(params, source) {
380382
/** @type {HistoryQuery['query'] | undefined} */
381383
let query;
382384
const range = toRange(params.get('range'));
@@ -396,6 +398,7 @@ export function paramsToQuery(params) {
396398
query,
397399
limit: HistoryService.CHUNK_SIZE,
398400
offset: 0,
401+
source,
399402
};
400403
}
401404

@@ -423,11 +426,27 @@ export function toRange(input) {
423426
}
424427

425428
/**
426-
* @param {Record<string, any>|null|undefined} q1
427-
* @param {Record<string, any>|null|undefined} q2
428-
* @return {boolean}
429+
* @param {HistoryQuery} a
430+
* @param {HistoryQuery|null} [b]
431+
* @returns {boolean}
429432
*/
430-
function eq(q1, q2) {
431-
if (!q1 || !q2) return false;
432-
return JSON.stringify(q1) === JSON.stringify(q2);
433+
function eq(a, b) {
434+
if (!b) return false;
435+
if (a.limit !== b.limit) return false;
436+
if (a.offset !== b.offset) return false;
437+
if (a.source !== b.source) return false;
438+
return queryEq(a.query, b.query);
439+
}
440+
441+
/**
442+
* @param {QueryKind} a
443+
* @param {QueryKind|null} [b]
444+
* @returns {boolean}
445+
*/
446+
function queryEq(a, b) {
447+
if (!b) return false;
448+
const k1 = Object.keys(a)[0];
449+
const k2 = Object.keys(b)[0];
450+
if (k1 === k2 && a[k1] === b[k2]) return true;
451+
return false;
433452
}

special-pages/pages/history/app/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export async function init(root, messaging, baseEnvironment) {
6565
});
6666

6767
const service = new HistoryService(messaging);
68-
const query = paramsToQuery(environment.urlParams);
68+
const query = paramsToQuery(environment.urlParams, 'initial');
6969
const initial = await service.getInitial(query);
7070

7171
if (environment.display === 'app') {

special-pages/pages/history/app/utils.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ export function generateHeights(rows) {
3131
return heights;
3232
}
3333

34+
/**
35+
* Convert the items 'id' field into something that is safe to use in
36+
* things CSS view transition names.
37+
*
38+
* Note: I am not checking if the generated id starts with a number,
39+
* because the names will always be prefixed in the component.
40+
*
41+
* @param {import("../types/history").HistoryItem[]} rows
42+
* @return {string[]}
43+
*/
44+
export function generateViewIds(rows) {
45+
return rows.map((row) => {
46+
return btoa(row.id).replace(/=/g, '');
47+
});
48+
}
49+
3450
/**
3551
* @typedef {'ctrl+click' | 'shift+click' | 'click' | 'escape' | 'delete' | 'shift+up' | 'shift+down' | 'up' | 'down' | 'unknown'} Intention
3652
*/

special-pages/pages/history/integration-tests/history-selections.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ test.describe('history selections', () => {
155155
test('`deleteAll` during search (no selections)', async ({ page }, workerInfo) => {
156156
const hp = HistoryTestPage.create(page, workerInfo).withEntries(2000);
157157
await hp.openPage({});
158-
await hp.didMakeNthQuery({ nth: 0, query: { term: '' } });
158+
await hp.didMakeNthQuery({ nth: 0, query: { term: '' }, source: 'initial' });
159159

160160
// do the search
161161
await hp.types('example.com');
@@ -165,7 +165,7 @@ test.describe('history selections', () => {
165165
await hp.deletesAllForTerm('example.com', { action: 'delete' });
166166

167167
// should have reset the UI now
168-
await hp.didMakeNthQuery({ nth: 2, query: { term: '' } });
168+
await hp.didMakeNthQuery({ nth: 2, query: { term: '' }, source: 'auto' });
169169
});
170170
test('removes all selections with ESC key', async ({ page }, workerInfo) => {
171171
const hp = HistoryTestPage.create(page, workerInfo).withEntries(2000);

0 commit comments

Comments
 (0)