Fix URL update not reflected in list hook (#350)

* Fix URL update not reflected in list hook

* Maintain query prefs on tag click
This commit is contained in:
WithoutPants
2020-02-08 14:54:41 +11:00
committed by GitHub
parent 1d9913d268
commit c99ba68181
2 changed files with 137 additions and 68 deletions

View File

@@ -95,6 +95,21 @@ export interface IListHookOptions {
renderSelectedOptions?: (result: QueryHookResult<any, any>, selectedIds: Set<string>) => JSX.Element | undefined;
}
function updateFromQueryString(queryStr: string, setFilter: (value: React.SetStateAction<ListFilterModel>) => void, forageData?: any) {
const queryParams = queryString.parse(queryStr);
setFilter((f) => {
const newFilter = _.cloneDeep(f);
newFilter.configureFromQueryParameters(queryParams);
if (forageData) {
const forageParams = queryString.parse(forageData.filter);
newFilter.overridePrefs(queryParams, forageParams);
}
return newFilter;
});
}
export class ListHook {
public static useList(options: IListHookOptions): IListHookData {
const [filter, setFilter] = useState<ListFilterModel>(new ListFilterModel(options.filterMode));
@@ -108,8 +123,7 @@ export class ListHook {
const filterListImpl = getFilterListImpl(options.filterMode);
// Update the filter when the query parameters change
// we want to use the local forage only when the location search has not been set
// Initialise from interface forage when loaded
useEffect(() => {
function updateFromLocalForage(queryData: any) {
const queryParams = queryString.parse(queryData.filter);
@@ -123,33 +137,46 @@ export class ListHook {
});
}
function updateFromQueryString(queryStr: string) {
const queryParams = queryString.parse(queryStr);
setFilter((f) => {
const newFilter = _.cloneDeep(f);
newFilter.configureFromQueryParameters(queryParams);
return newFilter;
});
function initialise() {
forageInitialised.current = true;
let forageData: any;
if (interfaceForage.data && interfaceForage.data.queries[options.filterMode]) {
forageData = interfaceForage.data.queries[options.filterMode];
}
if (!options.props!.location.search && forageData) {
// we have some data, try to load it
updateFromLocalForage(forageData);
} else {
// use query string instead - include the forageData to include the following
// preferences if not specified: displayMode, itemsPerPage, sortBy and sortDir
updateFromQueryString(options.props!.location.search, setFilter, forageData);
}
}
// don't use query parameters for sub-components
if (!options.subComponent) {
// do this only once after local forage has been initialised
// initialise once when the forage is loaded
if (!forageInitialised.current && !interfaceForage.loading) {
forageInitialised.current = true;
if (!options.props!.location.search && interfaceForage.data && interfaceForage.data.queries[options.filterMode]) {
let queryData = interfaceForage.data.queries[options.filterMode];
// we have some data, try to load it
updateFromLocalForage(queryData);
} else if (interfaceForage.data) {
// else fallback to query string
updateFromQueryString(options.props!.location.search);
}
initialise();
return;
}
}
}, [interfaceForage.data, interfaceForage.loading, options.props, options.filterMode, options.subComponent]);
// Update the filter when the query parameters change
useEffect(() => {
// don't use query parameters for sub-components
if (!options.subComponent) {
// only update from the URL if the forage is initialised
if (forageInitialised.current) {
updateFromQueryString(options.props!.location.search, setFilter);
}
}
}, [options.props, options.filterMode, options.subComponent]);
function getFilter() {
if (!options.filterHook) {
return filter;
@@ -178,8 +205,12 @@ export class ListHook {
// don't update this until local forage is loaded
if (forageInitialised.current) {
const location = Object.assign({}, options.props.history.location);
location.search = filter.makeQueryParameters();
const includePrefs = true;
location.search = "?" + filter.makeQueryParameters(includePrefs);
if (location.search !== options.props.history.location.search) {
options.props.history.replace(location);
}
setInterfaceForage((d) => {
const dataClone = _.cloneDeep(d);
@@ -367,7 +398,16 @@ export class ListHook {
}
}
const template = (
function getTemplate() {
if (!options.subComponent && !forageInitialised.current) {
return (
<div>
{!result.error ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
{result.error ? <h1>{result.error.message}</h1> : undefined}
</div>
)
} else {
return (
<div>
<ListFilter
onChangePageSize={onChangePageSize}
@@ -390,8 +430,10 @@ export class ListHook {
{maybeRenderContent()}
{maybeRenderPagination()}
</div>
);
)
}
}
return { filter, template, options, onSelectChange };
return { filter, template: getTemplate(), options, onSelectChange };
}
}

View File

@@ -151,10 +151,8 @@ export class ListFilterModel {
this.sortByOptions = [...this.sortByOptions, "created_at", "updated_at"];
}
public configureFromQueryParameters(rawParms: any) {
const params = rawParms as IQueryParameters;
if (params.sortby !== undefined) {
this.sortBy = params.sortby;
private setSortBy(sortBy: any) {
this.sortBy = sortBy;
// parse the random seed if provided
const randomPrefix = "random_";
@@ -169,6 +167,12 @@ export class ListFilterModel {
}
}
}
public configureFromQueryParameters(rawParms: any) {
const params = rawParms as IQueryParameters;
if (params.sortby !== undefined) {
this.setSortBy(params.sortby);
}
if (params.sortdir === "asc" || params.sortdir === "desc") {
this.sortDirection = params.sortdir;
}
@@ -205,6 +209,26 @@ export class ListFilterModel {
}
}
public overridePrefs(original: any, override: any) {
const originalParams = original as IQueryParameters;
const overrideParams = override as IQueryParameters;
if (originalParams.sortby === undefined && overrideParams.sortby !== undefined) {
this.setSortBy(overrideParams.sortby);
}
if (originalParams.sortdir === undefined && overrideParams.sortdir !== undefined) {
if (overrideParams.sortdir === "asc" || overrideParams.sortdir === "desc") {
this.sortDirection = overrideParams.sortdir;
}
}
if (originalParams.disp === undefined && overrideParams.disp !== undefined) {
this.displayMode = parseInt(overrideParams.disp, 10);
}
if (originalParams.perPage === undefined && overrideParams.perPage !== undefined) {
this.itemsPerPage = Number(overrideParams.perPage);
}
}
private setRandomSeed() {
if (this.sortBy == "random") {
// #321 - set the random seed if it is not set
@@ -227,7 +251,8 @@ export class ListFilterModel {
return this.sortBy;
}
public makeQueryParameters(): string {
// includePrefs includes displayMode, sortBy, sortDir and perPage
public makeQueryParameters(includePrefs?: boolean): string {
const encodedCriteria: string[] = [];
this.criteria.forEach((criterion) => {
const encodedCriterion: any = {};
@@ -238,16 +263,18 @@ export class ListFilterModel {
encodedCriteria.push(jsonCriterion);
});
const result = {
sortby: this.getSortBy(),
sortdir: this.sortDirection,
disp: this.displayMode,
const result: any = {
q: this.searchTerm,
p: this.currentPage,
perPage: this.itemsPerPage,
c: encodedCriteria,
};
if (includePrefs) {
result.disp = this.displayMode;
result.perPage = this.itemsPerPage;
result.sortby = this.getSortBy();
result.sortdir = this.sortDirection;
}
return queryString.stringify(result, {encode: false});
}