feat(apps): remove url variables (#1771)
This commit is contained in:
@@ -6,7 +6,6 @@ import { IconBox, IconPencil } from "@tabler/icons-react";
|
|||||||
import type { RouterOutputs } from "@homarr/api";
|
import type { RouterOutputs } from "@homarr/api";
|
||||||
import { api } from "@homarr/api/server";
|
import { api } from "@homarr/api/server";
|
||||||
import { auth } from "@homarr/auth/next";
|
import { auth } from "@homarr/auth/next";
|
||||||
import { parseAppHrefWithVariablesServer } from "@homarr/common/server";
|
|
||||||
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
||||||
|
|
||||||
import { ManageContainer } from "~/components/manage/manage-container";
|
import { ManageContainer } from "~/components/manage/manage-container";
|
||||||
@@ -82,8 +81,8 @@ const AppCard = async ({ app }: AppCardProps) => {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{app.href && (
|
{app.href && (
|
||||||
<Anchor href={parseAppHrefWithVariablesServer(app.href)} lineClamp={1} size="sm" w="min-content">
|
<Anchor href={app.href} lineClamp={1} size="sm" w="min-content">
|
||||||
{parseAppHrefWithVariablesServer(app.href)}
|
{app.href}
|
||||||
</Anchor>
|
</Anchor>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -28,8 +28,7 @@
|
|||||||
"@homarr/log": "workspace:^0.1.0",
|
"@homarr/log": "workspace:^0.1.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"next": "^14.2.21",
|
"next": "^14.2.21",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0"
|
||||||
"tldts": "^6.1.69"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import * as tldts from "tldts";
|
|
||||||
|
|
||||||
const safeParseTldts = (url: string) => {
|
|
||||||
try {
|
|
||||||
return tldts.parse(url);
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseAppHrefWithVariables = <TInput extends string | null>(url: TInput, currentHref: string): TInput => {
|
|
||||||
if (!url || url.length === 0) return url;
|
|
||||||
|
|
||||||
const tldtsResult = safeParseTldts(currentHref);
|
|
||||||
|
|
||||||
const urlObject = new URL(currentHref);
|
|
||||||
|
|
||||||
return url
|
|
||||||
.replaceAll("[homarr_base]", `${urlObject.protocol}//${urlObject.hostname}`)
|
|
||||||
.replaceAll("[homarr_hostname]", tldtsResult?.hostname ?? "")
|
|
||||||
.replaceAll("[homarr_domain]", tldtsResult?.domain ?? "")
|
|
||||||
.replaceAll("[homarr_protocol]", urlObject.protocol.replace(":", "")) as TInput;
|
|
||||||
};
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { parseAppHrefWithVariables } from "./base";
|
|
||||||
|
|
||||||
export const parseAppHrefWithVariablesClient = <TInput extends string | null>(url: TInput): TInput => {
|
|
||||||
if (typeof window === "undefined") return url;
|
|
||||||
return parseAppHrefWithVariables(url, window.location.href);
|
|
||||||
};
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import { headers } from "next/headers";
|
|
||||||
|
|
||||||
import { extractBaseUrlFromHeaders } from "../url";
|
|
||||||
import { parseAppHrefWithVariables } from "./base";
|
|
||||||
|
|
||||||
export const parseAppHrefWithVariablesServer = <TInput extends string | null>(url: TInput): TInput => {
|
|
||||||
return parseAppHrefWithVariables(url, extractBaseUrlFromHeaders(headers()));
|
|
||||||
};
|
|
||||||
@@ -1,2 +1 @@
|
|||||||
export * from "./app-url/client";
|
|
||||||
export * from "./revalidate-path-action";
|
export * from "./revalidate-path-action";
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from "./app-url/server";
|
|
||||||
export * from "./security";
|
export * from "./security";
|
||||||
export * from "./encryption";
|
export * from "./encryption";
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { IconLoader } from "@tabler/icons-react";
|
|||||||
import combineClasses from "clsx";
|
import combineClasses from "clsx";
|
||||||
|
|
||||||
import { clientApi } from "@homarr/api/client";
|
import { clientApi } from "@homarr/api/client";
|
||||||
import { parseAppHrefWithVariablesClient } from "@homarr/common/client";
|
|
||||||
import { useRegisterSpotlightContextResults } from "@homarr/spotlight";
|
import { useRegisterSpotlightContextResults } from "@homarr/spotlight";
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
@@ -31,26 +30,30 @@ export default function AppWidget({ options, isEditMode }: WidgetComponentProps<
|
|||||||
);
|
);
|
||||||
useRegisterSpotlightContextResults(
|
useRegisterSpotlightContextResults(
|
||||||
`app-${app.id}`,
|
`app-${app.id}`,
|
||||||
[
|
app.href
|
||||||
{
|
? [
|
||||||
id: app.id,
|
{
|
||||||
name: app.name,
|
id: app.id,
|
||||||
icon: app.iconUrl,
|
name: app.name,
|
||||||
interaction() {
|
icon: app.iconUrl,
|
||||||
return {
|
interaction() {
|
||||||
type: "link",
|
return {
|
||||||
href: parseAppHrefWithVariablesClient(app.href ?? ""),
|
type: "link",
|
||||||
newTab: options.openInNewTab,
|
// We checked above that app.href is defined
|
||||||
};
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
},
|
href: app.href!,
|
||||||
},
|
newTab: options.openInNewTab,
|
||||||
],
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [],
|
||||||
[app, options.openInNewTab],
|
[app, options.openInNewTab],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppLink
|
<AppLink
|
||||||
href={parseAppHrefWithVariablesClient(app.href ?? "")}
|
href={app.href ?? undefined}
|
||||||
openInNewTab={options.openInNewTab}
|
openInNewTab={options.openInNewTab}
|
||||||
enabled={Boolean(app.href) && !isEditMode}
|
enabled={Boolean(app.href) && !isEditMode}
|
||||||
>
|
>
|
||||||
@@ -88,7 +91,7 @@ export default function AppWidget({ options, isEditMode }: WidgetComponentProps<
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface AppLinkProps {
|
interface AppLinkProps {
|
||||||
href: string;
|
href: string | undefined;
|
||||||
openInNewTab: boolean;
|
openInNewTab: boolean;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { IconCheck, IconX } from "@tabler/icons-react";
|
|||||||
|
|
||||||
import type { RouterOutputs } from "@homarr/api";
|
import type { RouterOutputs } from "@homarr/api";
|
||||||
import { clientApi } from "@homarr/api/client";
|
import { clientApi } from "@homarr/api/client";
|
||||||
import { parseAppHrefWithVariablesClient } from "@homarr/common/client";
|
|
||||||
|
|
||||||
import { PingDot } from "./ping-dot";
|
import { PingDot } from "./ping-dot";
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ interface PingIndicatorProps {
|
|||||||
export const PingIndicator = ({ href }: PingIndicatorProps) => {
|
export const PingIndicator = ({ href }: PingIndicatorProps) => {
|
||||||
const [ping] = clientApi.widget.app.ping.useSuspenseQuery(
|
const [ping] = clientApi.widget.app.ping.useSuspenseQuery(
|
||||||
{
|
{
|
||||||
url: parseAppHrefWithVariablesClient(href),
|
url: href,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
refetchOnMount: false,
|
refetchOnMount: false,
|
||||||
@@ -25,7 +24,7 @@ export const PingIndicator = ({ href }: PingIndicatorProps) => {
|
|||||||
const [pingResult, setPingResult] = useState<RouterOutputs["widget"]["app"]["ping"]>(ping);
|
const [pingResult, setPingResult] = useState<RouterOutputs["widget"]["app"]["ping"]>(ping);
|
||||||
|
|
||||||
clientApi.widget.app.updatedPing.useSubscription(
|
clientApi.widget.app.updatedPing.useSubscription(
|
||||||
{ url: parseAppHrefWithVariablesClient(href) },
|
{ url: href },
|
||||||
{
|
{
|
||||||
onData(data) {
|
onData(data) {
|
||||||
setPingResult(data);
|
setPingResult(data);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { Anchor, Box, Card, Divider, Flex, Group, Stack, Text, Title, UnstyledBu
|
|||||||
|
|
||||||
import type { RouterOutputs } from "@homarr/api";
|
import type { RouterOutputs } from "@homarr/api";
|
||||||
import { clientApi } from "@homarr/api/client";
|
import { clientApi } from "@homarr/api/client";
|
||||||
import { parseAppHrefWithVariablesClient } from "@homarr/common/client";
|
|
||||||
import { useRegisterSpotlightContextResults } from "@homarr/spotlight";
|
import { useRegisterSpotlightContextResults } from "@homarr/spotlight";
|
||||||
|
|
||||||
import type { WidgetComponentProps } from "../definition";
|
import type { WidgetComponentProps } from "../definition";
|
||||||
@@ -19,18 +18,22 @@ export default function BookmarksWidget({ options, width, height, itemId }: Widg
|
|||||||
|
|
||||||
useRegisterSpotlightContextResults(
|
useRegisterSpotlightContextResults(
|
||||||
`bookmark-${itemId}`,
|
`bookmark-${itemId}`,
|
||||||
data.map((app) => ({
|
data
|
||||||
id: app.id,
|
.filter((app) => app.href !== null)
|
||||||
name: app.name,
|
.map((app) => ({
|
||||||
icon: app.iconUrl,
|
id: app.id,
|
||||||
interaction() {
|
name: app.name,
|
||||||
return {
|
icon: app.iconUrl,
|
||||||
type: "link",
|
interaction() {
|
||||||
href: parseAppHrefWithVariablesClient(app.href ?? ""),
|
return {
|
||||||
newTab: false,
|
type: "link",
|
||||||
};
|
// We checked above that app.href is defined
|
||||||
},
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
})),
|
href: app.href!,
|
||||||
|
newTab: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
})),
|
||||||
[data],
|
[data],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -707,9 +707,6 @@ importers:
|
|||||||
react:
|
react:
|
||||||
specifier: ^19.0.0
|
specifier: ^19.0.0
|
||||||
version: 19.0.0
|
version: 19.0.0
|
||||||
tldts:
|
|
||||||
specifier: ^6.1.69
|
|
||||||
version: 6.1.69
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@homarr/eslint-config':
|
'@homarr/eslint-config':
|
||||||
specifier: workspace:^0.2.0
|
specifier: workspace:^0.2.0
|
||||||
|
|||||||
Reference in New Issue
Block a user