From b5b6d2eccce4de75a69b6cdb9f389b29d7b4f902 Mon Sep 17 00:00:00 2001 From: sajjad isvand Date: Tue, 30 Dec 2025 05:17:48 +0330 Subject: [PATCH 01/14] feat: add explorer view with navbar tabs and content integration --- src/layouts/explorer/explorer.tsx | 226 ++++++++++++++++++ src/layouts/navbar/navbar.layout.tsx | 67 +++++- src/pages/home.tsx | 14 +- .../hooks/content/get-content.hook.ts | 23 ++ src/services/hooks/todo/get-tags.hook.ts | 3 +- 5 files changed, 324 insertions(+), 9 deletions(-) create mode 100644 src/layouts/explorer/explorer.tsx create mode 100644 src/services/hooks/content/get-content.hook.ts diff --git a/src/layouts/explorer/explorer.tsx b/src/layouts/explorer/explorer.tsx new file mode 100644 index 00000000..2335da49 --- /dev/null +++ b/src/layouts/explorer/explorer.tsx @@ -0,0 +1,226 @@ +import { DateProvider } from '@/context/date.context' +import { useGetContents } from '@/services/hooks/content/get-content.hook' +import { useRef, useState, useEffect } from 'react' +import { NetworkLayout } from '../widgets/network/network.layout' +import { ToolsLayout } from '../widgets/tools/tools.layout' +import Analytics from '@/analytics' +import { getFaviconFromUrl } from '@/common/utils/icon' + +interface LinkItem { + name: string + url: string + icon?: string + badge?: string + badgeColor?: string +} + +interface CategoryItem { + id: string + category: string + banner?: string + links: LinkItem[] + icon?: string +} + +export function ExplorerContent() { + const { data: catalogData } = useGetContents() + const [activeCategory, setActiveCategory] = useState(null) + const categoryRefs = useRef<{ [key: string]: HTMLDivElement | null }>({}) + const scrollContainerRef = useRef(null) + + useEffect(() => { + const observerOptions = { + root: scrollContainerRef.current, + rootMargin: '-10% 0px -80% 0px', + threshold: 0, + } + + const observerCallback = (entries: IntersectionObserverEntry[]) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + setActiveCategory(entry.target.id) + } + }) + } + + const observer = new IntersectionObserver(observerCallback, observerOptions) + + Object.values(categoryRefs.current).forEach((div) => { + if (div) observer.observe(div) + }) + + return () => observer.disconnect() + }, [catalogData]) + + const scrollToCategory = (id: string) => { + categoryRefs.current[id]?.scrollIntoView({ + behavior: 'smooth', + block: 'start', + }) + Analytics.event('explorer_click_category') + } + + return ( +
+
+
+
+ {catalogData?.contents?.map((cat: CategoryItem) => ( + + ))} +
+ +
+ {/*
+
+

سوپرایزم کن

+

+ +

+ +
+
+
+ +
+
+ +
+
+
*/} + +
+ {catalogData?.contents?.map((category: CategoryItem) => ( +
{ + categoryRefs.current[category.id] = el + }} + className="relative overflow-hidden border scroll-mt-4 bg-content bg-glass border-base-300 rounded-2xl" + > + {category.banner && ( +
+ +
+ )} + +
+
+
+ {category.icon ? ( +
+ +
+ ) : ( +
+ )} + +

+ {category.category} +

+
+
+
+ + +
+
+ ))} +
+
+
+
+
+ + + + +
+
+
+
+ ) +} + +function getUrl(url: string) { + if (url.startsWith('http')) { + return url + } else { + return `https://${url}` + } +} diff --git a/src/layouts/navbar/navbar.layout.tsx b/src/layouts/navbar/navbar.layout.tsx index 5efbf80e..43805ccd 100644 --- a/src/layouts/navbar/navbar.layout.tsx +++ b/src/layouts/navbar/navbar.layout.tsx @@ -13,11 +13,71 @@ import { SyncButton } from './sync/sync' import { useAppearanceSetting } from '@/context/appearance.context' import { MarketButton } from './market/market-button' import Analytics from '@/analytics' +import { HiRectangleGroup } from 'react-icons/hi2' const WIDGETIFY_URLS = { website: 'https://widgetify.ir', } as const +const tabs = [ + { + id: 'explorer', + icon: , + hasBadge: true, + }, + { + id: 'home', + icon: , + }, +] +export function NavbarTabs() { + const [activeTab, setActiveTab] = useState('home') + + const handleTabClick = (tab: string) => { + setActiveTab(tab) + if (tab === 'home') callEvent('closeJumpPage') + else callEvent('openJumpPage') + } + + return ( +
+ {tabs.map((tab) => ( + + ))} +
+ ) +} + export function NavbarLayout(): JSX.Element { const { canReOrderWidget, toggleCanReOrderWidget } = useAppearanceSetting() const [showSettings, setShowSettings] = useState(false) @@ -124,12 +184,7 @@ export function NavbarLayout(): JSX.Element {
- + (null) const [showTour, setShowTour] = useState(false) + const [currentView, setCurrentView] = useState<'home' | 'explore'>('home') useEffect(() => { async function displayModalIfNeeded() { @@ -160,11 +162,21 @@ export function HomePage() { } ) + const openJumpPageEvent = listenEvent('openJumpPage', () => { + setCurrentView('catalog') + }) + + const closeJumpPageEvent = listenEvent('closeJumpPage', () => { + setCurrentView('home') + }) + Analytics.pageView('Home', '/') return () => { wallpaperChangedEvent() openWidgetsSettingsEvent() + openJumpPageEvent() + closeJumpPageEvent() } }, []) @@ -254,7 +266,7 @@ export function HomePage() { - + {currentView === 'home' ? : } { diff --git a/src/services/hooks/content/get-content.hook.ts b/src/services/hooks/content/get-content.hook.ts new file mode 100644 index 00000000..32ed895d --- /dev/null +++ b/src/services/hooks/content/get-content.hook.ts @@ -0,0 +1,23 @@ +import { getMainClient } from '@/services/api' +import { useQuery } from '@tanstack/react-query' + +export interface FetchedContent { + contents: { + id: string + category: string + links: { name: string; url: string; icon?: string }[] + }[] +} + +export const useGetContents = () => { + return useQuery({ + queryKey: ['contents'], + queryFn: async () => { + const api = await getMainClient() + + const { data } = await api.get('/contents/beta') + return data + }, + staleTime: 5 * 60 * 1000, // 5 minutes + }) +} diff --git a/src/services/hooks/todo/get-tags.hook.ts b/src/services/hooks/todo/get-tags.hook.ts index 229e6869..1a10c5f3 100644 --- a/src/services/hooks/todo/get-tags.hook.ts +++ b/src/services/hooks/todo/get-tags.hook.ts @@ -5,9 +5,8 @@ export const useGetTags = (enabled: boolean) => { return useQuery({ queryKey: ['getTags'], queryFn: async () => getTags(), - retry: 0, + staleTime: 5 * 60 * 1000, // 5 minutes enabled, - initialData: [], }) } export async function getTags(): Promise { From 839fb5b1847e9a3c462417747304ef7c7333eaf7 Mon Sep 17 00:00:00 2001 From: sajjad isvand Date: Tue, 30 Dec 2025 05:20:02 +0330 Subject: [PATCH 02/14] feat: update event names for explorer page navigation --- src/common/utils/call-event.ts | 4 ++-- src/layouts/navbar/navbar.layout.tsx | 5 +++-- src/pages/home.tsx | 10 +++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/common/utils/call-event.ts b/src/common/utils/call-event.ts index 506d9654..ce44c23b 100644 --- a/src/common/utils/call-event.ts +++ b/src/common/utils/call-event.ts @@ -31,8 +31,8 @@ export interface EventName { name: string template: string } - openJumpPage: null - closeJumpPage: null + openExplorerPage: null + closeExplorerPage: null // setting keys wigiPadDateSettingsChanged: WigiPadDateSetting diff --git a/src/layouts/navbar/navbar.layout.tsx b/src/layouts/navbar/navbar.layout.tsx index 43805ccd..15125310 100644 --- a/src/layouts/navbar/navbar.layout.tsx +++ b/src/layouts/navbar/navbar.layout.tsx @@ -35,8 +35,9 @@ export function NavbarTabs() { const handleTabClick = (tab: string) => { setActiveTab(tab) - if (tab === 'home') callEvent('closeJumpPage') - else callEvent('openJumpPage') + if (tab === 'home') callEvent('closeExplorerPage') + else callEvent('openExplorerPage') + Analytics.event(`navbar_tab_${tab}_click`) } return ( diff --git a/src/pages/home.tsx b/src/pages/home.tsx index e1d1228e..ab82ccb5 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -162,11 +162,11 @@ export function HomePage() { } ) - const openJumpPageEvent = listenEvent('openJumpPage', () => { - setCurrentView('catalog') + const openExplorerPageEvent = listenEvent('openExplorerPage', () => { + setCurrentView('explore') }) - const closeJumpPageEvent = listenEvent('closeJumpPage', () => { + const closeExplorerPageEvent = listenEvent('closeExplorerPage', () => { setCurrentView('home') }) @@ -175,8 +175,8 @@ export function HomePage() { return () => { wallpaperChangedEvent() openWidgetsSettingsEvent() - openJumpPageEvent() - closeJumpPageEvent() + openExplorerPageEvent() + closeExplorerPageEvent() } }, []) From 9b1c8abecfae1b1d91249d5261858157c5e651b6 Mon Sep 17 00:00:00 2001 From: sajjad isvand Date: Tue, 30 Dec 2025 05:25:34 +0330 Subject: [PATCH 03/14] feat: enhance tab navigation styling and layout --- .../widgets/tools/components/tab-navigation.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/layouts/widgets/tools/components/tab-navigation.tsx b/src/layouts/widgets/tools/components/tab-navigation.tsx index 28422ed6..3dcf36c4 100644 --- a/src/layouts/widgets/tools/components/tab-navigation.tsx +++ b/src/layouts/widgets/tools/components/tab-navigation.tsx @@ -16,18 +16,18 @@ export const TabNavigation: React.FC = ({ onTabClick, }) => { return ( -
+
{tabs.map((tab) => (
onTabClick(tab.id)} - className={`cursor-pointer rounded-xl py-0.5 px-2 transition-colors ${ + className={`flex-1 cursor-pointer text-center rounded-xl py-1.5 px-3 transition-all duration-200 ${ activeTab === tab.id - ? 'bg-primary text-gray-200' - : 'hover:bg-primary/10' + ? 'bg-primary text-white' + : 'text-content hover:bg-base-300' }`} > - {tab.label} + {tab.label}
))}
From 063f3de84c40418f030bf7f1c93ab236a9c0b45f Mon Sep 17 00:00:00 2001 From: sajjad isvand Date: Tue, 30 Dec 2025 05:32:46 +0330 Subject: [PATCH 04/14] feat: simplify commented-out layout structure and adjust banner height --- src/layouts/explorer/explorer.tsx | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/layouts/explorer/explorer.tsx b/src/layouts/explorer/explorer.tsx index 2335da49..c8183c5c 100644 --- a/src/layouts/explorer/explorer.tsx +++ b/src/layouts/explorer/explorer.tsx @@ -84,25 +84,7 @@ export function ExplorerContent() { ref={scrollContainerRef} className="flex-1 pb-4 pr-1 space-y-6 overflow-y-auto scrollbar-none scroll-smooth" > - {/*
-
-

سوپرایزم کن

-

- -

- -
-
-
- -
-
- -
-
-
*/} + {/*
*/}
{catalogData?.contents?.map((category: CategoryItem) => ( @@ -115,7 +97,7 @@ export function ExplorerContent() { className="relative overflow-hidden border scroll-mt-4 bg-content bg-glass border-base-300 rounded-2xl" > {category.banner && ( -
+
Date: Tue, 30 Dec 2025 05:45:12 +0330 Subject: [PATCH 05/14] feat: adjust tab navigation styling by adding margin and modifying padding --- src/layouts/widgets/tools/components/tab-navigation.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layouts/widgets/tools/components/tab-navigation.tsx b/src/layouts/widgets/tools/components/tab-navigation.tsx index 3dcf36c4..eb355084 100644 --- a/src/layouts/widgets/tools/components/tab-navigation.tsx +++ b/src/layouts/widgets/tools/components/tab-navigation.tsx @@ -16,12 +16,12 @@ export const TabNavigation: React.FC = ({ onTabClick, }) => { return ( -
+
{tabs.map((tab) => (
onTabClick(tab.id)} - className={`flex-1 cursor-pointer text-center rounded-xl py-1.5 px-3 transition-all duration-200 ${ + className={`flex-1 cursor-pointer text-center rounded-xl py-1 px-3 transition-all duration-200 ${ activeTab === tab.id ? 'bg-primary text-white' : 'text-content hover:bg-base-300' From dc264aabe1acc2041872e8fade647a331e651ef3 Mon Sep 17 00:00:00 2001 From: sajjad isvand Date: Tue, 30 Dec 2025 05:47:03 +0330 Subject: [PATCH 06/14] feat: implement grid layout for category items in explorer view --- src/layouts/explorer/explorer.tsx | 195 ++++++++++++++++-------------- 1 file changed, 102 insertions(+), 93 deletions(-) diff --git a/src/layouts/explorer/explorer.tsx b/src/layouts/explorer/explorer.tsx index c8183c5c..d231292e 100644 --- a/src/layouts/explorer/explorer.tsx +++ b/src/layouts/explorer/explorer.tsx @@ -82,118 +82,127 @@ export function ExplorerContent() {
- {/*
*/} - -
- {catalogData?.contents?.map((category: CategoryItem) => ( -
{ - categoryRefs.current[category.id] = el - }} - className="relative overflow-hidden border scroll-mt-4 bg-content bg-glass border-base-300 rounded-2xl" - > - {category.banner && ( -
- -
- )} + {/* تغییر اصلی: استفاده از گرید برای دسته‌بندی‌ها */} +
+ {catalogData?.contents?.map( + (category: CategoryItem, index: number) => ( +
{ + categoryRefs.current[category.id] = el + }} + /* منطق Bento: هر دسته‌بندی سوم یا دسته‌هایی با تعداد لینک زیاد، تمام عرض را می‌گیرند */ + className={`relative overflow-hidden border scroll-mt-4 bg-content bg-glass border-base-300 rounded-2xl transition-all duration-300 ${ + index % 3 === 0 + ? 'md:col-span-2' + : 'md:col-span-1' + }`} + > + {category.banner && ( +
+ +
+ )} -
) From 3bd3e83b5fa248522536a27e5ddc0459c54d9aa1 Mon Sep 17 00:00:00 2001 From: sajjad isvand Date: Tue, 30 Dec 2025 05:50:29 +0330 Subject: [PATCH 07/14] feat: update scrollToCategory logic and clean up commented code in explorer view --- src/layouts/explorer/explorer.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/layouts/explorer/explorer.tsx b/src/layouts/explorer/explorer.tsx index d231292e..2d251c71 100644 --- a/src/layouts/explorer/explorer.tsx +++ b/src/layouts/explorer/explorer.tsx @@ -53,6 +53,7 @@ export function ExplorerContent() { }, [catalogData]) const scrollToCategory = (id: string) => { + setActiveCategory(id) categoryRefs.current[id]?.scrollIntoView({ behavior: 'smooth', block: 'start', @@ -84,7 +85,6 @@ export function ExplorerContent() { ref={scrollContainerRef} className="flex-1 pb-10 pr-1 overflow-y-auto scrollbar-none scroll-smooth" > - {/* تغییر اصلی: استفاده از گرید برای دسته‌بندی‌ها */}
{catalogData?.contents?.map( (category: CategoryItem, index: number) => ( @@ -94,7 +94,6 @@ export function ExplorerContent() { ref={(el) => { categoryRefs.current[category.id] = el }} - /* منطق Bento: هر دسته‌بندی سوم یا دسته‌هایی با تعداد لینک زیاد، تمام عرض را می‌گیرند */ className={`relative overflow-hidden border scroll-mt-4 bg-content bg-glass border-base-300 rounded-2xl transition-all duration-300 ${ index % 3 === 0 ? 'md:col-span-2' @@ -136,7 +135,6 @@ export function ExplorerContent() {
- {/* تنظیم تعداد ستون لینک‌ها بر اساس عرض کارت */}
Date: Tue, 30 Dec 2025 06:00:56 +0330 Subject: [PATCH 08/14] feat: remove commented-out code for layout in explorer view --- src/layouts/explorer/explorer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layouts/explorer/explorer.tsx b/src/layouts/explorer/explorer.tsx index 2d251c71..196e085b 100644 --- a/src/layouts/explorer/explorer.tsx +++ b/src/layouts/explorer/explorer.tsx @@ -193,14 +193,14 @@ export function ExplorerContent() {
- {/*
+
-
*/} +
) From 7d8ed83a1d5ee205b12da0d06ade46b5d1608e7b Mon Sep 17 00:00:00 2001 From: sajjad isvand Date: Tue, 30 Dec 2025 22:38:16 +0330 Subject: [PATCH 09/14] feat: refactor explorer view layout and enhance category item styling --- src/layouts/explorer/explorer.tsx | 310 +++++++++++++++++------------- 1 file changed, 175 insertions(+), 135 deletions(-) diff --git a/src/layouts/explorer/explorer.tsx b/src/layouts/explorer/explorer.tsx index 196e085b..44c2f4ad 100644 --- a/src/layouts/explorer/explorer.tsx +++ b/src/layouts/explorer/explorer.tsx @@ -1,8 +1,5 @@ -import { DateProvider } from '@/context/date.context' import { useGetContents } from '@/services/hooks/content/get-content.hook' import { useRef, useState, useEffect } from 'react' -import { NetworkLayout } from '../widgets/network/network.layout' -import { ToolsLayout } from '../widgets/tools/tools.layout' import Analytics from '@/analytics' import { getFaviconFromUrl } from '@/common/utils/icon' @@ -44,11 +41,9 @@ export function ExplorerContent() { } const observer = new IntersectionObserver(observerCallback, observerOptions) - Object.values(categoryRefs.current).forEach((div) => { if (div) observer.observe(div) }) - return () => observer.disconnect() }, [catalogData]) @@ -62,154 +57,199 @@ export function ExplorerContent() { } return ( -
-
-
-
- {catalogData?.contents?.map((cat: CategoryItem) => ( - - ))} -
+ -
-
- {catalogData?.contents?.map( - (category: CategoryItem, index: number) => ( -
{ - categoryRefs.current[category.id] = el - }} - className={`relative overflow-hidden border scroll-mt-4 bg-content bg-glass border-base-300 rounded-2xl transition-all duration-300 ${ - index % 3 === 0 - ? 'md:col-span-2' - : 'md:col-span-1' - }`} - > - {category.banner && ( -
- -
- )} - -
-
-
- {category.icon ? ( - - ) : ( -
- )} -

- {category.category} -

+
+ {cat.category} +
+
+ + ))} +
+ + +
+
+ {catalogData?.contents?.map((cat: CategoryItem) => ( + + ))} +
+ +
+
+
+
+ {catalogData?.contents?.map( + (category: CategoryItem, index: number) => ( +
- -
-
- - - - -
-
) } function getUrl(url: string) { - if (url.startsWith('http')) { - return url - } else { - return `https://${url}` - } + return url.startsWith('http') ? url : `https://${url}` } From 9f70780b2e7b30cf2f48d385e9e8c997916ae04b Mon Sep 17 00:00:00 2001 From: sajjad isvand Date: Tue, 30 Dec 2025 22:57:40 +0330 Subject: [PATCH 10/14] feat: add feedback section with link and icon in explorer view --- src/layouts/explorer/explorer.tsx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/layouts/explorer/explorer.tsx b/src/layouts/explorer/explorer.tsx index 44c2f4ad..6a0d3b68 100644 --- a/src/layouts/explorer/explorer.tsx +++ b/src/layouts/explorer/explorer.tsx @@ -2,6 +2,7 @@ import { useGetContents } from '@/services/hooks/content/get-content.hook' import { useRef, useState, useEffect } from 'react' import Analytics from '@/analytics' import { getFaviconFromUrl } from '@/common/utils/icon' +import { HiOutlineLightBulb } from 'react-icons/hi2' interface LinkItem { name: string @@ -244,6 +245,33 @@ export function ExplorerContent() {
+
From a8aad46e6bb4c982297825f3ed19902c06e9e220 Mon Sep 17 00:00:00 2001 From: sajjad isvand Date: Tue, 30 Dec 2025 23:11:46 +0330 Subject: [PATCH 11/14] feat: remove brightness effect from link icon in explorer view --- src/layouts/explorer/explorer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/explorer/explorer.tsx b/src/layouts/explorer/explorer.tsx index 6a0d3b68..cf63eb12 100644 --- a/src/layouts/explorer/explorer.tsx +++ b/src/layouts/explorer/explorer.tsx @@ -228,7 +228,7 @@ export function ExplorerContent() { link.url ) } - className="object-contain w-6 h-6 transition-transform rounded group-hover/item:scale-110 group-hover/item:brightness-125" + className="object-contain w-6 h-6 transition-transform rounded group-hover/item:scale-110" alt={link.name} />
From f0808bacdfd933c209927b0203449b7baf845915 Mon Sep 17 00:00:00 2001 From: sajjad isvand Date: Tue, 30 Dec 2025 23:27:37 +0330 Subject: [PATCH 12/14] feat: add overflow handling to content section in explorer view --- src/pages/home/content-section.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/content-section.tsx b/src/pages/home/content-section.tsx index 210a17e1..d0826089 100644 --- a/src/pages/home/content-section.tsx +++ b/src/pages/home/content-section.tsx @@ -119,7 +119,7 @@ export function ContentSection() {
From 63e52102662d79c5f2ff5a5ba75322aeb05b3999 Mon Sep 17 00:00:00 2001 From: sajjad isvand Date: Tue, 30 Dec 2025 23:27:47 +0330 Subject: [PATCH 13/14] feat: update background color in TabNavigation component --- src/layouts/widgets/tools/components/tab-navigation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/widgets/tools/components/tab-navigation.tsx b/src/layouts/widgets/tools/components/tab-navigation.tsx index eb355084..0588ebd7 100644 --- a/src/layouts/widgets/tools/components/tab-navigation.tsx +++ b/src/layouts/widgets/tools/components/tab-navigation.tsx @@ -16,7 +16,7 @@ export const TabNavigation: React.FC = ({ onTabClick, }) => { return ( -
+
{tabs.map((tab) => (
Date: Tue, 30 Dec 2025 23:28:23 +0330 Subject: [PATCH 14/14] feat: update background styling in WidgetContainer component --- src/layouts/widgets/widget-container.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/widgets/widget-container.tsx b/src/layouts/widgets/widget-container.tsx index b84d81bf..473850f8 100644 --- a/src/layouts/widgets/widget-container.tsx +++ b/src/layouts/widgets/widget-container.tsx @@ -12,7 +12,7 @@ export function WidgetContainer({ }: WidgetContainerProps) { return (
{children}