diff --git a/packages/app/src/app.js b/packages/app/src/app.js index ac092f8f..ec44b868 100644 --- a/packages/app/src/app.js +++ b/packages/app/src/app.js @@ -48,7 +48,6 @@ app.init = function (config) { app.use(auth); } router.addRouteInfo(_extendRouteInfo); - router.addContext(() => spinner.start() && {}); router.addContext(_getSyncInfo); // Router (wq/router.js) configuration @@ -116,9 +115,10 @@ app.init = function (config) { }); app.spin = { - start: (msg, duration, opts) => spinner.start(msg, duration, opts), - forSeconds: (duration) => spinner.start(null, duration), - stop: (msg) => spinner.stop(msg), + start: (msg, duration, opts) => + spinner.startSpinner(msg, duration, opts), + forSeconds: (duration) => spinner.startSpinner(null, duration), + stop: (msg) => spinner.stopSpinner(msg), }; // Outbox (wq/outbox.js) configuration @@ -244,8 +244,6 @@ app.init = function (config) { } }); - router.addContext(() => spinner.stop() && {}); - // Initialize wq/store.js and wq/outbox.js ds.init(config.store); app.service = ds.service; diff --git a/packages/app/src/auth.js b/packages/app/src/auth.js index 08726e67..db2c7274 100644 --- a/packages/app/src/auth.js +++ b/packages/app/src/auth.js @@ -165,9 +165,7 @@ export default { ); } // FIXME: Better way to do this? - app.spin.start(); await app.prefetchAll(); - app.spin.stop(); await app.router.reload(); }, diff --git a/packages/app/src/spinner.js b/packages/app/src/spinner.js index dcd8a224..9afcae95 100644 --- a/packages/app/src/spinner.js +++ b/packages/app/src/spinner.js @@ -5,14 +5,16 @@ const SPIN_STOP = "SPIN_STOP"; export default { name: "spinner", actions: { - start: (message, duration, type) => ({ - type: SPIN_START, - payload: { - message, - duration, - type, - }, - }), + startSpinner: (message, duration, type) => { + return { + type: SPIN_START, + payload: { + message, + duration, + type, + }, + }; + }, alert: (message) => ({ type: SPIN_DURATION, payload: { @@ -21,7 +23,7 @@ export default { type: "alert", }, }), - stop: (message) => ({ type: SPIN_STOP, payload: { message } }), + stopSpinner: (message) => ({ type: SPIN_STOP, payload: { message } }), }, thunks: { SPIN_DURATION: async (dispatch, getState, bag) => { diff --git a/packages/material-native/src/Root.js b/packages/material-native/src/Root.js index 0158551f..1b19d881 100644 --- a/packages/material-native/src/Root.js +++ b/packages/material-native/src/Root.js @@ -1,5 +1,6 @@ -import React, { useMemo } from "react"; +import React, { useEffect, useMemo } from "react"; import { Root as DefaultRoot } from "@wq/react"; +import { usePathname, useSegments, useGlobalSearchParams } from "expo-router"; import { MD2LightTheme, MD3LightTheme, @@ -16,9 +17,16 @@ const THEMES = { }; export default function Root({ app, children }) { - const { theme: configTheme } = app.plugins.material.config, + const pathname = usePathname(), + segments = useSegments(), + params = useGlobalSearchParams(), + { theme: configTheme } = app.plugins.material.config, theme = useMemo(() => createTheme(configTheme), [configTheme]); + useEffect(() => { + app.router.setRouteInfo({ pathname, segments, params }); + }, [pathname, segments, params]); + return ( {children} diff --git a/packages/material-web/src/Root.js b/packages/material-web/src/Root.js index ff74be01..0671a881 100644 --- a/packages/material-web/src/Root.js +++ b/packages/material-web/src/Root.js @@ -12,7 +12,7 @@ export default function Root({ app, children }) { return ( - {children} + {children} ); } diff --git a/packages/react/src/Root.js b/packages/react/src/Root.js index b80a3e8e..bc110224 100644 --- a/packages/react/src/Root.js +++ b/packages/react/src/Root.js @@ -1,3 +1,4 @@ +import React from "react"; import { Provider as StoreProvider } from "react-redux"; import { AppContext } from "./hooks.js"; diff --git a/packages/react/src/components/PropertyTable.js b/packages/react/src/components/PropertyTable.js index 71a40116..a89048c7 100644 --- a/packages/react/src/components/PropertyTable.js +++ b/packages/react/src/components/PropertyTable.js @@ -4,6 +4,7 @@ import PropTypes from "prop-types"; const Value = ({ values, field }) => { const { + Text, FormatJson, ImagePreview, FileLink, @@ -58,7 +59,7 @@ const Value = ({ values, field }) => { } else if (typeof value === "object") { return ; } else { - return value + ""; + return {value + ""}; } }; diff --git a/packages/router/src/__tests__/router.js b/packages/router/src/__tests__/router.js index 1e0b0529..ed686bc2 100644 --- a/packages/router/src/__tests__/router.js +++ b/packages/router/src/__tests__/router.js @@ -35,3 +35,13 @@ test("render page", () => { router.push("/test/1234?p=1"); }); }); + +test("match path", () => { + expect(router.matchPath("/", "/")).toBe(true); + expect(router.matchPath("/test", "/test/")).toBe(true); + expect(router.matchPath("/test", "/")).toBe(false); + expect(router.matchPath("/test/:slug", "/test/1234")).toBe(true); + expect(router.matchPath("/test/", "/test/1234")).toBe(false); + expect(router.matchPath("/test/1234", "/test/:slug")).toBe(true); + expect(router.matchPath("/test/1234", "/test/5678")).toBe(false); +}); diff --git a/packages/router/src/expo-router.js b/packages/router/src/expo-router.js index 7ec52200..d89b304a 100644 --- a/packages/router/src/expo-router.js +++ b/packages/router/src/expo-router.js @@ -1,7 +1,33 @@ import { useMemo, useCallback } from "react"; import { router as expoRouter } from "expo-router"; import queryString from "query-string"; -import router from "./src/router.js"; +import router from "./router.js"; + +router.config.parseRouteInfo = function ({ pathname, segments, params }) { + const info = {}; + info.name = router.getRouteName(pathname); + info.template = router.config.getTemplateName(info.name); + info.prev_path = null; + info.path = pathname; + info.path_enc = escape(info.path); + info.params = {}; + info.slugs = {}; + for (const [key, val] of Object.entries(params)) { + if (segments.includes(`[${key}]`)) { + info.slugs[key] = val; + } else { + info.params[key] = val; + } + } + info.full_pathname = + Object.keys(info.params).length === 0 + ? pathname + : pathname + + "?" + + queryString.stringify(info.params, { arrayFormat: "comma" }); + info.full_path_enc = escape(info.full_path); + return info; +}; router.push = (path) => expoRouter.push(path); router.notFound = () => { diff --git a/packages/router/src/router.js b/packages/router/src/router.js index 8c31b480..68753d01 100644 --- a/packages/router/src/router.js +++ b/packages/router/src/router.js @@ -205,7 +205,7 @@ router.addContext = function (fn) { }; router.addContextForRoute = function (pathOrName, fn) { - const name = _getRouteName(pathOrName); + const name = router.getRouteName(pathOrName); function contextForRoute(context) { if (context.router_info.name == name) { return fn(context); @@ -359,25 +359,50 @@ router.getRouteTitle = function (routeInfo) { function _normalizePath(path) { path = path.replace("", ":slug"); - return router.base_url + "/" + path; + return ( + router.base_url + "/" + (path.startsWith("/") ? path.slice(1) : path) + ); } -function _getRouteName(pathOrName) { +router.getRouteName = function (pathOrName) { var name; - if (router.routes[pathOrName.toLowerCase()]) { + if ( + router.routes[pathOrName.toLowerCase()] || + router.routes[pathOrName.toUpperCase()] + ) { name = pathOrName; } else { - Object.entries(router.routes).forEach(([rname, rpath]) => { - if (_normalizePath(pathOrName) === rpath.path) { + pathOrName = _normalizePath(pathOrName); + for (const [rname, rpath] of Object.entries(router.routes)) { + if (rpath.path && router.matchPath(pathOrName, rpath.path)) { name = rname; + break; } - }); + } } if (!name) { throw new Error("Unrecognized route: " + pathOrName); } return name.toLowerCase(); -} +}; + +router.matchPath = function (path1, path2) { + const parts1 = path1.split("/"), + parts2 = path2.split("/"), + maxLen = Math.max(parts1.length, parts2.length); + for (let i = 0; i < maxLen; i++) { + if ( + (!parts1[i] && !parts2[i]) || + (parts1[i] || "").startsWith(":") || + (parts2[i] || "").startsWith(":") + ) { + continue; + } else if (parts1[i] != parts2[i]) { + return false; + } + } + return true; +}; var _lastRouteInfo = null; router.computeRouteInfo = function (location) {