From 395574359039dbab30506825e887cb1c1d38ce73 Mon Sep 17 00:00:00 2001 From: Florian Hye <31217036+Flo2410@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:11:35 +0100 Subject: [PATCH 01/20] Enhancement: initially collapsed option for layout groups --- docs/configs/settings.md | 20 ++++++++++++++++++++ src/components/bookmarks/group.jsx | 11 ++++++++--- src/components/services/group.jsx | 18 +++++++++++++++--- src/pages/index.jsx | 5 +++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/docs/configs/settings.md b/docs/configs/settings.md index d3e9a837..9ee86a85 100644 --- a/docs/configs/settings.md +++ b/docs/configs/settings.md @@ -229,6 +229,26 @@ disableCollapse: true By default the feature is enabled. +### Initially collapsed sections + +You can initially collapse sections by adding the `initiallyCollapsed` option to the layout group. + +```yaml +layout: + Section A: + initiallyCollapsed: true +``` + +This can also be set globaly using the `groupsInitiallyCollapsed` option. + +```yaml +groupsInitiallyCollapsed: true +``` + +The value set on a group will overwrite the global setting. + +By default the feature is disabled. + ### Use Equal Height Cards You can enable equal height cards for groups of services, this will make all cards in a row the same height. diff --git a/src/components/bookmarks/group.jsx b/src/components/bookmarks/group.jsx index c5e6a2f1..b13aeac1 100644 --- a/src/components/bookmarks/group.jsx +++ b/src/components/bookmarks/group.jsx @@ -1,4 +1,4 @@ -import { useRef } from "react"; +import { useRef, useEffect } from "react"; import classNames from "classnames"; import { Disclosure, Transition } from "@headlessui/react"; import { MdKeyboardArrowDown } from "react-icons/md"; @@ -7,8 +7,13 @@ import ErrorBoundary from "components/errorboundry"; import List from "components/bookmarks/list"; import ResolvedIcon from "components/resolvedicon"; -export default function BookmarksGroup({ bookmarks, layout, disableCollapse }) { +export default function BookmarksGroup({ bookmarks, layout, disableCollapse, groupsInitiallyCollapsed }) { const panel = useRef(); + + useEffect(() => { + if (layout?.initiallyCollapsed ?? groupsInitiallyCollapsed) panel.current.style.height = `0`; + }, [layout, groupsInitiallyCollapsed]); + return (
- + {({ open }) => ( <> {layout?.header !== false && ( diff --git a/src/components/services/group.jsx b/src/components/services/group.jsx index bcc3ce5d..cdbb89f3 100644 --- a/src/components/services/group.jsx +++ b/src/components/services/group.jsx @@ -1,4 +1,4 @@ -import { useRef } from "react"; +import { useRef, useEffect } from "react"; import classNames from "classnames"; import { Disclosure, Transition } from "@headlessui/react"; import { MdKeyboardArrowDown } from "react-icons/md"; @@ -6,9 +6,21 @@ import { MdKeyboardArrowDown } from "react-icons/md"; import List from "components/services/list"; import ResolvedIcon from "components/resolvedicon"; -export default function ServicesGroup({ group, services, layout, fiveColumns, disableCollapse, useEqualHeights }) { +export default function ServicesGroup({ + group, + services, + layout, + fiveColumns, + disableCollapse, + useEqualHeights, + groupsInitiallyCollapsed, +}) { const panel = useRef(); + useEffect(() => { + if (layout?.initiallyCollapsed ?? groupsInitiallyCollapsed) panel.current.style.height = `0`; + }, [layout, groupsInitiallyCollapsed]); + return (
- + {({ open }) => ( <> {layout?.header !== false && ( diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 59a2ad12..0de51e32 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -311,6 +311,7 @@ function Home({ initialSettings }) { fiveColumns={settings.fiveColumns} disableCollapse={settings.disableCollapse} useEqualHeights={settings.useEqualHeights} + groupsInitiallyCollapsed={settings.groupsInitiallyCollapsed} /> ) : ( ), )} @@ -333,6 +335,7 @@ function Home({ initialSettings }) { layout={settings.layout?.[group.name]} fiveColumns={settings.fiveColumns} disableCollapse={settings.disableCollapse} + groupsInitiallyCollapsed={settings.groupsInitiallyCollapsed} /> ))}
@@ -345,6 +348,7 @@ function Home({ initialSettings }) { bookmarks={group} layout={settings.layout?.[group.name]} disableCollapse={settings.disableCollapse} + groupsInitiallyCollapsed={settings.groupsInitiallyCollapsed} /> ))}
@@ -361,6 +365,7 @@ function Home({ initialSettings }) { settings.disableCollapse, settings.useEqualHeights, settings.cardBlur, + settings.groupsInitiallyCollapsed, initialSettings.layout, ]); From 3b76772f81a14843d486450be66d765a564d475d Mon Sep 17 00:00:00 2001 From: Florian Hye <31217036+Flo2410@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:49:25 +0100 Subject: [PATCH 02/20] Fix: search opens when losing focus, prevent unnecessary search API calls (#2867) Co-Authored-By: shamoon <4887959+shamoon@users.noreply.github.com> --- src/components/quicklaunch.jsx | 2 +- src/components/widgets/search/search.jsx | 81 +++++++++++++++--------- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/components/quicklaunch.jsx b/src/components/quicklaunch.jsx index 23f7cef9..aaa40493 100644 --- a/src/components/quicklaunch.jsx +++ b/src/components/quicklaunch.jsx @@ -120,7 +120,7 @@ export default function QuickLaunch({ }); if (showSearchSuggestions && searchProvider.suggestionUrl) { - if (searchString.trim() !== searchSuggestions[0]) { + if (searchString.trim() !== searchSuggestions[0]?.trim()) { fetch( `/api/search/searchSuggestion?query=${encodeURIComponent(searchString)}&providerName=${ searchProvider.name ?? "Custom" diff --git a/src/components/widgets/search/search.jsx b/src/components/widgets/search/search.jsx index 6a634308..c9391d35 100644 --- a/src/components/widgets/search/search.jsx +++ b/src/components/widgets/search/search.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback, Fragment } from "react"; +import { useState, useEffect, Fragment } from "react"; import { useTranslation } from "next-i18next"; import { FiSearch } from "react-icons/fi"; import { SiDuckduckgo, SiMicrosoftbing, SiGoogle, SiBaidu, SiBrave } from "react-icons/si"; @@ -119,20 +119,27 @@ export default function Search({ options }) { }; }, [selectedProvider, options, query, searchSuggestions]); - const submitCallback = useCallback( - (value) => { - const q = encodeURIComponent(value); - const { url } = selectedProvider; - if (url) { - window.open(`${url}${q}`, options.target || "_blank"); - } else { - window.open(`${options.url}${q}`, options.target || "_blank"); - } + let currentSuggestion; - setQuery(""); - }, - [selectedProvider, options.url, options.target], - ); + function doSearch(value) { + const q = encodeURIComponent(value); + const { url } = selectedProvider; + if (url) { + window.open(`${url}${q}`, options.target || "_blank"); + } else { + window.open(`${options.url}${q}`, options.target || "_blank"); + } + + setQuery(""); + currentSuggestion = null; + } + + const handleSearchKeyDown = (event) => { + const useSuggestion = searchSuggestions.length && currentSuggestion; + if (event.key === "Enter") { + doSearch(useSuggestion ? currentSuggestion : event.target.value); + } + }; if (!availableProviderIds) { return null; @@ -148,7 +155,7 @@ export default function Search({ options }) {
- + setQuery(event.target.value)} + onChange={(event) => { + setQuery(event.target.value); + }} required autoCapitalize="off" autoCorrect="off" autoComplete="off" // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus={options.focus} + onBlur={(e) => e.preventDefault()} + onKeyDown={handleSearchKeyDown} /> {searchSuggestions[1].map((suggestion) => ( - - {({ active }) => ( -
- {suggestion.indexOf(query) === 0 ? query : ""} - - {suggestion.indexOf(query) === 0 ? suggestion.substring(query.length) : suggestion} - -
- )} + { + doSearch(suggestion); + }} + className="flex w-full" + > + {({ active }) => { + if (active) currentSuggestion = suggestion; + return ( +
+ {suggestion.indexOf(query) === 0 ? query : ""} + + {suggestion.indexOf(query) === 0 ? suggestion.substring(query.length) : suggestion} + +
+ ); + }}
))}
From eda5b0f0cf0fa0d8deb041e09781cbff977f4710 Mon Sep 17 00:00:00 2001 From: Lawton Manning Date: Fri, 9 Feb 2024 14:00:08 -0500 Subject: [PATCH 03/20] Fix: healthchecks widget does not respect fields parameter (#2875) --- src/widgets/healthchecks/component.jsx | 28 +++++++++++--------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/widgets/healthchecks/component.jsx b/src/widgets/healthchecks/component.jsx index bcb8d740..21fb7cb6 100644 --- a/src/widgets/healthchecks/component.jsx +++ b/src/widgets/healthchecks/component.jsx @@ -63,26 +63,22 @@ export default function Component({ service }) { ); } - const hasUuid = widget?.uuid; + const hasUuid = !!widget?.uuid; const { upCount, downCount } = countStatus(data); - return ( + return hasUuid ? ( - {hasUuid ? ( - <> - - - - ) : ( - <> - - - - )} + + + + ) : ( + + + ); } From 187291eecad34896f2c059807933df8e6bfa770a Mon Sep 17 00:00:00 2001 From: Florian Hye <31217036+Flo2410@users.noreply.github.com> Date: Fri, 9 Feb 2024 20:46:56 +0100 Subject: [PATCH 04/20] Chore: add Python requirements and prettier to devcontaier (#2878) --- .devcontainer/Dockerfile | 6 ++++ .devcontainer/devcontainer.json | 49 ++++++++++++++++----------------- .devcontainer/setup.sh | 2 ++ 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 9f20426f..44bc1759 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,4 +3,10 @@ FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:${VARIANT} RUN npm install -g pnpm +RUN apt-get update \ + && apt-get -y install --no-install-recommends \ + python3-pip \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + ENV PATH="${PATH}:./node_modules/.bin" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 15d1b9f3..06e7f6ee 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,27 +1,26 @@ { - "name": "homepage", - "build": { - "dockerfile": "Dockerfile", - "args": { - "VARIANT": "18-bullseye" - } - }, - "customizations": { - "vscode": { - "extensions": [ - "dbaeumer.vscode-eslint", - "mhutchie.git-graph", - "streetsidesoftware.code-spell-checker", - ], - "settings": { - "eslint.format.enable": true, - "eslint.lintTask.enable": true, - "eslint.packageManager": "pnpm" - } - } - }, - "postCreateCommand": ".devcontainer/setup.sh", - "forwardPorts": [ - 3000 - ] + "name": "homepage", + "build": { + "dockerfile": "Dockerfile", + "args": { + "VARIANT": "18-bullseye", + }, + }, + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "mhutchie.git-graph", + "streetsidesoftware.code-spell-checker", + "esbenp.prettier-vscode", + ], + "settings": { + "eslint.format.enable": true, + "eslint.lintTask.enable": true, + "eslint.packageManager": "pnpm", + }, + }, + }, + "postCreateCommand": ".devcontainer/setup.sh", + "forwardPorts": [3000], } diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh index 70bf96cf..ea5d2fe9 100755 --- a/.devcontainer/setup.sh +++ b/.devcontainer/setup.sh @@ -3,6 +3,8 @@ # Install Node packages pnpm install +python3 -m pip install -r requirements.txt + # Copy in skeleton configuration if there is no existing configuration if [ ! -d "config/" ]; then echo "Adding skeleton config" From 1c529c0e7d47d02f578fcec6f3b7b8306b2ab905 Mon Sep 17 00:00:00 2001 From: SASAGAWA Kiyoshi Date: Sun, 11 Feb 2024 05:30:37 +0900 Subject: [PATCH 05/20] Fix: iCal integration fails with all-day events (#2883) --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- src/widgets/calendar/integrations/ical.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/widgets/calendar/integrations/ical.jsx b/src/widgets/calendar/integrations/ical.jsx index ec642791..05d059c7 100644 --- a/src/widgets/calendar/integrations/ical.jsx +++ b/src/widgets/calendar/integrations/ical.jsx @@ -52,15 +52,17 @@ export default function Integration({ config, params, setEvents, hideErrors, tim } const eventToAdd = (date, i, type) => { - const duration = event.dtend.value - event.dtstart.value; - const days = duration / (1000 * 60 * 60 * 24); + // 'dtend' is null for all-day events + const { dtstart, dtend = { value: 0 } } = event; + + const days = dtend.value === 0 ? 1 : (dtend.value - dtstart.value) / (1000 * 60 * 60 * 24); const eventDate = timezone ? DateTime.fromJSDate(date, { zone: timezone }) : DateTime.fromJSDate(date); for (let j = 0; j < days; j += 1) { // See https://github.com/gethomepage/homepage/issues/2753 uid is not stable // assumption is that the event is the same if the start, end and title are all the same - const hash = simpleHash(`${event?.dtstart?.value}${event?.dtend?.value}${title}${i}${j}${type}}`); + const hash = simpleHash(`${dtstart?.value}${dtend?.value}${title}${i}${j}${type}}`); eventsToAdd[hash] = { title, date: eventDate.plus({ days: j }), From 35af27f209a7426baf8d0793b118b7432d0d15fe Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 10 Feb 2024 21:37:59 -0800 Subject: [PATCH 06/20] Update ical.jsx --- src/widgets/calendar/integrations/ical.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/widgets/calendar/integrations/ical.jsx b/src/widgets/calendar/integrations/ical.jsx index 05d059c7..059adfa2 100644 --- a/src/widgets/calendar/integrations/ical.jsx +++ b/src/widgets/calendar/integrations/ical.jsx @@ -54,9 +54,7 @@ export default function Integration({ config, params, setEvents, hideErrors, tim const eventToAdd = (date, i, type) => { // 'dtend' is null for all-day events const { dtstart, dtend = { value: 0 } } = event; - const days = dtend.value === 0 ? 1 : (dtend.value - dtstart.value) / (1000 * 60 * 60 * 24); - const eventDate = timezone ? DateTime.fromJSDate(date, { zone: timezone }) : DateTime.fromJSDate(date); for (let j = 0; j < days; j += 1) { From 87d1ea4f2e7bd3080a3e44786d4dea3a5c56454f Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 13 Feb 2024 10:54:34 -0800 Subject: [PATCH 07/20] Update services.md --- docs/configs/services.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configs/services.md b/docs/configs/services.md index 010950eb..490356fc 100644 --- a/docs/configs/services.md +++ b/docs/configs/services.md @@ -101,7 +101,7 @@ To use a local icon, first create a Docker mount to `/app/public/icons` and then ## Ping -Services may have an optional `ping` property that allows you to monitor the availability of an external host. As of v0.8.0, the ping feature attempts to use a true (ICMP) ping command on the underlying host. +Services may have an optional `ping` property that allows you to monitor the availability of an external host. As of v0.8.0, the ping feature attempts to use a true (ICMP) ping command on the underlying host. Currently, only IPv4 is supported. ```yaml - Group A: From ea0310548a190af11ba18fa2fd1f705bea518902 Mon Sep 17 00:00:00 2001 From: Benedek Kozma Date: Wed, 14 Feb 2024 01:42:50 +0100 Subject: [PATCH 08/20] Change: use Script component instead of async script for custom.js (#2901) --- src/pages/index.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 0de51e32..4bf1d9a2 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -1,6 +1,7 @@ /* eslint-disable react/no-array-index-key */ import useSWR, { SWRConfig } from "swr"; import Head from "next/head"; +import Script from "next/script"; import dynamic from "next/dynamic"; import classNames from "classnames"; import { useTranslation } from "next-i18next"; @@ -401,8 +402,7 @@ function Home({ initialSettings }) { emptyValue="/* No custom CSS */" /> - -