Skip to content

Commit 1fac628

Browse files
feat(preferences): match collection preferences visual
1 parent d55c3cb commit 1fac628

File tree

4 files changed

+99
-402
lines changed

4 files changed

+99
-402
lines changed

specifyweb/frontend/js_src/lib/components/Preferences/Aside.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ export function PreferencesAside({
2121
readonly references: React.RefObject<WritableArray<HTMLElement | undefined>>;
2222
readonly prefType?: PreferenceType;
2323
}): JSX.Element {
24+
const preferenceRoutes: Record<PreferenceType, string> = {
25+
user: '/specify/user-preferences/',
26+
collection: '/specify/collection-preferences/',
27+
global: '/specify/global-preferences/',
28+
};
29+
const preferencesPath = preferenceRoutes[prefType];
2430
const definitions = usePrefDefinitions(prefType);
2531
const navigate = useNavigate();
2632
const location = useLocation();
@@ -30,13 +36,10 @@ export function PreferencesAside({
3036
() =>
3137
isInOverlay || activeCategory === undefined
3238
? undefined
33-
: navigate(
34-
`/specify/user-preferences/#${definitions[activeCategory][0]}`,
35-
{
36-
replace: true,
37-
}
38-
),
39-
[isInOverlay, definitions, activeCategory]
39+
: navigate(`${preferencesPath}#${definitions[activeCategory][0]}`, {
40+
replace: true,
41+
}),
42+
[isInOverlay, definitions, activeCategory, preferencesPath]
4043
);
4144

4245
const [freezeCategory, setFreezeCategory] = useFrozenCategory();

specifyweb/frontend/js_src/lib/components/Preferences/BasePreferences.tsx

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,16 @@ export class BasePreferences<DEFINITIONS extends GenericPreferences> {
9494
if (typeof this.resourcePromise === 'object') return this.resourcePromise;
9595

9696
const { values, defaultValues } = this.options;
97+
const isAppResourceEndpoint = values.fetchUrl.includes('app.resource');
9798

98-
const valuesResource = fetchResourceId(
99-
values.fetchUrl,
100-
values.resourceName
101-
).then(async (appResourceId) =>
102-
typeof appResourceId === 'number'
103-
? fetchResourceData(values.fetchUrl, appResourceId)
104-
: createDataResource(values.fetchUrl, values.resourceName)
105-
);
99+
const valuesResource = isAppResourceEndpoint
100+
? fetchGlobalResource(values.fetchUrl, values.resourceName)
101+
: fetchResourceId(values.fetchUrl, values.resourceName).then(
102+
async (appResourceId) =>
103+
typeof appResourceId === 'number'
104+
? fetchResourceData(values.fetchUrl, appResourceId)
105+
: createDataResource(values.fetchUrl, values.resourceName)
106+
);
106107

107108
const defaultValuesResource =
108109
defaultValues === undefined
@@ -425,6 +426,8 @@ const mimeType = 'application/json';
425426
/**
426427
* Fetch ID of app resource containing preferences
427428
*/
429+
const appResourceMimeType = 'text/plain';
430+
428431
export const fetchResourceId = async (
429432
fetchUrl: string,
430433
resourceName: string
@@ -449,6 +452,32 @@ const fetchResourceData = async (
449452
headers: { Accept: mimeType },
450453
}).then(({ data }) => data);
451454

455+
const fetchGlobalResource = async (
456+
fetchUrl: string,
457+
resourceName: string
458+
): Promise<ResourceWithData> => {
459+
const url = cacheableUrl(
460+
formatUrl(fetchUrl, {
461+
name: resourceName,
462+
quiet: '',
463+
})
464+
);
465+
const { data, status, response } = await ajax<string>(url, {
466+
headers: { Accept: appResourceMimeType },
467+
expectedErrors: [Http.NO_CONTENT],
468+
});
469+
470+
const parsedId = f.parseInt(response.headers.get('X-Record-ID') ?? undefined);
471+
472+
return {
473+
id: parsedId ?? -1,
474+
data: status === Http.OK ? data : '',
475+
metadata: null,
476+
mimetype: response.headers.get('Content-Type') ?? appResourceMimeType,
477+
name: resourceName,
478+
};
479+
};
480+
452481
/**
453482
* Fetch default values overrides, if exist
454483
*/
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { ajax } from '../../utils/ajax';
2+
import { Http } from '../../utils/ajax/definitions';
3+
import { formatUrl } from '../Router/queryString';
4+
import { contextUnlockedPromise, foreverFetch } from '../InitialContext';
5+
import { type PartialPreferences } from './BasePreferences';
6+
import { globalPreferenceDefinitions } from './GlobalDefinitions';
7+
import { globalPreferences } from './globalPreferences';
8+
import { parseGlobalPreferences } from './globalPreferencesUtils';
9+
10+
export const loadGlobalPreferences = async (): Promise<void> => {
11+
const entryPoint = await contextUnlockedPromise;
12+
if (entryPoint !== 'main') {
13+
await foreverFetch();
14+
return;
15+
}
16+
17+
const { data, status } = await ajax<string>(
18+
formatUrl('/context/app.resource', {
19+
name: 'GlobalPreferences',
20+
quiet: '',
21+
}),
22+
{
23+
headers: { Accept: 'text/plain' },
24+
expectedErrors: [Http.NO_CONTENT],
25+
errorMode: 'visible',
26+
}
27+
);
28+
29+
const { raw } = parseGlobalPreferences(
30+
status === Http.NO_CONTENT ? null : data
31+
);
32+
33+
globalPreferences.setRaw(
34+
raw as PartialPreferences<typeof globalPreferenceDefinitions>
35+
);
36+
};

0 commit comments

Comments
 (0)