From 0e09e9e0277fa3d13f976e4a88fc4a39307a67b4 Mon Sep 17 00:00:00 2001 From: Yihao Wang Date: Sun, 13 Jul 2025 22:50:26 +1200 Subject: [PATCH 01/10] Add bookmark helper functions --- src/utils/bookmark.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/utils/bookmark.ts diff --git a/src/utils/bookmark.ts b/src/utils/bookmark.ts new file mode 100644 index 0000000..125e752 --- /dev/null +++ b/src/utils/bookmark.ts @@ -0,0 +1,32 @@ +import { DefinedTool } from '@tools/defineTool'; + +const bookmarkedToolsKey = 'bookmarkedTools'; + +export function getBookmarkedTools(): string[] { + return localStorage.getItem(bookmarkedToolsKey)?.split(',') ?? []; +} + +export function isBookmarked(tool: DefinedTool): boolean { + return getBookmarkedTools().some((path) => path === tool.path); +} + +export function toggleBookmarked(tool: DefinedTool) { + if (isBookmarked(tool)) { + unbookmark(tool); + } else { + bookmark(tool); + } +} + +function bookmark(tool: DefinedTool) { + localStorage.setItem( + bookmarkedToolsKey, + tool.path + localStorage.getItem(bookmarkedToolsKey) + ); +} + +function unbookmark(tool: DefinedTool) { + const bookmarked = localStorage.getItem(bookmarkedToolsKey)?.split(',') ?? []; + const unbookmarked = bookmarked.filter((path) => path != tool.path); + localStorage.setItem(bookmarkedToolsKey, unbookmarked.join(',')); +} From 0d3a17a923629d23faa77a7d808c18695b2cee58 Mon Sep 17 00:00:00 2001 From: Yihao Wang Date: Sun, 13 Jul 2025 23:49:26 +1200 Subject: [PATCH 02/10] Implement bookmarking --- src/components/Hero.tsx | 44 +++++++++++++++++++++++++++++++++++++++-- src/utils/bookmark.ts | 6 +++--- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index c1c6487..e1acc51 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -18,6 +18,11 @@ import { useNavigate } from 'react-router-dom'; import _ from 'lodash'; import { Icon } from '@iconify/react'; import { getToolCategoryTitle } from '@utils/string'; +import { + getBookmarkedToolPaths, + isBookmarked, + toggleBookmarked +} from '@utils/bookmark'; const GroupHeader = styled('div')(({ theme }) => ({ position: 'sticky', @@ -33,7 +38,12 @@ const GroupHeader = styled('div')(({ theme }) => ({ const GroupItems = styled('ul')({ padding: 0 }); -const exampleTools: { label: string; url: string }[] = [ + +type ToolInfo = { + label: string; + url: string; +}; +const exampleTools: ToolInfo[] = [ { label: 'Create a transparent image', url: '/image-generic/create-transparent' @@ -51,6 +61,9 @@ export default function Hero() { const [inputValue, setInputValue] = useState(''); const theme = useTheme(); const [filteredTools, setFilteredTools] = useState(tools); + const [bookmarkedToolPaths, setBookmarkedToolPaths] = useState( + getBookmarkedToolPaths() + ); const navigate = useNavigate(); const handleInputChange = ( event: React.ChangeEvent<{}>, @@ -59,6 +72,20 @@ export default function Hero() { setInputValue(newInputValue); setFilteredTools(filterTools(tools, newInputValue)); }; + const toolsMap = new Map(); + for (const tool of filteredTools) { + toolsMap.set(tool.path, { + label: tool.name, + url: '/' + tool.path + }); + } + + const displayedTools = + bookmarkedToolPaths.length > 0 + ? bookmarkedToolPaths + .map((path) => toolsMap.get(path)) + .filter((tools) => tools != undefined) + : exampleTools; return ( @@ -127,6 +154,19 @@ export default function Hero() { {option.name} {option.shortDescription} + { + e.stopPropagation(); + toggleBookmarked(option); + setBookmarkedToolPaths(getBookmarkedToolPaths()); + }} + icon={ + isBookmarked(option) + ? 'mdi:bookmark' + : 'mdi:bookmark-plus-outline' + } + /> )} @@ -137,7 +177,7 @@ export default function Hero() { }} /> - {exampleTools.map((tool) => ( + {displayedTools.map((tool) => ( navigate(tool.url.startsWith('/') ? tool.url : `/${tool.url}`) diff --git a/src/utils/bookmark.ts b/src/utils/bookmark.ts index 125e752..eb33ea9 100644 --- a/src/utils/bookmark.ts +++ b/src/utils/bookmark.ts @@ -2,12 +2,12 @@ import { DefinedTool } from '@tools/defineTool'; const bookmarkedToolsKey = 'bookmarkedTools'; -export function getBookmarkedTools(): string[] { +export function getBookmarkedToolPaths(): string[] { return localStorage.getItem(bookmarkedToolsKey)?.split(',') ?? []; } export function isBookmarked(tool: DefinedTool): boolean { - return getBookmarkedTools().some((path) => path === tool.path); + return getBookmarkedToolPaths().some((path) => path === tool.path); } export function toggleBookmarked(tool: DefinedTool) { @@ -21,7 +21,7 @@ export function toggleBookmarked(tool: DefinedTool) { function bookmark(tool: DefinedTool) { localStorage.setItem( bookmarkedToolsKey, - tool.path + localStorage.getItem(bookmarkedToolsKey) + tool.path + ',' + (localStorage.getItem(bookmarkedToolsKey) ?? '') ); } From 989aa7958e1b1c95772265923f9e947a43dce075 Mon Sep 17 00:00:00 2001 From: Yihao Wang Date: Mon, 14 Jul 2025 00:11:17 +1200 Subject: [PATCH 03/10] Fix bookmarked tools retrieval --- src/components/Hero.tsx | 21 +++++++++++++++------ src/utils/bookmark.ts | 18 +++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index e1acc51..0508014 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -148,12 +148,21 @@ export default function Hero() { {...props} onClick={() => navigate('/' + option.path)} > - - - - {option.name} - {option.shortDescription} - + + + + + {option.name} + + {option.shortDescription} + + + { diff --git a/src/utils/bookmark.ts b/src/utils/bookmark.ts index eb33ea9..9877973 100644 --- a/src/utils/bookmark.ts +++ b/src/utils/bookmark.ts @@ -3,7 +3,12 @@ import { DefinedTool } from '@tools/defineTool'; const bookmarkedToolsKey = 'bookmarkedTools'; export function getBookmarkedToolPaths(): string[] { - return localStorage.getItem(bookmarkedToolsKey)?.split(',') ?? []; + return ( + localStorage + .getItem(bookmarkedToolsKey) + ?.split(',') + ?.filter((path) => path) ?? [] + ); } export function isBookmarked(tool: DefinedTool): boolean { @@ -21,12 +26,15 @@ export function toggleBookmarked(tool: DefinedTool) { function bookmark(tool: DefinedTool) { localStorage.setItem( bookmarkedToolsKey, - tool.path + ',' + (localStorage.getItem(bookmarkedToolsKey) ?? '') + [tool.path, ...getBookmarkedToolPaths()].join(',') ); } function unbookmark(tool: DefinedTool) { - const bookmarked = localStorage.getItem(bookmarkedToolsKey)?.split(',') ?? []; - const unbookmarked = bookmarked.filter((path) => path != tool.path); - localStorage.setItem(bookmarkedToolsKey, unbookmarked.join(',')); + localStorage.setItem( + bookmarkedToolsKey, + getBookmarkedToolPaths() + .filter((path) => path !== tool.path) + .join(',') + ); } From a50028e1fbe9794e1e73b092679a6f3de34b02f5 Mon Sep 17 00:00:00 2001 From: Yihao Wang Date: Mon, 14 Jul 2025 13:35:48 +1200 Subject: [PATCH 04/10] Improve styling --- src/components/Hero.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 0508014..09f6843 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -151,7 +151,7 @@ export default function Hero() { @@ -170,6 +170,11 @@ export default function Hero() { toggleBookmarked(option); setBookmarkedToolPaths(getBookmarkedToolPaths()); }} + color={ + isBookmarked(option) + ? theme.palette.primary.main + : theme.palette.grey[500] + } icon={ isBookmarked(option) ? 'mdi:bookmark' @@ -185,6 +190,9 @@ export default function Hero() { } }} /> + {bookmarkedToolPaths.length > 0 && ( + Bookmarked tools: + )} {displayedTools.map((tool) => ( Date: Mon, 14 Jul 2025 14:01:54 +1200 Subject: [PATCH 05/10] Add bookmark button to tool page --- src/components/Hero.tsx | 6 +++--- src/components/ToolHeader.tsx | 30 +++++++++++++++++++++++++----- src/components/ToolLayout.tsx | 5 ++++- src/tools/defineTool.tsx | 1 + src/utils/bookmark.ts | 20 ++++++++++---------- 5 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 09f6843..47a4066 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -167,16 +167,16 @@ export default function Hero() { fontSize={20} onClick={(e) => { e.stopPropagation(); - toggleBookmarked(option); + toggleBookmarked(option.path); setBookmarkedToolPaths(getBookmarkedToolPaths()); }} color={ - isBookmarked(option) + isBookmarked(option.path) ? theme.palette.primary.main : theme.palette.grey[500] } icon={ - isBookmarked(option) + isBookmarked(option.path) ? 'mdi:bookmark' : 'mdi:bookmark-plus-outline' } diff --git a/src/components/ToolHeader.tsx b/src/components/ToolHeader.tsx index 8d88f13..29e0bf0 100644 --- a/src/components/ToolHeader.tsx +++ b/src/components/ToolHeader.tsx @@ -1,4 +1,4 @@ -import { Box, Button, styled, useTheme } from '@mui/material'; +import { Box, Button, Stack, styled, useTheme } from '@mui/material'; import Typography from '@mui/material/Typography'; import ToolBreadcrumb from './ToolBreadcrumb'; import { capitalizeFirstLetter } from '../utils/string'; @@ -7,6 +7,7 @@ import { Icon, IconifyIcon } from '@iconify/react'; import { categoriesColors } from '../config/uiConfig'; import { getToolsByCategory } from '@tools/index'; import { useEffect, useState } from 'react'; +import { isBookmarked, toggleBookmarked } from '@utils/bookmark'; const StyledButton = styled(Button)(({ theme }) => ({ backgroundColor: 'white', @@ -21,6 +22,7 @@ interface ToolHeaderProps { description: string; icon?: IconifyIcon | string; type: string; + path: string; } function ToolLinks() { @@ -80,8 +82,11 @@ export default function ToolHeader({ icon, title, description, - type + type, + path }: ToolHeaderProps) { + const theme = useTheme(); + const [bookmarked, setBookmarked] = useState(isBookmarked(path)); return ( - - {title} - + + + {title} + + { + toggleBookmarked(path); + setBookmarked(!bookmarked); + }} + icon={bookmarked ? 'mdi:bookmark' : 'mdi:bookmark-plus-outline'} + /> + {description} diff --git a/src/components/ToolLayout.tsx b/src/components/ToolLayout.tsx index 3da0837..6d843f6 100644 --- a/src/components/ToolLayout.tsx +++ b/src/components/ToolLayout.tsx @@ -13,12 +13,14 @@ export default function ToolLayout({ title, description, icon, - type + type, + path }: { title: string; description: string; icon?: IconifyIcon | string; type: string; + path: string; children: ReactNode; }) { const otherCategoryTools = @@ -49,6 +51,7 @@ export default function ToolLayout({ description={description} icon={icon} type={type} + path={path} /> {children} diff --git a/src/tools/defineTool.tsx b/src/tools/defineTool.tsx index ecb056c..1d0a605 100644 --- a/src/tools/defineTool.tsx +++ b/src/tools/defineTool.tsx @@ -74,6 +74,7 @@ export const defineTool = ( description={description} icon={icon} type={basePath} + path={`${basePath}/${path}`} > diff --git a/src/utils/bookmark.ts b/src/utils/bookmark.ts index 9877973..a63166c 100644 --- a/src/utils/bookmark.ts +++ b/src/utils/bookmark.ts @@ -11,30 +11,30 @@ export function getBookmarkedToolPaths(): string[] { ); } -export function isBookmarked(tool: DefinedTool): boolean { - return getBookmarkedToolPaths().some((path) => path === tool.path); +export function isBookmarked(toolPath: string): boolean { + return getBookmarkedToolPaths().some((path) => path === toolPath); } -export function toggleBookmarked(tool: DefinedTool) { - if (isBookmarked(tool)) { - unbookmark(tool); +export function toggleBookmarked(toolPath: string) { + if (isBookmarked(toolPath)) { + unbookmark(toolPath); } else { - bookmark(tool); + bookmark(toolPath); } } -function bookmark(tool: DefinedTool) { +function bookmark(toolPath: string) { localStorage.setItem( bookmarkedToolsKey, - [tool.path, ...getBookmarkedToolPaths()].join(',') + [toolPath, ...getBookmarkedToolPaths()].join(',') ); } -function unbookmark(tool: DefinedTool) { +function unbookmark(toolPath: string) { localStorage.setItem( bookmarkedToolsKey, getBookmarkedToolPaths() - .filter((path) => path !== tool.path) + .filter((path) => path !== toolPath) .join(',') ); } From 5b98dda19ba983c75a1f44ea4660e49e6f79a12e Mon Sep 17 00:00:00 2001 From: Yihao Wang Date: Mon, 14 Jul 2025 14:13:52 +1200 Subject: [PATCH 06/10] Allow unbookmarking from the card --- src/components/Hero.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 47a4066..9845b00 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -222,7 +222,22 @@ export default function Hero() { } }} > - {tool.label} + + {tool.label} + {bookmarkedToolPaths.length > 0 && ( + { + e.stopPropagation(); + const path = tool.url.substring(1); + toggleBookmarked(path); + setBookmarkedToolPaths(getBookmarkedToolPaths()); + }} + /> + )} + ))} From 911057d56075b493176f234ac2dc25d9d0c34a49 Mon Sep 17 00:00:00 2001 From: Yihao Wang Date: Mon, 14 Jul 2025 14:18:15 +1200 Subject: [PATCH 07/10] Clean up --- src/utils/bookmark.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/utils/bookmark.ts b/src/utils/bookmark.ts index a63166c..859d6e9 100644 --- a/src/utils/bookmark.ts +++ b/src/utils/bookmark.ts @@ -1,5 +1,3 @@ -import { DefinedTool } from '@tools/defineTool'; - const bookmarkedToolsKey = 'bookmarkedTools'; export function getBookmarkedToolPaths(): string[] { From afc61e6f4c03871070ce788451baf04f7b03b516 Mon Sep 17 00:00:00 2001 From: Yihao Wang Date: Mon, 14 Jul 2025 19:14:20 +1200 Subject: [PATCH 08/10] Fix typing --- src/components/Hero.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 9845b00..a05499d 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -82,9 +82,13 @@ export default function Hero() { const displayedTools = bookmarkedToolPaths.length > 0 - ? bookmarkedToolPaths - .map((path) => toolsMap.get(path)) - .filter((tools) => tools != undefined) + ? bookmarkedToolPaths.flatMap((path) => { + const tool = toolsMap.get(path); + if (tool === undefined) { + return []; + } + return [tool]; + }) : exampleTools; return ( From 1031db7ed2cb4c75882b94565afbd4b7fcec2814 Mon Sep 17 00:00:00 2001 From: "Ibrahima G. Coulibaly" Date: Tue, 15 Jul 2025 14:01:38 +0100 Subject: [PATCH 09/10] chore: refine bookmarking --- src/components/Hero.tsx | 54 +++++++++++++++++++---------------- src/components/ToolHeader.tsx | 22 ++++++++------ src/components/ToolLayout.tsx | 6 ++-- src/tools/defineTool.tsx | 2 +- 4 files changed, 47 insertions(+), 37 deletions(-) diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index a05499d..9a2ebe0 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -23,6 +23,7 @@ import { isBookmarked, toggleBookmarked } from '@utils/bookmark'; +import IconButton from '@mui/material/IconButton'; const GroupHeader = styled('div')(({ theme }) => ({ position: 'sticky', @@ -167,24 +168,27 @@ export default function Hero() { - { e.stopPropagation(); toggleBookmarked(option.path); setBookmarkedToolPaths(getBookmarkedToolPaths()); }} - color={ - isBookmarked(option.path) - ? theme.palette.primary.main - : theme.palette.grey[500] - } - icon={ - isBookmarked(option.path) - ? 'mdi:bookmark' - : 'mdi:bookmark-plus-outline' - } - /> + > + + )} @@ -194,9 +198,6 @@ export default function Hero() { } }} /> - {bookmarkedToolPaths.length > 0 && ( - Bookmarked tools: - )} {displayedTools.map((tool) => ( - - {tool.label} + + {tool.label} {bookmarkedToolPaths.length > 0 && ( - { e.stopPropagation(); const path = tool.url.substring(1); toggleBookmarked(path); setBookmarkedToolPaths(getBookmarkedToolPaths()); }} - /> + size={'small'} + > + + )} diff --git a/src/components/ToolHeader.tsx b/src/components/ToolHeader.tsx index 29e0bf0..fa24afb 100644 --- a/src/components/ToolHeader.tsx +++ b/src/components/ToolHeader.tsx @@ -8,6 +8,7 @@ import { categoriesColors } from '../config/uiConfig'; import { getToolsByCategory } from '@tools/index'; import { useEffect, useState } from 'react'; import { isBookmarked, toggleBookmarked } from '@utils/bookmark'; +import IconButton from '@mui/material/IconButton'; const StyledButton = styled(Button)(({ theme }) => ({ backgroundColor: 'white', @@ -107,19 +108,22 @@ export default function ToolHeader({ {title} - { toggleBookmarked(path); setBookmarked(!bookmarked); }} - icon={bookmarked ? 'mdi:bookmark' : 'mdi:bookmark-plus-outline'} - /> + > + + {description} diff --git a/src/components/ToolLayout.tsx b/src/components/ToolLayout.tsx index 6d843f6..1b2e50b 100644 --- a/src/components/ToolLayout.tsx +++ b/src/components/ToolLayout.tsx @@ -14,13 +14,13 @@ export default function ToolLayout({ description, icon, type, - path + fullPath }: { title: string; description: string; icon?: IconifyIcon | string; type: string; - path: string; + fullPath: string; children: ReactNode; }) { const otherCategoryTools = @@ -51,7 +51,7 @@ export default function ToolLayout({ description={description} icon={icon} type={type} - path={path} + path={fullPath} /> {children} diff --git a/src/tools/defineTool.tsx b/src/tools/defineTool.tsx index 1d0a605..6439839 100644 --- a/src/tools/defineTool.tsx +++ b/src/tools/defineTool.tsx @@ -74,7 +74,7 @@ export const defineTool = ( description={description} icon={icon} type={basePath} - path={`${basePath}/${path}`} + fullPath={`${basePath}/${path}`} > From 67e092ff1cdcf4a4d3313b6d298997f6bb25d364 Mon Sep 17 00:00:00 2001 From: "Ibrahima G. Coulibaly" Date: Tue, 15 Jul 2025 14:15:46 +0100 Subject: [PATCH 10/10] fix: translations --- src/components/Hero.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index ba2bc9d..4cae00e 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -117,7 +117,7 @@ export default function Hero() { if (tool === undefined) { return []; } - return [tool]; + return [{ ...tool, label: t(tool.label) }]; }) : exampleTools; @@ -189,9 +189,9 @@ export default function Hero() { - {option.name} + {t(option.name)} - {option.shortDescription} + {t(option.shortDescription)}