Skip to content

Commit f180b58

Browse files
committed
update select
1 parent e2725b0 commit f180b58

File tree

7 files changed

+186
-80
lines changed

7 files changed

+186
-80
lines changed

frontend/src/features/browser-profiles/select-browser-profile.ts

Lines changed: 135 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,38 @@
11
import { localized, msg } from "@lit/localize";
2+
import { Task } from "@lit/task";
23
import type {
34
SlChangeEvent,
45
SlDrawer,
56
SlSelect,
67
} from "@shoelace-style/shoelace";
7-
import { html, nothing, type PropertyValues } from "lit";
8+
import clsx from "clsx";
9+
import { html, nothing } from "lit";
810
import { customElement, property, query, state } from "lit/decorators.js";
911
import { ifDefined } from "lit/directives/if-defined.js";
1012
import { when } from "lit/directives/when.js";
11-
import orderBy from "lodash/fp/orderBy";
13+
import queryString from "query-string";
1214

1315
import { BtrixElement } from "@/classes/BtrixElement";
1416
import { none } from "@/layouts/empty";
1517
import { pageHeading } from "@/layouts/page";
1618
import { CrawlerChannelImage, type Profile } from "@/pages/org/types";
1719
import { OrgTab } from "@/routes";
18-
import type { APIPaginatedList } from "@/types/api";
20+
import type {
21+
APIPaginatedList,
22+
APIPaginationQuery,
23+
APISortQuery,
24+
} from "@/types/api";
25+
import { SortDirection } from "@/types/utils";
1926
import { AppStateService } from "@/utils/state";
27+
import { tw } from "@/utils/tailwind";
2028

2129
type SelectBrowserProfileChangeDetail = {
2230
value: Profile | undefined;
2331
};
2432

33+
// TODO Paginate results
34+
const INITIAL_PAGE_SIZE = 1000;
35+
2536
export type SelectBrowserProfileChangeEvent =
2637
CustomEvent<SelectBrowserProfileChangeDetail>;
2738

@@ -47,68 +58,132 @@ export class SelectBrowserProfile extends BtrixElement {
4758
profileId?: string;
4859

4960
@state()
50-
private selectedProfile?: Profile;
61+
selectedProfile?: Profile;
5162

52-
@state()
53-
private browserProfiles?: Profile[];
63+
@query("sl-select")
64+
private readonly select?: SlSelect | null;
5465

5566
@query("sl-drawer")
5667
private readonly drawer?: SlDrawer | null;
5768

58-
willUpdate(changedProperties: PropertyValues<this>) {
59-
if (changedProperties.has("profileId")) {
60-
void this.updateSelectedProfile();
61-
}
69+
public get value() {
70+
return this.select?.value as string;
6271
}
6372

64-
firstUpdated() {
65-
void this.updateSelectedProfile();
73+
private readonly profilesTask = new Task(this, {
74+
task: async (_args, { signal }) => {
75+
return this.getProfiles(
76+
{
77+
sortBy: "name",
78+
sortDirection: SortDirection.Ascending,
79+
pageSize: INITIAL_PAGE_SIZE,
80+
},
81+
signal,
82+
);
83+
},
84+
args: () => [] as const,
85+
});
86+
87+
private readonly selectedProfileTask = new Task(this, {
88+
task: async ([profileId, profiles], { signal }) => {
89+
if (!profileId || !profiles || signal.aborted) return;
90+
91+
this.selectedProfile = this.findProfileById(profileId);
92+
},
93+
args: () => [this.profileId, this.profilesTask.value] as const,
94+
});
95+
96+
private findProfileById(profileId?: string) {
97+
if (!profileId) return;
98+
return this.profilesTask.value?.items.find(({ id }) => id === profileId);
6699
}
67100

68101
render() {
102+
const selectedProfile = this.selectedProfile;
103+
const browserProfiles = this.profilesTask.value;
104+
69105
return html`
70106
<sl-select
71-
name="profileid"
72107
label=${msg("Browser Profile")}
73-
value=${this.selectedProfile?.id || ""}
74-
placeholder=${this.browserProfiles
108+
value=${selectedProfile?.id || ""}
109+
placeholder=${browserProfiles
75110
? msg("No custom profile")
76111
: msg("Loading")}
77112
size=${ifDefined(this.size)}
78113
hoist
114+
clearable
79115
@sl-change=${this.onChange}
80-
@sl-focus=${() => {
81-
// Refetch to keep list up to date
82-
void this.fetchBrowserProfiles();
83-
}}
84116
@sl-hide=${this.stopProp}
85117
@sl-after-hide=${this.stopProp}
86118
>
87-
${this.browserProfiles
119+
${when(
120+
selectedProfile?.proxyId,
121+
(proxyId) => html`
122+
<btrix-proxy-badge
123+
slot="suffix"
124+
proxyId=${proxyId}
125+
></btrix-proxy-badge>
126+
`,
127+
)}
128+
${browserProfiles
88129
? html`
89130
<sl-option value="">${msg("No custom profile")}</sl-option>
90-
<sl-divider></sl-divider>
131+
${browserProfiles.items.length
132+
? html`
133+
<sl-divider></sl-divider>
134+
<sl-menu-label>${msg("Saved Profiles")}</sl-menu-label>
135+
`
136+
: nothing}
91137
`
92138
: html` <sl-spinner slot="prefix"></sl-spinner> `}
93-
${this.browserProfiles?.map(
94-
(profile) => html`
95-
<sl-option value=${profile.id}>
96-
${profile.name}
97-
<div slot="suffix">
98-
<btrix-format-date
99-
class="text-xs"
100-
.date=${profile.modified || profile.created}
101-
dateStyle="medium"
102-
></btrix-format-date>
139+
${browserProfiles?.items.map(
140+
(profile, i) => html`
141+
<sl-option
142+
value=${profile.id}
143+
class=${clsx(
144+
tw`part-[base]:flex-wrap`,
145+
tw`part-[prefix]:order-2`,
146+
tw`part-[label]:order-1 part-[label]:basis-1/2 part-[label]:overflow-hidden`,
147+
tw`part-[suffix]:order-3 part-[suffix]:basis-full part-[suffix]:overflow-hidden`,
148+
i && tw`border-t`,
149+
)}
150+
>
151+
<span class="font-medium">${profile.name}</span>
152+
<span
153+
class="whitespace-nowrap text-xs text-neutral-500"
154+
slot="prefix"
155+
>
156+
${this.localize.relativeDate(
157+
profile.modified || profile.created,
158+
{ capitalize: true },
159+
)}
160+
</span>
161+
<div
162+
slot="suffix"
163+
class="flex w-full items-center justify-between gap-1.5 overflow-hidden pl-2.5 pt-0.5"
164+
>
165+
<btrix-code
166+
class="w-0 flex-1 text-xs"
167+
language="url"
168+
value=${profile.origins[0]}
169+
noWrap
170+
truncate
171+
></btrix-code>
172+
${when(
173+
profile.proxyId,
174+
(proxyId) => html`
175+
<btrix-proxy-badge proxyId=${proxyId}></btrix-proxy-badge>
176+
`,
177+
)}
103178
</div>
104179
</sl-option>
105180
`,
106181
)}
107-
${this.browserProfiles && !this.browserProfiles.length
182+
${browserProfiles && !browserProfiles.total
108183
? this.renderNoProfiles()
109184
: ""}
110185
<div slot="help-text" class="flex justify-between">
111-
${this.selectedProfile
186+
${selectedProfile
112187
? html`
113188
<button
114189
class="text-blue-500 transition-colors duration-fast hover:text-blue-600"
@@ -119,13 +194,12 @@ export class SelectBrowserProfile extends BtrixElement {
119194
<span>
120195
${msg("Last saved")}
121196
${this.localize.relativeDate(
122-
this.selectedProfile.modified ||
123-
this.selectedProfile.created,
197+
selectedProfile.modified || selectedProfile.created,
124198
{ capitalize: true },
125199
)}
126200
</span>
127201
`
128-
: this.browserProfiles
202+
: browserProfiles
129203
? html`
130204
<btrix-link
131205
class="ml-auto"
@@ -140,7 +214,9 @@ export class SelectBrowserProfile extends BtrixElement {
140214
</div>
141215
</sl-select>
142216
143-
${this.browserProfiles?.length ? this.renderSelectedProfileInfo() : ""}
217+
${browserProfiles || selectedProfile
218+
? this.renderSelectedProfileInfo()
219+
: ""}
144220
`;
145221
}
146222

@@ -285,9 +361,8 @@ export class SelectBrowserProfile extends BtrixElement {
285361
}
286362

287363
private async onChange(e: SlChangeEvent) {
288-
this.selectedProfile = this.browserProfiles?.find(
289-
({ id }) => id === (e.target as SlSelect | null)?.value,
290-
);
364+
const profileId = (e.target as SlSelect | null)?.value as string;
365+
this.selectedProfile = this.findProfileById(profileId);
291366

292367
await this.updateComplete;
293368

@@ -300,43 +375,30 @@ export class SelectBrowserProfile extends BtrixElement {
300375
);
301376
}
302377

303-
private async updateSelectedProfile() {
304-
await this.fetchBrowserProfiles();
305-
await this.updateComplete;
306-
307-
if (this.profileId && !this.selectedProfile) {
308-
this.selectedProfile = this.browserProfiles?.find(
309-
({ id }) => id === this.profileId,
310-
);
311-
}
312-
}
313-
314-
/**
315-
* Fetch browser profiles and update internal state
316-
*/
317-
private async fetchBrowserProfiles(): Promise<void> {
318-
try {
319-
const data = await this.getProfiles();
320-
321-
this.browserProfiles = orderBy(["name", "modified"])(["asc", "desc"])(
322-
data,
323-
) as Profile[];
324-
} catch (e) {
325-
this.notify.toast({
326-
message: msg("Sorry, couldn't retrieve browser profiles at this time."),
327-
variant: "danger",
328-
icon: "exclamation-octagon",
329-
id: "browser-profile-status",
330-
});
331-
}
332-
}
378+
private async getProfiles(
379+
params: {
380+
userid?: string;
381+
tags?: string[];
382+
tagMatch?: string;
383+
} & APIPaginationQuery &
384+
APISortQuery,
385+
signal: AbortSignal,
386+
) {
387+
const query = queryString.stringify(
388+
{
389+
...params,
390+
},
391+
{
392+
arrayFormat: "none", // For tags
393+
},
394+
);
333395

334-
private async getProfiles() {
335396
const data = await this.api.fetch<APIPaginatedList<Profile>>(
336-
`/orgs/${this.orgId}/profiles`,
397+
`/orgs/${this.orgId}/profiles?${query}`,
398+
{ signal },
337399
);
338400

339-
return data.items;
401+
return data;
340402
}
341403

342404
/**

frontend/src/features/crawl-workflows/workflow-editor.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { consume } from "@lit/context";
22
import { localized, msg, str } from "@lit/localize";
3+
import { Task } from "@lit/task";
34
import type {
45
SlBlurEvent,
56
SlChangeEvent,
@@ -95,6 +96,7 @@ import {
9596
Behavior,
9697
CrawlerChannelImage,
9798
ScopeType,
99+
type Profile,
98100
type Seed,
99101
type WorkflowParams,
100102
} from "@/types/crawler";
@@ -419,6 +421,15 @@ export class WorkflowEditor extends BtrixElement {
419421
// https://github.com/webrecorder/browsertrix-crawler/blob/v1.5.8/package.json#L23
420422
private readonly cssParser = createParser();
421423

424+
private readonly profileTask = new Task(this, {
425+
task: async ([formState], { signal }) => {
426+
if (!formState.browserProfile) return;
427+
428+
return this.getProfile(formState.browserProfile.id, signal);
429+
},
430+
args: () => [this.formState] as const,
431+
});
432+
422433
connectedCallback(): void {
423434
this.initializeEditor();
424435
super.connectedCallback();
@@ -1982,6 +1993,11 @@ https://archiveweb.page/images/${"logo.svg"}`}
19821993
if (!this.formState.lang) throw new Error("missing formstate.lang");
19831994

19841995
const proxies = this.proxies;
1996+
const selectedProfile = this.profileTask.value;
1997+
const selectedProxyId =
1998+
this.formState.browserProfile?.proxyId ||
1999+
selectedProfile?.proxyId ||
2000+
this.formState.proxyId;
19852001

19862002
return html`
19872003
${inputCol(html`
@@ -2006,7 +2022,7 @@ https://archiveweb.page/images/${"logo.svg"}`}
20062022
proxies.default_proxy_id ?? undefined,
20072023
)}
20082024
.proxyServers=${proxies.servers}
2009-
.proxyId="${this.formState.proxyId || ""}"
2025+
.proxyId=${selectedProxyId || ""}
20102026
?disabled=${!!this.formState.browserProfile}
20112027
@btrix-change=${(e: SelectCrawlerProxyChangeEvent) =>
20122028
this.updateFormState({
@@ -2021,7 +2037,7 @@ https://archiveweb.page/images/${"logo.svg"}`}
20212037
class="mr-0.5 align-[-.175em]"
20222038
name="info-circle"
20232039
></sl-icon>
2024-
${msg("Using proxy from browser profile.")}
2040+
${msg("Using browser profile proxy settings.")}
20252041
</div>
20262042
`,
20272043
)}
@@ -3270,7 +3286,7 @@ https://archiveweb.page/images/${"logo.svg"}`}
32703286
},
32713287
crawlerChannel:
32723288
this.formState.crawlerChannel || CrawlerChannelImage.Default,
3273-
proxyId: this.formState.proxyId,
3289+
proxyId: this.formState.browserProfile?.proxyId || this.formState.proxyId,
32743290
};
32753291

32763292
return config;
@@ -3427,4 +3443,13 @@ https://archiveweb.page/images/${"logo.svg"}`}
34273443
console.debug(e);
34283444
}
34293445
}
3446+
3447+
private async getProfile(profileId: string, signal: AbortSignal) {
3448+
const data = await this.api.fetch<Profile>(
3449+
`/orgs/${this.orgId}/profiles/${profileId}`,
3450+
{ signal },
3451+
);
3452+
3453+
return data;
3454+
}
34303455
}

0 commit comments

Comments
 (0)