-
Notifications
You must be signed in to change notification settings - Fork 14
Add search tags functionality #128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { TagSearchPage } from "src/page-loaders"; | ||
import { TagSearchResultSummary } from "types/entities"; | ||
|
||
const parseIntOrThrow = (text: string) => { | ||
essential-randomness marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const match = text.trim().match(/^(\d+)/); | ||
if (!match) { | ||
throw new Error(`Invalid integer: ${text}`); | ||
} | ||
return parseInt(match[1].trim(), 10); | ||
}; | ||
|
||
export const getTotalResults = (page: TagSearchPage) => { | ||
const totalResultsMatch = page("h3.heading") | ||
.first() | ||
.text() | ||
.match(/(\d+)\s+Found/); | ||
return totalResultsMatch ? parseIntOrThrow(totalResultsMatch[1]) : 0; | ||
}; | ||
|
||
export const getPagesCount = (page: TagSearchPage) => { | ||
const lastPageMatch = page(".pagination.actions li:not(.next, .previous)") | ||
.last() | ||
.text(); | ||
return lastPageMatch ? parseIntOrThrow(lastPageMatch) : 0; | ||
}; | ||
|
||
export const getTagsSearchResults = (page: TagSearchPage) => { | ||
return page("ol.tag.index.group > li") | ||
.map((_, li) => { | ||
const $li = page(li); | ||
const link = $li.find("a.tag").first(); | ||
if (!link.length) { | ||
return null; | ||
} | ||
|
||
const name = link.text().trim(); | ||
|
||
// Tags are in the format: "Type: Name (Works Count)" | ||
// Here we extract the works count. | ||
const worksMatch = $li.text().match(/\((\d+)\)\s*$/); | ||
const worksCount = parseIntOrThrow(worksMatch![1]); | ||
|
||
// Tags are in the format: "Type: Name (Works Count)" | ||
// Here we extract the type. | ||
const typeMatch = $li.text().match(/^([^:]+):/); | ||
if (!typeMatch) { | ||
throw new Error(`Invalid tag type: ${$li.text()}`); | ||
} | ||
const type = typeMatch[1].trim().toLowerCase(); | ||
|
||
const classes = new Set( | ||
($li.find("span").attr("class") ?? "").split(/\s+/).filter(Boolean) | ||
); | ||
|
||
return { | ||
name, | ||
type: | ||
type == "unsortedtag" | ||
? "unsorted" | ||
: (type as TagSearchResultSummary["tags"][number]["type"]), | ||
canonical: classes.has("canonical"), | ||
worksCount, | ||
} as const; | ||
}) | ||
.get() | ||
.filter((tag) => tag !== null); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ import { | |
isValidArchiveIdOrNullish, | ||
parseArchiveId, | ||
} from "./utils"; | ||
import { WorkSummary } from "types/entities"; | ||
import { TagSearchFilters, WorkSummary } from "types/entities"; | ||
|
||
declare global { | ||
var archiveBaseUrl: string; | ||
|
@@ -195,3 +195,46 @@ export const getWorkDetailsFromUrl = ({ | |
collectionName: url.match(/collections\/(\w+)/)?.[1], | ||
}; | ||
}; | ||
|
||
const getSearchParamsFromTagFilters = ( | ||
searchFilters: Partial<TagSearchFilters> | ||
) => { | ||
// Prepare the parameters for the search as a map first. This makes them a bit | ||
// more readable, since these parameters will all need to be wrapped with with | ||
// "tag_search[]" in the URL. | ||
const parameters = { | ||
name: searchFilters.tagName ?? "", | ||
fandoms: searchFilters.fandoms?.join(",") ?? "", | ||
type: searchFilters.type?.toLowerCase() ?? "", | ||
wrangling_status: | ||
searchFilters.wranglingStatus | ||
// We remove the _or_ and _and_ that we added for readability | ||
// so that the values match the expected values for the API. | ||
?.replaceAll("_or_", "_") | ||
.replaceAll("_and_", "_") ?? "any", | ||
sort_column: | ||
searchFilters.sortColumn === "works_count" | ||
? "uses" | ||
: searchFilters.sortColumn ?? "name", | ||
sort_direction: searchFilters.sortDirection ?? "asc", | ||
}; | ||
|
||
const searchParams = new URLSearchParams(); | ||
if (searchFilters.page) { | ||
searchParams.set("page", String(searchFilters.page)); | ||
} | ||
searchParams.set("commit", "Search Tags"); | ||
|
||
// Now add the parameters to the search params, wrapped with "tag_search[]" | ||
for (const [key, value] of Object.entries(parameters)) { | ||
searchParams.set(`tag_search[${key}]`, value); | ||
} | ||
|
||
return searchParams; | ||
}; | ||
|
||
export const getSearchUrlFromTagFilters = (searchFilters: TagSearchFilters) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've gone back and forth on this.... I feel like |
||
const url = new URL(`tags/search`, getArchiveBaseUrl()); | ||
url.search = getSearchParamsFromTagFilters(searchFilters).toString(); | ||
return url.href; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this variable name be changed now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is theoretically a breaking change, and it might end up getting a lot of files (hence the todo, since I don't want to forget that)