Skip to content

fix(releases-widget): error display, decouple database repository object from responses and batch widget queries #2891

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 138 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
237c66d
fix: added `withAsterisk` property to `IconPicker`, defaulted to true…
Feb 27, 2025
9706577
feat: working on new repositories widget, added inital UI
Aandree5 Feb 27, 2025
6254a30
fix: renamed "repositories" widget to "releases"
Aandree5 Mar 4, 2025
9de504b
Merge branch 'homarr-labs:dev' into fix/IconPicker-forcing-asterisk
Aandree5 Mar 4, 2025
ca0584e
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 4, 2025
fc41d4d
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 4, 2025
9ba6295
Merge branch 'homarr-labs:dev' into fix/IconPicker-forcing-asterisk
Aandree5 Mar 10, 2025
7ef5aaf
Merge branch 'homarr-labs:dev' into feat/releases-widget
Aandree5 Mar 10, 2025
01083a3
feat: added `highlightWhitin` and `showOnlyHighlighted` functionality
Aandree5 Mar 10, 2025
9e14dda
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 10, 2025
e11f9ff
fix: release repository type
Aandree5 Mar 10, 2025
616b18f
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 16, 2025
0deddc6
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 21, 2025
448b85f
Merge branch 'dev' into fix/IconPicker-forcing-asterisk
Aandree5 Mar 27, 2025
96e4364
feat: updated UI
Aandree5 Mar 21, 2025
1404591
feat: added more information to releases repository
Aandree5 Mar 24, 2025
83ff1ad
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 24, 2025
6189bc1
feat: improved widget UI
Aandree5 Mar 26, 2025
b8e781e
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 26, 2025
990e09b
fix: cleaned up previously changed file
Aandree5 Mar 26, 2025
4be462d
feat: extracted repository display code to it's own component
Aandree5 Mar 26, 2025
fe82f1b
feat: added stale release highlighting
Aandree5 Mar 27, 2025
ed78f12
feat: refactor repository statistics display to use compact number fo…
Aandree5 Mar 27, 2025
0fc9bce
fix: code formatting
Aandree5 Mar 27, 2025
04e49c3
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 27, 2025
edb6a6f
fix: speling mistake
Aandree5 Mar 27, 2025
cbcdb61
Merge branch 'fix/IconPicker-forcing-asterisk' of https://github.com/…
Aandree5 Mar 27, 2025
2e242a0
fix: improved code quality, fixed issues noted by `DeepSource`
Aandree5 Mar 27, 2025
e7f75e8
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 27, 2025
4529e2d
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 28, 2025
f91f358
fix: issue with installing `react-markdown` package
Aandree5 Mar 28, 2025
acd239a
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 28, 2025
c3485d7
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 28, 2025
af3733e
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 28, 2025
944f8ea
Merge branch 'dev' into feat/releases-widget
Aandree5 Mar 28, 2025
d39ff08
fix: pnpm lock file merge issues
Aandree5 Mar 28, 2025
7d948e6
Merge remote-tracking branch 'origin/dev' into feat/releases-widget
Meierschlumpf Apr 1, 2025
92bdc77
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 7, 2025
7f7c62e
fix: fixed lock file from merge
Aandree5 Apr 7, 2025
1f0268d
fix: Component PascalCase
Aandree5 Apr 7, 2025
bed412d
fix: moved regex list of options to outside the modal
Aandree5 Apr 7, 2025
27b672c
fix: updated vergion regex autocomplete options to get it's data from…
Aandree5 Apr 7, 2025
b5031cb
fix: previous merge issue with `en.json` file with missin `}`
Aandree5 Apr 7, 2025
ed657fe
fix: updated repository modal to only use the local state `onChange` …
Aandree5 Apr 7, 2025
9205b5a
fix: when mathing the releases check for provider name along with ide…
Aandree5 Apr 7, 2025
cffada5
fix: set request repositories to `useMemo()`
Aandree5 Apr 7, 2025
62d3bbc
fix: update release description button text
Aandree5 Apr 7, 2025
40b559c
fix: updated widget icon to a rocket
Aandree5 Apr 7, 2025
bcee348
fix: updated pre-release icon to differentiate from the widget icon
Aandree5 Apr 7, 2025
ae4ae85
fix: changed `repository` and `provider` classes to just `objects` wi…
Aandree5 Apr 7, 2025
7a06b08
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 7, 2025
299a0bd
fix: added impot `type`
Aandree5 Apr 7, 2025
b793350
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 8, 2025
6fd620f
fix: changed `for` loop to `array.map` and `useMemo` to improve stabi…
Aandree5 Apr 8, 2025
69d2db6
fix: issue with provider picker not allwoing to pick providers with s…
Aandree5 Apr 8, 2025
4f3841f
fix: improving `versionRegex` field
Aandree5 Apr 8, 2025
b1a133e
fix: issue with provider select input, showing the previouslly select…
Aandree5 Apr 9, 2025
d11ae02
fix: finalised change from `versionRegex` field to `versionFilter`
Aandree5 Apr 9, 2025
0ad4f53
fix: changed options edit form from `Grid` to `Group`
Aandree5 Apr 9, 2025
55e3955
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 9, 2025
2760ae5
fix: lock file from previous merge
Aandree5 Apr 9, 2025
3e4e7fb
fix: Use shorthand property syntax for object literals JS-0240
Aandree5 Apr 9, 2025
e24f6e5
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 9, 2025
36eca0b
fix: working on changing `Grid` to `flex`
Aandree5 Apr 9, 2025
b476ffc
fix: finalised changing `Grid` to `flex`
Aandree5 Apr 9, 2025
6bb9508
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 9, 2025
6698a75
feat: changed `ReleaseRepository` to only keep track of the `provider…
Aandree5 Apr 11, 2025
0c5341e
fix: validation of `vergionFilter`
Aandree5 Apr 11, 2025
4ce6b59
fix: improved `request-handler` code quality, changed provider relat…
Aandree5 Apr 11, 2025
ed362c2
feat: allowed for links in release description markdown
Aandree5 Apr 11, 2025
64b41bb
fix: added tooltip to version text, this will help when the version t…
Aandree5 Apr 11, 2025
fec53b8
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 11, 2025
b05537a
fix: changed `MaskedOrNormalImage` to allow undefined image url
Aandree5 Apr 11, 2025
0f7ecca
fix: moved regex generation to server side
Aandree5 Apr 11, 2025
34900a3
fix: added warning log on details parsing failure
Aandree5 Apr 11, 2025
2f25ca0
fix: removed `Nr.` from tooltips
Aandree5 Apr 11, 2025
16f5621
fix: added `@homarr/forms-collection` to `widgets` package
Aandree5 Apr 11, 2025
33a975a
fix: removed `key` from components not in an array
Aandree5 Apr 11, 2025
7445d1c
fix: removed logging the response content, has it has been already read
Aandree5 Apr 11, 2025
d948f57
fix: bug with component being changed from uncontrolled state to a co…
Aandree5 Apr 11, 2025
a445ef9
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 11, 2025
ab592bf
fix: format fix
Aandree5 Apr 11, 2025
e551c56
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 14, 2025
57e0dba
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 14, 2025
09d931f
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 15, 2025
e338056
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 15, 2025
e891629
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 16, 2025
93fa3bb
fix: moved provider api details to `request-handler` package
Aandree5 Apr 16, 2025
ba5fde1
fix: formatting
Aandree5 Apr 16, 2025
02523b5
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 16, 2025
2c7ba79
fix: lock file broken, missing `@types/react@19.1.0`
Aandree5 Apr 16, 2025
ab901e9
fix: issue with editing the widget repositories
Aandree5 Apr 16, 2025
ea12341
feat: [WIP] adding error displaying to failed request
Aandree5 Apr 16, 2025
990871c
feat: improved error display
Aandree5 Apr 17, 2025
744a4ce
fix: improved error display
Aandree5 Apr 17, 2025
6e95854
fix: decoupled repository obejct from widget options from the reposit…
Aandree5 Apr 17, 2025
fad0d21
fix: formatting
Aandree5 Apr 17, 2025
06dee62
fis: error message text display
Aandree5 Apr 17, 2025
e8b51a3
Merge branch 'dev' into releases-widget-error-handling
Aandree5 Apr 17, 2025
c6dc920
fix: previous auto update of `homarr-docs-sitemap.ts`
Aandree5 Apr 19, 2025
a86005e
fix: repository edit modal taking entire form validation into account
Aandree5 Apr 19, 2025
e5c0921
fix: improved repository edit form
Aandree5 Apr 19, 2025
2721014
fix: improved repository edit form
Aandree5 Apr 19, 2025
b02f3cf
Merge branch 'dev' into feat/releases-widget
Aandree5 Apr 19, 2025
66c8c40
Merge branch 'feat/releases-widget' of https://github.com/Aandree5/ho…
Aandree5 Apr 19, 2025
02a2ab1
fix: merge change
Aandree5 Apr 19, 2025
4b52612
fix: issue with some release's missing data not being properly ignored
Aandree5 Apr 19, 2025
c35c8f1
Merge branch 'dev' into releases-widget-error-handling
Aandree5 Apr 20, 2025
a912263
Merge branch 'dev' of https://github.com/homarr-labs/homarr into rele…
Aandree5 Apr 25, 2025
fa4a0e6
fix: merge formatting issue
Aandree5 Apr 25, 2025
d89e310
fix: formatting
Aandree5 Apr 25, 2025
8d3831d
fix: localised version filter missing releases error message
Aandree5 Apr 25, 2025
9a2596f
fix: issue with having too many repositories
Aandree5 Apr 25, 2025
a1cc709
Merge branch 'dev' into releases-widget-error-handling
Aandree5 Apr 25, 2025
39830be
Merge branch 'dev' into releases-widget-error-handling
Aandree5 Apr 26, 2025
6968bdb
fix: initialising variable with `undefined`
Aandree5 Apr 26, 2025
1769843
fix: set missing data from api calls to `undefined`
Aandree5 Apr 26, 2025
a9bbc1a
Merge branch 'dev' into releases-widget-error-handling
Aandree5 Apr 27, 2025
89e3d1e
Merge branch 'dev' into releases-widget-error-handling
Aandree5 Apr 28, 2025
a27ba24
Update packages/request-handler/src/releases-providers.ts
Aandree5 Apr 29, 2025
a4d2638
Merge branch 'dev' into releases-widget-error-handling
Aandree5 Apr 29, 2025
3aa31b4
fix(releases-widget): moved common functions out of `components.tsx` …
Aandree5 Apr 29, 2025
f2c38ac
Merge branch 'dev' into releases-widget-error-handling
Aandree5 Apr 29, 2025
5776817
fix(releases-widget): adapted `isDateWithin` to use `dayjs` for calcu…
Aandree5 Apr 30, 2025
43b8a28
Merge branch 'releases-widget-error-handling' of https://github.com/A…
Aandree5 Apr 30, 2025
c22d583
Merge branch 'dev' into releases-widget-error-handling
Aandree5 Apr 30, 2025
149dd95
Merge branch 'dev' into releases-widget-error-handling
Aandree5 Apr 30, 2025
9a3c304
fix(releases-widget): allowed for typing the relative date units inde…
Aandree5 Apr 30, 2025
e57f4f7
Merge branch 'releases-widget-error-handling' of https://github.com/A…
Aandree5 Apr 30, 2025
a8fab18
Merge branch 'dev' into releases-widget-error-handling
Aandree5 May 2, 2025
1349f32
Merge branch 'dev' into releases-widget-error-handling
Aandree5 May 4, 2025
9a07215
fix(releases-widget): renamed `items` to `itemCount` on `splitToChunk…
Aandree5 May 4, 2025
e49a520
fix(releases-widget): removed `_` from "private" variables
Aandree5 May 4, 2025
a5769e4
fix(releases-widget): renamed `isMinutes` to `isMonths` on `formatRe…
Aandree5 May 4, 2025
6b33522
Merge branch 'releases-widget-error-handling' of https://github.com/A…
Aandree5 May 4, 2025
74b3a1d
Merge branch 'dev' into releases-widget-error-handling
Aandree5 May 6, 2025
3461c79
fix(releases-widget): lint issues
Aandree5 May 6, 2025
816094f
Merge branch 'dev' into releases-widget-error-handling
Aandree5 May 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/api/src/router/widgets/releases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { releasesRequestHandler } from "@homarr/request-handler/releases";

import { createTRPCRouter, publicProcedure } from "../../trpc";

const formatVersionFilterRegex = (versionFilter: z.infer<typeof _releaseVersionFilterSchema> | undefined) => {
const formatVersionFilterRegex = (versionFilter: z.infer<typeof releaseVersionFilterSchema> | undefined) => {
if (!versionFilter) return undefined;

const escapedPrefix = versionFilter.prefix ? escapeForRegEx(versionFilter.prefix) : "";
Expand All @@ -15,7 +15,7 @@ const formatVersionFilterRegex = (versionFilter: z.infer<typeof _releaseVersionF
return `^${escapedPrefix}${precision}${escapedSuffix}$`;
};

const _releaseVersionFilterSchema = z.object({
const releaseVersionFilterSchema = z.object({
prefix: z.string().optional(),
precision: z.number(),
suffix: z.string().optional(),
Expand All @@ -29,7 +29,7 @@ export const releasesRouter = createTRPCRouter({
z.object({
providerKey: z.string(),
identifier: z.string(),
versionFilter: _releaseVersionFilterSchema.optional(),
versionFilter: releaseVersionFilterSchema.optional(),
}),
),
}),
Expand Down
8 changes: 8 additions & 0 deletions packages/common/src/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ export const splitToNChunks = <T>(array: T[], chunks: number): T[][] => {
}
return result;
};

export const splitToChunksWithNItems = <T>(array: T[], itemCount: number): T[][] => {
const result: T[][] = [];
for (let i = 0; i < array.length; i += itemCount) {
result.push(array.slice(i, i + itemCount));
}
return result;
};
26 changes: 26 additions & 0 deletions packages/common/src/date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import dayjs from "dayjs";
import type { UnitTypeShort } from "dayjs";
import isBetween from "dayjs/plugin/isBetween";

dayjs.extend(isBetween);

const validUnits = ["h", "d", "w", "M", "y"] as UnitTypeShort[];

export const isDateWithin = (date: Date, relativeDate: string): boolean => {
if (relativeDate.length < 2) {
throw new Error("Relative date must be at least 2 characters long");
}

const amount = parseInt(relativeDate.slice(0, -1), 10);
if (isNaN(amount) || amount <= 0) {
throw new Error("Relative date must be a number greater than 0");
}

const unit = relativeDate.slice(-1) as dayjs.UnitTypeShort;
if (!validUnits.includes(unit)) {
throw new Error("Invalid relative time unit");
}

const startDate = dayjs().subtract(amount, unit);
return dayjs(date).isBetween(startDate, dayjs(), null, "[]");
};
1 change: 1 addition & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from "./object";
export * from "./string";
export * from "./cookie";
export * from "./array";
export * from "./date";
export * from "./stopwatch";
export * from "./hooks";
export * from "./url";
Expand Down
49 changes: 49 additions & 0 deletions packages/common/src/test/array.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { describe, expect, it } from "vitest";

import { splitToChunksWithNItems, splitToNChunks } from "../array";

describe("splitToNChunks", () => {
it("should split an array into the specified number of chunks", () => {
const array = [1, 2, 3, 4, 5];
const chunks = 3;
const result = splitToNChunks(array, chunks);
expect(result).toEqual([[1, 2], [3, 4], [5]]);
});

it("should handle an empty array", () => {
const array: number[] = [];
const chunks = 3;
const result = splitToNChunks(array, chunks);
expect(result).toEqual([[], [], []]);
});

it("should handle more chunks than elements", () => {
const array = [1, 2];
const chunks = 5;
const result = splitToNChunks(array, chunks);
expect(result).toEqual([[1], [2], [], [], []]);
});
});

describe("splitToChunksWithNItems", () => {
it("should split an array into chunks with the specified number of items", () => {
const array = [1, 2, 3, 4, 5];
const items = 2;
const result = splitToChunksWithNItems(array, items);
expect(result).toEqual([[1, 2], [3, 4], [5]]);
});

it("should handle an empty array", () => {
const array: number[] = [];
const items = 2;
const result = splitToChunksWithNItems(array, items);
expect(result).toEqual([]);
});

it("should handle more items per chunk than elements", () => {
const array = [1, 2];
const items = 5;
const result = splitToChunksWithNItems(array, items);
expect(result).toEqual([[1, 2]]);
});
});
91 changes: 91 additions & 0 deletions packages/common/src/test/date.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { describe, expect, it } from "vitest";

import { isDateWithin } from "../date";

describe("isDateWithin", () => {
it("should return true for a date within the specified hours", () => {
const date = new Date();
date.setHours(date.getHours() - 20);
expect(isDateWithin(date, "100h")).toBe(true);
});

it("should return false for a date outside the specified hours", () => {
const date = new Date();
date.setHours(date.getHours() - 101);
expect(isDateWithin(date, "100h")).toBe(false);
});

it("should return true for a date within the specified days", () => {
const date = new Date();
date.setDate(date.getDate() - 5);
expect(isDateWithin(date, "10d")).toBe(true);
});

it("should return false for a date outside the specified days", () => {
const date = new Date();
date.setDate(date.getDate() - 11);
expect(isDateWithin(date, "10d")).toBe(false);
});

it("should return true for a date within the specified weeks", () => {
const date = new Date();
date.setDate(date.getDate() - 10);
expect(isDateWithin(date, "7w")).toBe(true);
});

it("should return false for a date outside the specified weeks", () => {
const date = new Date();
date.setDate(date.getDate() - 50);
expect(isDateWithin(date, "7w")).toBe(false);
});

it("should return true for a date within the specified months", () => {
const date = new Date();
date.setMonth(date.getMonth() - 1);
expect(isDateWithin(date, "2M")).toBe(true);
});

it("should return false for a date outside the specified months", () => {
const date = new Date();
date.setMonth(date.getMonth() - 3);
expect(isDateWithin(date, "2M")).toBe(false);
});

it("should return true for a date within the specified years", () => {
const date = new Date();
date.setFullYear(date.getFullYear() - 1);
expect(isDateWithin(date, "2y")).toBe(true);
});

it("should return false for a date outside the specified years", () => {
const date = new Date();
date.setFullYear(date.getFullYear() - 3);
expect(isDateWithin(date, "2y")).toBe(false);
});

it("should return false for a date after the specified relative time", () => {
const date = new Date();
date.setDate(date.getDate() + 2);
expect(isDateWithin(date, "1d")).toBe(false);
});

it("should throw an error for an invalid unit", () => {
const date = new Date();
expect(() => isDateWithin(date, "2x")).toThrow("Invalid relative time unit");
});

it("should throw an error if relativeDate is less than 2 characters long", () => {
const date = new Date();
expect(() => isDateWithin(date, "h")).toThrow("Relative date must be at least 2 characters long");
});

it("should throw an error if relativeDate has an invalid number", () => {
const date = new Date();
expect(() => isDateWithin(date, "hh")).toThrow("Relative date must be a number greater than 0");
});

it("should throw an error if relativeDate is set to 0", () => {
const date = new Date();
expect(() => isDateWithin(date, "0y")).toThrow("Relative date must be a number greater than 0");
});
});
46 changes: 22 additions & 24 deletions packages/request-handler/src/releases-providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,8 @@ export const Providers: ProvidersProps = {
.transform((resp) => ({
projectUrl: `https://hub.docker.com/r/${resp.namespace === "library" ? "_" : resp.namespace}/${resp.name}`,
projectDescription: resp.description,
isFork: false,
isArchived: false,
createdAt: resp.date_registered,
starsCount: resp.star_count,
openIssues: 0,
forksCount: 0,
}))
.safeParse(response);
},
Expand All @@ -67,12 +63,7 @@ export const Providers: ProvidersProps = {
),
})
.transform((resp) => {
return resp.results.map((release) => ({
...release,
releaseUrl: "",
releaseDescription: "",
isPreRelease: false,
}));
return resp.results;
})
.safeParse(response);
},
Expand Down Expand Up @@ -217,7 +208,6 @@ export const Providers: ProvidersProps = {
...release,
releaseUrl: `https://www.npmjs.com/package/${resp.name}/v/${release.latestRelease}`,
releaseDescription: resp.versions[release.latestRelease]?.description ?? "",
isPreRelease: false,
}));
})
.safeParse(response);
Expand Down Expand Up @@ -282,23 +272,31 @@ export const Providers: ProvidersProps = {
},
};

const _detailsSchema = z.object({
projectUrl: z.string(),
projectDescription: z.string(),
isFork: z.boolean(),
isArchived: z.boolean(),
createdAt: z.date(),
starsCount: z.number(),
openIssues: z.number(),
forksCount: z.number(),
});
const _detailsSchema = z
.object({
projectUrl: z.string().optional(),
projectDescription: z.string().optional(),
isFork: z.boolean().optional(),
isArchived: z.boolean().optional(),
createdAt: z.date().optional(),
starsCount: z.number().optional(),
openIssues: z.number().optional(),
forksCount: z.number().optional(),
})
.optional();

const _releasesSchema = z.object({
latestRelease: z.string(),
latestReleaseAt: z.date(),
releaseUrl: z.string(),
releaseDescription: z.string(),
isPreRelease: z.boolean(),
releaseUrl: z.string().optional(),
releaseDescription: z.string().optional(),
isPreRelease: z.boolean().optional(),
error: z
.object({
code: z.string().optional(),
message: z.string().optional(),
})
.optional(),
});

export type DetailsResponse = z.infer<typeof _detailsSchema>;
Expand Down
Loading
Loading