Skip to content

Commit

Permalink
refactor(ts): URL parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
arildm committed Jul 10, 2024
1 parent d707127 commit 65fa660
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- TypeScript typings for:
- config/settings
- URL parameters
- CQP queries
- CorpusListing
- `$rootScope`
Expand Down
3 changes: 2 additions & 1 deletion app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import $ from "jquery"
import currentMode from "@/mode"
import { locationSearchGet } from "@/util"
import { HashParams } from "@/urlparams"

declare global {
interface Window {
Expand All @@ -11,7 +12,7 @@ declare global {
* TODO Remove, currently used in tests
* @deprecated
*/
locationSearch: (key: string) => string
locationSearch: <K extends keyof HashParams>(key: K) => HashParams[K]
}
}

Expand Down
4 changes: 2 additions & 2 deletions app/scripts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
IComponentOptions,
ILocaleService,
ILocationProvider,
ILocationService,
IQService,
IScope,
ITimeoutService,
Expand All @@ -28,6 +27,7 @@ import "@/components/frontpage"
import "@/components/results"
import "@/components/korp-error"
import { JQueryExtended } from "./jquery.types"
import { LocationService } from "./urlparams"

// load all custom components
let customComponents: Record<string, IComponentOptions> = {}
Expand Down Expand Up @@ -96,7 +96,7 @@ korpApp.run([
"$uibModal",
async function (
$rootScope: RootScope,
$location: ILocationService,
$location: LocationService,
$locale: ILocaleService,
tmhDynamicLocale: tmh.tmh.IDynamicLocale,
tmhDynamicLocaleCache: ICacheObject,
Expand Down
5 changes: 3 additions & 2 deletions app/scripts/components/search-examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { html } from "@/util"
import settings from "@/settings"
import { SearchExample } from "@/settings/app-settings.types"
import { RootScope } from "@/root-scope.types"
import { HashParams, LocationService } from "@/urlparams"

export default angular.module("korpApp").component("searchExamples", {
template: html`
Expand All @@ -26,7 +27,7 @@ export default angular.module("korpApp").component("searchExamples", {
"$rootScope",
"$scope",
"$location",
function ($rootScope: RootScope, $scope: SearchExamplesScope, $location) {
function ($rootScope: RootScope, $scope: SearchExamplesScope, $location: LocationService) {
const $ctrl = this

$scope.examples = undefined
Expand All @@ -40,7 +41,7 @@ export default angular.module("korpApp").component("searchExamples", {
}
}

$ctrl.setSearch = (params: Record<string, any>) => {
$ctrl.setSearch = (params: HashParams) => {
if (params.corpus) {
const corpora = params.corpus.split(",")
settings.corpusListing.select(corpora)
Expand Down
4 changes: 2 additions & 2 deletions app/scripts/data_init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import timeProxyFactory from "@/backend/time-proxy"
import * as treeUtil from "./components/corpus_chooser/util"
import { CorpusListing } from "./corpus_listing"
import { ParallelCorpusListing } from "./parallel/corpus_listing"
import { fromKeys, httpConfAddMethodFetch } from "@/util"
import { fromKeys, getUrlHash, httpConfAddMethodFetch } from "@/util"
import { Labeled, LangLocMap, LocMap } from "./i18n/types"
import { CorpusInfoResponse } from "./settings/corpus-info.types"
import { Attribute, Config, Corpus, CustomAttribute } from "./settings/config.types"
Expand Down Expand Up @@ -213,7 +213,7 @@ function setInitialCorpora(): void {
settings.preselected_corpora = expandedCorpora
}

const corpusParam = new URLSearchParams(window.location.hash.slice(2)).get("corpus")
const corpusParam = getUrlHash("corpus")

const currentCorpora = corpusParam
? _.flatten(_.map(corpusParam.split(","), (val) => treeUtil.getAllCorporaInFolders(settings.folders, val)))
Expand Down
15 changes: 6 additions & 9 deletions app/scripts/main.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/** @format */
import _ from "lodash"
import angular from "angular"
import jStorage from "../lib/jstorage"
import settings from "@/settings"
import { updateSearchHistory } from "@/history"
import { fetchInitialData } from "@/data_init"
import currentMode from "@/mode"
import * as authenticationProxy from "@/components/auth/auth"
import { html } from "@/util"
import { getUrlHash, html } from "@/util"
import korpLogo from "../img/korp.svg"
import korpFail from "../img/korp_fail.svg"
import angular from "angular"

const createSplashScreen = () => {
const splash = document.getElementById("preload")
Expand All @@ -28,13 +28,10 @@ const createErrorScreen = () => {

function initApp() {
// rewriting old language codes to new ones
if (location.hash.includes("lang=")) {
const match = /lang\=(.*?)(&|$)/.exec(location.hash)
if (match) {
const lang = match[1]
if (settings["iso_languages"][lang]) {
location.hash = location.hash.replace(`lang=${lang}`, `lang=${settings["iso_languages"][lang]}`)
}
const lang = getUrlHash("lang")
if (lang) {
if (settings["iso_languages"][lang]) {
location.hash = location.hash.replace(`lang=${lang}`, `lang=${settings["iso_languages"][lang]}`)
}
}

Expand Down
4 changes: 3 additions & 1 deletion app/scripts/mode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/** @format */

const currentMode = new URLSearchParams(window.location.search).get("mode") || "default"
import { getUrlParam } from "./util"

const currentMode = getUrlParam("mode") || "default"

export default currentMode
4 changes: 2 additions & 2 deletions app/scripts/parallel/corpus_listing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import _ from "lodash"
import settings from "@/settings"
import { CorpusListing } from "@/corpus_listing"
import { locationSearchGet } from "@/util"
import { getUrlHash, locationSearchGet } from "@/util"
import { CorpusTransformed } from "@/settings/config-transformed.types"
import { Attribute } from "@/settings/config.types"
import { LangString } from "@/i18n/types"
Expand All @@ -14,7 +14,7 @@ export class ParallelCorpusListing extends CorpusListing {
super(corpora)

// Cannot use Angular helpers (`locationSearchGet`) here, it's not initialized yet.
const activeLangs = new URLSearchParams(window.location.hash.slice(2)).get("parallel_corpora") || ""
const activeLangs = getUrlHash("parallel_corpora") || ""
this.setActiveLangs(activeLangs.split(","))
}

Expand Down
1 change: 1 addition & 0 deletions app/scripts/root-scope.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { IDeferred, IRootScopeService } from "angular"
import { Settings } from "./settings/settings.types"
import { LangLocMap } from "./i18n/types"

/** Extends the Angular Root Scope interface with properties used by this app. */
export type RootScope = IRootScopeService & {
_settings: Settings
extendedCQP: string | null
Expand Down
3 changes: 2 additions & 1 deletion app/scripts/settings/app-settings.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { Labeled, LangString } from "@/i18n/types"
import { Attribute } from "./config.types"
import { RootScope } from "@/root-scope.types"
import { HashParams } from "@/urlparams"

export type AppSettings = {
auth_module?: string | { module: string; options: Record<string, any> }
Expand Down Expand Up @@ -68,7 +69,7 @@ export type AppSettings = {
export type SearchExample = {
label: LangString
hint?: LangString
params: Record<string, string | number>
params: HashParams
}

export type WordPictureDef = Array<"_" | { rel: string; css_class: string }>
61 changes: 61 additions & 0 deletions app/scripts/urlparams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/** @format */

import { ILocationService } from "angular"

/** Extends the Angular Location service to assign types for supported URL hash params. */
export type LocationService = Omit<ILocationService, "search"> & {
search(): HashParams
search(search: HashParams): LocationService
search<K extends keyof HashParams>(search: K, paramValue: HashParams[K]): LocationService
}

/** Supported parameters for the `?<key>=<value>` part of the URL. */
export type UrlParams = {
/** Current Korp mode (= a set of corpora and app configuration) */
mode: string
}

/** Supported parameters for the `#?<key>=<value>` part of the URL. */
export type HashParams = {
/** Selected corpus ids, comma-separated */
corpus?: string
/** CQP query for Extended search, possibly with frontend-specific operators */
cqp?: string
/** Conditions entered for search filters, as Base64-encoded JSON */
global_filter?: string
/** Opposite of `show_stats`, used if the `statistics_search_default` setting is enabled */
hide_stats?: boolean
/** Hits per page */
hpp?: `${number}`
/** Whether tokens in current query should match in order; default is true */
in_order?: "false"
/** UI language as three-letter code */
lang?: string
/** Current page number of the search result */
page?: string
parallel_corpora?: string
/** Whether the reading mode is enabled */
reading_mode?: boolean
/**
* Search query for Simple or Advanced search: `<mode>|<query>`
* where `mode` can be:
* - "word", for simple word search
* - "lemgram", when using autocomplete in Simple
* - "cqp", for advanced mode (`query` is a CQP expression)
*/
search?: `${string}|${string}` | "cqp"
/** Current search mode */
search_tab?: `${number}`
/** Whether a statistics query should be made when searching */
show_stats?: boolean
/** Search result order */
sort?: "" | "keyword" | "left" | "right" | "random"
/** Attributes on which to aggregate counts in statistics query */
stats_reduce?: string
/** Attributes on which to aggregate counts, case-insensitively, in statistics query */
stats_reduce_insensitive?: string
/** Chunk size to evaluate search query within, e.g. "sentence" or "paragraph" */
within?: string
/** Whether a word picture query should be made when searching */
word_pic?: boolean
}
30 changes: 18 additions & 12 deletions app/scripts/util.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
/** @format */
import _ from "lodash"
import angular, {
IControllerService,
IHttpService,
IRootScopeService,
type ILocationService,
type IRequestConfig,
type IScope,
} from "angular"
import angular, { IControllerService, IHttpService, type IRequestConfig, type IScope } from "angular"
import settings from "@/settings"
import { getLang, loc, locObj } from "@/i18n"
import { LangMap } from "./i18n/types"
import { RootScope } from "./root-scope.types"
import { JQueryExtended, JQueryStaticExtended } from "./jquery.types"
import { HashParams, LocationService, UrlParams } from "./urlparams"

/** Use html`<div>html here</div>` to enable formatting template strings with Prettier. */
export const html = String.raw
Expand All @@ -25,11 +19,23 @@ export const fromKeys = <K extends keyof any, T>(keys: K[], getValue: (key: K) =
type ServiceTypes = {
$controller: IControllerService
$http: IHttpService
$location: ILocationService
$location: LocationService
$rootScope: RootScope
// Add types here as needed.
}

/** Get a parameter from the `?<key>=<value>` part of the URL. */
export const getUrlParam = <K extends keyof UrlParams>(key: K) =>
new URLSearchParams(window.location.search).get(key) as UrlParams[K]

/**
* Get a parameter from the `#?<key>=<value>` part of the URL.
* It is preferred to use the Angular `$location` service to read and modify this.
* Use this only when outside Angular context.
*/
export const getUrlHash = <K extends keyof HashParams>(key: K) =>
new URLSearchParams(window.location.hash.slice(2)).get(key) as HashParams[K]

/** Get an Angular service from outside the Angular context. */
export const getService = <K extends keyof ServiceTypes>(name: K): ServiceTypes[K] =>
angular.element("body").injector().get(name)
Expand All @@ -46,14 +52,14 @@ export const withService = <K extends keyof ServiceTypes, R>(name: K, fn: (servi
* Get values from the URL search string via Angular.
* Only use this in code outside Angular. Inside, use `$location.search()`.
*/
export const locationSearchGet = (key: string): string =>
withService("$location", ($location) => $location.search()[key])
export const locationSearchGet = <K extends keyof HashParams>(key: K): HashParams[K] =>
withService("$location", ($location) => ($location.search() as HashParams)[key])

/**
* Set values in the URL search string via Angular.
* Only use this in code outside Angular. Inside, use `$location.search()`.
*/
export const locationSearchSet = (name: string, value: string | number | boolean | string[]): ILocationService =>
export const locationSearchSet = <K extends keyof HashParams>(name: K, value: HashParams[K]): LocationService =>
withService("$location", ($location) => $location.search(name, value))

/**
Expand Down

0 comments on commit 65fa660

Please sign in to comment.