Skip to content

Commit d3890b1

Browse files
committed
ntp: batched api
1 parent 7a597a2 commit d3890b1

23 files changed

+1069
-217
lines changed

special-pages/pages/new-tab/app/activity/ActivityProvider.js

Lines changed: 194 additions & 125 deletions
Large diffs are not rendered by default.

special-pages/pages/new-tab/app/activity/activity.md

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,35 @@ Notes:
6262
- {@link "NewTab Messages".ActivityGetConfigRequest}
6363
- Used to fetch the initial config data (eg: expanded vs collapsed)
6464
- returns {@link "NewTab Messages".ActivityConfig}
65-
-
65+
66+
### `activity_getUrls`
67+
- {@link "NewTab Messages".ActivityGetUrlsRequest}
68+
- Used to fetch the initial config data (eg: expanded vs collapsed)
69+
- returns {@link "NewTab Messages".UrlInfo}
70+
71+
```json
72+
{
73+
"urls": ["..."],
74+
"totalTrackersBlocked": 123
75+
}
76+
```
77+
78+
### `activity_getDataForUrls`
79+
- {@link "NewTab Messages".ActivityGetDataForUrlsRequest}
80+
- Used to confirm the burn action - native side may or may not show a modal
81+
- sends {@link "NewTab Messages".DataForUrlsParams}
82+
- returns {@link "NewTab Messages".ActivityData}
83+
- Note: This response is the same format as `activity_getData`, where DomainActivity items are delivered under `.activity`
84+
85+
```json
86+
{
87+
"activity": [
88+
{"...": "..."}
89+
]
90+
}
91+
```
92+
93+
6694
### `activity_confirmBurn`
6795
- {@link "NewTab Messages".ActivityConfirmBurnRequest}
6896
- Used to confirm the burn action - native side may or may not show a modal
@@ -93,6 +121,27 @@ by sending the notification `activity_burnAnimationComplete`
93121
- The activity data used in the feed.
94122
- returns {@link "NewTab Messages".ActivityData}
95123

124+
### `activity_onDataPatch`
125+
- {@link "NewTab Messages".ActivityOnDataPatchSubscription}
126+
- The activity data used in the feed.
127+
- returns {@link "NewTab Messages".UrlInfo} + optional {@link "NewTab Messages".PatchData}
128+
129+
```json
130+
{
131+
"urls": ["..."],
132+
"totalTrackersBlocked": 123
133+
}
134+
```
135+
```json
136+
{
137+
"urls": ["..."],
138+
"totalTrackersBlocked": 123,
139+
"patch": {
140+
"...": "..."
141+
}
142+
}
143+
```
144+
96145
### `activity_onConfigUpdate`
97146
- {@link "NewTab Messages".ActivityOnDataUpdateSubscription }
98147
- The widget config

special-pages/pages/new-tab/app/activity/activity.service.js renamed to special-pages/pages/new-tab/app/activity/batched-activity.service.js

Lines changed: 109 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,61 @@
11
/**
22
* @typedef {import("../../types/new-tab.js").ActivityData} ActivityData
33
* @typedef {import("../../types/new-tab.js").ActivityConfig} ActivityConfig
4+
* @typedef {import("../../types/new-tab.js").UrlInfo} UrlInfo
5+
* @typedef {import("../../types/new-tab.js").PatchData} PatchData
6+
* @typedef {import('../../types/new-tab.js').DomainActivity} DomainActivity
7+
* @typedef {import('../service.js').InvocationSource} InvocationSource
48
*/
59
import { Service } from '../service.js';
610

7-
export class ActivityService {
11+
export class BatchedActivityService {
12+
INITIAL = 5;
13+
CHUNK_SIZE = 10;
14+
isFetchingNext = false;
815
/**
916
* @param {import("../../src/index.js").NewTabPage} ntp - The internal data feed, expected to have a `subscribe` method.
17+
* @param {boolean} batched
1018
* @internal
1119
*/
12-
constructor(ntp) {
20+
constructor(ntp, batched = false) {
1321
this.ntp = ntp;
22+
this.batched = batched;
23+
24+
/** @type {Service<import('../../types/new-tab.js').UrlInfo>} */
25+
this.urlService = new Service({
26+
initial: () => {
27+
if (this.batched) {
28+
return this.ntp.messaging.request('activity_getUrls');
29+
} else {
30+
/** @type {UrlInfo} */
31+
const next = {
32+
urls: [],
33+
totalTrackersBlocked: 0,
34+
};
35+
return Promise.resolve(next);
36+
}
37+
},
38+
subscribe: (cb) => ntp.messaging.subscribe('activity_onDataPatch', cb),
39+
});
40+
1441
/** @type {Service<ActivityData>} */
1542
this.dataService = new Service({
16-
initial: () => ntp.messaging.request('activity_getData'),
43+
initial: (params) => {
44+
if (this.batched) {
45+
return this.ntp.messaging.request('activity_getDataForUrls', { urls: params.urls });
46+
} else {
47+
return this.ntp.messaging.request('activity_getData');
48+
}
49+
},
1750
subscribe: (cb) => ntp.messaging.subscribe('activity_onDataUpdate', cb),
51+
}).withUpdater((old, next, source) => {
52+
if (source === 'manual') {
53+
return next;
54+
}
55+
if (this.batched) {
56+
return { activity: old.activity.concat(next.activity) };
57+
}
58+
return next;
1859
});
1960

2061
/** @type {Service<ActivityConfig>} */
@@ -29,21 +70,31 @@ export class ActivityService {
2970
this.burnUnsub = this.ntp.messaging.subscribe('activity_onBurnComplete', () => {
3071
this.burns?.dispatchEvent(new CustomEvent('activity_onBurnComplete'));
3172
});
73+
this.patchesSub = this.onPatchData();
3274
}
3375

3476
name() {
35-
return 'ActivityService';
77+
return 'BatchedActivity';
3678
}
3779

3880
/**
39-
* @returns {Promise<{data: ActivityData; config: ActivityConfig}>}
81+
* @returns {Promise<{data: ActivityData; config: ActivityConfig }>}
4082
* @internal
4183
*/
4284
async getInitial() {
43-
const p1 = this.configService.fetchInitial();
44-
const p2 = this.dataService.fetchInitial();
45-
const [config, data] = await Promise.all([p1, p2]);
46-
return { config, data };
85+
if (this.batched) {
86+
const configPromise = this.configService.fetchInitial();
87+
const urlsPromise = this.urlService.fetchInitial();
88+
const [config, urlData] = await Promise.all([configPromise, urlsPromise]);
89+
const data = await this.dataService.fetchInitial({ urls: urlData.urls.slice(0, this.INITIAL) });
90+
return { config, data };
91+
} else {
92+
const configPromise = this.configService.fetchInitial();
93+
const dataPromise = this.dataService.fetchInitial();
94+
const urlInfoPromise = this.urlService.fetchInitial();
95+
const [config, data] = await Promise.all([configPromise, dataPromise, urlInfoPromise]);
96+
return { config, data };
97+
}
4798
}
4899

49100
/**
@@ -53,23 +104,62 @@ export class ActivityService {
53104
this.configService.destroy();
54105
this.dataService.destroy();
55106
this.burnUnsub();
107+
this.patchesSub();
56108
this.burns = null;
57109
}
58110

59111
/**
60-
* @param {(evt: {data: ActivityData, source: 'manual' | 'subscription'}) => void} cb
112+
* @param {string[]} urls
113+
*/
114+
next(urls) {
115+
if (!this.urlService.data) throw new Error('unreachable');
116+
if (urls.length === 0) return;
117+
this.isFetchingNext = true;
118+
this.dataService.triggerFetch({ urls });
119+
}
120+
121+
/**
122+
* @param {(evt: {data: UrlInfo & PatchData, source: InvocationSource}) => void} cb
123+
* @internal
124+
*/
125+
onUrlData(cb) {
126+
return this.urlService.onData((params) => {
127+
if ('patch' in params.data && params.data.patch !== null) return console.log('ignoring patch');
128+
cb(params);
129+
});
130+
}
131+
132+
/**
133+
* @internal
134+
*/
135+
onPatchData() {
136+
return this.urlService.onData((params) => {
137+
if (!('patch' in params.data && params.data.patch !== null)) return console.log('ignoring none-patch');
138+
this.dataService.publish({ activity: [/** @type {DomainActivity} */ (params.data.patch)] });
139+
});
140+
}
141+
142+
/**
143+
* @param {(evt: {data: ActivityData, source: InvocationSource}) => void} cb
61144
* @internal
62145
*/
63146
onData(cb) {
64-
return this.dataService.onData(cb);
147+
return this.dataService.onData((data) => {
148+
this.isFetchingNext = false;
149+
cb(data);
150+
});
65151
}
66152

67153
triggerDataFetch() {
68-
return this.dataService.triggerFetch();
154+
if (this.batched) {
155+
this.urlService.triggerFetch();
156+
} else {
157+
this.dataService.triggerFetch();
158+
}
69159
}
70160

71161
/**
72-
* @param {(evt: {data: ActivityConfig, source: 'manual' | 'subscription'}) => void} cb
162+
* @param {(evt: {data: ActivityConfig, source: InvocationSource}) => void} cb
73163
* @internal
74164
*/
75165
onConfig(cb) {
@@ -138,6 +228,12 @@ export class ActivityService {
138228
}),
139229
};
140230
});
231+
this.urlService.update((old) => {
232+
return {
233+
...old,
234+
urls: old.urls.filter((x) => x !== url),
235+
};
236+
});
141237
this.ntp.messaging.notify('activity_removeItem', { url });
142238
}
143239
/**

0 commit comments

Comments
 (0)