diff --git a/web/src/components/badge-cloud.tsx b/web/src/components/badge-cloud.tsx
new file mode 100644
index 000000000..566071a63
--- /dev/null
+++ b/web/src/components/badge-cloud.tsx
@@ -0,0 +1,62 @@
+"use client";
+
+import { Badge } from "@/components/ui/badge";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
+
+interface BadgeItem {
+ key: number;
+ value: string;
+ tooltip?: string;
+ href?: string;
+}
+
+interface BadgeCloudProps {
+ items: readonly BadgeItem[];
+}
+
+const BadgeElement = ({ item }: { item: BadgeItem }) => {
+ const badge = {item.value};
+
+ return item.href ? (
+
+ {badge}
+
+ ) : (
+ badge
+ );
+};
+
+export function BadgeCloud({ items }: BadgeCloudProps) {
+ const hasTooltips = items.some((item) => item.tooltip || item.href);
+
+ const badges = items.map((item) =>
+ item.tooltip || item.href ? (
+
+
+
+
+
+ {item.tooltip ?? `View ${item.value}'s homepage`}
+
+
+ ) : (
+
+ ),
+ );
+
+ return (
+
+ {hasTooltips ? {badges} : badges}
+
+ );
+}
diff --git a/web/src/components/filterlist-table/columns.tsx b/web/src/components/filterlist-table/columns.tsx
index 7fb61d2b0..472e28e72 100644
--- a/web/src/components/filterlist-table/columns.tsx
+++ b/web/src/components/filterlist-table/columns.tsx
@@ -1,9 +1,9 @@
"use client";
-import { FilterList } from "@/services/get-filterlists";
import { createColumnHelper } from "@tanstack/react-table";
import { FilterListsTableMeta } from ".";
-import { LanguageBadges } from "./language-badges";
+import { BadgeCloud } from "@/components/badge-cloud";
+import { FilterList } from "@/services/get-filterlists";
const columnHelper = createColumnHelper();
@@ -19,11 +19,19 @@ export const columns = [
cell: (info) => {
const meta = info.table.options.meta as FilterListsTableMeta;
const languages =
- info
- .getValue()
- ?.flatMap((id) => meta.languages.find((l) => l.id === id) || []) ??
- [];
- return ;
+ info.getValue()?.flatMap((id) => {
+ const language = meta.languages.find((l) => l.id === id);
+ return language
+ ? [
+ {
+ key: language.id,
+ value: language.iso6391,
+ tooltip: language.name,
+ },
+ ]
+ : [];
+ }) ?? [];
+ return ;
},
}),
columnHelper.accessor((row) => row.licenseId, {
@@ -31,7 +39,17 @@ export const columns = [
cell: (info) => {
const meta = info.table.options.meta as FilterListsTableMeta;
const license = meta.licenses.find((l) => l.id === info.getValue());
- return license?.name;
+ return license ? (
+
+ ) : null;
},
}),
];
diff --git a/web/src/components/filterlist-table/index.tsx b/web/src/components/filterlist-table/index.tsx
index 3faca8c3c..d10b9ee6c 100644
--- a/web/src/components/filterlist-table/index.tsx
+++ b/web/src/components/filterlist-table/index.tsx
@@ -28,19 +28,19 @@ import { Syntax } from "@/services/get-syntaxes";
import { Tag } from "@/services/get-tags";
export interface FilterListsTableMeta extends TableMeta {
- languages: Language[];
- licenses: License[];
+ languages: readonly Language[];
+ licenses: readonly License[];
}
interface FilterListTableProps {
columns: typeof columns;
initialFilterLists: FilterList[];
- languages: Language[];
- licenses: License[];
- maintainers: Maintainer[];
- software: Software[];
- syntaxes: Syntax[];
- tags: Tag[];
+ languages: readonly Language[];
+ licenses: readonly License[];
+ maintainers: readonly Maintainer[];
+ software: readonly Software[];
+ syntaxes: readonly Syntax[];
+ tags: readonly Tag[];
}
export function FilterListTable({
diff --git a/web/src/components/filterlist-table/language-badges.tsx b/web/src/components/filterlist-table/language-badges.tsx
deleted file mode 100644
index 93d1c72cc..000000000
--- a/web/src/components/filterlist-table/language-badges.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-"use client";
-
-import { Badge } from "@/components/ui/badge";
-import {
- Tooltip,
- TooltipContent,
- TooltipProvider,
- TooltipTrigger,
-} from "@/components/ui/tooltip";
-import { Language } from "@/services/get-languages";
-
-interface LanguageBadgesProps {
- languages: Language[];
-}
-
-export function LanguageBadges({ languages }: LanguageBadgesProps) {
- return (
-
-
- {languages.map((language) => (
-
-
- {language.iso6391}
-
-
- {language.name}
-
-
- ))}
-
-
- );
-}
diff --git a/web/src/services/get-filterlist-details.ts b/web/src/services/get-filterlist-details.ts
index b5795a1b0..32dc7b5bf 100644
--- a/web/src/services/get-filterlist-details.ts
+++ b/web/src/services/get-filterlist-details.ts
@@ -11,10 +11,10 @@ export type FilterListDetails = {
name: string;
description: string | null;
licenseId: number;
- syntaxIds: number[] | null;
- languageIds: number[] | null;
- tagIds: number[] | null;
- viewUrls: ViewUrl[] | null;
+ syntaxIds: readonly number[] | null;
+ languageIds: readonly number[] | null;
+ tagIds: readonly number[] | null;
+ viewUrls: readonly ViewUrl[] | null;
homeUrl: string | null;
onionUrl: string | null;
policyUrl: string | null;
@@ -24,13 +24,13 @@ export type FilterListDetails = {
chatUrl: string | null;
emailAddress: string | null;
donateUrl: string | null;
- maintainerIds: number[] | null;
- upstreamFilterListIds: number[] | null;
- forkFilterListIds: number[] | null;
- includedInFilterListIds: number[] | null;
- includesFilterListIds: number[] | null;
- dependencyFilterListIds: number[] | null;
- dependentFilterListIds: number[] | null;
+ maintainerIds: readonly number[] | null;
+ upstreamFilterListIds: readonly number[] | null;
+ forkFilterListIds: readonly number[] | null;
+ includedInFilterListIds: readonly number[] | null;
+ includesFilterListIds: readonly number[] | null;
+ dependencyFilterListIds: readonly number[] | null;
+ dependentFilterListIds: readonly number[] | null;
};
export async function getFilterListDetails(
diff --git a/web/src/services/get-filterlists.ts b/web/src/services/get-filterlists.ts
index e98fc9bbd..27dbf0f62 100644
--- a/web/src/services/get-filterlists.ts
+++ b/web/src/services/get-filterlists.ts
@@ -5,11 +5,11 @@ export type FilterList = {
name: string;
description: string | null;
licenseId: number;
- syntaxIds: number[];
- languageIds: number[];
- tagIds: number[];
+ syntaxIds: readonly number[];
+ languageIds: readonly number[];
+ tagIds: readonly number[];
primaryViewUrl: string | null;
- maintainerIds: number[];
+ maintainerIds: readonly number[];
};
export async function getFilterLists(): Promise {
diff --git a/web/src/services/get-languages.ts b/web/src/services/get-languages.ts
index 25a6d2a6f..b3d570027 100644
--- a/web/src/services/get-languages.ts
+++ b/web/src/services/get-languages.ts
@@ -4,7 +4,7 @@ export type Language = {
id: number;
iso6391: string;
name: string;
- filterListIds: number[]; // TODO: are these needed, or can we remove them from the API?
+ filterListIds: readonly number[]; // TODO: are these needed, or can we remove them from the API?
};
export async function getLanguages(): Promise {
diff --git a/web/src/services/get-licenses.ts b/web/src/services/get-licenses.ts
index 765d12cab..bb91df2e4 100644
--- a/web/src/services/get-licenses.ts
+++ b/web/src/services/get-licenses.ts
@@ -2,12 +2,12 @@ import { API_BASE_URL, DEFAULT_REVALIDATE_SECS } from "./constants";
export type License = {
id: number;
- name: string | null;
+ name: string;
url: string | null;
permitsModification: boolean;
permitsDistribution: boolean;
permitsCommercialUse: boolean;
- filterListIds: number[] | null;
+ filterListIds: readonly number[] | null; // TODO: are these needed, or can we remove them from the API?
};
export async function getLicenses(): Promise {
diff --git a/web/src/services/get-maintainers.ts b/web/src/services/get-maintainers.ts
index 2b392d44b..3a6fe3bbf 100644
--- a/web/src/services/get-maintainers.ts
+++ b/web/src/services/get-maintainers.ts
@@ -6,7 +6,7 @@ export type Maintainer = {
url: string | null;
emailAddress: string | null;
twitterHandle: string | null;
- filterListIds: number[] | null;
+ filterListIds: readonly number[] | null;
};
export async function getMaintainers(): Promise {
diff --git a/web/src/services/get-software.ts b/web/src/services/get-software.ts
index ce21b7e9c..9cdb775c1 100644
--- a/web/src/services/get-software.ts
+++ b/web/src/services/get-software.ts
@@ -7,7 +7,7 @@ export type Software = {
homeUrl: string | null;
downloadUrl: string | null;
supportsAbpUrlScheme: boolean;
- syntaxIds: number[] | null;
+ syntaxIds: readonly number[] | null;
};
export async function getSoftware(): Promise {
diff --git a/web/src/services/get-syntaxes.ts b/web/src/services/get-syntaxes.ts
index 35d8b5a39..8daaa558b 100644
--- a/web/src/services/get-syntaxes.ts
+++ b/web/src/services/get-syntaxes.ts
@@ -5,8 +5,8 @@ export type Syntax = {
name: string | null;
description: string | null;
url: string | null;
- filterListIds: number[] | null;
- softwareIds: number[] | null;
+ filterListIds: readonly number[] | null;
+ softwareIds: readonly number[] | null;
};
export async function getSyntaxes(): Promise {
diff --git a/web/src/services/get-tags.ts b/web/src/services/get-tags.ts
index a2be5d5da..e8a17348f 100644
--- a/web/src/services/get-tags.ts
+++ b/web/src/services/get-tags.ts
@@ -4,7 +4,7 @@ export type Tag = {
id: number;
name: string | null;
description: string | null;
- filterListIds: number[] | null;
+ filterListIds: readonly number[] | null;
};
export async function getTags(): Promise {