Skip to content

Commit 6476ae4

Browse files
committed
Merge branch 'dbajpeyi/refactor/decouple-import-meta' of github.com:duckduckgo/content-scope-scripts into dbajpeyi/refactor/decouple-import-meta
2 parents 998e9cc + 7c81cb2 commit 6476ae4

File tree

19 files changed

+259
-60
lines changed

19 files changed

+259
-60
lines changed

.stylelintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"ignoreFiles": ["build/**/*.css", "Sources/**/*.css", "docs/**/*.css", "special-pages/pages/**/*/dist/*.css"],
55
"rules": {
66
"csstree/validator": {
7-
"ignoreProperties": ["text-wrap"]
7+
"ignoreProperties": ["text-wrap", "view-transition-name"]
88
},
99
"alpha-value-notation": null,
1010
"at-rule-empty-line-before": null,

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

special-pages/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
3939
"@formkit/auto-animate": "^0.8.2",
4040
"@preact/signals": "^2.0.1",
41-
"@rive-app/canvas-single": "^2.26.3",
41+
"@rive-app/canvas-single": "^2.26.4",
4242
"classnames": "^2.5.1",
4343
"lottie-web": "^5.12.2",
4444
"preact": "^10.25.4"

special-pages/pages/history/app/components/App.jsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { useSearchCommit } from '../global/hooks/useSearchCommit.js';
1919
import { useRangesData } from '../global/Providers/HistoryServiceProvider.js';
2020
import { usePlatformName } from '../types.js';
2121
import { useLayoutMode } from '../global/hooks/useLayoutMode.js';
22+
import { useClickAnywhereElse } from '../global/hooks/useClickAnywhereElse.jsx';
2223

2324
export function App() {
2425
const platformName = usePlatformName();
@@ -39,6 +40,7 @@ export function App() {
3940
useURLReflection();
4041
useSearchCommit();
4142
useSearchCommitForRange();
43+
const clickAnywhere = useClickAnywhereElse();
4244

4345
/**
4446
* onClick can be passed directly to the main container,
@@ -62,7 +64,13 @@ export function App() {
6264
}, [onKeyDown, query]);
6365

6466
return (
65-
<div class={styles.layout} data-theme={isDarkMode ? 'dark' : 'light'} data-platform={platformName} data-layout-mode={mode}>
67+
<div
68+
class={styles.layout}
69+
data-theme={isDarkMode ? 'dark' : 'light'}
70+
data-platform={platformName}
71+
data-layout-mode={mode}
72+
onClick={clickAnywhere}
73+
>
6674
<aside class={styles.aside}>
6775
<Sidebar ranges={ranges} />
6876
</aside>
@@ -75,3 +83,24 @@ export function App() {
7583
</div>
7684
);
7785
}
86+
87+
export function AppLevelErrorBoundaryFallback({ children }) {
88+
return (
89+
<div class={styles.paddedError}>
90+
<p>{children}</p>
91+
<div class={styles.paddedErrorRecovery}>
92+
You can try to{' '}
93+
<button
94+
onClick={() => {
95+
const current = new URL(window.location.href);
96+
current.search = '';
97+
current.pathname = '';
98+
location.href = current.toString();
99+
}}
100+
>
101+
Reload this page
102+
</button>
103+
</div>
104+
</div>
105+
);
106+
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ body {
4040
grid-area: header;
4141
padding-left: var(--main-padding-left);
4242
padding-right: var(--main-padding-right);
43+
view-transition-name: header;
44+
z-index: 1;
45+
background-color: var(--history-background-color);
4346
}
4447
.search {
4548
justify-self: flex-end;
@@ -106,3 +109,11 @@ body {
106109
}
107110
}
108111
}
112+
113+
.paddedError {
114+
padding: 1rem;
115+
}
116+
117+
.paddedErrorRecovery {
118+
margin-top: 1rem;
119+
}

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,38 @@ import { h } from 'preact';
22
import { useTypedTranslation } from '../types.js';
33
import cn from 'classnames';
44
import styles from './VirtualizedList.module.css';
5+
import { useQueryContext } from '../global/Providers/QueryProvider.js';
56

67
/**
78
* Empty state component displayed when no results are available
9+
* @param {object} props
10+
* @param {object} props.title
11+
* @param {object} props.text
812
*/
9-
export function Empty() {
10-
const { t } = useTypedTranslation();
13+
export function Empty({ title, text }) {
1114
return (
1215
<div class={cn(styles.emptyState, styles.emptyStateOffset)}>
1316
<div class={styles.icons}>
1417
<img src="icons/backdrop.svg" width={128} height={96} alt="" />
1518
<img src="icons/clock.svg" width={60} height={60} alt="" class={styles.forground} />
1619
</div>
17-
<h2 class={styles.emptyTitle}>{t('empty_title')}</h2>
18-
<p class={styles.emptyText}>{t('empty_text')}</p>
20+
<h2 class={styles.emptyTitle}>{title}</h2>
21+
<p class={styles.emptyText}>{text}</p>
1922
</div>
2023
);
2124
}
25+
26+
/**
27+
* Use the application state to figure out which title+text to use.
28+
*/
29+
export function EmptyState() {
30+
const { t } = useTypedTranslation();
31+
const query = useQueryContext();
32+
const hasSearch = query.value.term !== null && query.value.term.trim().length > 0;
33+
34+
if (hasSearch) {
35+
return <Empty title={t('no_results_title', { term: `"${query.value.term}"` })} text={t('no_results_text')} />;
36+
}
37+
38+
return <Empty title={t('empty_title')} text={t('empty_text')} />;
39+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { DDG_DEFAULT_ICON_SIZE, OVERSCAN_AMOUNT } from '../constants.js';
33
import { Item } from './Item.js';
44
import styles from './VirtualizedList.module.css';
55
import { VisibleItems } from './VirtualizedList.js';
6-
import { Empty } from './Empty.js';
6+
import { EmptyState } from './Empty.js';
77
import { useSelected, useSelectionState } from '../global/Providers/SelectionProvider.js';
88
import { useHistoryServiceDispatch, useResultsData } from '../global/Providers/HistoryServiceProvider.js';
99
import { useCallback, useEffect } from 'preact/hooks';
@@ -45,7 +45,7 @@ export function ResultsContainer() {
4545
*/
4646
export function Results({ results, selected, onChange }) {
4747
if (results.value.items.length === 0) {
48-
return <Empty />;
48+
return <EmptyState />;
4949
}
5050

5151
/**

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,7 @@ export function useRowInteractions(mainRef) {
9999
if (handled) {
100100
event.preventDefault();
101101
event.stopImmediatePropagation();
102-
} else {
103-
console.log('did not handle selection');
104102
}
105-
} else {
106-
dispatch({ kind: 'reset', reason: 'click occurred outside of rows' });
107103
}
108104
}
109105

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useSelectionDispatch } from '../Providers/SelectionProvider.js';
2+
import { useCallback } from 'preact/hooks';
3+
4+
/**
5+
* Custom hook that creates a callback function to handle click events occurring outside of specified elements.
6+
* The callback dispatches a reset action when the click event target is not a button or an anchor element.
7+
*/
8+
export function useClickAnywhereElse() {
9+
const dispatch = useSelectionDispatch();
10+
return useCallback(
11+
(e) => {
12+
if (e.target?.closest?.('button,a') === null) {
13+
dispatch({ kind: 'reset', reason: 'click occurred outside of rows' });
14+
}
15+
},
16+
[dispatch],
17+
);
18+
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ params for a query: (note: can be an empty string!)
7373
"term": "example.com"
7474
},
7575
"offset": 0,
76-
"limit": 50
76+
"limit": 50,
77+
"source": "initial"
7778
}
7879
```
7980

@@ -85,7 +86,8 @@ params for a range, note: the values here will match what you returned from `get
8586
"range": "today"
8687
},
8788
"offset": 0,
88-
"limit": 50
89+
"limit": 50,
90+
"source": "initial"
8991
}
9092
```
9193

Lines changed: 82 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { h, render } from 'preact';
22
import { EnvironmentProvider, UpdateEnvironment } from '../../../shared/components/EnvironmentProvider.js';
33

4-
import { App } from './components/App.jsx';
4+
import { App, AppLevelErrorBoundaryFallback } from './components/App.jsx';
55
import { Components } from './components/Components.jsx';
66

77
import enStrings from '../public/locales/en/history.json';
@@ -14,6 +14,7 @@ import { HistoryServiceProvider } from './global/Providers/HistoryServiceProvide
1414
import { Settings } from './Settings.js';
1515
import { SelectionProvider } from './global/Providers/SelectionProvider.js';
1616
import { QueryProvider } from './global/Providers/QueryProvider.js';
17+
import { InlineErrorBoundary } from '../../../shared/components/InlineErrorBoundary.js';
1718

1819
/**
1920
* @param {Element} root
@@ -45,47 +46,54 @@ export async function init(root, messaging, baseEnvironment) {
4546
.withDebounce(baseEnvironment.urlParams.get('debounce'))
4647
.withUrlDebounce(baseEnvironment.urlParams.get('urlDebounce'));
4748

48-
console.log('initialSetup', init);
49-
console.log('environment', environment);
50-
console.log('settings', settings);
49+
if (!window.__playwright_01) {
50+
console.log('initialSetup', init);
51+
console.log('environment', environment);
52+
console.log('settings', settings);
53+
}
5154

52-
const strings =
53-
environment.locale === 'en'
54-
? enStrings
55-
: await fetch(`./locales/${environment.locale}/history.json`)
56-
.then((resp) => {
57-
if (!resp.ok) {
58-
throw new Error('did not give a result');
59-
}
60-
return resp.json();
61-
})
62-
.catch((e) => {
63-
console.error('Could not load locale', environment.locale, e);
64-
return enStrings;
65-
});
55+
/**
56+
* @param {string} message
57+
*/
58+
const didCatchInit = (message) => {
59+
messaging.reportInitException({ message });
60+
};
6661

62+
const strings = await getStrings(environment);
6763
const service = new HistoryService(messaging);
6864
const query = paramsToQuery(environment.urlParams, 'initial');
69-
const initial = await service.getInitial(query);
65+
const initial = await fetchInitial(query, service, didCatchInit);
7066

7167
if (environment.display === 'app') {
7268
render(
73-
<EnvironmentProvider debugState={environment.debugState} injectName={environment.injectName} willThrow={environment.willThrow}>
74-
<UpdateEnvironment search={window.location.search} />
75-
<TranslationProvider translationObject={strings} fallback={enStrings} textLength={environment.textLength}>
76-
<MessagingContext.Provider value={messaging}>
77-
<SettingsContext.Provider value={settings}>
78-
<QueryProvider query={query.query}>
79-
<HistoryServiceProvider service={service} initial={initial}>
80-
<SelectionProvider>
81-
<App />
82-
</SelectionProvider>
83-
</HistoryServiceProvider>
84-
</QueryProvider>
85-
</SettingsContext.Provider>
86-
</MessagingContext.Provider>
87-
</TranslationProvider>
88-
</EnvironmentProvider>,
69+
<InlineErrorBoundary
70+
messaging={messaging}
71+
context={'History view application'}
72+
fallback={(message) => {
73+
return <AppLevelErrorBoundaryFallback>{message}</AppLevelErrorBoundaryFallback>;
74+
}}
75+
>
76+
<EnvironmentProvider
77+
debugState={environment.debugState}
78+
injectName={environment.injectName}
79+
willThrow={environment.willThrow}
80+
>
81+
<UpdateEnvironment search={window.location.search} />
82+
<TranslationProvider translationObject={strings} fallback={enStrings} textLength={environment.textLength}>
83+
<MessagingContext.Provider value={messaging}>
84+
<SettingsContext.Provider value={settings}>
85+
<QueryProvider query={query.query}>
86+
<HistoryServiceProvider service={service} initial={initial}>
87+
<SelectionProvider>
88+
<App />
89+
</SelectionProvider>
90+
</HistoryServiceProvider>
91+
</QueryProvider>
92+
</SettingsContext.Provider>
93+
</MessagingContext.Provider>
94+
</TranslationProvider>
95+
</EnvironmentProvider>
96+
</InlineErrorBoundary>,
8997
root,
9098
);
9199
} else if (environment.display === 'components') {
@@ -99,3 +107,42 @@ export async function init(root, messaging, baseEnvironment) {
99107
);
100108
}
101109
}
110+
111+
/**
112+
* @param {import('../types/history.js').HistoryQuery} query
113+
* @param {HistoryService} service
114+
* @param {(message: string) => void} didCatch
115+
* @returns {Promise<import('./history.service.js').InitialServiceData>}
116+
*/
117+
async function fetchInitial(query, service, didCatch) {
118+
try {
119+
return await service.getInitial(query);
120+
} catch (e) {
121+
console.error(e);
122+
didCatch(e.message || String(e));
123+
return {
124+
ranges: {
125+
ranges: [{ id: 'all', count: 0 }],
126+
},
127+
query: {
128+
info: { query: { term: '' }, finished: true },
129+
results: [],
130+
lastQueryParams: null,
131+
},
132+
};
133+
}
134+
}
135+
136+
/**
137+
* @param {import("../../../shared/environment").Environment} environment
138+
*/
139+
async function getStrings(environment) {
140+
return environment.locale === 'en'
141+
? enStrings
142+
: await fetch(`./locales/${environment.locale}/new-tab.json`)
143+
.then((x) => x.json())
144+
.catch((e) => {
145+
console.error('Could not load locale', environment.locale, e);
146+
return enStrings;
147+
});
148+
}

special-pages/pages/history/app/mocks/mock-transport.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ function queryResponseFrom(memory, msg) {
218218
if ('term' in msg.params.query) {
219219
const { term } = msg.params.query;
220220
if (term !== '') {
221+
if (term === 'empty' || term.includes('"') || term.includes('<')) {
222+
return asResponse([], msg.params.offset, msg.params.limit);
223+
}
221224
if (term === 'empty') {
222225
return asResponse([], msg.params.offset, msg.params.limit);
223226
}

0 commit comments

Comments
 (0)