web_lib

Common web application libraries
git clone https://radroots.dev/git/web_lib.git
Log | Files | Refs | LICENSE

commit d5d9c77fafe6930567a9e48dbfedb355555f6f35
parent 64d9bdfe425ba34a65c05d70854a639b62b0efa2
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Fri, 14 Feb 2025 06:29:52 +0000

utils: add browser utils, edit app utils

Diffstat:
Mutils/src/app/lib.ts | 18++++++------------
Mutils/src/app/styles.ts | 7+++++--
Mutils/src/app/types/app.ts | 7++++---
Mutils/src/app/types/resolve.ts | 4++--
Mutils/src/app/util.ts | 57+++++++++++++++++++++++++++++++++++++--------------------
Autils/src/browser.ts | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mutils/src/index.ts | 1+
7 files changed, 147 insertions(+), 39 deletions(-)

diff --git a/utils/src/app/lib.ts b/utils/src/app/lib.ts @@ -10,16 +10,6 @@ type ConfigWindow = { } }; -/* -{ - xs: 0, - sm: 640, - md: 768, - lg: 1024, - xl: 1280, - "2xl": 1536 - }; -*/ export const cfg_app: ConfigWindow = { layout: { ios0: { @@ -30,11 +20,15 @@ export const cfg_app: ConfigWindow = { h: 750, w: 350, }, - webm0: { + web0: { h: 600, w: 300, }, - webm1: { + web_ios0: { + h: 600, + w: 300, + }, + web_ios1: { h: 750, w: 800, }, diff --git a/utils/src/app/styles.ts b/utils/src/app/styles.ts @@ -29,8 +29,11 @@ export const loading_style_map: Map<LoadingDimension, { dim_1: number; gl_2: num export const toast_layout_map: Map<AppLayoutKey, string> = new Map([ [`ios0`, `pt-8`], [`ios1`, `pt-16`], - [`webm0`, `pt-8`], - [`webm1`, `pt-16`], + [`web_mobile`, `pt-8`], + [`web_desktop`, `pt-16`], + [`web_ios0`, `pt-8`], + [`web_ios1`, `pt-16`], + [`web0`, `pt-8`], ]); export const toast_style_map: Map<IToastKind, { inner: string; outer: string }> = new Map([ diff --git a/utils/src/app/types/app.ts b/utils/src/app/types/app.ts @@ -6,15 +6,16 @@ export type AppConfigType = `farmer` | `personal` export type AppLayoutKeyIOS = `ios0` | `ios1`; export type AppLayoutKeyWeb = `web_mobile` | `web_desktop`; -export type AppLayoutKeyWebPwa = `webm0` | `webm1`; +export type AppLayoutKeyWebPwa = `web_ios0` | `web_ios1` | `web0`; export type AppLayoutKey = AppLayoutKeyIOS | AppLayoutKeyWeb | AppLayoutKeyWebPwa; export type AppLayoutIOS<T extends string> = `${T}_${AppLayoutKeyIOS}`; export type AppLayoutWeb<T extends string> = `${T}_${AppLayoutKeyWeb}`; -export type AppLayoutWebPwa<T extends string> = `${T}_${AppLayoutKeyWeb}`; +export type AppLayoutWebPwa<T extends string> = `${T}_${AppLayoutKeyWebPwa}`; export type AppLayoutKeyHeight = + | `lo_view_main` | `lo_bottom_button` | `nav_tabs` | `nav_page_header` @@ -30,7 +31,7 @@ export type AppHeightsResponsiveWebPwa = AppLayoutWebPwa<AppLayoutKeyHeight>; export type AppWidthsResponsiveIOS = AppLayoutIOS<AppLayoutKeyWidth>; export type AppWidthsResponsiveWeb = AppLayoutWeb<AppLayoutKeyWidth>; -export type AppWidthsResponsiveWebPwa = AppLayoutWebPwa<AppLayoutKeyHeight>; +export type AppWidthsResponsiveWebPwa = AppLayoutWebPwa<AppLayoutKeyWidth>; export type CallbackPromiseFigureResult<Ti, Tr> = (value: Ti) => Promise<Tr | undefined>; export type CallbackPromiseFull<Ti, Tr> = (value: Ti) => Promise<Tr>; diff --git a/utils/src/app/types/resolve.ts b/utils/src/app/types/resolve.ts @@ -8,14 +8,14 @@ export type ResolveEnumPayment_Period = 'biweekly' | 'hourly' | 'monthly' | 'wee export type ResolveEnumPayment_Status = 'confirmed' | 'pending'; export type ResolveEnumQuantity_Unit = 'g' | 'kg' | 'lb' | 'ton'; export type ResolveEnumWorker_Type = 'contractor' | 'laborer'; -export type ResolveAccountInfo = { id: string, created_at: string, updated_at: string, role: ResolveEnumAccount_Role, username: string, auth_ref: { credential: ResolveEnumAuth_Credential, email: { id: string, created_at: string, updated_at: string, address: string } }, profiles?: Array<{ id: string, created_at: string, updated_at: string, name: string, display_name?: string | null, primary: boolean, about?: string | null, emails: Array<{ id: string, created_at: string, updated_at: string, address: string }>, profile_photos?: Array<{ id: string, created_at: string, updated_at: string, primary: boolean, title?: string | null, description?: string | null, media_image: { id: string, created_at: string, updated_at: string, url: string } }> | null, nostr_keys: Array<{ id: string, created_at: string, updated_at: string, public_key: string }> }> | null, farms?: Array<{ id: string, created_at: string, updated_at: string, name: string, area?: number | null, area_unit: ResolveEnumArea_Unit, geolocation: { id: string, created_at: string, updated_at: string, point: { type: string, coordinates: Array<number> }, polygon?: { type: string, coordinates: Array<Array<Array<number>>> } | null, address: { id: string, created_at: string, updated_at: string, primary: string, admin: string, country: string } }, farm_products?: Array<{ id: string, created_at: string, updated_at: string, name: string, farm_lot_products?: Array<{ id: string }> | null }> | null, farm_lots?: Array<{ id: string, created_at: string, updated_at: string, name?: string | null, area?: number | null, area_unit: ResolveEnumArea_Unit, geolocation: { id: string, created_at: string, updated_at: string, point: { type: string, coordinates: Array<number> }, polygon?: { type: string, coordinates: Array<Array<Array<number>>> } | null, address: { id: string, created_at: string, updated_at: string, primary: string, admin: string, country: string } }, farm_lot_products?: Array<{ id: string, created_at: string, updated_at: string, area_planted?: number | null, area_planted_unit: ResolveEnumArea_Unit, date_planted?: string | null, days_to_maturity?: number | null, farm_product: { id: string, created_at: string, updated_at: string, name: string, farm_lot_products?: Array<{ id: string }> | null }, farm_trade_products?: Array<{ id: string, created_at: string, updated_at: string, title: string, description: string, process?: string | null, trade_product_prices?: Array<{ id: string, created_at: string, updated_at: string, amount: number, currency: string, quantity_amount?: number | null, quantity_unit: ResolveEnumQuantity_Unit, quantity_label?: string | null }> | null, trade_product_quantitys?: Array<{ id: string, created_at: string, updated_at: string, amount?: number | null, unit: ResolveEnumQuantity_Unit, label?: string | null }> | null }> | null, farm_lot_harvests?: Array<{ id: string, created_at: string, updated_at: string, quantity_harvested: number, quantity_harvested_unit: ResolveEnumQuantity_Unit }> | null }> | null }> | null }> | null }; +export type ResolveAccountInfo = { id: string, created_at: string, updated_at: string, role: ResolveEnumAccount_Role, username: string, auth_ref: { credential: ResolveEnumAuth_Credential, email: { id: string, created_at: string, updated_at: string, address: string } }, profiles?: Array<{ id: string, created_at: string, updated_at: string, name: string, display_name?: string | null, primary: boolean, about?: string | null, emails: Array<{ id: string, created_at: string, updated_at: string, address: string }>, profile_photos?: Array<{ id: string, created_at: string, updated_at: string, primary: boolean, title?: string | null, description?: string | null, media_image: { id: string, created_at: string, updated_at: string, url: string } }> | null, nostr_keys: Array<{ id: string, created_at: string, updated_at: string, public_key: string }> }> | null, farms?: Array<{ id: string, created_at: string, updated_at: string, name: string, area?: number | null, area_unit: ResolveEnumArea_Unit, geolocation?: { id: string, created_at: string, updated_at: string, point: { type: string, coordinates: Array<number> }, polygon?: { type: string, coordinates: Array<Array<Array<number>>> } | null, address: { id: string, created_at: string, updated_at: string, primary: string, admin: string, country: string } } | null, farm_products?: Array<{ id: string, created_at: string, updated_at: string, name: string, farm_lot_products?: Array<{ id: string }> | null }> | null, farm_lots?: Array<{ id: string, created_at: string, updated_at: string, name?: string | null, area?: number | null, area_unit: ResolveEnumArea_Unit, geolocation: { id: string, created_at: string, updated_at: string, point: { type: string, coordinates: Array<number> }, polygon?: { type: string, coordinates: Array<Array<Array<number>>> } | null, address: { id: string, created_at: string, updated_at: string, primary: string, admin: string, country: string } }, farm_lot_products?: Array<{ id: string, created_at: string, updated_at: string, area_planted?: number | null, area_planted_unit: ResolveEnumArea_Unit, date_planted?: string | null, days_to_maturity?: number | null, farm_product: { id: string, created_at: string, updated_at: string, name: string, farm_lot_products?: Array<{ id: string }> | null }, farm_trade_products?: Array<{ id: string, created_at: string, updated_at: string, title: string, description: string, process?: string | null, trade_product_prices?: Array<{ id: string, created_at: string, updated_at: string, amount: number, currency: string, quantity_amount?: number | null, quantity_unit: ResolveEnumQuantity_Unit, quantity_label?: string | null }> | null, trade_product_quantitys?: Array<{ id: string, created_at: string, updated_at: string, amount?: number | null, unit: ResolveEnumQuantity_Unit, label?: string | null }> | null }> | null, farm_lot_harvests?: Array<{ id: string, created_at: string, updated_at: string, quantity_harvested: number, quantity_harvested_unit: ResolveEnumQuantity_Unit }> | null }> | null }> | null }> | null }; export type ResolveAuthRefInfo = { credential: ResolveEnumAuth_Credential, email: { id: string, created_at: string, updated_at: string, address: string } }; export type ResolveEmailInfo = { id: string, created_at: string, updated_at: string, address: string }; export type ResolveProfileInfo = { id: string, created_at: string, updated_at: string, name: string, display_name?: string | null, primary: boolean, about?: string | null, emails: Array<{ id: string, created_at: string, updated_at: string, address: string }>, profile_photos?: Array<{ id: string, created_at: string, updated_at: string, primary: boolean, title?: string | null, description?: string | null, media_image: { id: string, created_at: string, updated_at: string, url: string } }> | null, nostr_keys: Array<{ id: string, created_at: string, updated_at: string, public_key: string }> }; export type ResolveProfilePhotoInfo = { id: string, created_at: string, updated_at: string, primary: boolean, title?: string | null, description?: string | null, media_image: { id: string, created_at: string, updated_at: string, url: string } }; export type ResolveMediaImageInfo = { id: string, created_at: string, updated_at: string, url: string }; export type ResolveNostrKeyInfo = { id: string, created_at: string, updated_at: string, public_key: string }; -export type ResolveFarmInfo = { id: string, created_at: string, updated_at: string, name: string, area?: number | null, area_unit: ResolveEnumArea_Unit, geolocation: { id: string, created_at: string, updated_at: string, point: { type: string, coordinates: Array<number> }, polygon?: { type: string, coordinates: Array<Array<Array<number>>> } | null, address: { id: string, created_at: string, updated_at: string, primary: string, admin: string, country: string } }, farm_products?: Array<{ id: string, created_at: string, updated_at: string, name: string, farm_lot_products?: Array<{ id: string }> | null }> | null, farm_lots?: Array<{ id: string, created_at: string, updated_at: string, name?: string | null, area?: number | null, area_unit: ResolveEnumArea_Unit, geolocation: { id: string, created_at: string, updated_at: string, point: { type: string, coordinates: Array<number> }, polygon?: { type: string, coordinates: Array<Array<Array<number>>> } | null, address: { id: string, created_at: string, updated_at: string, primary: string, admin: string, country: string } }, farm_lot_products?: Array<{ id: string, created_at: string, updated_at: string, area_planted?: number | null, area_planted_unit: ResolveEnumArea_Unit, date_planted?: string | null, days_to_maturity?: number | null, farm_product: { id: string, created_at: string, updated_at: string, name: string, farm_lot_products?: Array<{ id: string }> | null }, farm_trade_products?: Array<{ id: string, created_at: string, updated_at: string, title: string, description: string, process?: string | null, trade_product_prices?: Array<{ id: string, created_at: string, updated_at: string, amount: number, currency: string, quantity_amount?: number | null, quantity_unit: ResolveEnumQuantity_Unit, quantity_label?: string | null }> | null, trade_product_quantitys?: Array<{ id: string, created_at: string, updated_at: string, amount?: number | null, unit: ResolveEnumQuantity_Unit, label?: string | null }> | null }> | null, farm_lot_harvests?: Array<{ id: string, created_at: string, updated_at: string, quantity_harvested: number, quantity_harvested_unit: ResolveEnumQuantity_Unit }> | null }> | null }> | null }; +export type ResolveFarmInfo = { id: string, created_at: string, updated_at: string, name: string, area?: number | null, area_unit: ResolveEnumArea_Unit, geolocation?: { id: string, created_at: string, updated_at: string, point: { type: string, coordinates: Array<number> }, polygon?: { type: string, coordinates: Array<Array<Array<number>>> } | null, address: { id: string, created_at: string, updated_at: string, primary: string, admin: string, country: string } } | null, farm_products?: Array<{ id: string, created_at: string, updated_at: string, name: string, farm_lot_products?: Array<{ id: string }> | null }> | null, farm_lots?: Array<{ id: string, created_at: string, updated_at: string, name?: string | null, area?: number | null, area_unit: ResolveEnumArea_Unit, geolocation: { id: string, created_at: string, updated_at: string, point: { type: string, coordinates: Array<number> }, polygon?: { type: string, coordinates: Array<Array<Array<number>>> } | null, address: { id: string, created_at: string, updated_at: string, primary: string, admin: string, country: string } }, farm_lot_products?: Array<{ id: string, created_at: string, updated_at: string, area_planted?: number | null, area_planted_unit: ResolveEnumArea_Unit, date_planted?: string | null, days_to_maturity?: number | null, farm_product: { id: string, created_at: string, updated_at: string, name: string, farm_lot_products?: Array<{ id: string }> | null }, farm_trade_products?: Array<{ id: string, created_at: string, updated_at: string, title: string, description: string, process?: string | null, trade_product_prices?: Array<{ id: string, created_at: string, updated_at: string, amount: number, currency: string, quantity_amount?: number | null, quantity_unit: ResolveEnumQuantity_Unit, quantity_label?: string | null }> | null, trade_product_quantitys?: Array<{ id: string, created_at: string, updated_at: string, amount?: number | null, unit: ResolveEnumQuantity_Unit, label?: string | null }> | null }> | null, farm_lot_harvests?: Array<{ id: string, created_at: string, updated_at: string, quantity_harvested: number, quantity_harvested_unit: ResolveEnumQuantity_Unit }> | null }> | null }> | null }; export type ResolveGeolocationInfo = { id: string, created_at: string, updated_at: string, point: { type: string, coordinates: Array<number> }, polygon?: { type: string, coordinates: Array<Array<Array<number>>> } | null, address: { id: string, created_at: string, updated_at: string, primary: string, admin: string, country: string } }; export type ResolveAddressInfo = { id: string, created_at: string, updated_at: string, primary: string, admin: string, country: string }; export type ResolveFarmLotInfo = { id: string, created_at: string, updated_at: string, name?: string | null, area?: number | null, area_unit: ResolveEnumArea_Unit, geolocation: { id: string, created_at: string, updated_at: string, point: { type: string, coordinates: Array<number> }, polygon?: { type: string, coordinates: Array<Array<Array<number>>> } | null, address: { id: string, created_at: string, updated_at: string, primary: string, admin: string, country: string } }, farm_lot_products?: Array<{ id: string, created_at: string, updated_at: string, area_planted?: number | null, area_planted_unit: ResolveEnumArea_Unit, date_planted?: string | null, days_to_maturity?: number | null, farm_product: { id: string, created_at: string, updated_at: string, name: string, farm_lot_products?: Array<{ id: string }> | null }, farm_trade_products?: Array<{ id: string, created_at: string, updated_at: string, title: string, description: string, process?: string | null, trade_product_prices?: Array<{ id: string, created_at: string, updated_at: string, amount: number, currency: string, quantity_amount?: number | null, quantity_unit: ResolveEnumQuantity_Unit, quantity_label?: string | null }> | null, trade_product_quantitys?: Array<{ id: string, created_at: string, updated_at: string, amount?: number | null, unit: ResolveEnumQuantity_Unit, label?: string | null }> | null }> | null, farm_lot_harvests?: Array<{ id: string, created_at: string, updated_at: string, quantity_harvested: number, quantity_harvested_unit: ResolveEnumQuantity_Unit }> | null }> | null }; diff --git a/utils/src/app/util.ts b/utils/src/app/util.ts @@ -4,51 +4,68 @@ export const fmt_cl = (classes?: string): string => { return classes ? classes : ``; }; -export const get_layout = (val: string | false): AppLayoutKey => { - switch (val) { - case `ios0`: - case `ios1`: - case `webm0`: - case `webm1`: - case `web_mobile`: - case `web_desktop`: - return val; - default: - return `ios0`; - }; +export const get_layout = (val?: string): AppLayoutKey | undefined => { + const lo_ios = get_ios_layout(val); + if (lo_ios) return lo_ios; + const lo_web = get_web_layout(val); + if (lo_web) return lo_web; + const lo_web_pwa = get_web_pwa_layout(val); + if (lo_web_pwa) return lo_web_pwa; + return undefined; }; -export const get_ios_layout = (val: string | false): AppLayoutKeyIOS => { +export const get_layout_default = (val?: string): AppLayoutKey => { + const res = get_layout(val); + return res || `web0`; +}; + +export const get_ios_layout = (val?: string): AppLayoutKeyIOS | undefined => { switch (val) { case `ios0`: case `ios1`: return val; default: - return `ios0`; + return undefined; }; }; -export const get_web_layout = (val: string | false): AppLayoutKeyWeb => { +export const get_ios_layout_default = (val?: string): AppLayoutKeyIOS => { + const res = get_ios_layout(val); + return res || `ios0`; +}; + +export const get_web_layout = (val?: string): AppLayoutKeyWeb | undefined => { switch (val) { case `web_mobile`: case `web_desktop`: return val; default: - return `web_desktop`; + return undefined; }; }; +export const get_web_layout_default = (val?: string): AppLayoutKeyWeb => { + const res = get_web_layout(val); + return res || `web_desktop`; +}; + -export const get_web_pwa_layout = (val: string | false): AppLayoutKeyWebPwa => { +export const get_web_pwa_layout = (val?: string): AppLayoutKeyWebPwa | undefined => { switch (val) { - case `webm0`: - case `webm1`: + case `web_ios0`: + case `web_ios1`: + case `web0`: return val; default: - return `webm0`; + return undefined; }; }; +export const get_web_pwa_layout_default = (val?: string): AppLayoutKeyWebPwa => { + const res = get_web_pwa_layout(val); + return res || `web0`; +}; + export const parse_layer = (layer?: number, layer_default?: ThemeLayer): ThemeLayer => { switch (layer) { case 0: diff --git a/utils/src/browser.ts b/utils/src/browser.ts @@ -0,0 +1,92 @@ +export type BrowserPlatformInfo = { + os: string; + browser: string; + version: string; +}; + +const remove_excess_mozilla_and_version = /^mozilla\/\d\.\d\W/; +const browser_pattern = /(\w+)\/(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)/g; +const engine_and_version_pattern = /^(ver|cri|gec)/; +const brand_list = ['chrome', 'opera', 'safari', 'edge', 'firefox']; + +const mobiles: Record<string, RegExp> = { + iphone: /iphone/, + ipad: /ipad|macintosh/, + android: /android/ +}; + +const desktops: Record<string, RegExp> = { + windows: /win/, + mac: /macintosh/, + linux: /linux/ +}; + +const parse_user_agent_string = (ua_string: string): BrowserPlatformInfo => { + const ua = ua_string.toLowerCase().replace(remove_excess_mozilla_and_version, ''); + + const mobile_os = Object.keys(mobiles).find( + (key) => mobiles[key].test(ua) && navigator.maxTouchPoints >= 1 + ); + const desktop_os = Object.keys(desktops).find((key) => desktops[key].test(ua)); + const os = mobile_os || desktop_os || ''; + + const browser_matches = ua.match(browser_pattern); + const version_regex = /version\/(\d+(\.\d+)*)/; + const safari_version_match = ua.match(version_regex); + const safari_version = Array.isArray(safari_version_match) ? safari_version_match[1] : null; + + const browser_offset = + browser_matches && browser_matches.length > 2 && !engine_and_version_pattern.test(browser_matches[1]) + ? 1 + : 0; + const browser_result = + browser_matches && browser_matches[browser_matches.length - 1 - browser_offset].split('/'); + const browser = browser_result ? browser_result[0] : ''; + const version = safari_version || (browser_result ? browser_result[1] : ''); + + return { os, browser, version }; +}; + +export const browser_platform = (): BrowserPlatformInfo | undefined => { + if (typeof navigator !== 'undefined') { + if ('userAgentData' in navigator && navigator.userAgentData) { + const ua_data = navigator.userAgentData as { + platform: string; + brands: { brand: string; version: string }[]; + }; + const os = ua_data.platform.toLowerCase(); + let browser = ''; + let version = ''; + + if (Array.isArray(ua_data.brands)) { + for (const { brand, version: brand_version } of ua_data.brands) { + const lower_brand = brand.toLowerCase(); + if (brand_list.some((b) => lower_brand.includes(b))) { + browser = lower_brand; + version = brand_version; + break; + } + } + } + + if (!browser && navigator.userAgent) { + return parse_user_agent_string(navigator.userAgent); + } + return { os, browser, version }; + } + + if (navigator.userAgent) { + return parse_user_agent_string(navigator.userAgent); + } + + const nav_platform = navigator.platform; + if (!nav_platform) return undefined; + return { + os: nav_platform, + browser: '', + version: '' + }; + } + + return undefined; +}; diff --git a/utils/src/index.ts b/utils/src/index.ts @@ -19,6 +19,7 @@ export * from "./app/util/resolve.js" export * from "./app/util/search.js" export * from "./app/util.js" export * from "./app/validation/view.js" +export * from "./browser.js" export * from "./client/database.js" export * from "./client/datastore.js" export * from "./client/geo.js"